Skip to content

Object

Object描述

  • Object 是 JavaScript 的一种数据类型。它用于存储各种键值集合和更复杂的实体。可以通过 Object() 构造函数或者使用对象字面量的方式创建对象。
  • 在 JavaScript 中,几乎所有的对象都是 Object 的实例;一个典型的对象从 Object.prototype 继承属性(包括方法),尽管这些属性可能被覆盖(或重写)。
  • 唯一不从 Object.prototype 继承的对象是那些 null 原型对象,或者是从其他 null 原型对象继承而来的对象。

通过原型链,所有对象都能观察到 Object.prototype 对象的改变,除非这些改变所涉及的属性和方法沿着原型链被进一步重写。尽管有潜在的危险,但这为覆盖或扩展对象的行为提供了一个非常强大的机制。为了使其更加安全,Object.prototype 是核心 JavaScript 语言中唯一具有不可变原型的对象——Object.prototype 的原型始终为 null 且不可更改。

null 原型对象

几乎所有的 JavaScript 对象最终都继承自 Object.prototype。通过一下方法将原型更改为null:

  • 创建Object.create(null)对象;
  • 设置__proto__: null
  • 调用Object.setPrototypeOf(obj, null)方法;
null 原型对象拓展

null 原型对象可能会有一些预期外的行为表现,因为它不会从 Object.prototype 继承任何对象方法。

这在调试时尤其需要注意,因为常见的对象属性转换/检测实用方法可能会产生错误或丢失信息。

javascript
const normalObj = {}; // 创建一个普通对象
const nullObj = Object.create(null); // 创建一个 "null" 原型对象

console.log(`normalObj 是:${normalObj}`); // 显示 "normalObj 是:[object Object]"
console.log(`nullObj 是:${nullObj}`); // 抛出错误:Cannot convert object to primitive value

alert(normalObj); // 显示 [object Object]
alert(nullObj); // 抛出错误:Cannot convert object to primitive value

normalObj.toString(); // 显示 {}
nullObj.toString(); // nullObj.valueOf is not a function
const normalObj = {}; // 创建一个普通对象
const nullObj = Object.create(null); // 创建一个 "null" 原型对象

console.log(`normalObj 是:${normalObj}`); // 显示 "normalObj 是:[object Object]"
console.log(`nullObj 是:${nullObj}`); // 抛出错误:Cannot convert object to primitive value

alert(normalObj); // 显示 [object Object]
alert(nullObj); // 抛出错误:Cannot convert object to primitive value

normalObj.toString(); // 显示 {}
nullObj.toString(); // nullObj.valueOf is not a function

我们可以通过为 null 原型对象分配属性的方式将 toString 方法添加回去:

javascript
nullObj.valueOf = Object.prototype.valueOf; 
nullObj.valueOf = Object.prototype.toString; 
console.log(nullObj.toString()); // 显示 "[object Object]"
nullObj.valueOf = Object.prototype.valueOf; 
nullObj.valueOf = Object.prototype.toString; 
console.log(nullObj.toString()); // 显示 "[object Object]"

在实践中,null 原型对象通常被用作 map 的简单替代品。由于存在 Object.prototype 属性,会导致一些错误:

javascript
const ages = { alice: 18, bob: 27 };
function hasPerson(name) {
  return name in ages;
}
function getAge(name) {
  return ages[name];
}

hasPerson("hasOwnProperty"); // true  因为hasPerson的原型链上有hasOwnProperty属性
getAge("toString"); // [Function: toString]  因为getAge的原型链上有toString方法
const ages = { alice: 18, bob: 27 };
function hasPerson(name) {
  return name in ages;
}
function getAge(name) {
  return ages[name];
}

hasPerson("hasOwnProperty"); // true  因为hasPerson的原型链上有hasOwnProperty属性
getAge("toString"); // [Function: toString]  因为getAge的原型链上有toString方法

使用一个 null 原型对象可以消除这种风险,同时不会令 hasPerson 和 getAge 函数变得复杂:

javascript
const ages = Object.create(null, {
  alice: { value: 18, enumerable: true },
  bob: { value: 27, enumerable: true },
});

