JavaScript 基础反问

时常查漏补缺,巩固理论知识,无论在哪种环境下,都是很有必要的。下面是对自己javascript 基础的一些反问,先整理了100条,会持续更新,保持学习,共勉~

1. 创建对象的可能方法有哪些

在 javascript 中创建对象的方法有很多种,如下所述:

  1. 对象字面量语法:

    对象字面量语法(或对象初始值设定项)是用大括号括起来的一组以逗号分隔的名称-值对。对象字面量属性值可以是任何数据类型,包括数组、函数和嵌套对象。

    javascript 复制代码
    var object = {
         name: "Sudheer",
         age: 34
    };
  2. 对象构造函数:

    创建空对象的最简单方法是使用"Object"构造函数。目前不推荐这种方法。

    javascript 复制代码
    var object = new Object();

    Object() 是一个内置的构造函数,因此不需要"new"关键字。 上面的代码片段可以重写为:

    javascript 复制代码
    var object = Object();
  3. Object 的 create 方法:

    Object 的 create 方法用于通过传递指定的原型对象和属性作为参数来创建新对象,即这种模式有助于基于现有对象创建新对象。第二个参数是可选的,它用于在新创建的对象上创建属性。

    以下代码创建一个新的空对象,其原型为 null。

    javascript 复制代码
    var object = Object.create(null);
  4. 函数构造函数:

    在此方法中,创建任意函数并应用 new 运算符来创建对象实例。

    javascript 复制代码
    function Person(name) {
      this.name = name;
      this.age = 21;
    }
    var object = new Person("Sudheer");
  5. 带原型的函数构造函数:

    这类似于函数构造函数,但它使用原型作为其属性和方法。

    javascript 复制代码
    function Person() {}
    Person.prototype.name = "Sudheer";
    var object = new Person();

    这相当于使用带有函数原型的 Object.create 方法创建一个实例,然后使用实例和参数作为参数调用该函数。

    javascript 复制代码
    function func() {}
    
    new func(x, y, z);

    (或者)

    javascript 复制代码
    // 使用函数原型创建一个新实例。
    var newInstance = Object.create(func.prototype)
    
    // 使用call调用函数
    var result = func.call(newInstance, x, y, z),
    
    // 如果结果是非空对象,则使用它,否则仅使用新实例。
    console.log(result && typeof result === 'object' ? result : newInstance);
  6. ES6 类语法:

    ES6引入了类特性来创建对象。

    javascript 复制代码
    class Person {
      constructor(name) {
        this.name = name;
      }
    }
    
    var object = new Person("Sudheer");
  7. 单例模式:

    Singleton 是一种只能实例化一次的对象。 重复调用其构造函数会返回相同的实例。 这样,人们就可以确保他们不会意外创建多个实例。

    javascript 复制代码
    var object = new (function () {
      this.name = "Sudheer";
    })();

2. 什么是原型链

原型链用于基于现有对象构建新类型的对象。 它类似于基于类的语言中的继承。

对象实例上的原型可通过 Object.getPrototypeOf(object)proto 属性获得,而构造函数上的原型可通过 Object.prototype 获得。

3. call, apply 和 bind

call: call() 方法使用给定的 this 值和一一提供的参数来调用函数

javascript 复制代码
var employee1 = { firstName: "John", lastName: "Rodson" };
var employee2 = { firstName: "Jimmy", lastName: "Baily" };

function invite(greeting1, greeting2) {
  console.log(
    greeting1 + " " + this.firstName + " " + this.lastName + ", " + greeting2
  );
}

invite.call(employee1, "Hello", "How are you?"); // Hello John Rodson, How are you?
invite.call(employee2, "Hello", "How are you?"); // Hello Jimmy Baily, How are you?

apply: 使用给定的"this"值调用函数,并允许你以数组形式传入参数

javascript 复制代码
var employee1 = { firstName: "John", lastName: "Rodson" };
var employee2 = { firstName: "Jimmy", lastName: "Baily" };

function invite(greeting1, greeting2) {
  console.log(
    greeting1 + " " + this.firstName + " " + this.lastName + ", " + greeting2
  );
}

invite.apply(employee1, ["Hello", "How are you?"]); // Hello John Rodson, How are you?
invite.apply(employee2, ["Hello", "How are you?"]); // Hello Jimmy Baily, How are you?

bind: 返回一个新函数,允许你传递任意数量的参数

javascript 复制代码
   var employee1 = { firstName: "John", lastName: "Rodson" };
   var employee2 = { firstName: "Jimmy", lastName: "Baily" };

   function invite(greeting1, greeting2) {
     console.log(
       greeting1 + " " + this.firstName + " " + this.lastName + ", " + greeting2
     );
   }

   var inviteEmployee1 = invite.bind(employee1);
   var inviteEmployee2 = invite.bind(employee2);
   inviteEmployee1("Hello", "How are you?"); // Hello John Rodson, How are you?
   inviteEmployee2("Hello", "How are you?"); // Hello Jimmy Baily, How are you?

call和apply几乎可以互换。 两者都立即执行当前函数。Call 是用于逗号 (分隔列表),Apply 是用于 Array

bind 创建一个新函数,将"this"设置为传递给 bind() 的第一个参数。

4. 如何比较 Object 和 Map

1. 数据结构:

Object 是 JavaScript 中的基本数据结构,它使用大括号 {} 来表示。键值对以键值对形式存储在对象中,键是字符串或符号,值可以是任意数据类型。

javascript 复制代码
const obj = {
  key1: 'value1',
  key2: 'value2',
};

Map 是 ES6 引入的新数据结构,它使用 Map 构造函数来创建。与 Object 不同,Map 的键可以是任意数据类型,包括对象、函数和其他原始类型数据。

javascript 复制代码
const map = new Map();
map.set('key1', 'value1');
map.set('key2', 'value2');

2. 键的顺序性:

Object 的键是无序的,无法保证键值对的遍历顺序与定义时的顺序一致。

Map 保留键的插入顺序,可以按照插入顺序进行迭代和遍历。

javascript 复制代码
const map = new Map();
map.set('key1', 'value1');
map.set('key2', 'value2');

for (let [key, value] of map) {
  console.log(key, value);
}
// 输出:
// key1 value1
// key2 value2

3. 性能:

在大型数据集上进行查找和插入操作时,Map 的性能通常比 Object 更好。Map 使用了更高效的哈希算法来实现键值对的存储和检索。

4. 功能扩展性:

Object 在 JavaScript 中广泛使用,并且具有丰富的内置方法和功能。你可以使用 Object.keys()Object.values()Object.entries() 等方法来操作对象。

Map 提供了一些额外的方法,如 set()get()delete()has() 等,用于操作键值对。它还提供了 size 属性来获取键值对的数量。

javascript 复制代码
const map = new Map();
map.set('key1', 'value1');
map.set('key2', 'value2');

console.log(map.size); // 输出:2
console.log(map.get('key1')); // 输出:value1

5. 什么是一等函数

在 Javascript 中,函数是第一类对象。 一等函数意味着该语言中的函数被像任何其他变量一样对待。

例如,函数可以作为参数传递给其他函数,可以由另一个函数返回,并且可以作为值分配给变量。 例如,在下面的示例中,分配给侦听器的处理程序函数

javascript 复制代码
const handler = () => console.log("This is a click handler function");
document.addEventListener("click", handler);

6. 什么是一阶函数

一阶函数是不接受另一个函数作为参数并且不返回函数作为其返回值的函数。

ini 复制代码
const firstOrder = () => console.log("I am a first order function!");

7. 什么是高阶函数

高阶函数是接受另一个函数作为参数或返回一个函数作为返回值或两者兼而有之的函数。

javascript 复制代码
const firstOrderFunc = () =>
  console.log("Hello, I am a First order function");
const higherOrder = (ReturnFirstOrderFunc) => ReturnFirstOrderFunc();
higherOrder(firstOrderFunc);

8. 什么是一元函数

一元函数(即一元函数)是一种只接受一个参数的函数。 它代表函数接受的单个参数。

让我们举一个一元函数的例子,

js 复制代码
const unaryFunction = (a) => console.log(a + 10); 

9. 什么是柯里化函数

柯里化是采用具有多个参数的函数并将其转换为每个仅具有单个参数的函数序列的过程。 柯里化 (Currying) 以数学家哈斯克尔·柯里 (Haskell Curry) 的名字命名。 通过应用柯里化,n 元函数变成一元函数。

