1.数组的静态方法 #

1.1 Array.of #

let arr = Array.of(1, 2, 3); // [1, 2, 3]
let arr = new Array(3); // [undefined, undefined, undefined]
let arr = Array.of(3); // [3]

1.2 Array.isArray #

Array.isArray() 是一个 JavaScript 的内置函数,用于确定传递的值是否是一个 Array。

这个方法在引入之前,判断一个变量是否为数组类型有些困难,因为在 JavaScript 中,数组其实是一种特殊的对象。如果你试图用 typeof 来判断一个数组,你会得到 "object",而不是 "array"。

let arr = [1, 2, 3];
console.log(typeof arr);  // "object"

因此,Array.isArray() 方法应运而生,专门用来判断一个值是否为数组:

let arr = [1, 2, 3];
console.log(Array.isArray(arr));  // true

let obj = {foo: 'bar'};
console.log(Array.isArray(obj));  // false

let string = "Hello, world!";
console.log(Array.isArray(string));  // false

如果传递给 Array.isArray() 的值是一个数组,那么它就会返回 true,否则返回 false。这个方法对于在 JavaScript 中正确识别数组类型非常有用。

1.3 Array.from #

Array.from 是JavaScript的一个内置函数,它可以把不是真正数组的对象转换成真正的数组。它通常用于类似数组的对象或者可迭代的对象。

这个方法的基本语法如下:

Array.from(arrayLike[, mapFn[, thisArg]])

这个函数接受三个参数:

  1. arrayLike (必须):一个类似数组或者可迭代的对象,这个对象将会被转化成一个新的数组。

  2. mapFn (可选):这是一个映射函数,类似 Array.prototype.map。如果提供了这个参数,新产生的数组将通过这个函数进行映射。

  3. thisArg (可选):当调用 mapFn 的时候,这个值会作为 mapFnthis 参数。如果没有提供,那么 undefined 将会被用作 this

下面是一些使用 Array.from 的例子:

  1. 将类似数组的对象转换为数组:
let arrayLike = {0: 'a', 1: 'b', 2: 'c', length: 3};
let arr = Array.from(arrayLike);
console.log(arr);  // 输出: ['a', 'b', 'c']
  1. 将 Set 转化为数组:
let set = new Set(['a', 'b', 'c']);
let arr = Array.from(set);
console.log(arr);  // 输出: ['a', 'b', 'c']
  1. 将字符串转化为数组:
let str = 'hello';
let arr = Array.from(str);
console.log(arr);  // 输出: ['h', 'e', 'l', 'l', 'o']
  1. 使用映射函数:
let arr = Array.from([1, 2, 3], x => x * x);
console.log(arr);  // 输出: [1, 4, 9]

注意,Array.fromArray.prototype.slice 有些类似,但是 Array.from 可以接受任何可迭代的对象,而 Array.prototype.slice 只能用于真正的数组或者类似数组的对象。

2. 数组的实例方法 #

2.1 filter #

filter() 是 JavaScript 中的一个数组方法,它创建一个新数组,包含通过一个提供的函数实现的测试的所有元素。 filter() 方法接受一个函数作为参数,这个函数可以接受三个参数:

这是 filter() 的基本语法:

let newArray = arr.filter(function(currentValue, index, array) {
  // 你的代码
});

这个函数应该返回一个布尔值:

这里有一些使用 filter() 的例子:

let arr = [1, 2, 3, 4, 5];

// 创建一个新数组,只包含原数组中的偶数
let evenArr = arr.filter(function(item) {
  return item % 2 === 0;
});
console.log(evenArr); // 输出: [2, 4]

// 创建一个新数组,只包含原数组中大于2的元素
let greaterThanTwoArr = arr.filter(function(item) {
  return item > 2;
});
console.log(greaterThanTwoArr); // 输出: [3, 4, 5]

需要注意的是,filter() 方法不会改变原数组,而是返回一个新的数组。这使得 filter() 方法在处理数组时更加纯净,不会影响原数组。

2.2 find #

find() 是 JavaScript 中的一个数组方法,它返回数组中满足提供的测试函数的第一个元素的值。否则返回 undefined。 find() 方法接受一个函数作为参数,这个函数可以接受三个参数:

这是 find() 的基本语法:

let foundValue = arr.find(function(currentValue, index, array) {
  // 你的代码
});

这个函数应该返回一个布尔值:

let arr = [1, 2, 3, 4, 5];

// 找到数组中的第一个偶数
let firstEven = arr.find(function(item) {
  return item % 2 === 0;
});
console.log(firstEven); // 输出: 2

// 找到数组中的第一个大于2的元素
let firstGreaterThanTwo = arr.find(function(item) {
  return item > 2;
});
console.log(firstGreaterThanTwo); // 输出: 3

需要注意的是,find() 方法不会改变原数组。另外,一旦找到满足条件的元素,find() 就会立即返回,不再继续检查剩余的元素。

2.3 findIndex #

findIndex() 是 JavaScript 中的一个数组方法,它返回数组中满足提供的测试函数的第一个元素的索引。如果没有找到任何元素,则返回 -1。 findIndex() 方法接受一个函数作为参数,这个函数可以接受三个参数:

这是 findIndex() 的基本语法:

let foundIndex = arr.findIndex(function(currentValue, index, array) {
  // 你的代码
});

这个函数应该返回一个布尔值:

这里有一些使用 findIndex() 的例子:

let arr = [1, 2, 3, 4, 5];

// 找到数组中的第一个偶数的索引
let firstEvenIndex = arr.findIndex(function(item) {
  return item % 2 === 0;
});
console.log(firstEvenIndex); // 输出: 1

// 找到数组中的第一个大于2的元素的索引
let firstGreaterThanTwoIndex = arr.findIndex(function(item) {
  return item > 2;
});
console.log(firstGreaterThanTwoIndex); // 输出: 2

需要注意的是,findIndex() 方法不会改变原数组。另外,一旦找到满足条件的元素,findIndex() 就会立即返回,不再继续检查剩余的元素。

2.4 every #

let result = arr.every(function(currentValue, index, array) {
  // 你的代码
});

这个函数应该返回一个布尔值:

这里有一些使用 every() 的例子:

let arr = [1, 2, 3, 4, 5];

// 检查数组中的所有元素是否都是数字
let allNumbers = arr.every(function(item) {
  return typeof item === 'number';
});
console.log(allNumbers); // 输出: true

// 检查数组中的所有元素是否都大于0
let allPositive = arr.every(function(item) {
  return item > 0;
});
console.log(allPositive); // 输出: true

// 检查数组中的所有元素是否都大于2
let allGreaterThanTwo = arr.every(function(item) {
  return item > 2;
});
console.log(allGreaterThanTwo); // 输出: false

需要注意的是,every() 方法不会改变原数组。另外,一旦有元素没有通过测试,every() 就会立即返回,不再继续检查剩余的元素。

2.5 some #

some() 是 JavaScript 中的一个数组方法,它测试数组中是否有至少一个元素通过了指定函数的测试。 some() 方法接受一个函数作为参数,这个函数可以接受三个参数:

这是 some() 的基本语法:

let result = arr.some(function(currentValue, index, array) {
  // 你的代码
});

这个函数应该返回一个布尔值:

这里有一些使用 some() 的例子:

let arr = [1, 2, 3, 4, 5];

// 检查数组中是否有元素大于4
let anyGreaterThanFour = arr.some(function(item) {
  return item > 4;
});
console.log(anyGreaterThanFour); // 输出: true

// 检查数组中是否有元素等于0
let anyZero = arr.some(function(item) {
  return item === 0;
});
console.log(anyZero); // 输出: false

需要注意的是,some() 方法不会改变原数组。另外,一旦有元素通过了测试,some() 就会立即返回,不再继续检查剩余的元素

2.6 reduce #

reduce() 是 JavaScript 中的一个数组方法,它对数组中的每个元素执行一个 reducer 函数(由您提供),将其结果汇总为单个输出值。 reduce() 方法接受两个参数:

这是 reduce() 的基本语法:

let result = arr.reduce(function(accumulator, currentValue, index, array) {
  // 你的代码
}, initialValue);

这里有一些使用 reduce() 的例子:

let arr = [1, 2, 3, 4, 5];

// 计算数组中所有元素的和
let sum = arr.reduce(function(acc, item) {
  return acc + item;
}, 0);
console.log(sum); // 输出: 15

// 计算数组中所有元素的乘积
let product = arr.reduce(function(acc, item) {
  return acc * item;
}, 1);
console.log(product); // 输出: 120

需要注意的是,reduce() 方法不会改变原数组。另外,reduce() 对于空数组是没有默认值的,除非在调用 reduce() 时提供了 initialValue。如果没有提供 initialValue,则 reduce() 会从数组的第二个元素开始执行 callback 函数。

2.7 reduceRight #

reduceRight() 是 JavaScript 中的一个数组方法,它与 reduce() 方法类似,但是遍历的方向是从数组的末尾开始,而不是从头开始。 reduceRight() 方法接受两个参数:

这是 reduceRight() 的基本语法:

let result = arr.reduceRight(function(accumulator, currentValue, index, array) {
  // 你的代码
}, initialValue);

这里有一些使用 reduceRight() 的例子:

let arr = [1, 2, 3, 4, 5];

