对象 | Object_API

Object的API众多且重要,按照Object原型方法、进行分类

Object原型方法(所有实例共享)

以下方法来自于Object.prototype,参考《红宝书》P56

(1)constructor

用于创建当前对象的函数

(2)hasOwnProperty(propertyName)

用于判断当前对象实例(不是原型)上是否存在给定的属性,要检查的属性名必须是字符串(如obj.hasOwnProperty("name"))或Symbol

1
2
3
4
5
let person = {
name: 'Katrina'
};

console.log(person.hasOwnProperty('name')); // true

(3)isPrototypeOf(object)

用于判断当前对象是否为另一个对象的原型

1
2
3
4
5
6
7
let person = {
name: 'Katrina',
age: 18,
};

let p = Object.create(person);
console.log(person.isPrototypeOf(p)); // true

(4)propertyIsEnumerable(propertyName)

用于判断给定的属性是否可以使用for-in 语句枚举,属性名必须是字符串

1
2
3
4
5
6
7
8
9
10
11
12
13
14
let person = {};

Object.defineProperty(person, 'name', {
enumerable:true,
value: 'Katrina',
});

Object.defineProperty(person, 'age', {
enumerable:false,
value: 18,
});

console.log(person.propertyIsEnumerable('name')); // true
console.log(person.propertyIsEnumerable('age')); // false

(5)toLocaleString()

返回对象的字符串表示,该字符串反映对象所在的本地化执行环境

1
2
3
4
5
6
let person = {
name: 'Katrina',
age: 18,
};

person.toLocaleString(); // '[object Object]'

(6)toString()

返回对象的字符串表示

1
2
3
4
5
6
let person = {
name: 'Katrina',
age: 18,
};

person.toString(); // '[object Object]'

(7) valueOf()

返回对象对应的字符串、数值或布尔值表示,通常与toString()的返回值相同

1
2
3
4
5
6
let person = {
name: 'Katrina',
age: 18,
};

console.log(person.valueOf()); // {name: Katrina, age: 18}

设置原型方法

(1)Object.create() 【ES5】

Object.create()用于创建一个新对象,使用现有的对象来作为新创建对象的原型。

语法如下:

1
2
Object.create(proto);
Object.create(proto, propertiesObject);

参数说明:

proto:新建对象的原型对象

propertiesObject(可选):传入对象的可枚举属性将为新创建的对象添加指定的属性值和对应的属性描述符,这些属性对应于Object.defineProperties()的第二个参数

返回值:

一个新对象,带着指定的原型对象及其属性

1
2
3
4
5
6
7
let person = {
name: 'Katrina',
age: 18,
};

let p = Object.create(person);
console.log(person.isPrototypeOf(p)); // true

(2)Object.setPrototypeOf(obj, prototype) 【ES6】

Object.setPrototypeOf()方法设置一个指定的对象的原型 ( 即,内部 [[Prototype]] 属性)到另一个对象或 null

语法如下

1
Object.setPrototypeOf(obj, prototype)

参数

  • obj

    要设置其原型的对象。

  • prototype

    该对象的新原型 (一个对象 或 null)

警告:由于现代 JavaScript 引擎优化属性访问所带来的特性的关系,更改对象的 [[Prototype]]各个浏览器和 JavaScript 引擎上都是一个很慢的操作。其在更改继承的性能上的影响是微妙而又广泛的,这不仅仅限于 obj.__proto__ = ... 语句上的时间花费,而且可能会延伸到任何代码,那些可以访问任何 [[Prototype]] 已被更改的对象的代码。如果你关心性能,你应该避免设置一个对象的 [[Prototype]]。相反,你应该使用 Object.create() 来创建带有你想要的 [[Prototype]] 的新对象。

Object属性方法

自觉补课:Object属性类型

(1)定义属性:Object.defineProperty() 【ES5】 && Object.defineProperties() 【ES5】

两者的区别在于前者单个定义,后者多个定义

1
2
3
4
5
6
7
8
9
10
Object.defineProperty(obj, prop, descriptor);

- obj: 要定义属性的对象
- prop: 要定义或修改的属性的名称或Symbol
- descriptor: 要定义或修改的属性描述符


Object.defineProperties(obj,props);
- obj: 要定义属性的对象
- props: 要定义其可枚举属性或者修改的属性描述符的对象

举例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
// Object.defineProperty(obj, prop, descriptor);
let person = {};

Object.defineProperty(person, 'name', {
writable: false,
value: 'Katrina',
});

// Object.defineProperties(obj,props);
let person = {};