让我们举一个 n 元函数的例子以及它如何变成柯里化函数,

javascript 复制代码
const multiArgFunction = (a, b, c) => a + b + c;
console.log(multiArgFunction(1, 2, 3)); // 6

const curryUnaryFunction = (a) => (b) => (c) => a + b + c;
curryUnaryFunction(1); // returns a function: b => c =>  1 + b + c
curryUnaryFunction(1)(2); // returns a function: c => 3 + c
curryUnaryFunction(1)(2)(3); // returns the number 6

柯里化函数对于提高代码可重用性和函数组合非常有用。

10. 什么是纯函数

纯函数是一种返回值仅由其参数确定的函数,没有任何副作用。 即如果你在应用程序中调用具有相同参数"n"次,那么它将始终返回相同的值。

我们举个例子来看看纯函数和非纯函数的区别,

javascript 复制代码
// 非纯函数
let numberArray = [];
const impureAddNumber = (number) => numberArray.push(number);
// 纯函数
const pureAddNumber = (number) => (argNumberArray) =>
  argNumberArray.concat([number]);

// 展示结果
console.log(impureAddNumber(6)); // returns 1
console.log(numberArray); // returns [6]
console.log(pureAddNumber(7)(numberArray)); // returns [6, 7]
console.log(numberArray); // returns [6]

11. let 关键字的用途是什么

let 语句声明一个块作用域局部变量。 因此,使用 let 关键字定义的变量的范围仅限于使用它的块、语句或表达式。 而使用 var 关键字声明的变量用于全局定义变量,或在整个函数中局部定义变量,而不管块作用域如何。

我们举个例子来演示一下用法,

javascript 复制代码
let counter = 30;
if (counter === 30) {
  let counter = 31;
  console.log(counter); // 31
}
console.log(counter); // 30 (因为 if 块中的变量在这里不存在)

12. let 和 var 有什么区别

var let
它从 JavaScript 诞生之初就已经可用 作为 ES6 的一部分引入
函数作用域 块作用域
变量将被提升 已提升但未初始化

让我们举个例子来看看区别,

javascript 复制代码
function userDetails(username) {
 if (username) {
   console.log(salary); // undefined, 声明提升var salary,
   console.log(age); // ReferenceError,初始化前无法访问age
   let age = 30;
   var salary = 10000;
 }
 console.log(salary); //10000, 函数作用域
 console.log(age); //error,age 未定义,块作用域
}
userDetails("John");

13. 在 switch 块中重新声明变量

如果你尝试在"switch block"中重新声明变量,则会导致错误,因为只有一个块。 例如,下面的代码块会抛出语法错误,如下所示:

javascript 复制代码
let counter = 1;
switch (x) {
  case 0:
    let name;
    break;

  case 1:
    let name; // SyntaxError,重新声明的语法错误
    break;
}

为了避免此错误,你可以在 case 子句中创建嵌套块并创建新的块作用域词法环境。

javascript 复制代码
let counter = 1;
switch (x) {
  case 0: {
    let name;
    break;
  }
  case 1: {
    let name; // 重新声明不会出现语法错误。
    break;
  }
}

14. 什么是"暂时性死区"

"暂时性死区"是 JavaScript 中的一种行为,当使用 let 和 const 关键字声明变量时会发生这种情况,但使用 var 则不会。 在 ECMAScript 6 中,在声明之前(在其范围内)访问"let"或"const"变量会导致引用错误。 发生这种情况的时间跨度,即变量绑定的创建和声明之间,称为暂时性死区。

让我们通过一个例子来看看这个行为,

javascript 复制代码
function somemethod() {
  console.log(counter1); // undefined
  console.log(counter2); // ReferenceError
  var counter1 = 1;
  let counter2 = 2;
}

15. 什么是 IIFE

IIFE(立即调用函数表达式)是一个 JavaScript 函数,一旦定义就立即运行。 它的签名如下,

javascript 复制代码
(function () {
 // logic here
})();

使用 IIFE 的主要原因是获得数据隐私,因为外界无法访问 IIFE 中声明的任何变量。 即,如果你尝试从 IIFE 访问变量,则会抛出如下错误,

javascript 复制代码
(function () {
  var message = "IIFE";
  console.log(message);
})();
console.log(message); //Error: message is not defined

17. 什么是记忆化

记忆化是一种函数式编程技术,它试图通过缓存先前计算的结果来提高函数的性能。 每次调用记忆函数时,都会使用其参数来索引缓存。 如果数据存在,则可以返回它,而无需执行整个函数。 否则,执行该函数,然后将结果添加到缓存中。

让我们以添加带有记忆功能的功能为例,

javascript 复制代码
const memoizAddition = () => {
  let cache = {};
  return (value) => {
    if (value in cache) {
      console.log("Fetching from cache");
      return cache[value]; // Here, cache.value cannot be used as property name starts with the number which is not a valid JavaScript  identifier. Hence, can only be accessed using the square bracket notation.
    } else {
      console.log("Calculating result");
      let result = value + 20;
      cache[value] = result;
      return result;
    }
  };
};
// 从 memoizAddition 返回的函数
const addition = memoizAddition();
console.log(addition(20)); // 输出:计算出40
console.log(addition(20)); // 输出:40缓存

18. 什么是提升

提升是一种 JavaScript 机制,其中变量、函数声明和类在代码执行之前被移动到其作用域的顶部。 请记住,JavaScript 仅提升声明,而不提升初始化。

让我们举一个变量提升的简单例子,

javascript 复制代码
console.log(message); // 输出 : undefined
var message = "The variable Has been hoisted";

上面的代码对于解释器来说如下所示:

javascript 复制代码
var message;
console.log(message);
message = "The variable Has been hoisted";

以同样的方式,函数声明也被提升

javascript 复制代码
message("Good morning"); //Good morning

function message(name) {
  console.log(name);
}

注意:

如果函数声明是通过变量赋值的方式进行的,那么在变量声明之前是无法访问该函数的。这是因为在 JavaScript 中,变量提升将变量声明提升到作用域的顶部,但不会提升变量的赋值操作。

javascript 复制代码
foo(); // TypeError: foo is not a function

var foo = function() {
  console.log("Hello, world!");
};

19. ES6 中的类是什么

在 ES6 中,Javascript 类主要是 JavaScript 现有的基于原型的继承的语法糖。

例如,基于原型的继承写成函数表达式如下:

javascript 复制代码
function Bike(model, color) {
 this.model = model;
 this.color = color;
}

Bike.prototype.getDetails = function () {
 return this.model + " bike has" + this.color + " color";
};

而 ES6 类可以定义为替代方案

javascript 复制代码
class Bike {
  constructor(color, model) {
    this.color = color;
    this.model = model;
  }

  getDetails() {
    return this.model + " bike has" + this.color + " color";
  }
}

20. 什么是闭包

闭包是函数和声明该函数的词法环境的组合。 即,它是一个内部函数,可以访问外部或封闭函数的变量。 闭包具有三个作用域链

  1. 自己的作用域,其中变量在大括号之间定义
  2. 外部函数的变量
  3. 全局变量
javascript 复制代码
function Welcome(name) {
  var greetingInfo = function (message) {
    console.log(message + " " + name);
  };
  return greetingInfo;
}
var myFunction = Welcome("John");
myFunction("Welcome "); //Output: Welcome John
myFunction("Hello Mr."); //output: Hello Mr.John

根据上面的代码,即使外部函数返回后,内部函数(即greetingInfo)也可以访问外部函数作用域(即Welcome)中的变量。

21. 什么是模块

模块是指独立的、可重用的代码的小单元,也是许多 JavaScript 设计模式的基础。 大多数 JavaScript 模块导出对象字面量、函数或构造函数

22. 作用域是什么

作用域是运行时代码某些特定部分中变量、函数和对象的可访问性。 换句话说,范围决定了代码区域中变量和其他资源的可见性。

23. 什么是 Service Worker

Service Worker 基本上是一个在后台运行的脚本(JavaScript 文件),与网页分开,并提供不需要网页或用户交互的功能。 Service Worker 的一些主要功能包括丰富的离线体验(离线第一个 Web 应用程序开发)、定期后台同步、推送通知、拦截和处理网络请求以及以编程方式管理响应缓存。

24. 使用 Service Worker 操作 DOM