// 使用 reduceRight 计算数组中所有元素的和
let sum = arr.reduceRight(function(acc, item) {
  return acc + item;
}, 0);
console.log(sum); // 输出: 15

// 使用 reduceRight 计算数组中所有元素的乘积
let product = arr.reduceRight(function(acc, item) {
  return acc * item;
}, 1);
console.log(product); // 输出: 120

需要注意的是,reduceRight() 方法不会改变原数组。另外,reduceRight() 对于空数组是没有默认值的,除非在调用 reduceRight() 时提供了 initialValue。如果没有提供 initialValue,则 reduceRight() 会从数组的倒数第二个元素开始执行 callback 函数。

2.8 fill #

fill() 是 JavaScript 中的一个数组方法,它可以用一个固定值来填充一个数组,或者数组中的一部分。这个方法会改变原数组。

fill() 方法接受三个参数:

这是 fill() 的基本语法:

arr.fill(value[, start[, end]])

这里有一些使用 fill() 的例子:

let arr = [1, 2, 3, 4, 5];

// 用0填充整个数组
arr.fill(0);
console.log(arr); // 输出: [0, 0, 0, 0, 0]

// 用'foo'填充数组的第2个到第4个元素(不包括第4个)
arr.fill('foo', 1, 3);
console.log(arr); // 输出: [0, 'foo', 'foo', 0, 0]

fill() 方法会直接修改原数组,而不是创建一个新的数组。如果你不想改变原数组,你可能需要先复制这个数组,然后再使用 fill() 方法。

3. 对象的实例方法 #

3.1 constructor #

在JavaScript中,每个对象都有一个 constructor 属性。这个属性返回一个函数,该函数就是创建这个对象实例的构造函数。如果一个对象是通过特定的构造函数创建的,那么它的 constructor 属性就指向那个构造函数。

来看一个例子:

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

let john = new Person("John");

console.log(john.constructor); // 输出:[Function: Person]

在这个例子中,我们定义了一个名为 Person 的构造函数,并使用它来创建一个新的对象 john。当我们查看 johnconstructor 属性时,我们可以看到它指向 Person 函数。

需要注意的是,constructor 属性可以被更改,这意味着它可能不总是返回创建对象的实际构造函数。此外,像 {}[] 这样的字面量对象也有 constructor 属性,分别指向 ObjectArray 构造函数。

let obj = {};
console.log(obj.constructor); // 输出:[Function: Object]

let arr = [];
console.log(arr.constructor); // 输出:[Function: Array]

在一些情况下,可以使用 constructor 属性来判断一个变量是否属于特定的数据类型。例如,要检查变量 john 是否是 Person 类型,可以这样做:

console.log(john instanceof Person); // 输出:true

或者:

console.log(john.constructor === Person); // 输出:true

然而,由于 constructor 属性可以被更改,所以 instanceof 操作符通常是更可靠的方式来检查一个对象的类型。

3.2 hasOwnProperty #

hasOwnProperty 是 JavaScript 中 Object 类型的一个重要的方法。这个方法是用来检查一个对象是否有某个特定的自有属性。

所谓的“自有属性”就是直接定义在这个对象上的属性,而不是从它的原型链上继承下来的。在 JavaScript 中,所有的对象都从 Object.prototype 继承,因此很多常见的方法(例如 toString())其实是定义在 Object.prototype 上的,而不是每个对象自己有的。

hasOwnProperty 的使用方式是 obj.hasOwnProperty(prop),其中 obj 是你要检查的对象,prop 是你想要查找的属性名(字符串)。它会返回一个布尔值,如果 objprop 这个自有属性,就返回 true,否则返回 false

下面是一个简单的例子:

let obj = {
  a: 1,
  b: 2
};

console.log(obj.hasOwnProperty('a'));  // 输出:true
console.log(obj.hasOwnProperty('b'));  // 输出:true
console.log(obj.hasOwnProperty('c'));  // 输出:false
console.log(obj.hasOwnProperty('toString'));  // 输出:false

在这个例子中,obj 有自有属性 ab,但是没有 ctoStringtoString 实际上是从 Object.prototype 继承过来的,所以 hasOwnProperty 返回 false

注意,在使用 hasOwnProperty 时,要小心对象可能没有继承自 Object.prototype,这种情况下直接使用 obj.hasOwnProperty 会抛出错误。可以使用 Object.prototype.hasOwnProperty.call(obj, prop) 来避免这个问题。

3.3 isPrototypeOf #

isPrototypeOf 是 JavaScript 中 Object 类型的一个方法,这个方法用于检测调用此方法的对象是否出现在指定对象的原型链上。

所谓"原型链"是 JavaScript 的一个重要概念。每个对象都有一个原型对象,从这个原型对象上可以继承属性和方法。原型对象也有它自己的原型,形成了一个链式的结构,这就是原型链。如果我们试图访问一个对象的某个属性,JavaScript 会首先在这个对象本身查找,如果没有找到,就会去它的原型对象中查找,如果还没有找到,就会继续往上查找,直到找到为止。

isPrototypeOf 的使用方式是 prototypeObj.isPrototypeOf(obj),其中 prototypeObj 是可能出现在 obj 的原型链上的对象。如果 prototypeObjobj 的原型链上,那么这个方法就会返回 true,否则返回 false

下面是一个简单的例子:

function MyConstructor() {}
let myObj = new MyConstructor();

console.log(MyConstructor.prototype.isPrototypeOf(myObj)); // 输出:true
console.log(Object.prototype.isPrototypeOf(myObj)); // 输出:true
console.log(MyConstructor.prototype.isPrototypeOf({})); // 输出:false

在这个例子中,myObj 是通过 MyConstructor 构造的,所以 MyConstructor.prototypemyObj 的直接原型。而 Object.prototype 是所有对象的最顶级原型,所以它也在 myObj 的原型链上。但是对于一个普通的对象 {}MyConstructor.prototype 不在它的原型链上,所以 isPrototypeOf 返回 false

3.4 propertyIsEnumerable #

propertyIsEnumerable 是 JavaScript 中 Object 类型的一个方法,这个方法用于检查指定的属性是否可枚举。

"可枚举"在 JavaScript 中是指一个属性是否可以通过 for...in 循环或 Object.keys() 等方法遍历出来。在创建一个对象属性的时候,我们可以通过属性描述符来设置这个属性是否可枚举(默认是可枚举的)。

propertyIsEnumerable 的使用方式是 obj.propertyIsEnumerable(prop),其中 obj 是你要检查的对象,prop 是你想要查找的属性名(字符串)。如果 objprop 属性是可枚举的,那么这个方法就会返回 true,否则返回 false

下面是一个简单的例子:

let obj = {
  a: 1,
  b: 2
};

console.log(obj.propertyIsEnumerable('a'));  // 输出:true
console.log(obj.propertyIsEnumerable('b'));  // 输出:true
console.log(obj.propertyIsEnumerable('toString'));  // 输出:false

在这个例子中,obj 有自有属性 ab,它们都是可枚举的,所以 propertyIsEnumerable 返回 true。但 toString 属性是从 Object.prototype 继承的,它不可枚举,所以 propertyIsEnumerable 返回 false

另外,即使一个对象有某个自有属性,如果这个属性是不可枚举的,propertyIsEnumerable 也会返回 false。例如,如果我们用 Object.defineProperty 创建一个不可枚举的属性,propertyIsEnumerable 就会返回 false

let obj = {};

Object.defineProperty(obj, 'a', {
  value: 1,
  enumerable: false  // 设置为不可枚举
});

console.log(obj.propertyIsEnumerable('a'));  // 输出:false

3.5 valueOf #

valueOf 是 JavaScript 中所有对象都拥有的方法,它属于 Object 原型对象 (Object.prototype) 的一个成员。这个方法的主要目的是将一个对象转换为原始值(primitive value)。

在 JavaScript 中,对象的原始值通常用于比较和运算。例如,当我们试图将一个对象用于数学运算(如 +, -, *, / 等),或者将其与其他原始类型(如字符串,数值)进行比较时,JavaScript 引擎会自动调用对象的 valueOf 方法来获取其原始值。

默认情况下,valueOf 方法直接返回对象本身,因为大多数对象没有真正的“原始值”。但是一些内置对象类型(如 Date,Number,String 和 Boolean)会重写 valueOf 方法,使其返回一个更有意义的原始值。

例如,对于 Date 对象,valueOf 方法会返回表示该日期的毫秒数(自1970年1月1日00:00:00 UTC以来的毫秒数)。对于 Number 和 Boolean 对象,valueOf 方法会返回对应的原始数值或布尔值。

let myDate = new Date();
console.log(myDate.valueOf());  // 输出:一个毫秒数

let myNumber = new Number(123);
console.log(myNumber.valueOf());  // 输出:123

let myBoolean = new Boolean(true);
console.log(myBoolean.valueOf());  // 输出:true

对于自定义对象,如果你想在将其用于比较和运算时得到一个特定的原始值,你可以重写 valueOf 方法:

let myObj = {
  value: 123,
  valueOf: function() {
    return this.value;
  }
};

console.log(myObj + 1);  // 输出:124,因为 myObj.valueOf() 返回 123

3.6 toString #