Object.defineProperties(person, {
name: {
writable: false,
value: 'Katrina',
},

age: {
enumerable: false,
value: 18,
}
})

(2)获取属性的特性:Object.getOwnPropertyDescriptor() 【ES5】 && Object.getOwnPropertyDescriptors() 【ES8】

可以取得指定属性的属性描述符

1
2
3
4
5
6
7
Object.getOwnPropertyDescriptor(obj, prop);

- obj:目标对象
- prop:目标对象内的属性

Object.getOwnPropertyDescriptors(obj)
- obj:目标对象

举例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
let person = {};

Object.defineProperties(person, {
name: {
writable: false,
value: 'Katrina',
},

age: {
enumerable: false,
value: 18,
}
});

let descriptor = Object.getOwnPropertyDescriptor(person, 'name');
console.log(descriptor); // {value: 'Katrina', writable: false, enumerable: false, configurable: false}
console.log(descriptor.writable); // false
console.log(descriptor.enumerable); // false

(3)获取属性名: Object.getOwnPropertyNames() 【ES5】 && Object.getOwnPropertySymbols() 【ES6】

Object.getOwnPropertyNames()返回一个由指定对象的所有自身属性的属性名(包括不可枚举属性但不包括 Symbol 值作为名称的属性)组成的数组

Object.getOwnPropertySymbols()返回一个给定对象自身的所有 Symbol 属性的数组

返回值都是数组,注意限定条件

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
// Object.getOwnPropertyNames()
var arr = ["a", "b", "c"];
console.log(Object.getOwnPropertyNames(arr).sort()); // ["0", "1", "2", "length"]

// Object.getOwnPropertySymbols()
var obj = {};
var a = Symbol("a");
var b = Symbol.for("b");

obj[a] = "localSymbol";
obj[b] = "globalSymbol";

var objectSymbols = Object.getOwnPropertySymbols(obj);

console.log(objectSymbols.length); // 2
console.log(objectSymbols) // [Symbol(a), Symbol(b)]
console.log(objectSymbols[0]) // Symbol(a)

(4)对象不可扩展: Object.preventExtensions() && Object.seal() && Object.freeze() 【ES5】ALL

Object.preventExtensions(obj)方法让一个对象变的不可扩展,也就是永远不能再添加新的属性,原有的属性可以修改和删除 【阻止】

Object.seal(obj)方法封闭一个对象,阻止添加新属性并将所有现有属性标记为不可配置,当前属性的值只要原来是可写的就可以改变,即不允许删除和添加,能不能修改根据writable属性决定 【封闭】

Object.freeze(obj)方法可以冻结一个对象,一个被冻结的对象再也不能被修改;冻结了一个对象则不能向这个对象添加新的属性,不能删除已有属性,不能修改该对象已有属性的可枚举性、可配置性、可写性,以及不能修改已有属性的值,啥也干不了呗~ 【冻结】

【根据中文含义,锁定深度是层层递进的~】

举例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
// Object.preventExtensions(obj)
var obj = {};
obj.a = 1;
Object.defineProperty(obj, 'c', {value: 3, configurable: true})
Object.defineProperty(obj, 'd', {value: 4, configurable: true, writable: true})
var obj2 = Object.preventExtensions(obj);
obj === obj2; // true
obj.a = 'a';
obj.b = 2;
obj.c = 'c';
obj.d = 'd'
console.log(obj.a, obj.b, obj.c, obj.d); // a, undefined, 3, d

delete obj.a
console.log(obj.a, obj.b, obj.c, obj.d); // undefined, undefined, 3, d

Object.defineProperty(obj, 'c', {writable: true});
obj.c = 'c';
Object.defineProperty(obj, 'd', {writable: false});
obj.d = 4;
console.log(obj.a, obj.b, obj.c, obj.d); // undefined, undefined, c, d

// Object.seal()
var obj = {};
obj.a = 1;
Object.defineProperty(obj, 'c', {value: 3, configurable: true})
Object.defineProperty(obj, 'd', {value: 4, configurable: true, writable: true})
var obj2 = Object.seal(obj);
obj === obj2; // true
obj.a = 'a';
obj.b = 2;
obj.c = 'c';
obj.d = 'd'
console.log(obj.a, obj.b, obj.c, obj.d); // a, undefined, 3, d

delete obj.a
console.log(obj.a, obj.b, obj.c, obj.d); // a, undefined, 3, d

Object.defineProperty(obj, 'c', {writable: true}); // TypeError
Object.defineProperty(obj, 'd', {writable: false}); // TypeError