Service Worker 无法直接访问 DOM。 但它可以通过响应通过"postMessage"接口发送的消息来与其控制的页面进行通信,并且这些页面可以操作 DOM。

25. 在 Service Worker 重启过程中重用信息

Service Worker 的问题在于,它会在不使用时终止,并在下次需要时重新启动,因此你不能依赖 Service Worker 的"onfetch"和"onmessage"处理程序中的全局状态。 在这种情况下,服务工作人员将有权访问 IndexedDB API,以便在重新启动时保持和重用。

26. 什么是 IndexedDB

IndexedDB 是一种低级 API,用于客户端存储大量结构化数据(包括文件/blob)。 此 API 使用索引来实现对此数据的高性能搜索。

27. 什么是post message

Post message 消息是一种支持 Window 对象之间跨域通信的方法。(即,页面与其生成的弹出窗口之间,或者页面与嵌入其中的 iframe 之间)。 一般来说,当且仅当页面遵循同源策略(即页面共享相同的协议、端口号和主机)时,不同页面上的脚本才允许相互访问。

Cookie 是存储在你的计算机上供浏览器访问的一段数据。 Cookie 以键/值对的形式保存。 例如,你可以创建一个名为 username 的 cookie,如下所示:

javascript 复制代码
document.cookie = "username=John";

Cookie 用于记住有关用户个人资料的信息(例如用户名)。 它基本上涉及两个步骤,

  1. 当用户访问网页时,用户个人资料可以存储在cookie中。
  2. 下次用户访问该页面时,cookie 会记住用户配置文件。

以下几个选项可用于 cookie,

  1. 默认情况下,当浏览器关闭时,cookie 将被删除,但你可以通过设置到期日期(以 UTC 时间表示)来更改此行为。
javascript 复制代码
document.cookie = "username=John; expires=Sat, 8 Jun 2019 12:00:00 UTC";
  1. 默认情况下,cookie属于当前页面。 但是你可以使用路径参数告诉浏览器该 cookie 属于哪个路径。
javascript 复制代码
document.cookie = "username=John; path=/services";

你可以通过将到期日期设置为已过日期来删除 cookie。 在这种情况下,你不需要指定 cookie 值。 例如,你可以删除当前页面中的用户名 cookie,如下所示。

javascript 复制代码
document.cookie =
  "username=; expires=Fri, 07 Jun 2019 00:00:00 UTC; path=/;";

注意: 你应该定义 cookie 路径选项以确保删除正确的 cookie。 除非你指定路径参数,否则某些浏览器不允许删除 cookie。

32. cookie、本地存储和会话存储

下面是cookie、本地存储和会话存储之间的一些区别,

特征 Cookie Local storage Session storage
在客户端或服务器端访问 服务器端和客户端 仅客户端 仅客户端
终身 使用 Expires 选项配置 直到删除 直到选项卡关闭
SSL 支持 支持 不支持 不支持
最大数据大小 4KB 5 MB 5MB

33. 什么是存储事件及其事件柄

StorageEvent 是当另一个文档的上下文中的存储区域发生更改时触发的事件。 而 onstorage 属性是一个用于处理存储事件的 EventHandler 。

javascript 复制代码
window.onstorage = functionRef;

让我们以 onstorage 事件处理程序为例,该处理程序记录存储键及其值

javascript 复制代码
window.onstorage = function (e) {
   console.log(
     "The " +
       e.key +
       " key has been changed from " +
       e.oldValue +
       " to " +
       e.newValue +
       "."
   );
};

34. 为什么需要网络存储

Web存储更加安全,可以在本地存储大量数据,而不影响网站性能。 此外,信息永远不会传输到服务器。 因此,这是比 Cookie 更值得推荐的方法。

35. 如何检查网络存储浏览器支持

在使用网络存储之前,你需要检查浏览器对 localStorage 和 sessionStorage 的支持,

javascript 复制代码
if (typeof Storage !== "undefined") {
  // Code for localStorage/sessionStorage.
} else {
  // Sorry! No Web Storage support..
}

36. 检查 Web Workers 浏览器支持

使用前需要检查浏览器对网络工作者的支持

javascript 复制代码
if (typeof Worker !== "undefined") {
  // code for Web worker support.
} else {
  // Sorry! No Web Worker support..
}

37. 举一个web worker的例子

你需要按照以下步骤开始使用web workers进行计数示例

  1. 创建一个Web Worker 文件:你需要编写一个脚本来增加计数值。 我们将其命名为 counter.js
javascript 复制代码
    let i = 0;

    function timedCount() {
      i = i + 1;
      postMessage(i);
      setTimeout("timedCount()", 500);
    }

    timedCount();

这里 postMessage() 方法用于将消息发送回 HTML 页面

  1. 创建 Web Worker 对象:你可以通过检查浏览器支持来创建 Web Worker 对象。 我们将此文件命名为 web_worker_example.js
javascript 复制代码
if (typeof w == "undefined") {
  w = new Worker("counter.js");
}

我们可以接收来自web worker的消息

javascript 复制代码
w.onmessage = function (event) {
  document.getElementById("message").innerHTML = event.data;
};
  • 终止 Web Worker: Web Worker 将继续侦听消息(即使在外部脚本完成之后),直到其终止。 你可以使用terminate()方法来终止监听消息。

    javascript 复制代码
    w.terminate();
  • 复用Web Worker:如果将worker变量设置为undefined,则可以复用代码

    javascript 复制代码
    let w = new Worker("counter.js");
    
    // 使用 worker 进行一些操作...
    
    // 当操作完成后,将 worker 变量设置为 undefined
    w = undefined;
    
    // 在之后的代码中可以重新使用 worker 变量
    w = new Worker("anotherWorker.js");

38. Web Workers对DOM的限制

WebWorkers 无法访问以下 javascript 对象,因为它们是在外部文件中定义的

  1. Window object
  2. Document object
  3. Parent object

39. promise是什么

Promise 是一个对象,它可能会在未来某个时间生成单个值,并带有已解决的值或未解决的原因(例如,网络错误)。 它将处于 3 种可能状态之一:fulfilled, rejected, or pending。

Promise 创建的语法如下所示,

javascript 复制代码
const promise = new Promise(function (resolve, reject) {
  // promise description
});

Promise 的用法如下,

javascript 复制代码
const promise = new Promise(
  (resolve) => {
    setTimeout(() => {
      resolve("I'm a Promise!");
    }, 5000);
  },
  (reject) => {}
);

promise.then((value) => console.log(value));

Promise 的操作流程如下:

40. 什么是服务器推送事件

服务器推送事件 (SSE) 是一种服务器推送技术,使浏览器能够通过 HTTP 连接从服务器接收自动更新,而无需诉诸轮询。 这是一种单向通信通道 - 事件仅从服务器流向客户端。 这已用于 Facebook/Twitter 更新、股票价格更新、新闻提要等。

下面是一个使用 SSE 的简单示例,展示了服务器端和客户端的代码:

服务器端代码(Node.js):

javascript 复制代码
const http = require('http');

// 创建 HTTP 服务器
const server = http.createServer((req, res) => {
  // 设置响应头,指定内容类型为 text/event-stream
  res.setHeader('Content-Type', 'text/event-stream');
  res.setHeader('Cache-Control', 'no-cache');
  res.setHeader('Connection', 'keep-alive');

  // 发送初始的事件数据
  res.write('data: Initial data\n\n');

  // 定期发送事件数据
  setInterval(() => {
    const eventData = `data: ${new Date().toISOString()}\n\n`;
    res.write(eventData);
  }, 1000);
});

// 监听端口
server.listen(3000, () => {
  console.log('Server is listening on port 3000');
});

在上述服务器端代码中,我们首先创建了一个 HTTP 服务器。当客户端发起请求时,服务器会设置响应头,包括 Content-Type: text/event-stream,指示服务器将发送 SSE 数据流。然后,服务器发送初始的事件数据,之后每隔一秒发送一个事件数据(当前时间)。

客户端代码(HTML + JavaScript):

html 复制代码
<!DOCTYPE html>
<html>
<head>
  <title>SSE Example</title>
</head>
<body>
  <div id="event-data"></div>

  <script>
    const eventSource = new EventSource('/sse');

    eventSource.onmessage = function(event) {
      const eventData = event.data;
      const eventDataElement = document.getElementById('event-data');
      eventDataElement.innerText += eventData + '\n';
    };
  </script>
</body>
</html>