hasPerson("hasOwnProperty"); // false
getAge("toString"); // undefined
const ages = Object.create(null, {
  alice: { value: 18, enumerable: true },
  bob: { value: 27, enumerable: true },
});

hasPerson("hasOwnProperty"); // false
getAge("toString"); // undefined

JavaScript 还具有内置的 API,用于生成 null 原型对象,特别是那些将对象用作临时键值对集合的 API。例如:

  1. Object.groupBy() 方法的返回值;
  2. RegExp.prototype.exec() 方法返回结果中的 groups 和 indices.groups 属性;
  3. Array.prototype[Symbol.unscopables] 属性(所有 [Symbol.unscopables] 对象原型都应该为 null);
  4. 通过 import * as ns from "module" 或 import() 获取的模块命名空间对象;

null 原型对象这个术语通常也包括其原型链中没有 Object.prototype 的任何对象。当使用类时,可以通过 extends null 来创建这样的对象。

对象强制转换

内置操作过程

  • 对象则按原样返回。
  • Number、String、Boolean、Symbol、BigInt 等基本类型被封装成其对应的基本类型对象。
  • undefined 和 null 则抛出 TypeError。

TIP

JavaScript 中实现相同效果的最佳方式是使用 Object() 构造函数。对于 undefined 或 null,它会返回一个普通对象而不是抛出 TypeError 异常。

使用对象强制转换的地方包括:

  1. for...in 循环的 object 参数。
  2. Array 方法的 this 值。
  3. Object 方法的参数,如 Object.keys()。
  4. 当访问基本类型的属性时进行自动转换,因为基本类型没有属性。
  5. 在调用非严格函数时的 this 值。基本类型值被封装为对象,而 null 和 undefined 被替换为全局对象。

与转换为基本类型不同对象强制转换过程本身无法以任何方式被观察到,因为它不会调用像 toString 或 valueOf 方法这样的自定义代码。

构造函数

javascript
new Object(value)
Object(value)
new Object(value)
Object(value)

当调用或者构造 Object() 构造函数本身时,其返回值是一个对象:

  1. 如果该值是 null 或者 undefined,它会生成并返回一个空对象。
  2. 如果该值已经是一个对象,则返回该值。
  3. 否则,它将返回与给定值对应的类型的对象

当通过继承 Object 的类的构造函数中的 super() 隐式调用 Object() 时,它以 new.target.prototype 为原型初始化一个新对象。传递给 super() 的任意值都将被忽略——例如,即使你传递一个数字,构造函数中的 this 值也不会变成 Number 实例。

数据描述符 和 访问器描述符

访问器描述符

  • configurable: 此属性描述符的类型可以更改并且属性可以从相应的对象中删除,则为 true。默认为 false。
  • enumerable:此属性在枚举相应对象的属性时应显示出来,则为 true。默认为 false。
  • get:作为该属性的 getter 函数,如果没有 getter 则为 undefined。函数返回值将被用作属性的值。默认为 undefined。
  • set:作为该属性的 setter 函数,如果没有 setter 则为 undefined。该函数将只接收一个参数,即被分配给属性的新值。默认为 undefined。

数据描述符

  • value:与属性关联的值。可以是任何有效的 JavaScript 值(数字、对象、函数等)。默认为 undefined。
  • writable:如果与属性关联的值可以使用赋值运算符更改,则为 true。默认为 false。

TIP

同时设置 [value 或 writable] 和 [get 或 set] 键,则会抛出异常。

静态方法

Object.create()

  • 描述:Object.create() 静态方法以一个现有对象作为原型,创建一个新对象。

Object.is()

  • 描述:判定两个值是否为相同值。Object.is() 不会对其操作数进行类型转换。

如果以下其中一项成立,则两个值相同,

javascript
Object.is(value1, value2)
Object.is(value1, value2)
  1. 都是 undefined
  2. 都是 null
  3. 都是 true 或者都是 false
  4. 都是长度相同、字符相同、顺序相同的字符串
  5. 都是相同的对象(意味着两个值都引用了内存中的同一对象)
  6. 都是 BigInt 且具有相同的数值
  7. 都是 symbol 且引用相同的 symbol 值
  8. 都是数字(且都是 +0,都是 -0,都是 NaN,都有相同的值,非零且都不是 NaN)