// Object.freeze()
var obj = {};
obj.a = 1;
Object.defineProperty(obj, 'c', {value: 3, configurable: true})
Object.defineProperty(obj, 'd', {value: 4, configurable: true, writable: true})
var obj2 = Object.freeze(obj);
obj === obj2; // true
obj.a = 'a';
obj.b = 2;
obj.c = 'c';
obj.d = 'd'
console.log(obj.a, obj.b, obj.c, obj.d); // a, undefined, 3, 4
delete obj.a
console.log(obj.a, obj.b, obj.c, obj.d); // a, undefined, 3, 4


Object.defineProperty(obj, 'c', {writable: true}); // TypeError
Object.defineProperty(obj, 'd', {writable: false}); // TypeError

(5)对象是否可扩展 是否被冻结 是否被封锁:Object.isExtensible() Object.isFrozen() Object.isSealed() 【ES5】ALL

Object.isExtensible判断一个对象是否是可扩展的(是否可以添加新的属性)

Object.isFrozen()方法判断一个对象是否被冻结

Object.isSealed()判断一个对象是否被密封

1
2
Object.isExtensible(obj)  // 返回布尔值
Object.isFrozen(obj) // 返回布尔值

默认情况下,对象是可扩展的:即可以为他们添加新的属性。以及它们的 __proto__Deprecated 属性可以被更改。Object.preventExtensionsObject.sealObject.freeze 方法都可以标记一个对象为不可扩展(non-extensible)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
/ 新对象默认是可扩展的。
var empty = {};
Object.isExtensible(empty); // === true

// ...可以变的不可扩展。
Object.preventExtensions(empty);
Object.isExtensible(empty); // === false

// 密封对象是不可扩展的。
var sealed = Object.seal({});
Object.isExtensible(sealed); // === false

// 冻结对象也是不可扩展。
var frozen = Object.freeze({});
Object.isExtensible(frozen); // === false

其他

(1)枚举方法: Object.entries() 【ES8】 && Object.keys() 【ES5】 && Object.values() 【ES8】

Object.entries()方法返回一个给定对象自身可枚举属性的键值对数组,其排列与使用 for...in 循环遍历该对象时返回的顺序一致(区别在于 for-in 循环还会枚举原型链中的属性)

Object.keys() 方法会返回一个由一个给定对象的自身可枚举属性组成的数组,数组中属性名的排列顺序和正常循环遍历该对象时返回的顺序一致。

Object.values()方法返回一个给定对象自身的所有可枚举属性值的数组,值的顺序与使用for...in循环的顺序相同 ( 区别在于 for-in 循环枚举原型链中的属性 )

返回值都是数组

举例:

1
2
3
4
5
6
7
8
9
10
11
12
// Object.entries()
const obj = { foo: 'bar', baz: 42 };
console.log(Object.entries(obj)); // [ ['foo', 'bar'], ['baz', 42] ]

// Object.keys()
var anObj = { 100: 'a', 2: 'b', 7: 'c' };
console.log(Object.keys(anObj)); // console: ['2', '7', '100']


// Object.values()
var obj = { foo: 'bar', baz: 42 };
console.log(Object.values(obj)); // ['bar', 42]

(2)合并对象:Object.assign() 【ES6】

Object.assign() 方法将所有可枚举Object.propertyIsEnumerable() 返回 true)和自有(Object.hasOwnProperty() 返回 true)属性从一个或多个源对象复制到目标对象,返回修改后的对象

语法如下

1
Object.assign(target, ...sources);

参数

  • target:目标对象,接收源对象属性的对象,也是修改后的返回值

  • sources:源对象,包含将被合并的属性

举例:

1
2
3
4
5
6
7
const p1 = {name: 'Katrina', age: 20};
const p2 = {age: 18};

const person = Object.assign(p1, p2);

console.log(person); // {name: 'Katrina', age: 18};
console.log(p1); // {name: 'Katrina', age: 18};
  • Object.assign()会改变目标对象

  • Object.assign()只复制属性值,是一种浅拷贝

  • Object.assign()会覆盖从后向前覆盖相同属性

(3)比较方法: Object.is() 【ES6】

Object.is(value1, value2)判断两个值是否为同一个值

相较于===(严格相等),Object.is对两种情况做了改变:

  1. Object.is(NaN, NaN) ; // true
  2. Object.is(-0, +0); // true

其余和===一样

补充:手写Object.is

知识补充

对象 | 对象属性的类型

手写Object.is

深浅拷贝

手写原理| Object.create

循环语句 | for-in&&for-of的区别

参考

JavaScript 对象所有API解析

Object.preventExtensions()、Object.seal()、Object.freeze() 的区别

MDN Object