在客户端代码中,我们创建了一个 EventSource 对象,并指定 SSE 的 URL(这里假设为 /sse)。然后,我们监听 onmessage 事件,当接收到服务器发送的事件数据时,将其追加到页面中的 event-data 元素中。

通过运行上述服务器端代码,并在浏览器中打开客户端代码,你将看到页面上不断显示来自服务器的事件数据,每秒更新一次。

41. promise的主要规则是什么

  1. promise 是一个提供符合标准的 .then() 方法的对象
  2. pending状态的promise可能会转变为fulfilled或rejected状态
  3. fulfilled或rejected状态的promise是已完成状态,并且不得转变为任何其他状态。
  4. 一旦promise被完成,其价值就不能改变。

42. null与undefined的区别是什么

Null Undefined
它是一个赋值值,表示变量不指向任何对象。 这是一个未赋值的变量声明,表示变量已经被声明但尚未被赋予一个值。
null 的类型是 object undefined 的类型是undefined
null 值是一个原始值,表示 null、空或不存在的引用。 undefined是当变量尚未赋值时使用的原始值。
指示变量缺少值 表示变量本身不存在
执行原始操作时转换为零 (0) 执行原始操作时转换为 NaN

43. window与document有什么区别

Window Document
它是任何网页中的根级元素 它是窗口对象的直接子对象。 这也称为文档对象模型 (DOM)
默认情况下,窗口对象在页面中隐式可用 你可以通过 window.document 或 document 访问它。
它具有alert()、confirm() 等方法以及文档、位置等属性。 它提供了 getElementById、getElementsByTagName、createElement 等方法

44. 如何检测大写锁定键是否打开

mouseEvent getModifierState() 用于返回一个布尔值,指示指定的修饰键是否被激活。 CapsLock、ScrollLock 和 NumLock 等修饰键在单击时激活,再次单击时停用。

让我们以一个输入元素来检测 CapsLock 开/关行为为例,

html 复制代码
<input type="password" onmousedown="enterInput(event)" />

<p id="feedback"></p>

<script>
  function enterInput(e) {
    var flag = e.getModifierState("CapsLock");
    if (flag) {
      document.getElementById("feedback").innerHTML = "CapsLock activated";
    } else {
      document.getElementById("feedback").innerHTML =
        "CapsLock not activated";
    }
  }
</script>

45. 什么是事件流

事件流是指网页上接收事件的顺序。 当你单击嵌套在各种其他元素中的元素时,在你的单击实际到达其目的地或目标元素之前,它必须首先触发其每个父元素的单击事件,从顶部的全局窗口对象开始。 事件流有两种方式

从上到下(事件捕获) 从下到上(事件冒泡)

46. 如何查找操作系统详细信息

window.navigator 对象包含有关访问者浏览器操作系统详细信息的信息。 一些操作系统属性可在平台属性下找到,

javascript 复制代码
console.log(navigator.platform);

47. document load和 DOMContentLoaded

当初始 HTML 文档完全加载和解析后,将触发 DOMContentLoaded 事件,无需等待资源(样式表、图像和子框架)完成加载。 而 load 事件在整个页面加载后触发,包括所有依赖资源(样式表、图像)。

48. 本机对象、宿主对象和用户对象有什么区别

本机对象是 ECMAScript 规范定义的 JavaScript 语言一部分的对象。 例如,ECMAScript 规范中定义的 String、Math、RegExp、Object、Function 等核心对象。 宿主对象是浏览器或运行时环境(Node)提供的对象。 例如,窗口、XmlHttpRequest、DOM 节点等都被视为宿主对象。 用户对象是在 javascript 代码中定义的对象。 例如,为配置文件信息创建的用户对象。

49. Promise 相对于回调有哪些优缺点

优点:

  • 它避免了不可读的回调地狱
  • 使用 .then() 轻松编写顺序异步代码
  • 使用 Promise.all() 轻松编写并行异步代码
  • 解决回调的一些常见问题(回调调用太晚、太早、多次以及吞掉错误/异常)

缺点:

  • 它会使代码变得复杂一些。
  • 如果不支持 ES6,则需要加载一个 polyfill

50. 属性(Attribute)和属性(Property)

Property是在 HTML 标记上定义的,而Attribute是在 DOM 上定义的。例如,下面的 HTML 元素有两个属性:type 和 value。

javascript 复制代码
<input type="text" value="Name:">

你可以按如下方式检索属性值,

javascript 复制代码
const input = document.querySelector("input");
console.log(input.getAttribute("value")); // Good morning
console.log(input.value); // Good morning

将文本字段的值更改为"晚上好"后,它会变成这样

javascript 复制代码
console.log(input.getAttribute("value")); // Good evening
console.log(input.value); // Good evening

51. void 0 的目的是什么

void(0)用于防止页面刷新。 这将有助于消除不需要的副作用,因为它将返回未定义的原始值。 它通常用于使用 href="JavaScript:Void(0);" 的 HTML 文档。 在<a> 元素内。 即,当你单击链接时,浏览器会加载新页面或刷新同一页面。 但使用此表达式可以防止这种行为。 例如,下面的链接通知消息而不重新加载页面。

javascript 复制代码
<a href="JavaScript:void(0);" onclick="alert('Well done!')">
  Click Me!
</a>

52. 谁创建了 JavaScript

JavaScript 是由 Brendan Eich 于 1995 年在 Netscape Communications 工作期间创建的。 最初它以 Mocha 的名称开发,但后来当它首次在 Netscape 测试版中发布时,该语言被正式称为 LiveScript。

53. PreventDefault 方法有什么用

如果事件是可取消的,则 PreventDefault() 方法会取消该事件,这意味着属于该事件的默认操作或行为将不会发生。 例如,在单击提交按钮时阻止表单提交以及在单击超链接时阻止打开页面 URL 是一些常见的用例。

javascript 复制代码
document
  .getElementById("link")
  .addEventListener("click", function (event) {
    event.preventDefault();
  });

54. stopPropagation方法有什么用

stopPropagation 方法用于阻止事件在事件链中向上冒泡。 例如,下面带有 stopPropagation 方法的嵌套 div 可防止单击嵌套 div(Div1) 时的默认事件传播

javascript 复制代码
<p>Click DIV1 Element</p>
<div onclick="secondFunc()">DIV 2
  <div onclick="firstFunc(event)">DIV 1</div>
</div>

<script>
function firstFunc(event) {
  alert("DIV 1");
  event.stopPropagation();
}

function secondFunc() {
  alert("DIV 2");
}
</script>

55. return false使用涉及哪些步骤

事件处理程序中的 return false 语句执行以下步骤,

  • 首先,它会停止浏览器的默认操作或行为。
  • 它阻止事件传播 DOM
  • 停止回调执行并在调用时立即返回。

56. 什么是BOM

浏览器对象模型 (BOM) 允许 JavaScript 与浏览器"对话"。 它由窗口的子对象navigator, history, screen, location 和 document组成。 浏览器对象模型不是标准化的,可以根据不同的浏览器而改变。

57. 为什么 JavaScript 被视为单线程

JavaScript 是一种单线程语言。 因为语言规范不允许程序员编写代码以便解释器可以在多个线程或进程中并行运行部分代码。 而像java、go、C++这样的语言可以编写多线程和多进程程序。

58. 什么是事件委托

事件委托是一种侦听事件的技术,你可以将父元素委托为其中发生的所有事件的侦听器。

例如,如果你想检测特定表单内的字段更改,你可以使用事件委托技术,

javascript 复制代码
var form = document.querySelector("#registration-form");

// 监听表单内字段的更改
form.addEventListener(
  "input",
  function (event) {
    console.log(event.target);
  },
  false
);

59. 什么是 ECMAScript

ECMAScript 是构成 JavaScript 基础的脚本语言。 ECMAScript 由 ECMA 国际标准组织在 ECMA-262 和 ECMA-402 规范中标准化。 ECMAScript 第一版于 1997 年发布。

60. 什么是 JSON

JSON(JavaScript 对象表示法)是一种用于数据交换的轻量级格式。 它基于 JavaScript 语言的一个子集,就像在 JavaScript 中构建对象的方式一样。

61. 什么是 PWA

渐进式 Web 应用程序 (PWA) 是一种通过 Web 交付的移动应用程序,使用常见的 Web 技术(包括 HTML、CSS 和 JavaScript)构建。 这些 PWA 部署到服务器,可通过 URL 访问,并由搜索引擎编制索引。