Object.is() 与 ==

Object.is() 与 == 运算符并不等价。== 运算符在测试相等性之前,会对两个操作数进行类型转换(如果它们不是相同的类型),这可能会导致一些非预期的行为,例如 "" == false 的结果是 true,但是 Object.is() 不会对其操作数进行类型转换。


Object.is() 与 ===

Object.is() 也不等价于 === 运算符。Object.is() 和 === 之间的唯一区别在于它们处理带符号的 0 和 NaN 值的时候。=== 运算符(和 == 运算符)将数值 -0 和 +0 视为相等,但是会将 NaN 视为彼此不相等。

Object.hasOwn()

  • 描述:如果指定的对象自身有指定的属性,则静态方法 Object.hasOwn() 返回 true。如果属性是继承的或者不存在,该方法返回 false。

TIP

  1. Object.hasOwn() 旨在取代 Object.prototype.hasOwnProperty()。

  1. 只判定对象的直接属性,它不像 in 运算符,这个方法不检查对象的原型链中的指定属性。
javascript
const obj = {}
Object.hasOwn(obj,'valueOf') // false
'valueOf' in obj // true  去原型链上查找了
const obj = {}
Object.hasOwn(obj,'valueOf') // false
'valueOf' in obj // true  去原型链上查找了

Object.assign()

  • 描述:方法只会拷贝源对象可枚举的的自有属性到目标对象。该方法在源对象上使用 [Get],在目标对象上使用 [Set],因此它会调用 getter 和 setter。(对象合并)

Object.entries()

  • 描述:返回一个数组,包含给定对象自有的可枚举字符串键属性的键值对。

Object.values()

  • 描述:返回一个给定对象的自有可枚举字符串键属性值组成的数组。

Object.keys()

  • 描述:返回一个由给定对象自身的可枚举的字符串键属性名组成的数组。
javascript
const obj = {a: 1, b: 2, c: 3}
Object.entries(obj) // [ ['a', 1], ['b', 2], ['c', 3]
Object.values(obj)  // [1, 2, 3]
Object.keys(obj)    // [a, b, c]
const obj = {a: 1, b: 2, c: 3}
Object.entries(obj) // [ ['a', 1], ['b', 2], ['c', 3]
Object.values(obj)  // [1, 2, 3]
Object.keys(obj)    // [a, b, c]

Object.fromEntries()

  • 描述:方法将键值对列表转换为一个对象。
示例
javascript
// 数组
const arr = [
  ["0", "a"],
  ["1", "b"],
  ["2", "c"],
];
const obj = Object.fromEntries(arr); //  { 0: "a", 1: "b", 2: "c" }

// 对象
const object1 = { a: 1, b: 2, c: 3 }; // 计算为 { a: 2, b: 4, c: 6 }
const object2 = Object.fromEntries(
  Object.entries(object1).map(([key, val]) => [key, val * 2]),
);
// 数组
const arr = [
  ["0", "a"],
  ["1", "b"],
  ["2", "c"],
];
const obj = Object.fromEntries(arr); //  { 0: "a", 1: "b", 2: "c" }

// 对象
const object1 = { a: 1, b: 2, c: 3 }; // 计算为 { a: 2, b: 4, c: 6 }
const object2 = Object.fromEntries(
  Object.entries(object1).map(([key, val]) => [key, val * 2]),
);

Object.freeze()

  • 描述:方法可以使一个对象被冻结。当于阻止其扩展然后将所有现有属性的描述符的 configurable 特性更改为 false——对于数据属性,将同时把 writable 特性更改为 false。
示例
  1. 尝试冻结带有元素的 TypedArrayDataView
javascript
Object.freeze(new Uint8Array(0)); // 没有元素
// Uint8Array []
Object.freeze(new Uint8Array(1)); // 有元素
// TypeError: Cannot freeze array buffer views with elements

Object.freeze(new DataView(new ArrayBuffer(32))); // 没有元素
// DataView {}
Object.freeze(new Uint8Array(0)); // 没有元素
// Uint8Array []
Object.freeze(new Uint8Array(1)); // 有元素
// TypeError: Cannot freeze array buffer views with elements