JavaScript中的 toString 是 Object 的一个原型方法,几乎所有的对象都会继承这个方法。它的主要目的是返回一个表示该对象的字符串。

当你尝试将一个对象转换为字符串,比如在字符串拼接或输出到控制台等情况下,JavaScript 会自动调用 toString 方法。例如,当你使用 console.log(obj) 输出一个对象时,控制台实际上显示的是 obj.toString() 的结果。

默认情况下,Object.prototype 的 toString 方法返回一个字符串,格式如 [object Type],其中 "Type" 是对象的类型。例如,对于一个普通的对象,toString 会返回 "[object Object]":

let obj = {};
console.log(obj.toString());  // 输出:"[object Object]"

但是,许多内置对象类型会重写 toString 方法,使其返回一个更有意义的字符串。例如,Date 对象的 toString 方法会返回一个表示该日期的字符串,Array 对象的 toString 方法会返回一个由数组元素组成的字符串:

let myDate = new Date();
console.log(myDate.toString());  // 输出:"Tue May 24 2023 15:02:40 GMT-0700 (Pacific Daylight Time)"

let myArray = [1, 2, 3];
console.log(myArray.toString());  // 输出:"1,2,3"

如果你想要自定义对象的 toString 方法,你可以这样做:

let person = {
  firstName: "John",
  lastName: "Doe",
  toString: function() {
    return this.firstName + " " + this.lastName;
  }
};

console.log(person.toString());  // 输出:"John Doe"

在这个例子中,我们重写了 person 对象的 toString 方法,使其返回一个表示这个人的全名的字符串。

3.7 toLocaleString #

toLocaleString 是 JavaScript 中 Object 类型的一个方法,用于返回一个对象的字符串表示,这个字符串表示应当考虑到当前的区域设置 (locale)。

"区域设置"通常包括一些地理或文化相关的信息,比如语言,数字格式,日期格式,货币格式等。对于许多内置对象类型(如 Date,Number,Array),它们会重写 toLocaleString 方法,使其返回一个符合当前区域设置的字符串。

例如,对于 Date 对象,toLocaleString 方法会返回一个表示该日期的字符串,这个字符串的格式符合当前的语言和地区:

let myDate = new Date();
console.log(myDate.toLocaleString());  // 输出的字符串会根据你的区域设置有所不同

对于 Number 对象,toLocaleString 方法会返回一个表示该数值的字符串,这个字符串的数字和小数点格式符合当前的语言和地区:

let myNumber = 123456.789;
console.log(myNumber.toLocaleString());  // 输出的字符串会根据你的区域设置有所不同

对于一个普通的对象,toLocaleString 默认情况下等同于 toString 方法。但是,你可以重写 toLocaleString 方法,让它返回一个符合特定区域设置的字符串。例如:

let person = {
  firstName: "John",
  lastName: "Doe",
  toLocaleString: function() {
    // 假设我们在一个将姓放在名字前面的区域
    return this.lastName + " " + this.firstName;
  }
};

console.log(person.toLocaleString());  // 输出:"Doe John"

在这个例子中,我们重写了 person 对象的 toLocaleString 方法,使其返回一个在某些区域更常见的名字格式。

4. 对象的静态方法 #

4.1 Symbol #

在 JavaScript 中,Symbol 是一种特殊的原始数据类型(primitive data type),具有唯一性,它是在 ES6(也就是 ECMAScript 2015)中引入的新特性。Symbol 的主要用途是作为对象属性的键(keys),但这种键是隐藏的,不会出现在常规的对象键枚举中。

Symbol 的基本用法如下:

let sym1 = Symbol();

let obj = {
    [sym1]: "value"
};

console.log(obj[sym1]); // 输出 "value"

在上面的例子中,sym1 是一个新的 Symbol。我们用它作为对象 obj 的一个属性键,然后我们可以通过这个 Symbol 来访问该属性。

现在,让我们来谈谈 Symbol.for

Symbol.for(key) 方法会首先在全局 symbol 注册表中搜索给定键的 symbol。如果这个 symbol 被找到,那么它将被返回。如果没有找到,那么 Symbol.for 会创建一个新的 symbol,并将其与给定的键在全局 symbol 注册表中关联起来,然后返回这个新的 symbol。

let sym2 = Symbol.for("key"); // 创建一个新的 symbol,并将其与键 "key" 关联
let sym3 = Symbol.for("key"); // 搜索全局 symbol 注册表,找到与键 "key" 关联的 symbol

console.log(sym2 === sym3); // 输出 true

在这个例子中,我们首先用 Symbol.for 创建了一个新的 symbol,并将其与键 "key" 关联。然后我们再次调用 Symbol.for("key"),它将在全局 symbol 注册表中搜索与键 "key" 关联的 symbol。因为我们之前已经创建了这个 symbol,所以 Symbol.for 就返回了它,而不是创建一个新的 symbol。

需要注意的是,Symbol()Symbol.for() 创建的 symbol 是不同的。Symbol() 每次都会创建一个新的、全局唯一的 symbol,而 Symbol.for() 则会首先尝试在全局 symbol 注册表中查找,只有当找不到的时候才会创建一个新的 symbol。

let sym4 = Symbol("key");
let sym5 = Symbol.for("key");

console.log(sym4 === sym5); // 输出 false

在这个例子中,sym4sym5 都与同一个键 "key" 关联,但是 sym4 是用 Symbol() 创建的,而 sym5 是用 Symbol.for() 创建的。即使它们的键是相同的,但是它们是不同的 symbol,所以 sym4 === sym5 的结果是 false

Symbol("key")中,"key"被称为符号描述符(Symbol Description)。这只是一个字符串,用于描述创建的 Symbol,但它并不影响 Symbol 的唯一性。这个描述只是为了在调试过程中易于识别符号,它并不影响符号的行为。例如,如果你打印一个符号,JavaScript 将显示它的描述:

let sym1 = Symbol("key");
console.log(sym1); // 输出 "Symbol(key)"

然而,即使两个 Symbol 的描述是相同的,这两个 Symbol 仍然是不同的。每次你调用 Symbol(),它总是创建一个新的,全局唯一的 Symbol

let sym2 = Symbol("key");
let sym3 = Symbol("key");

console.log(sym2 === sym3); // 输出 false

在这个例子中,尽管 sym2sym3 都是用相同的描述 "key" 创建的,但是它们是不同的符号,所以 sym2 === sym3 的结果是 false

4.2 获取数据对比 #

方法 是否包含不可枚举属性 是否包含继承属性 是否包含Symbol
getOwnPropertyNames
getOwnPropertyDescriptors
getOwnPropertySymbols
keys
values
entries

这些方法都是JavaScript中的Object方法,它们的功能如下:

let proto = {};
Object.defineProperty(proto, 'fromProto', {
    value:'fromProto'
});
let obj = {};
Object.defineProperties(obj, {
    'enumerableTrue': {
        value: 'true',
        enumerable: true
    },
    'enumerableFalse': {
        value: 'false',
        enumerable: false
    },
    [Symbol('symbol')]: {
        value: 'symbol',
    },
    [Symbol.for('symbolFor')]: {
        value: 'symbolFor',
    }
});
Object.setPrototypeOf(obj, proto);
console.log(Object.getOwnPropertyNames(obj));//[ 'enumerableTrue', 'enumerableFalse' ]
console.log(Object.getOwnPropertyDescriptors(obj));//[ 'enumerableTrue', 'enumerableFalse',Symbol(symbol),Symbol(symbolFor) ]
console.log(Object.getOwnPropertySymbols(obj));//[ Symbol(symbol), Symbol(symbolFor) ]
console.log(Object.keys(obj));//[ 'enumerableTrue' ]
console.log(Object.values(obj));//[ 'true' ]
console.log(Object.entries(obj));//[ [ 'enumerableTrue', 'true' ] ]

4.3 属性对比 #

方法 是否可以修改现有属性 是否可以添加新属性 是否可以删除老属性
freeze
seal
preventExtensions

这些方法都是JavaScript中的Object方法,它们的功能如下:

4.4 defineProperty #

Object.defineProperty() 是一个 JavaScript 的内置函数,它可以用来在一个对象上定义新的属性或修改现有属性,并返回该对象。新属性或修改的属性通过一个描述符来描述。

属性描述符可以有以下属性:

注意,一个属性描述符必须是 valuewritable 或者 getset 的组合,不能混合这两组。

以下是 Object.defineProperty() 的使用示例:

var obj = {};

Object.defineProperty(obj, 'property1', {
  value: true,
  writable: true
});

console.log(obj.property1); // 输出:true

在上述代码中,我们使用 Object.defineProperty() 在一个空对象 obj 上定义了一个属性 property1property1 的值为 true,并且可以被改写。然后,我们输出这个属性的值,可以看到它已经被成功地添加到了对象上。

如果你试图更改一个不可写的属性的值,那么在严格模式下,JavaScript 会抛出一个错误。在非严格模式下,这种尝试会被静默地忽略。

4.5 defineProperties #

Object.defineProperties() 是一个 JavaScript 的内置函数,它可以用来在一个对象上定义新的属性或修改现有属性,并返回该对象。每个新的属性都通过各自的描述符来描述。

每个属性描述符可以有以下属性:

注意,一个属性描述符必须是 valuewritable 或者 getset 的组合,不能混合这两组。