62. 在javascript中获取查询字符串值

你可以使用 URLSearchParams 在 JavaScript 中获取查询字符串值。

javascript 复制代码
const urlParams = new URLSearchParams(window.location.search);
const clientCode = urlParams.get("clientCode");

63. 如何测试空对象

根据ECMAScript版本有不同的解决方案

  • 使用对象条目(ECMA 7+):你可以使用对象条目长度以及构造函数类型。
javascript 复制代码
Object.entries(obj).length === 0 && obj.constructor === Object; // 由于日期对象长度为0,因此还需要检查构造函数检查
  • 使用对象键(ECMA 5+):你可以使用对象键长度和构造函数类型。
javascript 复制代码
Object.keys(obj).length === 0 && obj.constructor === Object; // 由于日期对象长度为0,因此还需要检查构造函数检查
  • 将 for-in 与 hasOwnProperty 结合使用(ECMA 5 之前的版本):你可以将 for-in 循环与 hasOwnProperty 一起使用。
javascript 复制代码
function isEmpty(obj) {
  for (var prop in obj) {
    if (obj.hasOwnProperty(prop)) {
      return false;
    }
  }

  return JSON.stringify(obj) === JSON.stringify({});
}

64. 什么是参数对象 arguments

参数对象arguments是一个类似数组的对象,可在函数内部访问,其中包含传递给该函数的参数值。 例如,让我们看看如何在 sum 函数中使用参数对象,

javascript 复制代码
function sum() {
  var total = 0;
  for (var i = 0, len = arguments.length; i < len; ++i) {
    total += arguments[i];
  }
  return total;
}

sum(1, 2, 3); // returns 6

注意:你不能对参数对象应用数组方法。 但你可以如下转换为常规数组。

javascript 复制代码
var argsArray = Array.prototype.slice.call(arguments);

65. 什么是js标签

标签语句允许我们在 JavaScript 中命名循环和块。 然后我们可以使用这些标签稍后引用代码。 例如,下面带有标签的代码可以避免在数字相同时打印数字,

javascript 复制代码
var i, j;

loop1: for (i = 0; i < 3; i++) {
  loop2: for (j = 0; j < 3; j++) {
    if (i === j) {
      continue loop1;
    }
    console.log("i = " + i + ", j = " + j);
  }
}

// 结果是:
//   "i = 1, j = 0"
//   "i = 2, j = 0"
//   "i = 2, j = 1"

66. 如何生成随机整数

你可以将 Math.random() 与 Math.floor() 结合使用来返回随机整数。 例如,如果要生成 1 到 10 之间的随机整数,则乘法因子应为 10,

javascript 复制代码
Math.floor(Math.random() * 10) + 1; // 返回 1 到 10 之间的随机整数
Math.floor(Math.random() * 100) + 1; // 返回 1 到 100 之间的随机整数

注意: Math.random() 返回 0(含)和 1(不含)之间的随机数

67. 什么是tree shaking

Tree Shaking 是消除无用代码的一种形式。 这意味着在构建过程中未使用的模块不会包含在捆绑包中,因此它依赖于 ES2015 模块语法的静态结构(即导入和导出)。 最初,这是通过 ES2015 模块捆绑器汇总而普及的。

68. 如何检测手机浏览器

你可以使用正则表达式,它根据用户是否使用移动设备浏览而返回 true 或 false 值。

javascript 复制代码
window.mobilecheck = function () {
  var mobileCheck = false;
  (function (a) {
    if (
      /(android|bb\d+|meego).+mobile|avantgo|bada\/|blackberry|blazer|compal|elaine|fennec|hiptop|iemobile|ip(hone|od)|iris|kindle|lge |maemo|midp|mmp|mobile.+firefox|netfront|opera m(ob|in)i|palm( os)?|phone|p(ixi|re)\/|plucker|pocket|psp|series(4|6)0|symbian|treo|up\.(browser|link)|vodafone|wap|windows ce|xda|xiino/i.test(
        a
      ) ||
      /1207|6310|6590|3gso|4thp|50[1-6]i|770s|802s|a wa|abac|ac(er|oo|s\-)|ai(ko|rn)|al(av|ca|co)|amoi|an(ex|ny|yw)|aptu|ar(ch|go)|as(te|us)|attw|au(di|\-m|r |s )|avan|be(ck|ll|nq)|bi(lb|rd)|bl(ac|az)|br(e|v)w|bumb|bw\-(n|u)|c55\/|capi|ccwa|cdm\-|cell|chtm|cldc|cmd\-|co(mp|nd)|craw|da(it|ll|ng)|dbte|dc\-s|devi|dica|dmob|do(c|p)o|ds(12|\-d)|el(49|ai)|em(l2|ul)|er(ic|k0)|esl8|ez([4-7]0|os|wa|ze)|fetc|fly(\-|_)|g1 u|g560|gene|gf\-5|g\-mo|go(\.w|od)|gr(ad|un)|haie|hcit|hd\-(m|p|t)|hei\-|hi(pt|ta)|hp( i|ip)|hs\-c|ht(c(\-| |_|a|g|p|s|t)|tp)|hu(aw|tc)|i\-(20|go|ma)|i230|iac( |\-|\/)|ibro|idea|ig01|ikom|im1k|inno|ipaq|iris|ja(t|v)a|jbro|jemu|jigs|kddi|keji|kgt( |\/)|klon|kpt |kwc\-|kyo(c|k)|le(no|xi)|lg( g|\/(k|l|u)|50|54|\-[a-w])|libw|lynx|m1\-w|m3ga|m50\/|ma(te|ui|xo)|mc(01|21|ca)|m\-cr|me(rc|ri)|mi(o8|oa|ts)|mmef|mo(01|02|bi|de|do|t(\-| |o|v)|zz)|mt(50|p1|v )|mwbp|mywa|n10[0-2]|n20[2-3]|n30(0|2)|n50(0|2|5)|n7(0(0|1)|10)|ne((c|m)\-|on|tf|wf|wg|wt)|nok(6|i)|nzph|o2im|op(ti|wv)|oran|owg1|p800|pan(a|d|t)|pdxg|pg(13|\-([1-8]|c))|phil|pire|pl(ay|uc)|pn\-2|po(ck|rt|se)|prox|psio|pt\-g|qa\-a|qc(07|12|21|32|60|\-[2-7]|i\-)|qtek|r380|r600|raks|rim9|ro(ve|zo)|s55\/|sa(ge|ma|mm|ms|ny|va)|sc(01|h\-|oo|p\-)|sdk\/|se(c(\-|0|1)|47|mc|nd|ri)|sgh\-|shar|sie(\-|m)|sk\-0|sl(45|id)|sm(al|ar|b3|it|t5)|so(ft|ny)|sp(01|h\-|v\-|v )|sy(01|mb)|t2(18|50)|t6(00|10|18)|ta(gt|lk)|tcl\-|tdg\-|tel(i|m)|tim\-|t\-mo|to(pl|sh)|ts(70|m\-|m3|m5)|tx\-9|up(\.b|g1|si)|utst|v400|v750|veri|vi(rg|te)|vk(40|5[0-3]|\-v)|vm40|voda|vulc|vx(52|53|60|61|70|80|81|83|85|98)|w3c(\-| )|webc|whit|wi(g |nc|nw)|wmlb|wonu|x700|yas\-|your|zeto|zte\-/i.test(
        a.substr(0, 4)
      )
    )
      mobileCheck = true;
  })(navigator.userAgent || navigator.vendor || window.opera);
  return mobileCheck;
};

69. 在没有正则表达式的情况下检测移动浏览器

你只需运行设备列表并检查用户代理是否匹配任何内容即可检测移动浏览器。 这是 RegExp 使用的替代解决方案,

javascript 复制代码
function detectmob() {
  if (
    navigator.userAgent.match(/Android/i) ||
    navigator.userAgent.match(/webOS/i) ||
    navigator.userAgent.match(/iPhone/i) ||
    navigator.userAgent.match(/iPad/i) ||
    navigator.userAgent.match(/iPod/i) ||
    navigator.userAgent.match(/BlackBerry/i) ||
    navigator.userAgent.match(/Windows Phone/i)
  ) {
    return true;
  } else {
    return false;
  }
}

70. 使用JS获取图像的宽度和高度

你可以使用 Javascript 以编程方式获取图像并检查尺寸(宽度和高度)。