Object.freeze(new DataView(new ArrayBuffer(32))); // 没有元素
// DataView {}

  1. 冻结对象或数组,不能被修改,严格模式将抛出 TypeError;
javascript
const a = [0];
const b = {B: 0}
Object.freeze(a);// a 数组无法被修改
Object.freeze(b);// a 对象无法被修改
const a = [0];
const b = {B: 0}
Object.freeze(a);// a 数组无法被修改
Object.freeze(b);// a 对象无法被修改

  1. “浅冻结”:调用 Object.freeze(object) 的结果仅适用于 object 本身的直接属性,
javascript
const a = {
  b: {},
};
Object.freeze(a); // 这里冻结的是 a 对象
a.b.c = "aValue"; // c 不是a的直接属性,可以被修改
a.b.c; // 'aValue'
const a = {
  b: {},
};
Object.freeze(a); // 这里冻结的是 a 对象
a.b.c = "aValue"; // c 不是a的直接属性,可以被修改
a.b.c; // 'aValue'

Object.isFrozen()

  • 描述:判断一个对象是否被冻结。

Object.preventExtensions()

  • 描述
    1. 防止新属性被添加到对象中(即防止该对象被扩展)。它还可以防止对象的原型被重新指定。
    2. 与 Object.seal() 和 Object.freeze() 不同,Object.preventExtensions() 调用了内在的 JavaScript 行为,不能用几个其他操作的组合替代。
    3. 它还有它的 Reflect 对应方法(仅存在于内部操作中)。
    4. 该方法使得目标对象的 [Prototype] 不可变;任何重新赋值 [Prototype] 操作都会抛出 TypeError 。
    5. Object.preventExtensions() 只能防止添加自有属性。但其对象类型的原型依然可以添加新的属性。

Object.isExtensible()

  • 描述:判断一个对象是否是可扩展的(是否可以在它上面添加新的属性)。

Object.seal()

  • 描述
    1. 方法密封一个对象。密封一个对象会阻止其扩展并且使得现有属性不可配置。
    2. 不能添加新属性、不能删除现有属性或更改其可枚举性和可配置性、不能重新分配其原型。但它不会防止数据属性的值被更改。
    3. 不同于 Object.freeze() 的是,通过 Object.seal() 密封的对象可以更改其现有属性,只要它们是可写的。

Object.isSealed()

  • 描述:法判断一个对象是否被密封。

Object.defineProperties()

javascript
Object.defineProperties(obj, props)
Object.defineProperties(obj, props)
  • 描述:方法直接在一个对象上定义新的属性或修改现有属性,并返回该对象。
示例
javascript
const obj = {};
Object.defineProperties(obj, {
  property1: {
    value: true,
    writable: true,
  },
  property2: {
    value: "Hello",
    writable: false,
  },
  // ...
});
const obj = {};
Object.defineProperties(obj, {
  property1: {
    value: true,
    writable: true,
  },
  property2: {
    value: "Hello",
    writable: false,
  },
  // ...
});

Object.defineProperty()

javascript
Object.defineProperty(obj, prop, descriptor)
Object.defineProperty(obj, prop, descriptor)
  • 描述:方法会直接在一个对象上定义一个新属性,或修改其现有属性,并返回此对象。
示例
javascript
const o = {}; // 创建一个新对象
// 通过 defineProperty 使用数据描述符添加对象属性的示例
Object.defineProperty(o, "a", {
  value: 37,
  writable: true,
  enumerable: true,
  configurable: true,
});
// 'a' 属性存在于对象 o 中,其值为 37

let bValue = 38;
Object.defineProperty(o, "b", {
  get() {
    return bValue;
  },
  set(newValue) {
    bValue = newValue;
  },
  enumerable: true,
  configurable: true,
});
const o = {}; // 创建一个新对象
// 通过 defineProperty 使用数据描述符添加对象属性的示例
Object.defineProperty(o, "a", {
  value: 37,
  writable: true,
  enumerable: true,
  configurable: true,
});
// 'a' 属性存在于对象 o 中,其值为 37