以下是 Object.defineProperties() 的使用示例:

var obj = {};

Object.defineProperties(obj, {
  'property1': {
    value: true,
    writable: true
  },
  'property2': {
    value: 'Hello',
    writable: false
  }
});

console.log(obj.property1); // 输出:true
console.log(obj.property2); // 输出:'Hello'

在上述代码中,我们使用 Object.defineProperties() 在一个空对象 obj 上定义了两个属性:property1property2property1 的值为 true,并且可以被改写;而 property2 的值为 'Hello',并且不能被改写。然后,我们输出这两个属性的值,可以看到它们已经被成功地添加到了对象上。

如果你试图更改 property2 的值,那么在严格模式下,JavaScript 会抛出一个错误。在非严格模式下,这种尝试会被静默地忽略。

4.6 assign #

Object.assign() 是 JavaScript 中的一个静态方法,它用于将所有可枚举属性的值从一个或多个源对象复制到目标对象。它将返回目标对象。

Object.assign() 方法的第一个参数是目标对象,后面的参数都是源对象。它会按照参数的顺序,将源对象的属性复制到目标对象,如果有相同的属性,后面的源对象会覆盖前面的源对象。

这是一个简单的例子:

let target = { a: 1, b: 2 };
let source1 = { b: 3, c: 4 };
let source2 = { c: 5 };

Object.assign(target, source1, source2);

console.log(target);  // 输出:{ a: 1, b: 3, c: 5 }

在这个例子中,source1b 属性覆盖了 targetb 属性,然后 source2c 属性覆盖了 source1c 属性。

注意,Object.assign() 只会进行浅复制,也就是说,如果源对象的属性值是一个对象,那么它只会复制引用,而不会复制这个对象本身。这意味着如果你修改了这个属性值对象,源对象和目标对象都会受到影响。

let target = {};
let source = { a: { b: 1 } };

Object.assign(target, source);

console.log(target);  // 输出:{ a: { b: 1 } }

source.a.b = 2;
console.log(target);  // 输出:{ a: { b: 2 } },target 也被改变了!

如果你想进行深复制,需要使用其他的方法,比如通过 JSON.parse(JSON.stringify(obj)) 可以创建一个对象的深复制,但是这种方法有一些局限性,例如它不能复制函数和循环引用。

4.7 create #

Object.create() 是 JavaScript 中的一个静态方法,它创建一个新对象,使用现有的对象来提供新创建的对象的 __proto__。换句话说,Object.create() 方法创建一个新对象,并设置这个新对象的原型为指定的对象。

Object.create() 方法的第一个参数是作为新创建对象原型的对象。这个方法还有一个可选的第二个参数,用于定义新对象的属性,这个参数应该是一个属性描述符对象。

以下是一个简单的例子:

let person = {
  isHuman: false,
  printIntroduction: function() {
    console.log(`My name is ${this.name}. Am I human? ${this.isHuman}`);
  }
};

let me = Object.create(person);

me.name = 'Matthew'; // "name" 是 "me" 的属性
me.isHuman = true; // 覆盖了原型的属性

me.printIntroduction();
// 输出: 'My name is Matthew. Am I human? true'

在这个例子中,我们首先定义了一个 person 对象,它有一个 isHuman 属性和一个 printIntroduction 方法。然后我们用 Object.create(person) 创建了一个新对象 me,并设置 me 的原型为 person。这意味着 me 会继承 person 的所有属性和方法。然后我们给 me 添加了一个 name 属性,并修改了 isHuman 属性。

Object.create() 也常常用来实现类的继承。假设我们有一个 Animal 对象,我们可以用 Object.create(Animal) 来创建一个 Dog 对象,然后在 Dog 对象上添加或修改一些属性和方法,以实现 Dog 对象从 Animal 对象继承的效果。

4.8 entries #

Object.entries() 是 JavaScript 中的一个静态方法,它返回一个数组,这个数组的每个元素都是一个包含属性键和值的数组,这些属性都是对象的自有可枚举属性。

这是 Object.entries() 方法的签名:

Object.entries(object)

这个方法接受一个参数,就是你要获取其键值对的对象。

这是一个简单的例子:

let obj = {
  a: 1,
  b: 2,
  c: 3
};

let entries = Object.entries(obj);

console.log(entries);  // 输出:[["a", 1], ["b", 2], ["c", 3]]

在这个例子中,obj 对象有三个自有属性:ab,和 c。所以 Object.entries(obj) 返回一个数组,这个数组的每个元素都是一个包含属性键和值的数组。

注意,Object.entries() 只返回可枚举的自有属性的键值对,不返回不可枚举的属性的键值对和继承的属性的键值对。

此外,Object.entries() 返回的数组中的键值对的顺序与它们在对象中的顺序相同。这个顺序是由 JavaScript 引擎决定的,通常是按照属性被添加到对象的顺序。

Object.entries() 方法常常用于遍历一个对象的所有可枚举的自有属性。例如,你可以这样遍历 obj 对象的所有属性:

Object.entries(obj).forEach(([key, value]) => {
  console.log(`Key: ${key}, Value: ${value}`);
});

这段代码首先用 Object.entries(obj) 获取 obj 对象的所有键值对,然后用 Array.prototype.forEach() 遍历这些键值对。在 forEach() 的回调函数中,我们用 [key, value] 结构每个键值对的数组,得到每个属性的键和值。

Object.entries() 方法也可以用于将对象转换为 Map:

let map = new Map(Object.entries(obj));

console.log(map.get('a'));  // 输出:1

在这个例子中,new Map(Object.entries(obj)) 创建了一个新的 Map,这个 Map 的键值对与 obj 对象的属性键值对相同。然后我们用 map.get('a') 获取 'a' 键对应的值。

4.9 fromEntries #

Object.fromEntries() 是 JavaScript 中的一个静态方法,它可以将一个键值对的数组(或者其他可迭代对象)转化为一个新的对象。

这个方法的参数应该是一个可迭代对象,比如数组,Set,Map 等。这个可迭代对象的每个元素都应该是一个键值对(比如长度为2的数组)。这个方法会将这些键值对转化为新对象的属性。

以下是一个简单的例子:

let entries = [
  ['name', 'John'],
  ['age', 25],
  ['city', 'New York']
];

let obj = Object.fromEntries(entries);

console.log(obj);  // 输出:{ name: 'John', age: 25, city: 'New York' }

在这个例子中,我们首先定义了一个数组 entries,这个数组的每个元素都是一个键值对。然后我们用 Object.fromEntries(entries) 将这个数组转化为一个新的对象 obj

Object.fromEntries()Object.entries() 方法的逆操作。Object.entries() 可以将一个对象的所有可枚举属性转化为一个键值对的数组。所以我们可以用 Object.entries()Object.fromEntries() 来进行对象的深复制:

let obj1 = { name: 'John', age: 25, city: 'New York' };
let obj2 = Object.fromEntries(Object.entries(obj1));

console.log(obj2);  // 输出:{ name: 'John', age: 25, city: 'New York' }

在这个例子中,Object.entries(obj1)obj1 转化为一个键值对的数组,然后 Object.fromEntries() 将这个数组转化为一个新的对象 obj2。因为 Object.fromEntries() 总是创建一个新的对象,所以 obj1obj2 是完全独立的,修改 obj2 不会影响 obj1

4.10 getPrototypeOf #

Object.getPrototypeOf() 是 JavaScript 中的一个静态方法,用于获取一个对象的原型。如果对象没有原型(即,这个对象是通过 Object.create(null) 创建的),则返回 null

这是一个简单的例子:

let animal = {
  eats: true
};

let rabbit = Object.create(animal);

console.log(Object.getPrototypeOf(rabbit) === animal);  // 输出:true
console.log(Object.getPrototypeOf(animal) === Object.prototype);  // 输出:true

在这个例子中,我们首先创建了一个 animal 对象,然后我们用 Object.create(animal) 创建了一个 rabbit 对象,设置 rabbit 的原型为 animal。所以 Object.getPrototypeOf(rabbit) 返回 animal。而 animal 的原型是 Object.prototype,所以 Object.getPrototypeOf(animal) 返回 Object.prototype

注意,Object.getPrototypeOf() 获取的是对象的原型,而不是这个对象的构造函数的 prototype 属性。对于通过构造函数创建的对象,这两者是相同的。但是对于通过 Object.create() 创建的对象,这两者可能不同。例如,在上面的例子中,rabbit 的原型是 animal,而不是 Object.prototype

虽然 JavaScript 中的对象都有一个原型,但是不推荐直接修改原型。因为这样可能会影响到所有继承自这个原型的对象,从而导致不可预见的结果。如果你想在对象之间共享属性或方法,应该使用类或者 Object.create() 方法来明确地设置新对象的原型。

4.11 setPrototypeOf #

Object.setPrototypeOf() 是 JavaScript 中的一个静态方法,它主要用于设置一个对象的原型(即内部 [[Prototype]] 属性)为另一个对象或 null。

这是 Object.setPrototypeOf() 方法的签名:

Object.setPrototypeOf(object, prototype)

这个方法接受两个参数:

这是一个简单的例子:

let animal = {
  eats: true
};

let rabbit = {
  jumps: true
};

Object.setPrototypeOf(rabbit, animal);

