关于我 壹文 项目 三五好友
JavaScript 问题集(一) 2024-05-25
文章摘要

一、箭头函数中的 this 会继承自包含它的常规函数的 this 值。如果没有被包含,则指向 window

const shape = {
  radius: 10,
  diameter() {
    return this.radius * 2
  },
  perimeter: () => 2 * Math.PI * this.radius
}

shape.diameter()
shape.perimeter()

考点:常规函数和箭头函数在处理 this 关键字时的不同行为

1、常规函数中的 thisthis 的值通常指向调用该函数的对象。

const shape = {
  radius: 10,
  diameter() {
    return this.radius * 2;
  },
  perimeter: () => 2 * Math.PI * this.radius
};

shape.diameter();

shape.diameter() 被调用时,this 关键字指向 shape 对象。因此,this.radius 的值是 10shape.diameter() 返回 20

2、箭头函数中的 this:箭头函数不会绑定自己的 this,它的 this 值继承自包含它的常规函数的 this 值。如果箭头函数没有在任何常规函数中被包含,那么 this 将指向全局对象(在浏览器中是 window

perimeter 是一个箭头函数。在定义 perimeter 时,它的 this 并不会指向 shape 对象,而是继承自它的外围作用域。在这里,外围作用域是全局作用域(window)。

在全局作用域中,this.radiusundefined,因为 window 对象没有 radius 属性。因此,shape.perimeter() 的返回值是 NaN2 * Math.PI * undefined)。

二、JavaScript 中对象属性访问的两种主要方式——点语法(.)和括号语法([])

  • 点语法简单直观,但不支持动态属性名。

  • 括号语法支持动态属性名,可以通过变量或表达式来访问属性。

  • 访问不存在的属性时,两种语法都不会抛出错误,而是返回undefined

  • 如果属性名是undefinednull,使用括号语法访问属性会抛出错误。

  • 当你尝试调用一个不存在的函数属性时,不管使用点语法还是括号语法,都会在运行时抛出一个TypeError错误

    pet.bark();
    
const bird = {
  size: 'small'
}

const mouse = {
  name: 'Mickey',
  small: true
}