let bValue = 38;
Object.defineProperty(o, "b", {
  get() {
    return bValue;
  },
  set(newValue) {
    bValue = newValue;
  },
  enumerable: true,
  configurable: true,
});

Object.setPrototypeOf()不推荐

  • 描述:方法可以将一个指定对象的原型(即内部的 [Prototype] 属性)设置为另一个对象或者 null。(存在性能问题)

Object.getOwnPropertyDescriptor()

  • 描述
    1. 返回一个对象,该对象描述给定对象上特定属性(即直接存在于对象上而不在对象的原型链中的属性)的配置。
    2. 返回的对象是可变的,但对其进行更改不会影响原始属性的配置。
    3. 如果指定的属性存在于对象上,则返回其属性描述符,否则返回 undefined。
javascript
const a = { bar: 42 };
b = Object.getOwnPropertyDescriptor(a, "bar");
console.log(b);
// {
//   configurable: true,
//   enumerable: true,
//   value: 42,
//   writable: true
// }
const a = { bar: 42 };
b = Object.getOwnPropertyDescriptor(a, "bar");
console.log(b);
// {
//   configurable: true,
//   enumerable: true,
//   value: 42,
//   writable: true
// }

Object.getOwnPropertyDescriptors()

  • 描述:返回给定对象的所有自有属性描述符。
javascript
let obj = { a: 1,b: 2 }
Object.getOwnPropertyDescriptors(obj)
// a: {value: 1, writable: true, enumerable: true, configurable: true}
// b: {value: 2, writable: true, enumerable: true, configurable: true}
let obj = { a: 1,b: 2 }
Object.getOwnPropertyDescriptors(obj)
// a: {value: 1, writable: true, enumerable: true, configurable: true}
// b: {value: 2, writable: true, enumerable: true, configurable: true}

Object.getOwnPropertyNames()

  • 描述:返回一个数组,其包含给定对象中所有自有属性(包括不可枚举属性,但不包括使用 symbol 值作为名称的属性)。
javascript
let obj = { a: 1,b: 2 }
Object.getOwnPropertyNames(obj) //['a', 'b'] 不会包括使用 symbol 值作为名称的属性
let obj = { a: 1,b: 2 }
Object.getOwnPropertyNames(obj) //['a', 'b'] 不会包括使用 symbol 值作为名称的属性

Object.getOwnPropertySymbols()

  • 描述:返回一个包含给定对象所有自有 Symbol 属性的数组。
javascript
const obj = {};
const a = Symbol("a");
const b = Symbol.for("b");
obj[a] = "localSymbol";
obj[b] = "globalSymbol";
obj[c] = "c"
Object.getOwnPropertySymbols(obj); // [Symbol(a), Symbol(b)]
const obj = {};
const a = Symbol("a");
const b = Symbol.for("b");
obj[a] = "localSymbol";
obj[b] = "globalSymbol";
obj[c] = "c"
Object.getOwnPropertySymbols(obj); // [Symbol(a), Symbol(b)]

Object.getPrototypeOf()

  • 描述:方法返回指定对象的原型(即内部 [Prototype] 属性的值)。
javascript
const proto = {a:1};
const obj = Object.create(proto);
obj.b = 2
Object.getPrototypeOf(obj) // {a:1}
const proto = {a:1};
const obj = Object.create(proto);
obj.b = 2
Object.getPrototypeOf(obj) // {a:1}

Object.groupBy() 实验

实例方法

Object.prototype.hasOwnProperty()不推荐

推荐使用 Object.hasOwn()

Object.prototype.isPrototypeOf()

  • 描述
    1. 用于检查一个对象是否存在于另一个对象的原型链中。
    2. 所有继承自 Object.prototype 的对象(即除了 null 原型对象之外的所有对象)都继承了 isPrototypeOf() 方法。
    3. 如果作为参数的 object 不是对象(即基本类型),则该方法直接返回 false。否则,this 值被转换为对象,并且在 object 的原型链中搜索 this 值,直到到达链的末端或找到 this 值为止。

TIP

与 instanceof 运算符不同。在表达式 object instanceof AFunction 中,会检查 object 的原型链是否与 AFunction.prototype 匹配,而不是与 AFunction本身匹配。