console.log(rabbit.eats);  // 输出:true
console.log(Object.getPrototypeOf(rabbit) === animal);  // 输出:true

在这个例子中,我们首先创建了两个对象,animalrabbit。然后我们用 Object.setPrototypeOf(rabbit, animal) 设置 rabbit 的原型为 animal。所以现在 rabbit 继承了 animaleats 属性。

虽然 Object.setPrototypeOf() 方法可以用来动态地更改一个对象的原型,但是你应该非常谨慎地使用它,因为它可能会对性能产生负面影响。在创建对象时,设置原型通常是最好的选择,如使用 Object.create() 或者通过构造函数和 new 操作符来创建对象。这些方法在创建对象时就设置了对象的原型,而不是在创建对象之后再去更改原型。

4.12 hasOwn #

JavaScript 中的 Object.hasOwn() 是一个静态方法,用于检查一个对象是否具有自己的(非继承的)指定属性。

方法的签名如下:

Object.hasOwn(object, propertyName)

这个方法接受两个参数:

  1. object:你要检查的对象。
  2. propertyName:你要查找的属性名(字符串)。

如果 object 具有名为 propertyName 的自有属性,那么这个方法就会返回 true。否则,它将返回 false

这是一个简单的使用例子:

let obj = {
  a: 1,
  b: 2
};

console.log(Object.hasOwn(obj, 'a'));  // 输出:true
console.log(Object.hasOwn(obj, 'b'));  // 输出:true
console.log(Object.hasOwn(obj, 'c'));  // 输出:false

在这个例子中,obj 有自有属性 ab,所以 Object.hasOwn(obj, 'a')Object.hasOwn(obj, 'b') 都返回 true。但是 obj 没有 c 属性,所以 Object.hasOwn(obj, 'c') 返回 false

这个方法提供了一个更简洁和更直观的方式来检查一个对象是否具有某个自有属性,相比于 Object.prototype.hasOwnPropertyObject.hasOwn() 是推荐的方式进行此类检查。

4.13 getOwnPropertyNames #

Object.getOwnPropertyNames() 是 JavaScript 中的一个静态方法,用于返回一个数组,这个数组包含了一个对象的所有自有属性名(不包括 Symbol 类型的属性名)。这些属性包括可枚举的和不可枚举的属性,但不包括继承的属性。

这是 Object.getOwnPropertyNames() 方法的签名:

Object.getOwnPropertyNames(object)

这个方法接受一个参数,就是你要获取其属性名的对象。

这是一个简单的例子:

let obj = {
  a: 1,
  b: 2,
  c: 3
};

console.log(Object.getOwnPropertyNames(obj));  // 输出:["a", "b", "c"]

在这个例子中,obj 对象有三个自有属性:ab,和 c。所以 Object.getOwnPropertyNames(obj) 返回一个包含这三个属性名的数组。

注意,Object.getOwnPropertyNames() 返回的数组可能不按照对象属性的创建顺序排序。如果你需要按照固定的顺序遍历对象的属性,你可能需要自己对这个数组进行排序。

Object.getOwnPropertyNames() 方法可以用来获取一个对象的所有自有属性名,无论这些属性是否可枚举。这与 for...in 循环不同,for...in 循环会遍历一个对象的所有可枚举属性,包括继承的属性。如果你只关心自有属性,你应该使用 Object.getOwnPropertyNames() 或者 Object.keys()(只返回可枚举的自有属性名)。

4.14 getOwnPropertyDescriptor #

Object.getOwnPropertyDescriptor() 是 JavaScript 中的一个静态方法,它返回一个对象的指定属性的属性描述符(property descriptor)。属性描述符是一个对象,用于描述一个属性的特性,例如这个属性是否可写,是否可枚举等。

Object.getOwnPropertyDescriptor() 方法的签名如下:

Object.getOwnPropertyDescriptor(object, property)

这个方法接受两个参数:

这是一个简单的例子:

let obj = {
  a: 1
};

let descriptor = Object.getOwnPropertyDescriptor(obj, 'a');

console.log(descriptor);

在这个例子中,我们首先创建了一个对象 obj,然后我们用 Object.getOwnPropertyDescriptor(obj, 'a') 获取 a 属性的属性描述符。

属性描述符是一个对象,它可能包含以下属性:

如果 object 对象没有 property 属性,Object.getOwnPropertyDescriptor() 返回 undefined

注意,Object.getOwnPropertyDescriptor() 只能用来获取自有属性的描述符,如果你要获取的是继承的属性,它会返回 undefined

4.15 getOwnPropertyDescriptors #

Object.getOwnPropertyDescriptors() 是 JavaScript 中的一个静态方法,它返回一个对象,这个对象包含了指定对象的所有自有属性的属性描述符。这些属性包括可枚举的和不可枚举的属性,但不包括继承的属性。

这是 Object.getOwnPropertyDescriptors() 方法的签名:

Object.getOwnPropertyDescriptors(object)

这个方法接受一个参数,就是你要获取其属性描述符的对象。

这是一个简单的例子:

let obj = {
  a: 1,
  b: 2,
  c: 3
};

let descriptors = Object.getOwnPropertyDescriptors(obj);

console.log(descriptors);

在这个例子中,obj 对象有三个自有属性:ab,和 c。所以 Object.getOwnPropertyDescriptors(obj) 返回一个对象,这个对象有三个属性:ab,和 c,每个属性的值是对应属性的属性描述符。

每个属性描述符是一个对象,它可能包含以下属性:

Object.getOwnPropertyDescriptors() 方法可以用来获取一个对象的所有自有属性的属性描述符。例如,你可以使用此方法来复制一个对象的所有自有属性(包括不可枚举的属性)到另一个对象:

let obj1 = {
  a: 1,
  b: 2,
  c: 3
};

let obj2 = Object.create(
  Object.getPrototypeOf(obj1),
  Object.getOwnPropertyDescriptors(obj1)
);

console.log(obj2);  // 输出:{ a: 1, b: 2, c: 3 }

在这个例子中,Object.create() 方法接受两个参数:新对象的原型和新对象的属性描述符。我们用 Object.getPrototypeOf(obj1) 获取 obj1 的原型,用 Object.getOwnPropertyDescriptors(obj1) 获取 obj1 的所有自有属性的属性描述符,然后创建了一个新的对象 obj2。因为 Object.create() 会使用给定的属性描述符来创建新对象的属性,所以 obj2 会复制 obj1 的所有自有属性,包括不可枚举的属性。

4.16 getOwnPropertySymbols #

Object.getOwnPropertySymbols() 是 JavaScript 中的一个静态方法,它返回一个数组,这个数组包含了一个对象的所有自有属性的 Symbol 类型的键。

这是 Object.getOwnPropertySymbols() 方法的签名:

Object.getOwnPropertySymbols(object)

这个方法接受一个参数,就是你要获取其 Symbol 类型的键的对象。

这是一个简单的例子:

let obj = {
  [Symbol('a')]: 1,
  [Symbol.for('b')]: 2,
  c: 3
};

let symbols = Object.getOwnPropertySymbols(obj);

console.log(symbols.length);  // 输出:2
console.log(symbols[0] === Symbol('a'));  // 输出:false
console.log(symbols[1] === Symbol.for('b'));  // 输出:true

在这个例子中,obj 对象有三个自有属性,其中两个属性的键是 Symbol 类型的。所以 Object.getOwnPropertySymbols(obj) 返回一个包含这两个 Symbol 类型的键的数组。

注意,Object.getOwnPropertySymbols() 只返回 Symbol 类型的键,不返回字符串类型的键。如果你想获取一个对象的所有自有属性的键(包括 Symbol 类型和字符串类型的),你可以使用 Reflect.ownKeys() 方法。

此外,Object.getOwnPropertySymbols() 返回的数组中的 Symbol 类型的键的顺序与它们被定义的顺序相同。

在 JavaScript 中,Symbol 类型的键主要用于创建对象的内部方法和隐藏属性。因为每个 Symbol 类型的值都是唯一的,所以你可以使用 Symbol 类型的键来避免属性名冲突。

4.17 keys #

Object.keys() 是 JavaScript 中的一个静态方法,它返回一个数组,这个数组包含了一个对象的所有可枚举的自有属性的键。

这是 Object.keys() 方法的签名:

Object.keys(object)

这个方法接受一个参数,就是你要获取其键的对象。

这是一个简单的例子:

let obj = {
  a: 1,
  b: 2,
  c: 3
};

let keys = Object.keys(obj);

console.log(keys);  // 输出:["a", "b", "c"]

在这个例子中,obj 对象有三个自有属性:ab,和 c。所以 Object.keys(obj) 返回一个包含这三个键的数组。

注意,Object.keys() 只返回可枚举的自有属性的键,不返回不可枚举的属性的键和继承的属性的键。如果你想获取一个对象的所有自有属性的键(包括不可枚举的属性的键),你可以使用 Object.getOwnPropertyNames() 方法。如果你还想获取 Symbol 类型的键,你可以使用 Reflect.ownKeys() 方法。

此外,Object.keys() 返回的数组中的键的顺序与它们在对象中的顺序相同。这个顺序是由 JavaScript 引擎决定的,通常是按照属性被添加到对象的顺序。