1、点语法(.

点语法是访问对象属性的最直观方式。它要求属性名必须是一个有效的 JavaScript 标识符,并且不能是保留字或包含空格或特殊字符。点语法不能用于动态属性名。

如果尝试使用点语法访问一个不存在的属性,将返回undefined,而不会抛出错误:

console.log(mouse.bird); // 输出 undefined,不抛出错误

2、括号语法([]

括号语法允许你使用变量或表达式来动态地访问对象的属性。属性名可以是任何字符串,甚至是由变量或表达式计算得到的字符串。

const bird = {
  size: 'small'
};

console.log(mouse[bird.size]); // 首先计算 bird.size,得到 'small',然后访问 mouse['small'],输出 true

使用括号语法时,如果属性名对应的值不存在,同样会返回undefined,但不会抛出错误。

三、new Number() 是一个内建的函数构造器,有一堆额外的功能并且它是一个对象

let a = 3
let b = new Number(3)
let c = 3

console.log(a == b)
console.log(a === b)
console.log(b === c)

new Number() 是一个内建的函数构造器。虽然它看着像是一个 number,但它实际上并不是一个真实的 number:它有一堆额外的功能并且它是一个对象。

四、函数允许你将数据(属性)和行为(函数)结合在一起

function bark() {
  console.log('Woof!')
}

bark.animal = 'dog'

这在 JavaScript 中是可以的,因为函数是对象!(除了基本类型之外其他都是对象)

函数是一个特殊的对象。你写的这个代码其实不是一个实际的函数。函数是一个拥有属性的对象,并且属性也可被调用。

五、可以直接给常规对象扩展属性和方法。而给构造函数添加属性:如果你想一次性给所有实例添加特性,你应该使用原型

Person.prototype.getFullName = function () {
  return `${this.firstName} ${this.lastName}`;
}
function Person(firstName, lastName) {
  this.firstName = firstName;
  this.lastName = lastName;
}

const member = new Person("Lydia", "Hallie");
Person.getFullName = function () {
  return `${this.firstName} ${this.lastName}`;
}

console.log(member.getFullName());

输出:TypeError

构造函数添加属性和方法

构造函数本身:构造函数本身可以拥有属性和方法,这些被称为静态属性和静态方法。静态属性和方法可以通过构造函数直接访问,而不需要创建实例。

function Person(firstName, lastName) {
  this.firstName = firstName;
  this.lastName = lastName;
}

// 给构造函数添加静态属性
Person.staticProperty = 'This is a static property';

// 给构造函数添加静态方法
Person.staticMethod = function() {
  console.log('This is a static method');
};

console.log(Person.staticProperty); // 输出: This is a static property
Person.staticMethod(); // 输出: This is a static method

构造函数的原型:构造函数的原型(Person.prototype)是用来在所有实例之间共享属性和方法的。当你给原型添加一个方法时,所有通过该构造函数创建的实例都可以访问这个方法,而不需要在每个实例上单独存储这个方法。

Person.prototype.getFullName = function () {
  return `${this.firstName} ${this.lastName}`;
};

const member = new Person("Lydia", "Hallie");
console.log(member.getFullName()); // 输出: Lydia Hallie

六、当使用 new 时,this 引用我们创建的空对象。当未使用 new 时,this 引用的是全局对象(global object)

function Person(firstName, lastName) {
  this.firstName = firstName
  this.lastName = lastName
}

const lydia = new Person('Lydia', 'Hallie')
const sarah = Person('Sarah', 'Smith')

console.log(lydia)
console.log(sarah)

输出:Person {firstName: “Lydia”, lastName: “Hallie”}andundefined

对于 sarah,我们没有使用 new 关键字。当使用 new 时,this 引用我们创建的空对象。当未使用 new 时,this 引用的是全局对象(global object)。

我们说 this.firstName 等于 "Sarah",并且 this.lastName 等于 "Smith"。实际上我们做的是,定义了 global.firstName = 'Sarah'global.lastName = 'Smith'。而 sarah 本身是 undefined

七、一元后自增运算符x++,先返回后增加;一元前自增运算符 ++x,先增加后返回

let number = 0
console.log(number++)
console.log(++number)
console.log(number)

输出:0 2 2

一元后自增运算符 ++

  1. 返回值(返回 0
  2. 值自增(number 现在是 1

一元前自增运算符 ++

  1. 值自增(number 现在是 2
  2. 返回值(返回 2

八、扩展运算符(...args)会返回实参组成的数组。而数组是对象,因此 typeof args 返回 "object"

function getAge(...args) {
  console.log(typeof args)
}

getAge(21)

输出:“object”

九、关闭 tab 标签页 后,sessionStorage 存储的数据才会删除。

如果使用 localStorage,那么数据将永远在那里,除非调用了 localStorage.clear()

十、所有对象的键(不包括 Symbol)在底层都是字符串,即使你自己没有将其作为字符串输入。对于集合,它不是这样工作的。在我们的集合中没有 '1'set.has('1') 返回 false。它有数字类型为 1set.has(1) 返回 true

const obj = { 1: 'a', 2: 'b', 3: 'c' }
const set = new Set([1, 2, 3, 4, 5])

obj.hasOwnProperty('1')
obj.hasOwnProperty(1)
set.has('1')
set.has(1)

输出:true true false true

所有对象的键(不包括 Symbol)在底层都是字符串,即使你自己没有将其作为字符串输入。这就是为什么 obj.hasOwnProperty('1') 也返回 true

对于集合,它不是这样工作的。在我们的集合中没有 '1'set.has('1') 返回 false。它有数字类型为 1set.has(1) 返回 true

十、.call立即执行的。.bind方法创建了一个新的函数, 返回函数的副本,但带有绑定上下文!它不是立即执行的。

函数执行的上下文:this 的值

const person = { name: 'Lydia' }

function sayHi(age) {
  console.log(`${this.name} is ${age}`)
}

sayHi.call(person, 21)
sayHi.bind(person, 21)

输出:Lydia is 21 function

使用 bind 的例子

const person = { name: 'Lydia' };
function sayHi(age) {
  console.log(`${this.name} is ${age}`);
}
const personSayHi = sayHi.bind(person, 21);
personSayHi();

使用callbind的选择取决于你的需求:

  • 如果你需要立即调用函数并改变this的上下文,使用call
  • 如果你需要创建一个新的函数,以备后用,并且带有固定的this上下文和参数,使用bind

十一、事件处理机制有三个阶段:捕获阶段、目标阶段和冒泡阶段

<div onclick="console.log('first div')">
  <div onclick="console.log('second div')">
    <button onclick="console.log('button')">
      Click!
    </button>
  </div>
</div>

事件捕获

  • 事件从祖先元素开始,沿着 DOM 树向下传播,直到目标元素。
  • 比如,在你的示例中,事件捕获的顺序是:<div onclick="console.log('first div')"> -> <div onclick="console.log('second div')"> -> <button onclick="console.log('button')">

目标阶段

  • 事件到达目标元素,触发目标元素的事件处理函数。
  • 在这个案例中,目标元素是<button>,当点击按钮时,会触发 console.log('button')

事件冒泡

  • 事件从目标元素开始,沿着 DOM 树向上传播,直到根元素。
  • 在你的示例中,事件冒泡的顺序是:<button onclick="console.log('button')"> -> <div onclick="console.log('second div')"> -> <div onclick="console.log('first div')">

扩展:事件流控制

你可以通过event.stopPropagation()来阻止事件在某个阶段继续传播。比如,如果你在<button>的事件处理函数中调用event.stopPropagation(),事件将不会冒泡到<div>元素。

<button onclick="event.stopPropagation(); console.log('button')">
  Click!
</button>

这样,点击按钮时,控制台只会输出 button,而不会输出 second divfirst div

十二、只有 7 种内置类型:nullundefinedbooleannumberstringobject, symbolbigintfunction不是一种类型,函数是对象,它的类型是object

十三、Promise.race的工作原理:

  1. 创建Promise.race接受一个Promise数组作为参数,并立即创建一个新的Promise对象。
  2. 竞争:数组中的每个Promise开始执行,它们之间开始竞争,看哪个首先解决或拒绝。
  3. 解决:一旦数组中的任何一个Promise解决,Promise.race返回的Promise对象也会解决,并且其解决的值是第一个解决的Promise的值。
  4. 拒绝:如果第一个解决的Promise是被拒绝的,Promise.race返回的Promise对象也会被拒绝,并且其拒绝的原因是第一个被拒绝的Promise的原因。

Promise.all的工作原理:

  1. 创建Promise.all接受一个Promise数组,并立即创建一个新的Promise对象。
  2. 等待:它等待所有输入的Promise都解决。
  3. 解决:当所有的Promise都解决后,返回的Promise对象解决,并且其解决的值是一个数组,包含了所有输入Promise解决的值,顺序与输入数组相同。
  4. 拒绝:如果任何一个输入的Promise被拒绝,返回的Promise对象也会立即被拒绝,其拒绝的原因是第一个被拒绝的Promise的原因。

十四、在 JavaScript 中,当一个对象被赋值给一个变量时,该变量实际上保存的是指向该对象内存地址的引用。当你将对象赋给另一个变量或将其作为参数传递给函数时,你复制的是这个引用,而不是对象本身。

let person = { name: "Lydia" }; // 创建对象并保存引用在变量 person
const members = [person]; // 将 person 的引用复制到数组 members 中
person = null; // 改变 person 变量的引用,现在它指向 null

注意:这里不是更改引用地址上的值,而是改变了 person 变量的引用

如果想改变person引用上的值,并且让members数组中的引用也变化

你需要做的是修改person对象的属性,而不是改变person变量的引用本身。由于personmembers数组中的元素都指向同一个对象,对对象属性的任何修改都会反映在所有指向该对象的引用上。

let person = { name: "Lydia" }; // 创建对象并保存引用在变量 person
const members = [person]; // 将 person 的引用复制到数组 members 中

// 修改 person 对象的属性
person.name = "John";

// 输出 members 数组,将看到 person 对象的 name 属性已经被修改
console.log(members[0].name); // 输出: John

十五、parseInt 检查字符串中的字符是否合法。一旦遇到一个在指定进制中不合法的字符后,立即停止解析并且忽略后面所有的字符。

const num = parseInt("7*6", 10);

输出:7

设定了 进制 后 (也就是第二个参数,指定需要解析的数字是什么进制:十进制、十六机制、八进制、二进制等等……),parseInt 检查字符串中的字符是否合法。一旦遇到一个在指定进制中不合法的字符后,立即停止解析并且忽略后面所有的字符。

*就是不合法的数字字符。所以只解析到"7",并将其解析为十进制的7. num的值即为7.

十六

(() => {
  let x = (y = 10);
})();

console.log(typeof x);
console.log(typeof y);

输出:“undefined”, “number”

解析:y = 10

x = y = 10

因为 x 是 let 定义的块作用域变量,在作用域外无法访问到,所以输出 undefined

y 是全局作用域变量,在外部可以正确访问到

十七、通过 var, constlet 关键字声明的变量无法用 delete 操作符来删除

通常的做法是将它们的值重新赋值为undefined或者将它们赋值为null

1. 重新赋值为 undefined

var myVar = 'some value';
// 当你想要"删除"这个变量时
myVar = undefined;

2. 重新赋值为 null

let myLet = 'some value';
// 当你想要"删除"这个变量时
myLet = null;

3. 使用 const 声明的常量

对于使用const声明的常量,由于它们的值不能被重新赋值,你不能将它们设置为undefinednull。但是,你可以通过将它们赋给一个不再使用的变量来实际上让它们不再被访问。

const myConst = 'some value';
// 你不能重新赋值给常量,但可以覆盖变量
myConst = 'another value'; // TypeError

十八、

const person = { name: "Lydia" };

Object.defineProperty(person, "age", { value: 21 });

console.log(person);
console.log(Object.keys(person));

输出:{ name: “Lydia”, age: 21 }, [“name”]

通过defineProperty方法,我们可以给对象添加一个新属性,或者修改已经存在的属性。而我们使用defineProperty方法给对象添加了一个属性之后,属性默认为 不可枚举 (not enumerable). Object.keys方法仅返回对象中 可枚举 (enumerable) 的属性,因此只剩下了"name".

defineProperty方法添加的属性默认不可变。你可以通过writable, configurableenumerable属性来改变这一行为。这样,defineProperty方法可以让您更好地控制要添加到对象的属性。

十九、JSON.stringify 的第二个参数是 替代者 (replacer).

JSON.stringify的第二个参数是 替代者 (replacer). 替代者 (replacer) 可以是个函数或数组,用以控制哪些值如何被转换为字符串。

如果替代者 (replacer) 是个 数组,那么就只有包含在数组中的属性将会被转化为字符串。在本例中,只有名为"level""health" 的属性被包括进来,"username"则被排除在外。data 就等于 "{"level":19, "health":90}".

而如果替代者 (replacer) 是个 函数,这个函数将被对象的每个属性都调用一遍。 函数返回的值会成为这个属性的值,最终体现在转化后的 JSON 字符串中(译者注:Chrome 下,经过实验,如果所有属性均返回同一个值的时候有异常,会直接将返回值作为结果输出而不会输出 JSON 字符串),而如果返回值为undefined,则该属性会被排除在外。

二十、push()方法返回新数组的长度

二十一、for-in循环:可以遍历一个对象自有的继承的可枚举的非 Symbol 的属性,在数组中,可枚举属性是数组元素的“键”,即它们的索引。for-of循环,我们可以迭代可迭代对象(包括 ArrayMapSetStringarguments等)

const myLifeSummedUp = ["☕", "💻", "🍷", "🍫"]

for (let item in myLifeSummedUp) {
  console.log(item)
}

for (let item of myLifeSummedUp) {
  console.log(item)
}

输出:0 1 2 3and”☕“ ”💻“ ”🍷“ ”🍫”

通过for-in循环,我们可以遍历一个对象自有的继承的可枚举的非 Symbol 的属性。在数组中,可枚举属性是数组元素的“键”,即它们的索引。类似于下面这个对象:

{0: "☕", 1: "💻", 2: "🍷", 3: "🍫"}

其中键则是可枚举属性,因此 0123被记录。

通过for-of循环,我们可以迭代可迭代对象(包括 ArrayMapSetStringarguments等)。当我们迭代数组时,在每次迭代中,不同属性的值将被分配给变量item,因此“☕”“💻”“🍷”“🍫”被打印。

二十二、constlet声明的变量是具有块级作用域的,块是大括号({})之间的任何东西

function checkAge(age) {
  if (age < 18) {
    const message = "Sorry, you're too young."
  } else {
    const message = "Yay! You're old enough!"
  }

  return message
}

console.log(checkAge(21))

输出:ReferenceError

constlet声明的变量是具有块级作用域的,块是大括号({})之间的任何东西,即上述情况if / else语句的花括号。由于块级作用域,我们无法在声明的块之外引用变量,因此抛出ReferenceError

const message = "Sorry, you're too young."是在if语句块中声明的,因此message变量只在if块内可见。同样,else块中的const message = "Yay! You're old enough!"也只在else块内可见。

二十三、箭头函数与常规函数的一个显著区别在于:箭头函数没有 prototype 属性,不能用作构造函数,不能使用 new 操作符创建实例

function giveLydiaPizza() {
  return "Here is pizza!"
}

const giveLydiaChocolate = () => "Here's chocolate... now go hit the gym already."

console.log(giveLydiaPizza.prototype)
console.log(giveLydiaChocolate.prototype)

输出:{ constructor: …} undefined

扩展:

Prototype 属性

常规函数有一个 prototype 属性,这是 JavaScript 用于构造函数继承的重要特性。每一个常规函数在创建时,都会自动带有一个 prototype 属性,它是一个对象,这个对象包含了一个 constructor 属性,指向该函数本身。这使得常规函数可以用作构造函数,用 new 操作符创建新的对象实例。例如:

function Person(name) {
  this.name = name;
}

const person1 = new Person('Lydia');

在这个例子中,Person 函数的 prototype 属性可以用来定义所有实例共享的方法和属性

Person.prototype.greet = function() {
  return `Hello, my name is ${this.name}`;
};

console.log(person1.greet()); // "Hello, my name is Lydia"

当你访问一个常规函数的 prototype 属性时,你会得到一个对象:

console.log(giveLydiaPizza.prototype); // { constructor: giveLydiaPizza }

箭头函数与常规函数的一个显著区别在于:箭头函数没有 prototype 属性。它们设计为简洁的非构造函数,因此不能用作构造函数,不能使用 new 操作符创建实例。由于这个原因,箭头函数也不需要 prototype 属性,因此访问时会返回 undefined

console.log(giveLydiaChocolate.prototype); // undefined

二十四、Symbol类型是不可枚举的。Object.keys方法返回对象上的所有可枚举的键属性

const info = {
  [Symbol('a')]: 'b'
}

console.log(info)
console.log(Object.keys(info))

输出:{Symbol(‘a’): ‘b’}and[]

Symbol类型是不可枚举的。Object.keys方法返回对象上的所有可枚举的键属性。Symbol类型是不可见的,并返回一个空数组。记录整个对象时,所有属性都是可见的,甚至是不可枚举的属性。

这是Symbol的众多特性之一:除了表示完全唯一的值(防止对象意外名称冲突,例如当使用 2 个想要向同一对象添加属性的库时),您还可以隐藏这种方式对象的属性(尽管不完全。你仍然可以使用Object.getOwnPropertySymbols()方法访问 Symbol

尽管 Symbol 属性不可枚举,但可以使用Object.getOwnPropertySymbols()方法获取对象上的所有 Symbol 属性:

const symbols = Object.getOwnPropertySymbols(info);
console.log(symbols); // [ Symbol(a) ]
console.log(info[symbols[0]]); // 'b'

当使用console.log()打印整个对象时,所有属性(包括不可枚举的 Symbol 属性)都会显示出来。这是因为console.log()展示的是对象的完整视图,而不仅仅是可枚举属性。

console.log(info); // { [Symbol(a)]: 'b' }

二十五、当值不是预期类型时,会抛出TypeErrors,当你编写了一些非有效的 JavaScript 时,会抛出语法错误

const name = "Lydia"

console.log(name())

输出:TypeError

当值不是预期类型时,会抛出TypeErrors。JavaScript 期望name是一个函数,因为我们试图调用它。但它是一个字符串,因此抛出TypeErrorname is not a function

当你编写了一些非有效的 JavaScript 时,会抛出语法错误,例如当你把return这个词写成retrun时。 当 JavaScript 无法找到您尝试访问的值的引用时,抛出ReferenceErrors

二十六、Promise 和其状态

Promise 是一种用于处理异步操作的对象。它代表一个在未来某个时间点可能完成或失败的操作及其结果值。Promise 有三种状态:

  • Pending(待定): 初始状态,既没有被解决(fulfilled),也没有被拒绝(rejected)。

  • Fulfilled(已解决): 意味着操作成功完成。

  • Rejected(已拒绝): 意味着操作失败。

  • 状态转换

    当我们创建一个 Promise,它默认处于pending状态。在某个时间点,Promise 可能会被解决(fulfilled)或拒绝(rejected)。

二十七、mapfilterslice返回一个新数组,find返回一个元素,而reduce返回一个减小的值。spliceunshift改变原数组

map 方法

map 方法用于通过对数组中的每个元素调用提供的函数,创建一个新数组。

array.map(callback(currentValue, index, array), thisArg)

filter 方法

filter 方法用于创建一个新数组,其中包含所有通过提供函数实现的测试的数组元素。

array.filter(callback(element, index, array), thisArg)

slice 方法

slice 方法用于从一个数组中返回选定的元素(浅拷贝),以新数组的形式返回。

array.slice(start, end)
  • start(可选): 从该索引开始提取元素(包含该元素)。

  • end(可选): 在该索引之前停止提取元素(不包含该元素)。如果未指定,则默认为数组的长度。

    const fruits = ['apple', 'banana', 'cherry', 'date'];
    const slicedFruits = fruits.slice(1, 3);
    console.log(slicedFruits); // ['banana', 'cherry']
    

find 方法

find 方法返回数组中满足提供的测试函数的第一个元素的值。如果没有找到这样的元素,则返回 undefined

const numbers = [1, 2, 3, 4, 5];
const found = numbers.find(num => num > 3);
console.log(found); // 4

splice 方法

splice 方法通过删除或替换现有元素或原地添加新元素来更改数组的内容,并返回被删除的元素(如果有)。

array.splice(start, deleteCount, item1, item2, ...)
  • start: 开始更改的索引位置。
  • deleteCount(可选): 一个整数,表示要移除的数组元素的数量。如果为 0,则不删除元素。
  • item1, item2, ...(可选): 要添加到数组的新元素。
const fruits = ['apple', 'banana', 'cherry', 'date'];
const removed = fruits.splice(1, 2, 'blueberry', 'blackberry');
console.log(fruits); // ['apple', 'blueberry', 'blackberry', 'date']
console.log(removed); // ['banana', 'cherry']

二十八、JSON.stringifyJSON.parse

JSON.stringify 方法

JSON.stringify 方法用于将 JavaScript 对象或值转换为 JSON 字符串。

JSON.stringify(value, replacer, space)
  • value: 要转换为 JSON 字符串的 JavaScript 值(通常是对象或数组)。
  • replacer(可选): 一个函数或数组,用于指定哪些属性应该被包含在 JSON 字符串中。
  • space(可选): 一个用于控制结果字符串缩进、空白和换行符的字符串或数字。
const obj = { name: "Alice", age: 25 };
const jsonString = JSON.stringify(obj);
console.log(jsonString); // '{"name":"Alice","age":25}'

// 使用 space 参数进行缩进
const jsonStringPretty = JSON.stringify(obj, null, 2);
console.log(jsonStringPretty);
/*
{
  "name": "Alice",
  "age": 25
}
*/

JSON.parse 方法

JSON.parse 方法用于将 JSON 字符串转换为 JavaScript 对象。

JSON.parse(text, reviver)
  • text: 要解析为 JavaScript 对象的 JSON 字符串。
  • reviver(可选): 一个用于转换结果的函数,在每个键值对被解析时调用。
const jsonString = '{"name":"Alice","age":25}';
const obj = JSON.parse(jsonString);
console.log(obj); // { name: 'Alice', age: 25 }

二十九、Object.prototype.toString.call()

console.log(Object.prototype.toString.call([])); // "[object Array]"
console.log(Object.prototype.toString.call({})); // "[object Object]"
console.log(Object.prototype.toString.call(null)); // "[object Null]"
console.log(Object.prototype.toString.call(undefined)); // "[object Undefined]"
console.log(Object.prototype.toString.call(function(){})); // "[object Function]"
console.log(Object.prototype.toString.call(/regex/)); // "[object RegExp]"
console.log(Object.prototype.toString.call(new Date())); // "[object Date]"
console.log(Object.prototype.toString.call(Symbol())); // "[object Symbol]"
console.log(Object.prototype.toString.call(10n)); // "[object BigInt]"

三十、

const handler = {
	set: () => console.log("Added a new property!"),
	get: () => console.log("Accessed a property!")
};

const person = new Proxy({}, handler);

person.name = "Lydia";
person.name;

输出:Added a new property! Accessed a property!

Proxy 对象

Proxy 对象用于定义基本操作(例如属性查找、赋值、枚举、函数调用等)的自定义行为。这些行为可以通过一个称为 “handler” 的对象来指定。

const proxy = new Proxy(target, handler);
  • target:要包装的目标对象(可以是任何类型的对象,包括函数)。
  • handler:包含一组定义捕捉器(trap)的对象,这些捕捉器拦截对目标对象的操作。

handler 对象和捕捉器

handler 对象是一个普通的 JavaScript 对象,其中的属性是捕捉器函数。这些函数会在相应的操作被执行时调用。常用的捕捉器包括:

  • get(target, property, receiver):拦截对象属性的读取。
  • set(target, property, value, receiver):拦截对象属性的设置。
  • has(target, property):拦截 in 操作符。
  • deleteProperty(target, property):拦截 delete 操作符。
  • apply(target, thisArg, argumentsList):拦截函数调用。
  • construct(target, args, newTarget):拦截 new 操作符。
Not-By-AI