javascript
class Foo {}
class Bar extends Foo {}
class Baz extends Bar {}

const foo = new Foo();
const bar = new Bar();
const baz = new Baz();

// 原型链:
// foo: Foo --> Object
// bar: Bar --> Foo --> Object
// baz: Baz --> Bar --> Foo --> Object
console.log(Baz.prototype.isPrototypeOf(baz)); // true
console.log(Baz.prototype.isPrototypeOf(bar)); // false
console.log(Baz.prototype.isPrototypeOf(foo)); // false
console.log(Bar.prototype.isPrototypeOf(baz)); // true
console.log(Bar.prototype.isPrototypeOf(foo)); // false
console.log(Foo.prototype.isPrototypeOf(baz)); // true
console.log(Foo.prototype.isPrototypeOf(bar)); // true
console.log(Object.prototype.isPrototypeOf(baz)); // true
class Foo {}
class Bar extends Foo {}
class Baz extends Bar {}

const foo = new Foo();
const bar = new Bar();
const baz = new Baz();

// 原型链:
// foo: Foo --> Object
// bar: Bar --> Foo --> Object
// baz: Baz --> Bar --> Foo --> Object
console.log(Baz.prototype.isPrototypeOf(baz)); // true
console.log(Baz.prototype.isPrototypeOf(bar)); // false
console.log(Baz.prototype.isPrototypeOf(foo)); // false
console.log(Bar.prototype.isPrototypeOf(baz)); // true
console.log(Bar.prototype.isPrototypeOf(foo)); // false
console.log(Foo.prototype.isPrototypeOf(baz)); // true
console.log(Foo.prototype.isPrototypeOf(bar)); // true
console.log(Object.prototype.isPrototypeOf(baz)); // true

Object.prototype.propertyIsEnumerable()

  • 描述:返回一个布尔值,表示指定的属性是否是对象的可枚举自有属性。

等价于:Object.getOwnPropertyDescriptor(obj, prop)?.enumerable ?? false。

Object.prototype.toLocaleString()

  • 描述
    1. 返回一个表示对象的字符串。该方法旨在由派生对象重写,以达到其特定于语言环境的目的。
    2. 提供此方法是为了给对象一个通用的 toLocaleString 方法,即使不是所有对象都会使用它。在核心语言中,这些内置对象重写了 toLocaleString 以提供特定于语言环境的格式:
      • Array:Array.prototype.toLocaleString()
      • Number:Number.prototype.toLocaleString()
      • Date:Date.prototype.toLocaleString()
      • TypedArray:TypedArray.prototype.toLocaleString()
      • BigInt:BigInt.prototype.toLocaleString()
javascript
const testDate = new Date();
const deDate = testDate.toLocaleString("de"); // "29.5.2020, 18:04:24"
const frDate = testDate.toLocaleString("fr"); // "29/05/2020, 18:04:24"

const testNumber = 2901234564;
const deNumber = testNumber.toLocaleString("de"); // "2.901.234.564"
const frNumber = testNumber.toLocaleString("fr"); // "2 901 234 564"
const testDate = new Date();
const deDate = testDate.toLocaleString("de"); // "29.5.2020, 18:04:24"
const frDate = testDate.toLocaleString("fr"); // "29/05/2020, 18:04:24"

const testNumber = 2901234564;
const deNumber = testNumber.toLocaleString("de"); // "2.901.234.564"
const frNumber = testNumber.toLocaleString("fr"); // "2 901 234 564"