Object.keys() 方法常常用于遍历一个对象的所有可枚举的自有属性。例如,你可以这样遍历 obj 对象的所有属性:

Object.keys(obj).forEach(function(key) {
  console.log('Key : ' + key + ', Value : ' + obj[key]);
});

这段代码首先用 Object.keys(obj) 获取 obj 对象的所有键,然后用 Array.prototype.forEach() 遍历这些键。在 forEach() 的回调函数中,我们用 keyobj[key] 分别获取每个属性的键和值。

4.18 values #

Object.values() 是 JavaScript 中的一个静态方法,它返回一个数组,这个数组包含了一个对象的所有可枚举的自有属性的值。

这是 Object.values() 方法的签名:

Object.values(object)

这个方法接受一个参数,就是你要获取其值的对象。

这是一个简单的例子:

let obj = {
  a: 1,
  b: 2,
  c: 3
};

let values = Object.values(obj);

console.log(values);  // 输出:[1, 2, 3]

在这个例子中,obj 对象有三个自有属性:ab,和 c。所以 Object.values(obj) 返回一个包含这三个属性的值的数组。

注意,Object.values() 只返回可枚举的自有属性的值,不返回不可枚举的属性的值和继承的属性的值。

此外,Object.values() 返回的数组中的值的顺序与它们在对象中的顺序相同。这个顺序是由 JavaScript 引擎决定的,通常是按照属性被添加到对象的顺序。

Object.values() 方法常常用于遍历一个对象的所有可枚举的自有属性的值。例如,你可以这样计算 obj 对象的所有属性的值的总和:

let total = Object.values(obj).reduce(function(sum, value) {
  return sum + value;
}, 0);

console.log(total);  // 输出:6

这段代码首先用 Object.values(obj) 获取 obj 对象的所有值,然后用 Array.prototype.reduce() 计算这些值的总和。在 reduce() 的回调函数中,我们把每个值加到累积的总和上,最后得到所有值的总和。

4.19 is #

Object.is() 是 JavaScript 中的一个静态方法,它用于比较两个值是否完全相等。

这是 Object.is() 方法的签名:

Object.is(value1, value2)

这个方法接受两个参数,返回这两个参数是否完全相等。

这是一个简单的例子:

console.log(Object.is('foo', 'foo'));  // 输出:true
console.log(Object.is(window, window));  // 输出:true

console.log(Object.is('foo', 'bar'));  // 输出:false
console.log(Object.is([], []));  // 输出:false

let foo = { a: 1 };
let bar = { a: 1 };
console.log(Object.is(foo, foo));  // 输出:true
console.log(Object.is(foo, bar));  // 输出:false

console.log(Object.is(null, null));  // 输出:true

// 特殊案例
console.log(Object.is(0, -0));  // 输出:false
console.log(Object.is(-0, -0));  // 输出:true
console.log(Object.is(NaN, 0/0));  // 输出:true

Object.is() 与严格等于 (===) 类似,但有两个重要的区别:

  1. Object.is() 认为 NaN 等于 NaN,而 NaN === NaNfalse
  2. Object.is() 认为 +0-0 是不等的,而 +0 === -0true

因此,在大多数情况下,你可以使用 === 进行比较,但在需要特别处理 NaN+0-0 的情况下,你可以使用 Object.is()

4.20 freeze #

Object.freeze() 是 JavaScript 中的一个方法,它可以使一个对象变得不可变。这意味着您无法添加新的属性,无法修改现有的属性和值,也无法删除现有的属性。换句话说,这个对象被“冻结”了。

使用 Object.freeze() 方法的基本语法如下:

let frozenObject = Object.freeze({ key: value });

在上面的示例中,frozenObject 是一个不可变对象。试图修改它的 key 属性、添加新的属性或删除任何属性,都将失败。请注意,如果你试图修改冻结对象,JavaScript 不会报错,但修改将会静默地失败。

这是一个更具体的例子:

let obj = {
  prop: 42
};

Object.freeze(obj);

obj.prop = 33; 
// 不会报错,但 obj.prop 依然是 42,因为 obj 对象已被冻结
console.log(obj.prop);
// Output: 42

请注意,Object.freeze() 只能冻结对象的表面层级。这意味着,如果你的对象中有一个属性值是另一个对象,那么这个内部对象不会被冻结。为了解决这个问题,你需要实现“深冻结”,这通常需要递归的使用 Object.freeze() 方法。

另外,Object.freeze() 是不可逆的,一旦一个对象被冻结,就不能被“解冻”。这种情况下,如果你需要修改这个对象,你需要创建一个新的对象。

4.21 isFrozen #

Object.isFrozen() 是 JavaScript 中的一个函数,它用于检查一个对象是否已被冻结。一个对象被“冻结”是指该对象不能被修改:无法添加新的属性、无法修改已有属性的值,以及无法删除属性。

这个函数的基本语法是:

Object.isFrozen(object);

这里 object 是要检查是否被冻结的对象。这个函数会返回一个布尔值,如果对象已被冻结则返回 true,否则返回 false

下面是一个使用 Object.isFrozen() 的例子:

let obj = {
  prop: 42
};

console.log(Object.isFrozen(obj)); // 输出: false

Object.freeze(obj);

console.log(Object.isFrozen(obj)); // 输出: true

在上述例子中,开始时,我们创建了一个名为 obj 的对象,并检查它是否已被冻结。由于我们还没有冻结该对象,所以 Object.isFrozen(obj) 返回 false。然后,我们使用 Object.freeze(obj) 冻结该对象,并再次检查其冻结状态。这一次,Object.isFrozen(obj) 返回 true,因为 obj 已经被冻结了。

需要注意的是,Object.isFrozen() 只能检查对象的表面层级是否被冻结。如果对象的属性值包含另一个对象,即使表面对象已被冻结,内部对象仍然可以被修改,除非它们也被冻结。

4.22 seal #

Object.seal() 是一个 JavaScript 的内置方法,它可以用来封闭一个对象。当一个对象被封闭后,就不能再添加新的属性,也不能删除已有的属性,但可以修改已有属性的值。

以下是 Object.seal() 的使用示例:

var obj = {
  property1: 'value1',
  property2: 'value2'
};

console.log(Object.isSealed(obj)); // 输出:false

Object.seal(obj);

console.log(Object.isSealed(obj)); // 输出:true

在上述代码中,我们首先创建了一个对象 obj,然后使用 Object.isSealed() 检查该对象是否被封闭。最初,对象没有被封闭,所以输出 false。然后我们用 Object.seal() 方法封闭这个对象,并再次检查它是否被封闭,这次输出 true,因为现在这个对象已经被封闭。

如果你尝试向已封闭的对象添加新的属性或删除已有的属性,那么在严格模式下,JavaScript 会抛出一个错误。在非严格模式下,这种尝试会被静默地忽略。

需要注意的是,尽管 Object.seal() 可以防止添加或删除属性,但它并不能阻止修改属性值。如果你想要一个对象的属性完全不可改变,你可以考虑使用 Object.freeze()

4.23 isSealed #

Object.isSealed() 是 JavaScript 的一个内置方法,用来检查一个对象是否被封闭。

一个被封闭的对象不能添加新的属性,也不能删除已有的属性,但其已有属性的值是可以被更改的。这与冻结对象(frozen object)有所不同,冻结对象连属性值都不能更改。

Object.isSealed()的使用方法如下:

var obj = {
  property1: 'value1',
  property2: 'value2'
};

console.log(Object.isSealed(obj)); // 输出:false

Object.seal(obj);

console.log(Object.isSealed(obj)); // 输出:true

在以上代码中,我们首先创建了一个对象 obj,然后使用 Object.isSealed() 检查该对象是否被封闭。最初,对象没有被封闭,所以输出 false。然后我们用 Object.seal() 方法封闭这个对象,并再次检查它是否被封闭,这次输出 true,因为现在这个对象已经被封闭。

注意,如果尝试向已封闭的对象添加新属性或删除已有属性,那么在严格模式(strict mode)下,JavaScript 会抛出错误。在非严格模式下,这些操作将会被忽略。

4.24 preventExtensions #

Object.preventExtensions() 是 JavaScript 中的一个方法,它用于防止新的属性被添加到一个对象中。

当你调用这个方法并将一个对象作为参数传入时,这个对象将不再允许添加新的属性。但请注意,这并不意味着你不能修改或删除该对象的现有属性。

Object.preventExtensions() 的使用方法如下:

var obj = {
  property1: 'value1',
  property2: 'value2'
};

console.log(Object.isExtensible(obj)); // 输出:true

Object.preventExtensions(obj);

console.log(Object.isExtensible(obj)); // 输出:false

在上面的代码中,我们首先创建了一个对象 obj,然后用 Object.isExtensible() 检查这个对象是否可以扩展。在开始时,对象是可以扩展的,所以输出 true。然后我们用 Object.preventExtensions() 阻止这个对象的扩展,再次检查它是否可扩展,这次输出 false,因为现在这个对象已经无法添加新的属性。

如果尝试向已阻止扩展的对象添加新的属性,那么在严格模式下,JavaScript 会抛出错误。在非严格模式下,这种尝试会被静默地忽略。

4.25 isExtensible #

Object.isExtensible() 是 JavaScript 的一个内置方法,它用于检查一个对象是否可以被添加新的属性。