javascript 复制代码
var img = new Image();
img.onload = function () {
  console.log(this.width + "x" + this.height);
};
img.src = "http://www.google.com/intl/en_ALL/images/logo.gif";

71. 如何发出同步 HTTP 请求

浏览器提供 XMLHttpRequest 对象,可用于从 JavaScript 发出同步 HTTP 请求

javascript 复制代码
function httpGet(theUrl) {
  var xmlHttpReq = new XMLHttpRequest();
  xmlHttpReq.open("GET", theUrl, false); // false 表示同步请求 request
  xmlHttpReq.send(null);
  return xmlHttpReq.responseText;
}

72. 在 JavaScript 中将日期转换为另一个时区

你可以使用 toLocaleString() 方法将一个时区的日期转换为另一个时区的日期。 例如,让我们将当前日期转换为英国英语时区,如下所示,

javascript 复制代码
console.log(event.toLocaleString("en-GB", { timeZone: "UTC" })); //29/06/2019, 09:56:00

73. 用于获取窗口大小的属性是什么

你可以使windows, document元素 和 document body对象的innerWidth、innerHeight、clientWidth、clientHeight 属性来查找窗口的大小。 让我们使用这些属性的组合来计算窗口或文档的大小,

javascript 复制代码
var width =
  window.innerWidth ||
  document.documentElement.clientWidth ||
  document.body.clientWidth;

var height =
  window.innerHeight ||
  document.documentElement.clientHeight ||
  document.body.clientHeight;

74. 可以在条件运算符上应用链式吗

可以在条件运算符上应用链接,类似于 if ... else if ... else if ... else 链。 语法如下,

javascript 复制代码
function traceValue(someParam) {
  return condition1
    ? value1
    : condition2
    ? value2
    : condition3
    ? value3
    : value4;
}

// 上面的条件运算符相当于

function traceValue(someParam) {
  if (condition1) {
    return value1;
  } else if (condition2) {
    return value2;
  } else if (condition3) {
    return value3;
  } else {
    return value4;
  }
}

75. 页面加载后执行javascript的方式

可以通过多种不同的方式在页面加载后执行 javascript,

  1. window.onload:
javascript 复制代码
window.onload = function ...
  1. document.onload:
javascript 复制代码
document.onload = function ...
  1. body onload:
javascript 复制代码
<body onload="script();">

76. proto和prototype有什么区别

proto 对象是在查找链中用于解析方法等的实际对象。而prototype 是在使用 new 创建对象时用于构建 proto 的对象。

javascript 复制代码
new Employee().__proto__ === Employee.prototype;
new Employee().prototype === undefined;

77. 为什么建议使用分号

建议在 JavaScript 中的每个语句后使用分号。 例如,在下面的情况下,由于缺少分号,它在运行时抛出错误"..不是函数"。

javascript 复制代码
// 定义一个函数
var fn = (function () {
  //...
})(
  // 此行缺少分号

  // 然后在闭包内执行一些代码
  function () {
    //...
  }
)();

它将被解释为

javascript 复制代码
var fn = (function () {
  //...
})(function () {
  //...
})();

在这种情况下,我们将第二个函数作为参数传递给第一个函数,然后尝试将第一个函数调用的结果作为函数调用。 因此,第二个函数将在运行时失败并出现"...不是函数"错误。

78. freeze方法的作用

freeze() 方法用于冻结对象。 冻结对象不允许向对象添加新属性,防止删除并防止更改现有属性的可枚举性、可配置性或可写性。 即,它返回传递的对象并且不创建冻结副本。

javascript 复制代码
const obj = {
  prop: 100,
};

Object.freeze(obj);
obj.prop = 200; // Throws an error in strict mode

console.log(obj.prop); //100

请记住,冻结仅适用于对象中的顶级属性,而不适用于嵌套对象。 例如,

javascript 复制代码
const user = {
  name: "John",
  employment: {
    department: "IT",
  },
};

Object.freeze(user);
user.employment.department = "HR"; // 此处department被修改

79. 如何检测浏览器语言首选项

可以使用navigator对象来检测浏览器语言首选项,如下所示,

javascript 复制代码
var language =
  (navigator.languages && navigator.languages[0]) || // Chrome / Firefox
  navigator.language || // 所有浏览器
  navigator.userLanguage; // IE <= 10

console.log(language);

80. 如何检测页面中禁用的 javascript

你可以使用 <noscript> 标签来检测 javascript 是否被禁用。 <noscript> 内的代码块在 JavaScript 被禁用时执行,通常用于在 JavaScript 生成页面时显示替代内容。

javascript 复制代码
<script type="javascript">
    // JS相关代码放在这里
</script>
<noscript>
    <a href="next_page.html?noJS=true">JavaScript 在页面中被禁用。 请点击下一页</a>
</noscript>

81. javascript中可用的按位运算符

下面是 JavaScript 中使用的按位逻辑运算符列表

按位与 (&) 按位或 (|) 按位异或 (^) 按位非 (~) 左移 (<<) 符号传播右移 ( >> ) 零填充右移 ( >>> )

82. 使用对象确定两个值相同或不同

Object.is() 方法确定两个值是否是相同的值。 例如,不同类型值的用法是:

javascript 复制代码
Object.is("hello", "hello"); // true
Object.is(window, window); // true
Object.is([], []); // false

如果满足以下条件之一,则两个值相同:

  • 两者均未定义(undefined)
  • 都为空(null)
  • 均为true或均为false
  • 两个字符串具有相同的长度、相同的字符且顺序相同
  • 都是同一个对象(意味着两个对象具有相同的引用)
  • 两个数字和两个 +0 两个 -0 两个 NaN 都非零且都不是 NaN 并且两者具有相同的值。

83. seal方法的目的是什么

Object.seal() 方法用于密封对象,方法是防止向对象添加新属性并将所有现有属性标记为不可配置。 但只要当前属性可写,它们的值仍然可以更改。 让我们看下面的例子来了解更多关于 seal() 方法的信息。

javascript 复制代码
const object = {
  property: "Welcome JS world",
};
Object.seal(object);
object.property = "Welcome to object world";
console.log(Object.isSealed(object)); // true
delete object.property; // 无法删除
console.log(object.property); //Welcome to object world

注意以下两点

  • Object.seal() 允许修改已有属性的值,但不允许添加和删除属性。
  • Object.freeze() 不允许修改、添加和删除属性,对象及其属性值都是不可变的。

84. 随机数范围 [min, max]

产生随机数范围[min, max]为什么是Math.floor(Math.random() * (max + 1 - min)) + min而不是Math.floor(Math.random() * (max + 1)) + min ?

这两个表达式在功能上是相似的,它们都用于生成一个范围在 minmax(包括 minmax)之间的随机整数。

然而,它们在计算方式上有一些微小的差异:

  1. Math.floor(Math.random() * (max + 1 - min)) + min;

    • 这个表达式使用了 (max + 1 - min) 来确定随机数的范围。
    • 首先,Math.random() 生成一个范围在 [0, 1) 的随机数。
    • 然后,将这个随机数乘以 (max + 1 - min),将范围扩大到 [0, max + 1 - min)
    • 最后,使用 Math.floor() 向下取整,得到一个范围在 [0, max + 1 - min - 1] 的整数。
    • 最后,将结果加上 min,使得随机数的范围变为 [min, max]
  2. Math.floor(Math.random() * (max + 1)) + min;

    • 这个表达式使用了 (max + 1) 来确定随机数的范围。
    • 首先,Math.random() 生成一个范围在 [0, 1) 的随机数。
    • 然后,将这个随机数乘以 (max + 1),将范围扩大到 [0, max + 1)
    • 最后,使用 Math.floor() 向下取整,得到一个范围在 [0, max] 的整数。
    • 最后,将结果加上 min,使得随机数的范围变为 [min, max + min]

85. Object.create(func)与Object.create(func.prototype)

Object.create(func)Object.create(func.prototype) 之间有一些区别,这涉及到原型链和对象创建的方式。

  1. Object.create(func)

    • 这个语法创建一个新对象,并将 func 的原型设置为新对象的原型。
    • 新对象的原型链如下:新对象 -> func.prototype -> Object.prototype -> null
    • 通过 new func() 创建的实例将继承 func.prototype 上的属性和方法。
    • 这种方式常用于实现经典的原型继承。
  2. Object.create(func.prototype)

    • 这个语法创建一个新对象,并将 func.prototype 直接设置为新对象的原型。
    • 新对象的原型链如下:新对象 -> func.prototype -> null
    • 与上面的方式相比,它直接将 func.prototype 设置为新对象的原型,省略了 Object.prototype 的层级。
    • 这种方式常用于创建一个仅继承自特定原型的对象。