Object.prototype.toString()

  • 描述
    1. JavaScript 调用 toString 方法将对象转换为一个原始值。你很少需要自己去调用 toString 方法;当遇到需要原始值的对象时,JavaScript 会自己调用它。
    2. 该方法由字符串转换优先调用,但是数字的强制转换和原始值的强制转换会优先调用 valueOf()。然而,因为基本的 valueOf() 方法返回一个对象,toString() 方法通常在结束时调用,除非对象重写了 valueOf()。
    3. 所有继承自 Object.prototype 的对象(即,除了 null-prototype 对象之外的对象)都继承 toString() 方法。
    4. 当你创建一个自定义对象时,你可以重写 toString() 以调用自定义方法,以便将自定义对象转换为一个字符串。或者,你可以增加一个 Symbol.toPrimitive 方法,该方法允许对转换过程有更多的控制,并且对于任意的类型转换,且总是优先于 valueOf 或 toString。
    5. 要将基本的 Object.prototype.toString() 用于重写的对象(或者在 null 或 undefined 上调用它),你需要在它上面调用 Function.prototype.call() 或者 Function.prototype.apply(),将要检查的对象作为第一个参数传递(称为 thisArg)。
    6. Object.prototype.toString() 返回 "[object Type]",这里的 Type 是对象的类型。
    7. 如果对象有 Symbol.toStringTag 属性,其值是一个字符串,则它的值将被用作 Type。许多内置的对象,包括 Map 和 Symbol,都有 Symbol.toStringTag。
javascript
Object.prototype.toString.call([]); // "[object Array]"
Object.prototype.toString.call([]); // "[object Array]"

Object.prototype.valueOf()

  • 描述
    1. JavaScript 调用 valueOf 方法来将对象转换成基本类型值。你很少需要自己调用 valueOf 方法;当遇到需要基本类型值的对象时,JavaScript 会自动的调用该方法。
    2. 强制数字类型转换强制基本类型转换优先会调用该方法,而强制字符串转换会优先调用 toString(),并且 toString() 很可能返回字符串值。因此在这种情况下不会调用 valueOf()。
    3. 从 Object.prototype 继承的所有对象(即,除了null 原型对象之外的所有对象)都继承了 toString() 方法。
    4. Object.prototype.valueOf() 的基本实现被有意设计为无用的返回一个对象;其返回值将永远不会被任何基本类型转换算法使用
    5. 许多内置对象重写此方法以返回适当的基本类型值。创建自定义对象时,可以重写 valueOf() 来调用自定义方法,以便将自定义对象转换为基本类型值。
    6. 通常,valueOf() 用于返回对对象具有意义的值——与 toString() 不同,它不需要字符串。
    7. 你可以添加一个 Symbol.toPrimitive 方法,该方法允许对转换过程进行更多控制,并且对于任何类型的转换,始终优先于 valueOf 或 toString。
javascript
// 基本的 valueOf() 方法返回 this 值本身,如果尚未转换为对象,则转换成对象。因此,任何基本类型转换算法都不会使用它的返回值。
const obj = { foo: 1 };
console.log(obj.valueOf() === obj); // true
console.log(Object.prototype.valueOf.call("primitive"));// [String: 'primitive'](一个包装对象)
// 基本的 valueOf() 方法返回 this 值本身,如果尚未转换为对象,则转换成对象。因此,任何基本类型转换算法都不会使用它的返回值。
const obj = { foo: 1 };
console.log(obj.valueOf() === obj); // true
console.log(Object.prototype.valueOf.call("primitive"));// [String: 'primitive'](一个包装对象)

对对象使用一元加运算符

一元加(+)对其操作数进行强制数字转换,对于大多数没有 Symbol.toPrimitive 的对象,这意味着调用其 valueOf()。但是,如果对象没有自定义的 valueOf() 方法,则基本实现将导致忽略 valueOf(),而使用 toString() 的返回值。

javascript

+new Date(); // 当前时间戳;与 new Date().getTime() 相同
+{}; // NaN(toString() 返回 "[object Object]")
+[]; // 0(toString() 返回一个空的字符串列表)
+[1]; // 1(toString() 返回 "1")
+[1, 2]; // NaN(toString() 返回 "1,2")
+new Set([1]); // NaN(toString() 返回 "[object Set]")
+{ valueOf: () => 42 }; // 42

+new Date(); // 当前时间戳;与 new Date().getTime() 相同
+{}; // NaN(toString() 返回 "[object Object]")
+[]; // 0(toString() 返回一个空的字符串列表)
+[1]; // 1(toString() 返回 "1")
+[1, 2]; // NaN(toString() 返回 "1,2")
+new Set([1]); // NaN(toString() 返回 "[object Set]")
+{ valueOf: () => 42 }; // 42