如果对象可以添加新的属性,那么 Object.isExtensible() 会返回 true;如果对象不能添加新的属性(也就是说,它已经被 Object.preventExtensions() 方法或其他类似的方法处理过),那么 Object.isExtensible() 就会返回 false

以下是 Object.isExtensible() 的使用示例:

var obj = {
  property1: 'value1',
  property2: 'value2'
};

console.log(Object.isExtensible(obj)); // 输出:true

Object.preventExtensions(obj);

console.log(Object.isExtensible(obj)); // 输出:false

在上述代码中,我们首先创建了一个对象 obj,然后使用 Object.isExtensible() 检查该对象是否可以被添加新的属性。最初,对象可以被添加新的属性,所以输出 true。然后我们用 Object.preventExtensions() 方法阻止这个对象的扩展,并再次检查它是否可以添加新的属性。这次输出 false,因为现在这个对象已经无法添加新的属性。

5. Reflect #

5.1 getPrototypeOf #

在JavaScript中,Reflect.getPrototypeOf(obj) 方法主要用于获取一个对象的原型。这个原型通常指向创建该对象的构造函数的 prototype 属性,也就是我们常说的原型对象。如果给定的参数不是一个对象,这个方法将会抛出一个类型错误。

在代码中,可以通过下面的方式来使用 Reflect.getPrototypeOf

let obj = {};
console.log(Reflect.getPrototypeOf(obj) === Object.prototype);  // logs true

function MyConstructor() {}
let myInstance = new MyConstructor();
console.log(Reflect.getPrototypeOf(myInstance) === MyConstructor.prototype);  // logs true

在第一个例子中,我们创建了一个新的空对象 obj。所有的普通对象在JavaScript中都是通过 Object 构造函数创建的,所以 Reflect.getPrototypeOf(obj) 返回 Object.prototype

在第二个例子中,我们创建了一个名为 MyConstructor 的新构造函数,然后使用这个构造函数创建了一个新的对象 myInstance。因此,Reflect.getPrototypeOf(myInstance) 返回的就是 MyConstructor.prototype

值得注意的是,Reflect.getPrototypeOf 方法提供了一个更安全的方式来获取对象的原型,相比于旧的 Object.getPrototypeOf 方法,它会对参数类型进行更严格的检查。如果你传递一个非对象类型给 Reflect.getPrototypeOf,它将会抛出一个错误,而 Object.getPrototypeOf 则只会返回 null

5.2 setPrototypeOf #

5.3 isExtensible #

Reflect.isExtensible(obj) 是一个JavaScript方法,它用于检查一个对象是否是可扩展的。一个对象是可扩展的,就意味着可以向这个对象添加新的属性。

我们来看一个例子:

let obj = {};
console.log(Reflect.isExtensible(obj));  // 输出 true

Object.preventExtensions(obj);
console.log(Reflect.isExtensible(obj));  // 输出 false

在这个例子中,我们首先创建了一个空对象 obj。默认情况下,新创建的对象都是可扩展的,所以 Reflect.isExtensible(obj) 返回 true

然后我们使用 Object.preventExtensions(obj) 方法让 obj 不再可扩展。这时,Reflect.isExtensible(obj) 返回 false,说明 obj 现在不能再添加新的属性了。

Object.isExtensible(obj) 相比,Reflect.isExtensible(obj) 的行为是相同的。但是,Reflect.isExtensible(obj)Reflect 对象API中的一部分,与其他Reflect方法一起,它们提供了一种更一致、功能性更强大的方式来操作对象。

5.4 preventExtensions #

Reflect.preventExtensions() 是 JavaScript 的一个内置函数,用于防止新的属性被添加到一个对象中。它是 Object.preventExtensions() 方法的反射版本,行为基本一致,但在某些方面有一些微妙的差异。

它的使用方法如下:

let obj = {
  property1: 'value1',
  property2: 'value2'
};

console.log(Reflect.isExtensible(obj)); // 输出:true

Reflect.preventExtensions(obj);

console.log(Reflect.isExtensible(obj)); // 输出:false

在上面的代码中,我们首先创建了一个对象 obj,然后使用 Reflect.isExtensible() 检查这个对象是否可以扩展。在开始时,对象是可以扩展的,所以输出 true。然后我们用 Reflect.preventExtensions() 阻止这个对象的扩展,再次检查它是否可扩展,这次输出 false,因为现在这个对象已经无法添加新的属性。

Reflect.preventExtensions()Object.preventExtensions() 的一个主要区别在于它们处理不可扩展对象的方式不同。如果你尝试将一个已经不可扩展的对象作为参数传递给 Object.preventExtensions(),那么它会静默地失败,不会有任何异常。相反,如果你尝试将一个已经不可扩展的对象作为参数传递给 Reflect.preventExtensions(),那么它会返回一个布尔值 false,表明操作没有成功。

另外,Reflect.preventExtensions() 无论成功或失败,都会返回一个布尔值,而 Object.preventExtensions() 则总是返回它的第一个参数。这使得 Reflect.preventExtensions() 更适合于使用在需要根据操作是否成功进行不同处理的场景中。

5.5 getOwnPropertyDescriptor #

Reflect.getOwnPropertyDescriptor(obj, propName) 方法用于获取指定对象上一个自有属性对应的属性描述符。这个方法会返回一个对象,其中包含了与该属性相关的信息,如它的值(value)、是否可写(writable)、是否可枚举(enumerable)以及是否可配置(configurable)。如果指定的属性不存在于对象中,则返回 undefined

这是使用 Reflect.getOwnPropertyDescriptor 的一个例子:

let obj = {
  property1: 42
};

let descriptor = Reflect.getOwnPropertyDescriptor(obj, 'property1');

console.log(descriptor.value);  // 输出 42
console.log(descriptor.writable);  // 输出 true
console.log(descriptor.enumerable);  // 输出 true
console.log(descriptor.configurable);  // 输出 true

在这个例子中,我们首先创建了一个对象 obj,它有一个属性 property1。然后,我们使用 Reflect.getOwnPropertyDescriptor 来获取这个属性的描述符。该描述符是一个对象,它包含了关于 property1 的各种信息。

请注意,Reflect.getOwnPropertyDescriptor 只能获取对象自有属性的描述符,也就是说,它只能获取直接定义在对象上的属性,而不能获取对象原型链上的属性。如果你需要获取原型链上的属性,你需要逐层向上查找原型链,这通常需要结合 Reflect.getPrototypeOf 方法来使用。

相比之下,Reflect.getOwnPropertyDescriptor 方法与 Object.getOwnPropertyDescriptor 的行为几乎完全相同,但它是 Reflect 对象 API 的一部分,与其他 Reflect 方法一起,提供了一种更一致、功能性更强大的方式来操作对象。

5.6 defineProperty #

Reflect.defineProperty() 是 JavaScript 的一个内置方法,它允许你定义或修改对象的属性,这与 Object.defineProperty() 方法非常相似,但在某些方面存在一些微妙的区别。

Reflect.defineProperty() 方法接受三个参数:目标对象、属性名和一个描述符对象,后者定义了该属性的特性。

以下是 Reflect.defineProperty() 的使用示例:

let obj = {};

Reflect.defineProperty(obj, 'property1', {
  value: true,
  writable: true,
  enumerable: true,
  configurable: true
});

console.log(obj.property1); // 输出:true

在这个例子中,我们定义了一个新的属性 property1,并且设置了它的 valuewritableenumerableconfigurable 属性。

Reflect.defineProperty()Object.defineProperty() 的主要区别在于它们的返回值和错误处理方式。如果属性定义失败,Object.defineProperty() 会抛出一个错误,而 Reflect.defineProperty() 则会返回 false。如果属性定义成功,Object.defineProperty() 返回原对象,而 Reflect.defineProperty() 返回 true

因此,Reflect.defineProperty() 通常更适合于你需要知道操作是否成功的情况。使用 Reflect.defineProperty(),你可以更清楚地知道操作是否成功,因为它会返回一个布尔值,而不是抛出一个错误或返回一个对象。

5.7 ownKeys #

Reflect.ownKeys(obj) 方法返回一个由目标对象自身(不包含继承的)的属性键组成的数组。这些键包括非Symbol类型的键和Symbol类型的键。

下面是一个例子来展示 Reflect.ownKeys 的用法:

let obj = {
  prop1: 'value1',
  prop2: 'value2',
  [Symbol('key')]: 'value3'
};

let keys = Reflect.ownKeys(obj);

console.log(keys); // 输出 ['prop1', 'prop2', Symbol(key)]

在这个例子中,我们创建了一个对象 obj,它有两个字符串键 prop1prop2,以及一个Symbol键。然后,我们使用 Reflect.ownKeys 来获取对象的所有键,包括非Symbol键和Symbol键。

Object.getOwnPropertyNamesObject.getOwnPropertySymbols 方法相比,Reflect.ownKeys 的主要优势在于,它能同时返回非Symbol键和Symbol键。而要获取相同的结果,使用 Object.getOwnPropertyNamesObject.getOwnPropertySymbols 需要调用两次,并将结果合并。所以,Reflect.ownKeys 提供了一种更方便的方式来获取对象的所有自有键。