示例:

javascript 复制代码
function Person(name) {
  this.name = name;
}

Person.prototype.sayHello = function() {
  console.log("Hello, " + this.name);
};

const obj1 = Object.create(Person);
console.log(obj1 instanceof Person); // false
console.log(obj1 instanceof Object); // true

const obj2 = Object.create(Person.prototype);
console.log(obj2 instanceof Person); // true
console.log(obj2 instanceof Object); // false

在上面的示例中,Object.create(Person) 创建的对象 obj1 的原型链包含了 Object.prototype,因此它是 Object 的实例,但不是 Person 的实例。

Object.create(Person.prototype) 创建的对象 obj2 的原型链只包含了 Person.prototype,因此它是 Person 的实例,但不是 Object 的实例。

需要注意的是,Object.create() 方法创建的对象不会调用构造函数,因此在这两种方式下,新对象不会执行 Person 的构造函数。如果需要执行构造函数并初始化对象,请使用其他方式,例如使用 new 关键字调用构造函数。

86. 如何创建带有原型的对象

Object.create() 方法用于创建具有指定原型对象和属性的新对象。 即,它使用现有对象作为新创建对象的原型。 它返回一个具有指定原型对象和属性的新对象。

javascript 复制代码
const user = {
  name: "John",
  printInfo: function () {
    console.log(`My name is ${this.name}.`);
  },
};

const admin = Object.create(user);

admin.name = "Nick"; // 请记住,"name"是在"admin"上设置的属性,而不是在"user"对象上设置的属性

admin.printInfo(); // My name is Nick

87. WeakSet与Set 有什么区别

WeakSet 和 Set 主要区别在于 Set 中对象的引用是强引用,而 WeakSet 中对象的引用是弱引用。 即,如果没有其他引用,WeakSet 中的对象可以被垃圾回收。 其他差异是,

  • Sets 可以存储任何值,而 WeakSets 只能存储对象的集合
  • WeakSet 与 Set 不同,没有 size 属性
  • WeakSet没有clear、keys、values、entries、forEach等方法。
  • WeakSet 是不可迭代的。
javascript 复制代码
new WeakSet([iterable]);

var ws = new WeakSet();
var user = {};
ws.add(user);
ws.has(user); // true
ws.delete(user); // removes user from the set
ws.has(user); // false, user has been removed

88. WeakMap 和 Map 有什么区别

主要区别在于,Map 中关键对象的引用是强引用,而 WeakMap 中关键对象的引用是弱引用。 即,如果没有其他引用,WeakMap 中的关键对象可以被垃圾回收。 其他差异是,

  • Map 可以存储任何键类型,而 WeakMap 只能存储键对象的集合
  • WeakMap 与 Map 不同,没有 size 属性
  • WeakMap没有clear、keys、values、entries、forEach等方法。
  • WeakMap 是不可迭代的。
javascript 复制代码
new WeakMap([iterable]);

var ws = new WeakMap();
var user = {};
ws.set(user);
ws.has(user); // true
ws.delete(user); // removes user from the map
ws.has(user); // false, user has been removed

89. uneval的目的是什么

uneval() 是一个内置函数,用于创建对象源代码的字符串表示形式。 它是一个顶级函数,不与任何对象关联。 让我们看下面的例子来了解更多关于它的功能,

javascript 复制代码
var a = 1;
uneval(a); // returns a String containing 1
uneval(function user() {}); // returns "(function user(){})"

uneval() 函数已被弃用。 建议对函数使用 toString(),对其他情况使用 JSON.toStringify()。

javascript 复制代码
function user() {}
console.log(user.toString()); // returns "(function user(){})"

90. 使用requestAnimationFrame写一个动画

当使用 requestAnimationFrame 方法执行动画时,可以根据具体的需求来更新动画的状态和绘制帧的逻辑。以下是一个示例,展示了如何更新动画状态和绘制一个简单的移动方块的帧:

javascript 复制代码
// 获取动画区域的元素
const animationArea = document.getElementById('animation-area');

// 定义方块的初始位置和速度
let positionX = 0;
let velocity = 2;

function updateAnimation() {
  // 更新动画状态
  positionX += velocity;

  // 绘制帧
  animationArea.style.transform = `translateX(${positionX}px)`;

  // 检查是否超出动画区域边界
  const animationAreaWidth = animationArea.clientWidth;
  const boxWidth = 50; // 假设方块的宽度为 50px
  if (positionX + boxWidth > animationAreaWidth || positionX < 0) {
    // 反转速度,使方块改变方向
    velocity *= -1;
  }

  // 调用 requestAnimationFrame 更新下一帧
  requestAnimationFrame(updateAnimation);
}

// 启动动画
requestAnimationFrame(updateAnimation);

在上述示例中,我们假设有一个具有 id 为 animation-area 的 HTML 元素,表示动画区域。通过获取该元素的引用,我们可以通过修改 transform 属性来改变方块的水平位置。

通过在 updateAnimation 函数的末尾再次调用 requestAnimationFrame(updateAnimation),我们实现了动画的循环,从而在每一帧都更新动画状态和绘制帧。

91. 什么是 JavaScript 访问器

ECMAScript 5 通过 getter 和 setter 引入了 javascript 对象访问器或计算属性。 Getters 使用 get 关键字,而 Setters 使用 set 关键字。

javascript 复制代码
var user = {
  firstName: "John",
  lastName: "Abraham",
  language: "en",
  get lang() {
    return this.language;
  },
  set lang(lang) {
    this.language = lang;
  },
};
console.log(user.lang); // getter访问lang返回 en
user.lang = "fr";
console.log(user.lang); // setter将lang设置为 fr

92. 原始数据类型有哪些

原始数据类型是具有原始值(没有属性或方法)的数据。 原始数据类型有 7 种。

  • string
  • number
  • boolean
  • null
  • undefined
  • bigint
  • symbol

注意: 当说一个原始数据类型的值没有属性或方法时,是指该值不能访问或调用任何附加的属性或方法。原始数据类型是基本的数据类型,它们是简单的、不可变的值。

例如,以下是预期的结果

javascript 复制代码
const num = 42;
console.log(num.toString()); // 错误,无法调用方法

const str = "Hello";
console.log(str.length); // 错误,无法获取属性

然而,JavaScript 提供了相应的包装对象(如 NumberBooleanString),这些对象提供了属性和方法的访问。当你尝试在原始值上访问属性或方法时,JavaScript 会自动将原始值转换为相应的包装对象。所以,实际结果如下

javascript 复制代码
const num = 42;
console.log(num.toString()); // 转换为 Number 对象,调用 toString() 方法

const str = "Hello";
console.log(str.length); // 转换为 String 对象,获取 length 属性

93. 函数参数规则是什么

JavaScript 函数遵循以下参数规则,

  • 函数定义不指定参数的数据类型。
  • 不要对传递的参数执行类型检查。
  • 不检查收到的参数数量。

如:

javascript 复制代码
function functionName(parameter1, parameter2, parameter3) {
  console.log(parameter1); // 1
}
functionName(1);

94. 什么是error对象

error对象是一个内置的错误对象,它在发生错误时提供错误信息。 它有两个属性:name和message。 例如,以下函数记录错误详细信息,

javascript 复制代码
try {
  greeting("Welcome");
} catch (err) {
  console.log(err.name + "<br>" + err.message);
}

95. 错误类型有哪些

错误名称 描述
EvalError eval() 函数发生错误
RangeError 发生错误,数字"超出范围"
ReferenceError 由于非法引用而导致的错误
SyntaxError 由于语法错误而导致的错误
TypeError 由于类型错误而导致的错误
URIError 由于encodeURI() 导致的错误

96. javascript中的循环有哪两种类型

  • 入口控制循环:在这种循环类型中,在进入循环体之前测试测试条件。 例如,For 循环和 While 循环就属于这一类。
  • 退出受控循环:在这种循环类型中,测试条件在循环体末尾进行测试或评估。 即,无论测试条件为真还是假,循环体都将至少执行一次。 例如,do-while 循环就属于这一类。

97. 什么是 Intl 对象