此外,与其他 Reflect 方法一样,Reflect.ownKeysReflect 对象 API 的一部分,提供了一种更一致、功能性更强大的方式来操作对象。

5.8 has #

Reflect.has() 是 JavaScript 中的一个内置函数,它用于检查一个对象是否具有指定的属性。它的行为和 in 操作符相同。

Reflect.has() 方法接受两个参数:目标对象和要检查的属性名。

以下是 Reflect.has() 的使用示例:

let obj = {
  property1: 'value1',
  property2: 'value2'
};

console.log(Reflect.has(obj, 'property1')); // 输出:true
console.log(Reflect.has(obj, 'property3')); // 输出:false

在上述代码中,我们首先创建了一个对象 obj,然后使用 Reflect.has() 检查这个对象是否有 property1property3 这两个属性。因为 obj 对象确实有 property1 属性,所以第一次调用 Reflect.has() 输出 true;但 obj 对象没有 property3 属性,所以第二次调用 Reflect.has() 输出 false

Reflect.has() 方法的一个重要优点是它总是返回一个布尔值,这使得它在需要根据属性是否存在来决定下一步操作的场景中非常有用。

5.9 get #

Reflect.get() 是 JavaScript 中的一个内置函数,它用于获取一个对象的属性值。这个函数的行为类似于常见的属性访问语法(.[]),但有更多的控制和更细粒度的行为。

Reflect.get() 接受两个必需的参数,分别是目标对象和要获取的属性名。此外,它还可以接受一个可选的参数,用于指定当目标属性是一个 getter 函数时,它的 this 应该绑定到哪个对象。

以下是 Reflect.get() 的使用示例:

let obj = {
  property1: 'value1',
  property2: 'value2'
};

console.log(Reflect.get(obj, 'property1')); // 输出:'value1'
console.log(Reflect.get(obj, 'property3')); // 输出:undefined

在这个示例中,我们使用 Reflect.get() 获取对象 objproperty1property3 属性的值。由于 obj 对象具有 property1 属性,所以第一次调用 Reflect.get() 会输出 'value1'。但 obj 对象没有 property3 属性,所以第二次调用 Reflect.get() 会输出 undefined

如果你尝试获取一个不存在的属性,Reflect.get() 会返回 undefined,而不是抛出一个错误。这是 Reflect.get() 与直接访问属性的一个重要区别:直接访问一个不存在的属性可能会导致抛出错误,特别是在严格模式下。

5.10 set #

Reflect.set(target, propertyKey, value, receiver) 是JavaScript中的一个方法,它用于设置一个对象上的属性值。这个方法接收四个参数:

如果 set 操作成功,Reflect.set 会返回 true,如果失败(比如目标属性是一个只读属性),则会返回 false

下面是使用 Reflect.set 的一个例子:

let obj = {
  prop1: 'value1'
};

console.log(Reflect.set(obj, 'prop1', 'newValue'));  // 输出 true
console.log(obj.prop1);  // 输出 'newValue'

let obj2 = {};
Object.defineProperty(obj2, 'prop', {
  value: 'originalValue',
  writable: false  // 这使得属性不可写
});

console.log(Reflect.set(obj2, 'prop', 'newValue'));  // 输出 false
console.log(obj2.prop);  // 输出 'originalValue'

在这个例子中,我们首先创建了一个对象 obj,然后使用 Reflect.set 修改了 prop1 属性的值。由于 set 操作成功,所以 Reflect.set 返回 true

然后,我们创建了另一个对象 obj2,并使用 Object.defineProperty 给它添加了一个只读属性 prop。当我们尝试使用 Reflect.set 修改这个属性的值时,操作失败,因为属性是只读的,所以 Reflect.set 返回 false

总的来说,Reflect.set 提供了一种动态设置对象属性值的方法。虽然你可以直接使用赋值操作符 = 来设置对象的属性值,但 Reflect.set 在某些情况下可能更适合,比如你需要检查操作是否成功,或者你需要在设置值时改变 this 的绑定。

5.11 deleteProperty #

Reflect.deleteProperty() 是 JavaScript 的一个内置函数,它用于删除一个对象的属性。它的行为和 delete 操作符类似,但有一些不同。

Reflect.deleteProperty() 方法接受两个参数:目标对象和要删除的属性名。

以下是 Reflect.deleteProperty() 的使用示例:

let obj = {
  property1: 'value1',
  property2: 'value2'
};

console.log(Reflect.has(obj, 'property1')); // 输出:true

Reflect.deleteProperty(obj, 'property1');

console.log(Reflect.has(obj, 'property1')); // 输出:false

在上述代码中,我们首先创建了一个对象 obj,然后使用 Reflect.has() 检查这个对象是否有 property1 属性。因为 obj 对象确实有 property1 属性,所以第一次调用 Reflect.has() 输出 true。然后我们使用 Reflect.deleteProperty() 删除了 property1 属性,再次检查 property1 属性是否存在,这次输出 false,因为 property1 属性已经被删除。

Reflect.deleteProperty() 的一个主要优点是它总是返回一个布尔值,表明操作是否成功。如果删除成功,它会返回 true;如果删除失败,它会返回 false。这与 delete 操作符不同,delete 操作符在非严格模式下删除一个不可配置的属性时,不会报错,而是静默失败,返回 false;而在严格模式下,会抛出一个错误。因此,Reflect.deleteProperty() 提供了一种更一致、更可预测的方式来删除对象的属性。

5.12 apply #

Reflect.apply(target, thisArgument, argumentsList) 是JavaScript中的一个方法,它的作用是调用一个函数,并为其提供一个特定的 this 值和参数列表。

这个方法接收三个参数:

如果调用成功,Reflect.apply 会返回目标函数的返回值。如果目标函数抛出错误,则 Reflect.apply 也会抛出相同的错误。

以下是一个 Reflect.apply 的使用例子:

function greet(greeting, punctuation) {
  return greeting + ', ' + this.name + punctuation;
}

let person = {
  name: 'Alice'
};

console.log(Reflect.apply(greet, person, ['Hello', '!']));  // 输出 'Hello, Alice!'

在这个例子中,我们首先定义了一个 greet 函数,它接收一个问候语和一个标点符号,然后使用 this.name 拼接字符串。然后我们创建了一个 person 对象,并使用 Reflect.apply 调用 greet 函数,将 person 作为 this 传入,并传入一个参数列表 ['Hello', '!']

Reflect.apply 方法与 Function.prototype.apply 方法类似,但它接受任意函数作为目标,包括构造函数。而且,Reflect.applyReflect 对象 API 的一部分,与其他 Reflect 方法一起,提供了一种更一致、功能性更强大的方式来操作对象和函数。

5.13 construct #

Reflect.construct() 是 JavaScript 的一个内置函数,它可以用来创建一个新的对象实例,其行为类似于 new 操作符。

Reflect.construct() 方法接收两个必需参数:一个构造函数和一个参数数组。同时,它还可以接受一个可选的新目标(new target)参数。

以下是 Reflect.construct() 的使用示例:

function MyConstructor(a, b) {
  this.a = a;
  this.b = b;
}

let args = [1, 2];
let instance = Reflect.construct(MyConstructor, args);

console.log(instance instanceof MyConstructor); // 输出:true
console.log(instance.a); // 输出:1
console.log(instance.b); // 输出:2

在上述代码中,我们首先定义了一个构造函数 MyConstructor,然后使用 Reflect.construct() 创建了一个 MyConstructor 的新实例。Reflect.construct() 的第一个参数是构造函数,第二个参数是一个参数数组,这个数组的元素将作为构造函数的参数。创建的新实例被赋值给了 instance 变量,然后我们检查 instance 是否是 MyConstructor 的一个实例,以及它的 ab 属性的值。

Reflect.construct() 提供了一种更灵活的方式来创建新对象,特别是当你需要动态地调用不同的构造函数,或者使用不定数量的参数时。此外,如果你提供了第三个参数(新目标),那么在构造函数内部,new.target 将会被设置为这个新目标,这在构造函数继承的情况下可能很有用。

6. Proxy #

在ES6中,Proxy是一种新的内置类型,它为JavaScript对象或函数提供了一种新的代理机制。通过这种机制,你可以在不修改原始对象或函数的情况下,改变或自定义他们的一些默认行为。

创建Proxy

创建一个Proxy对象很简单,只需要使用new Proxy(target, handler)即可。这个构造函数接受两个参数:

let target = {};
let handler = {};

let proxy = new Proxy(target, handler);

这个proxy对象现在是target对象的代理,你可以像使用target对象一样使用它。

Proxy的陷阱函数

handler对象可以定义一些陷阱函数,这些函数可以拦截对target的各种操作。下面列出了一些常见的陷阱函数:

以上只是handler对象的一些常见陷阱函数,还有更多的陷阱函数可以用于拦截其他的操作。

let target = {
  name: 'target'
};

let handler = {
  get: function(target, property, receiver) {
    console.log(`Getting ${property}`);
    return target[property];
  },
  set: function(target, property, value, receiver) {
    console.log(`Setting ${property} to ${value}`);
    target[property] = value;
  }
};

let proxy = new Proxy(target, handler);

console.log(proxy.name);  // "Getting name" "target"
proxy.name = 'proxy';     // "Setting name to proxy"
console.log(proxy.name);  // "Getting name" "proxy"