Intl 是 JavaScript 中的一个内置对象,它提供了对国际化(Internationalization)和本地化(Localization)的支持。通过 Intl 对象,开发者可以处理不同语言、地区和文化的数据,包括日期和时间、数字格式化、货币、排序等。

Intl 对象提供了一系列的构造函数和静态方法,用于执行各种国际化相关的操作。以下是 Intl 对象的一些常用功能:

  1. 日期和时间格式化Intl.DateTimeFormat 构造函数用于格式化日期和时间,根据指定的语言和地区显示不同的日期和时间格式。

    javascript 复制代码
    const date = new Date();
    const options = { year: 'numeric', month: 'long', day: 'numeric' };
    const formattedDate = new Intl.DateTimeFormat('en-US', options).format(date);
    console.log(formattedDate);
    // 输出: February 20, 2024
  2. 数字格式化Intl.NumberFormat 构造函数用于格式化数字,根据指定的语言和地区显示适当的数字格式,包括千位分隔符、小数位数等。

    javascript 复制代码
    const number = 12345.6789;
    const formattedNumber = new Intl.NumberFormat('en-US').format(number);
    console.log(formattedNumber);
    // 输出: 12,345.6789
  3. 货币格式化Intl.NumberFormat 构造函数也可以用于格式化货币,根据指定的语言和地区显示适当的货币格式。

    javascript 复制代码
    const amount = 12345.67;
    const currency = 'USD';
    const formattedCurrency = new Intl.NumberFormat('en-US', { style: 'currency', currency }).format(amount);
    console.log(formattedCurrency);
    // 输出: $12,345.67
  4. 排序Intl.Collator 构造函数用于排序字符串,根据指定的语言和地区进行正确的排序。

    javascript 复制代码
    const strings = ['apple', 'banana', 'cherry', 'Date', 'Eggplant'];
    const sortedStrings = strings.sort(new Intl.Collator('en-US').compare);
    console.log(sortedStrings);
    // 输出: ["apple", "banana", "cherry", "Date", "Eggplant"]

98. 什么是迭代器

迭代器(Iterator)是一种对象,它提供了一种方法来访问集合或序列中的元素,一次一个。通过迭代器,我们可以遍历和访问集合中的元素,而无需暴露集合内部的实现细节。

迭代器是一种抽象的概念,它定义了两个基本方法:

  1. next() 方法 :该方法返回迭代器中的下一个元素,并以对象形式包含两个属性:valuedone

    • value:表示迭代器返回的当前元素的值。
    • done:表示迭代器是否已经遍历完所有元素的布尔值。当迭代器遍历完集合中的所有元素时,donetrue;否则为 false
  2. return() 方法:该方法用于提前终止迭代过程。它允许迭代器在遍历过程中执行清理操作,并返回一个指定的值作为迭代的结果。

迭代器可以用于各种数据结构,如数组、集合、映射和自定义的数据结构。它提供了一种统一的方式来遍历这些数据结构中的元素,无需关心其内部实现。

在 JavaScript 中,迭代器的概念由 Symbol.iterator 符号和 Iterable 接口组成。可迭代对象(Iterable)是具有实现 Symbol.iterator 方法的对象,该方法返回一个迭代器对象。

下面是一个简单的示例,展示了如何使用迭代器遍历数组中的元素:

javascript 复制代码
const array = [1, 2, 3];

// 获取数组的迭代器对象
const iterator = array[Symbol.iterator]();

// 使用迭代器遍历数组元素
let result = iterator.next();
while (!result.done) {
  console.log(result.value);
  result = iterator.next();
}

迭代器提供了一种灵活且通用的方式来处理集合中的元素,使得遍历和访问集合变得简单和统一。它在 JavaScript 中被广泛应用于各种迭代操作,如 for...of 循环和数组的高阶方法(如 forEachmapfilter 等)。

100. 事件循环机制(Event Loop)

事件循环(Event Loop)是 JavaScript 中处理异步操作的一种机制。在 JavaScript中,单线程的特性意味着一次只能执行一个任务。如果某个任务需要花费较长时间,那么其他任务就会被阻塞,这会导致用户界面冻结或无响应。为了解决这个问题,JavaScript 使用了事件循环机制。

事件循环的基本原理是将任务分为两类:同步任务和异步任务。同步任务是按照它们出现的顺序依次执行的,而异步任务则在将来的某个时间点执行。

事件循环的流程

  1. 执行同步任务队列中的任务,直到队列为空。
  2. 检查是否有异步任务需要执行。如果有,执行这些异步任务,并等待它们完成。
  3. 当异步任务完成后,将它们添加到异步任务队列。
  4. 如果存在微任务队列(Promise 、MutationObserver 等),执行微任务队列中的任务,直到队列为空。
  5. 返回步骤 1,继续执行同步任务。

如何区分宏任务与微任务

根据来源和调度方式进行判断

宏任务

  1. 宏任务是由宿主环境(如浏览器或 Node.js)提供的任务。
  2. 常见的宏任务包括定时器回调(setTimeout、setInterval)、事件回调(DOM 事件处理器)、Ajax 请求回调等。
  3. 宏任务通过宿主环境的任务队列进行调度和执行。

微任务

  1. 微任务是由 JavaScript 引擎提供的任务。
  2. 常见的微任务包括 Promise 的回调、MutationObserver 的回调等。
  3. 微任务通过 JavaScript 引擎内部的微任务队列进行调度和执行。

关于区分宏任务和微任务的执行顺序,请注意以下几点:

  1. 在每个宏任务执行完毕之后,会立即执行所有微任务,然后再执行下一个宏任务。换句话说,微任务总是在宏任务之间执行。
  2. 微任务具有更高的优先级,它们会在下一个宏任务执行之前执行。这意味着微任务可以在宏任务之间更新应用程序状态或进行其他重要的操作。

理解宏任务和微任务的示例代码如下:

javascript 复制代码
console.log('同步任务1'); // 同步任务1

setTimeout(function() {
  console.log('宏任务1'); // 宏任务1

  Promise.resolve().then(function() {
    console.log('微任务1'); // 微任务1
  });
}, 0);

Promise.resolve().then(function() {
  console.log('微任务2'); // 微任务2
});

console.log('同步任务3'); // 同步任务3

// 输出:
// 同步任务1
// 同步任务3
// 微任务2
// 宏任务1
// 微任务1

上面脚本的执行顺序是这样的:

  1. console.log('同步任务1'); 执行同步输出 "同步任务1"。
  2. setTimeout(...) 设置了一个宏任务(定时器),但不会立即执行里面的回调函数。
  3. Promise.resolve().then(...) 创建了一个微任务,该任务会在当前宏任务(主脚本的执行)完成后立即执行。
  4. console.log('同步任务3'); 执行同步输出 "同步任务3"。
  5. 当前宏任务执行完毕,开始执行微任务队列中的任务。此时,微任务队列中有一个任务,执行输出 "微任务2"。
  6. 微任务队列清空后,事件循环取出下一个宏任务,即 setTimeout(...) 的回调函数,执行输出 "宏任务2"。
  7. 在 setTimeout(...) 的回调函数中,Promise.resolve().then(...) 创建了另一个微任务,该任务会在这个宏任务(setTimeout 的回调)执行完之后立即执行,输出 "微任务1"。
相关推荐
彭世瑜18 分钟前
ts: TypeScript跳过检查/忽略类型检查
前端·javascript·typescript
FØund40419 分钟前
antd form.setFieldsValue问题总结
前端·react.js·typescript·html
Backstroke fish20 分钟前
Token刷新机制
前端·javascript·vue.js·typescript·vue
小五Five21 分钟前
TypeScript项目中Axios的封装
开发语言·前端·javascript
小曲程序21 分钟前
vue3 封装request请求
java·前端·typescript·vue
临枫54121 分钟前
Nuxt3封装网络请求 useFetch & $fetch
前端·javascript·vue.js·typescript
前端每日三省23 分钟前
面试题-TS(八):什么是装饰器(decorators)?如何在 TypeScript 中使用它们?
开发语言·前端·javascript
小刺猬_98523 分钟前
(超详细)数组方法 ——— splice( )
前端·javascript·typescript
渊兮兮25 分钟前
Vue3 + TypeScript +动画,实现动态登陆页面
前端·javascript·css·typescript·动画
鑫宝Code25 分钟前
【TS】TypeScript中的接口(Interface):对象类型的强大工具
前端·javascript·typescript