对象
对象是属性的无序集合。
# 创建
- 使用对象直接量
var obj = {}
- 通过 new 创建(构造函数)
var obj = new Object();
var arr = new Array();
2
- 使用 Object.create() 创建 (ES6)
Object.create(原型对象,自定义(不可枚举)属性对象)
方法创建一个新对象,使用现有的对象来提供新创建的对象的__proto__
原型有关请看:原型
Object有关请看: 对象方法
var tempObj = {
x: 1,
y: 2
}
var newAttrObj = {
a: {
writable: true,
configurable: true,
value: 100
}
}
var obj = Object.create(tempObj, newAttrObj);
console.log(obj);
// {
// a: 100,
// __proto__: {
// x: 1,
// y: 2
// }
// }
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
创建一个普通空对象
var obj3 = Object.create(Object.prototype);
obj3.toString(); // [object Object]
2
创建一个空对象,但该对象不继承任何属性和方法
var obj2 = Object.create(null);
obj2.toString(); // TypeError: obj2.toString is not a function
2
# 属性查询与设置
- 通过
.
来访问或赋值
var obj = {x:1}
obj.x; // 1
obj.y = 2;
2
3
- 通过
[]
来访问或赋值
var obj = {y:2}
obj[y]; // 2
obj[x] = 1;
2
3
区别:
方法 | 右侧 | 右侧是否可以为数字/变量/中划线 |
---|---|---|
. | 一个以 “属性名称” 命名的简单标识符 | N |
[] | 一个计算结果为 ”字符串“ 的表达式 | Y |
var obj = {name: 'jack', 12: '哈哈哈', 'old-price': 188};
obj[12]; // 哈哈哈
obj.12; // SyntaxError: Unexpected number
obj['old-price']; // 188
obj.old-price; // ReferenceError: price is not defined
var attrName = 'name';
obj[attrName]; // jack
obj.attrName; // undefined
2
3
4
5
6
7
8
9
# 属性类型
对象属性是由名字、值和一组特性(attribute)组成。在ECMAScript5中,属性值可以用一个或者两个方法替代,就是getter和setter。 由getter和setter定义的属性称为“存储器属性”(accessor property),也可以叫访问器属性。
对象的每个属性都有一个描述对象(Descriptor),用来控制该属性的行为。
ECMAScript 中有两种属性:数据属性和访问器属性。
# 数据属性
数据属性包含一个数据值的位置。在这个位置可以读取和写入值。数据属性有 4 个描述其行为的特性,又叫属性特性。
属性名 | 说明 | 默认值 |
---|---|---|
configurable | 能否通过 delete 删除属性从而重新定义属性,能否修改属性的特性,或者能否把属性修改为访问器属性 | true |
enumerable | 能否通过 for-in 循环返回属性 | true |
writable | 能否修改属性的值 | true |
value | 属性的数据值,属性值存储和读取都是在这个位置 | undefined |
1. 可通过 Object.getOwnPropertyDescriptor()
查看属性的描述对象
使用:Object.getOwnPropertyDescriptor(属性所在对象, 属性名)
let obj = { foo: 123 };
Object.getOwnPropertyDescriptor(obj, 'foo')
// {
// configurable: true
// enumerable: true
// value: 123
// writable: true
// }
2
3
4
5
6
7
8
2. 可通过 Object.defineProperty()
修改属性默认特性
使用:Object.defineProperty(属性所在对象, 属性名字, 描述符对象)
var person = {};
Object.defineProperty(person, "name", {
configurable: false,
writable: false,
value: "Nicholas"
});
console.log(person.name); //"Nicholas"
person.name = "Greg";
console.log(person.name); //"Nicholas" => writable为false,修改无效
delete person.name;
console.log(person.name); //"Nicholas" => configurable为false,删除无效
...
2
3
4
5
6
7
8
9
10
11
12
13
14
注意:
- 使用
Object.defineProperty()
定义新的属性,描述对象的属性默认值为false。
var person = {
age: 10
};
Object.defineProperty(person, "name", {
value: "Nicholas"
});
Object.getOwnPropertyDescriptor(person, 'name')
// 新的属性,描述对象的属性默认是false
// {
// configurable: false
// enumerable: false
// value: "Nicholas"
// writable: false
// }
Object.defineProperty(person, "age", {
value: "30",
configurable: false
});
Object.getOwnPropertyDescriptor(person, 'age')
// 已有属性则只影响设置的属性
// {
// configurable: false
// enumerable: true
// value: "30"
// writable: true
// }
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
- 一旦把属性定义为不可配置的,就不能再把它变回可配置了。此时,再调用 Object.defineProperty()方法修改除 writable 之外的特性,都会导致错误。
var person = {};
Object.defineProperty(person, "name", {
configurable: false,
value: "Nicholas"
});
//抛出错误
Object.defineProperty(person, "name", {
configurable: true,
value: "Nicholas"
});
2
3
4
5
6
7
8
9
10
# 访问器属性getter和setter
访问器属性不包含数据值;它们包含一对 getter 和 setter 函数(不过,这两个函数都不是必需的)。
- 读取访问器属性时,会调用 getter 函数,这个函数负责返回有效的值;
- 写入访问器属性时,会调用setter 函数并传入新值,这个函数负责决定如何处理数据。
访问器属性有如下 4 个特性。
属性名 | 说明 | 默认值 |
---|---|---|
configurable | 能否通过 delete 删除属性从而重新定义属性,能否修改属性的特性,或者能否把属性修改为访问器属性 | true |
enumerable | 能否通过 for-in 循环返回属性 | true |
get | 读取属性时调用的函数,只有get则是一个只读属性 | undefined |
set | 写入属性时调用的函数,只有set则是一个只写属性 | undefined |
注意:访问器属性不能直接定义,必须使用 Object.defineProperty()来定义
- 使用
Object.defineProperty()
定义单个属性
var book = {
_year: 2018,
edition: 1
};
Object.defineProperty(book, "year", {
get: function(){
return this._year;
},
set: function(newValue){
if (newValue > 2004) {
this._year = newValue;
this.edition += newValue - 2018;
}
}
});
book.year = 2020; // year: 2020
book.edition; // 3
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
- 使用
Object.defineProperties()
定义多个属性
var book = {};
Object.defineProperties(book, {
_year: { // 数据属性
value: 2018
},
edition: { // 数据属性
value: 1
},
year: { // 访问器属性
get: function(){
return this._year;
},
set: function(newValue){
if (newValue > 2018) {
this._year = newValue;
this.edition += newValue - 2018;
}
}
}
});
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
# 应用场景
# 总结
- 数据属性是属性自带的,数据属性可以被继承
- 访问器属性是需要创建的,访问器属性可以被继承
相关方法:
方法 | 作用 | 说明 |
---|---|---|
Object.getOwnPropertyDescriptor() | 属性的描述对象 | |
Object.defineProperty() | 修改属性默认特性/定义单个属性 | 数据属性和访问器属性都可用,使用后数据属性默认值为false |
Object.defineProperties() | 定义多个属性 | 数据属性和访问器属性可用,但注意数据属性设置false后不能改为true,所以慎用 |
# 删除属性
delete
操作符可以删除对象属性,并返回一个布尔值(成功返回true,否则返回false)。
语法:
delete object.property
delete object['property']
删除对象属性
var obj = {x:1}
delete obj.x; // true
2
删除数组元素,但数组的长度不受影响
var arr = ['jack', 'tony', 'jenny']
delete arr[1];
console.log(arr); // ['jack', empty, 'jenny']
console.log(arr[1]); // undefined
console.log(arr.length); // 3
2
3
4
5
6
注意:
- 删除属性不存在,delete 不会起作用,但仍会返回 true
- delete 只能删除自身属性,无法删除原型属性,除非从原型上删除
- 不可设置的(Non-configurable)属性不能被移除
- 无法删除任何使用 var 声明的属性(包括全局和函数作用域)
- 无法删除 let/const 声明的属性
/**
* 删除属性不存在,delete 不会起作用,但仍会返回 true
**/
var obj = {x:1}
delete obj.y; // true
/**
* delete 只能删除自身属性,无法删除原型属性,除非从原型上删除
*/
function Foo() {
this.bar = 10;
}
Foo.prototype.bar = 42;
var foo = new Foo();
delete foo.bar; // true,因为删除的是 foo 对象的自身属性
console.log(foo.bar); // 42, foo.bar 仍然可用,因为它在原型链上可用。
// 从原型上删除属性
delete Foo.prototype.bar; // true
// 由于已删除“ bar”属性,因此不能再从Foo继承它。
console.log(foo.bar); //undefined
/**
* 不可设置的(Non-configurable)属性不能被移除
*/
var Employee = {};
Object.defineProperty(Employee, 'name', {configurable: false});
console.log(delete Employee.name); // false
/**
* 无法删除任何使用 var 声明的属性
*/
var ss = 100;
delete ss; // false
window.ss; // 100
console.log(Object.getOwnPropertyDescriptor(window, 'ss'));
// {configurable: false, enumerable: true, value:100,writable: true}
/**
* 无法删除 let/const 声明的属性
*/
let name = 'jack';
const age = '10';
console.log(Object.getOwnPropertyDescriptor(this, 'name'))
// {configurable: false, enumerable: true}
console.log(delete name); // false
console.log(delete age); // false
console.log(name, age) // 'jack' 10
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
# 检测属性
检测某个属性是否存在于某个对象中。 传送门 =》检测是否是对象
- in
可检测自有属性和继承属性,如果包含则返回true。
var o = {x: 1, k: undefined}
"x" in o; // true - 自有属性
"k" in o; // true - 自有属性
"y" in o; // false - 非 o 对象属性
"toString" in o; // true - 继承属性
2
3
4
5
- hasOwnProperty()
可检测自有属性,包含则返回true。如果是继承属性将返回false。
var o = {x: 1, k: undefined};
o.hasOwnProperty("x"); // true - 自有属性
o.hasOwnProperty("k"); // true - 自有属性
o.hasOwnProperty("y"); // false - 非 o 对象属性
o.hasOwnProperty("toString"); // false - 继承属性
2
3
4
5
- propertyIsEnumerable()
hasOwnpreperty()的增强版, 只有检测到是自有属性且该属性的可枚举性为true时,才返回true。
var o = {x: 1}
// todo, 输出x的可枚举行
o.propertyIsEnumerable("x"); // true - 自有属性,且有可枚举
// todo 设置y,并且y的可枚举性为false
o.propertyIsEnumerable("y"); // false - 非 o 对象属性
o.propertyIsEnumerable("toString"); // false - 继承属性
2
3
4
5
6
- 使用!==
通过 !==
判断一个属性是否是 undefined,如果属性值为 undefined 则无法检测
var o = {x: 1, k: undefined}
o.x !== undefined; // true - 自有属性
o.y !== undefined; // false - 非 o 对象属性
o.toString !== undefined; // true - 继承属性
o.k !== undefined; // false - 如果属性值为undefined,则无法检测
2
3
4
5
6
对比
方法 | 检测自有属性 | 检测继承属性 | 检测值为undefined的属性 | 检测属性的可枚举性 |
---|---|---|---|---|
in | Y | Y | Y | N |
hasOwnProperty() | Y | N | Y | N |
propertyIsEnumerable() | Y | N | ? | Y |
!== | Y | Y | N | N |
# 固有属性
每个对象都有原型属性(prototype)、类(class)、可拓展性(extensible) 这三个属性。
# 1.原型属性(prototype)
对象的原型属性是用来继承属性的,原型属性也简称为原型。
# 原型链
当谈到继承时,JavaScript 只有一种结构:对象。每个实例对象( object )都有一个私有属性(称之为 __proto__
)指向它的构造函数的原型对象(prototype )即。该原型对象也有一个自己的原型对象( proto ) ,层层向上直到一个对象的原型对象为 null。根据定义,null 没有原型,并作为这个原型链中的最后一个环节。
使用不同的方法创建对象,原型链的查找有差异,具体请看:《使用不同的方法创建对象和生成原型链》
# __proto__
和 prototype 的关系和区别
在所有实现中,对象实例都没办法通过 [[prototype]] 来访问原型,所以定义了
__proto__
指向对象构造函数的 prototype
- prototype 是对象的原型属性,可被继承
__proto__
指向对象构造函数的 prototype- constructor 属性将原型对象指向关联的构造函数
function Person() {};
var p1= new Person();
var p2 = new Person();
typeof Person; // function
typeof p1; // object
p1.prototype; // undefined => 实例对象没有 prototype 属性
Object.getPrototypeOf(p1) == Person.prototype; // true => p1 的原型对象是 Person 的原型对象
p1.constructor == Person; // true => p1 的constructor 指向关联的构造函数
Person.prototype.constructor == Person; // true => constructor属性将原型对象指向关联的构造函数
p1.__proto__ === p2.__proto__; // true
p1.__proto__ === p1.constructor.prototype; // true
p1.__proto__ === Person.prototype; // true
p1.constructor.prototype === Person.prototype; // true
Person.__proto__ == Person.constructor.prototype; // true
Person.__proto__ == Object.constructor.prototype // true
Person.constructor.prototype == Object.constructor.prototype // true
Object.__proto__ == Object.constructor.prototype // true
// 字面量方法创建
var a = {}
a.__proto__ == a.constructor.prototype; // true
a.__proto__ == Object.prototype; // true
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
参考文章:一篇文章看懂_proto_和prototype的关系及区别
# isPrototypeOf()
isPrototypeOf():测试一个对象是否存在于另一个对象的原型链上
function Foo() {}
function Bar() {}
function Baz() {}
Bar.prototype = Object.create(Foo.prototype);
Baz.prototype = Object.create(Bar.prototype);
var baz = new Baz();
console.log(Baz.prototype.isPrototypeOf(baz)); // true
console.log(Bar.prototype.isPrototypeOf(baz)); // true
console.log(Foo.prototype.isPrototypeOf(baz)); // true
console.log(Object.prototype.isPrototypeOf(baz)); // true
2
3
4
5
6
7
8
9
10
11
12
13
# 2.类属性
对象的类属性(class attribute)是一个字符串,用来表示对象的类型信息。可以通过 toString() 方法来查询它。 默认的 toString() 方法会返回下面格式的字符串:
[object class]
toString()方法可以被重写,所以为了能得到正确的数据,需要间接调用Function.call()方法
function classof(o) {
return Object.prototype.toString.call(o);
// return Object.prototype.toString.call(o).slice(8, -1);
}
classof(null); // [object Null]
classof(undefined); // [object Undefined]
classof(1); // [object Number]
classof(""); // [object String]
classof(false); // [object Boolean]
classof({}); // [object Object]
classof([]); // [object Array]
classof(/./); // [object RegExp]
classof(new Date()); // [object Date]
classof(window); // [object Window]
2
3
4
5
6
7
8
9
10
11
12
13
14
15
# 3.可拓展性
对象的可拓展性表示是否可以给对象添加新属性。所有内置对象和自定义对象都是显示可拓展的。
# 检测可拓展性
- Object.isExtensible()
- Object.isSealed()
- Object.isFrozen()
# 影响可拓展性的方法
- Object.preventExtensions()
- Object.seal()
- Object.freeze()
# 注意
- 一旦转为不可拓展后,就无法再转回可拓展
- preventExtensions 只影响到对象本身的可拓展性
var a = {};
Object.isExtensible(a); // true
Object.preventExtensions(a); // Object.seal()、Object.freeze() 同理
a.name = 'a';
a; // {}
Object.isExtensible(a); // false
2
3
4
5
6
# 对象方法
这些方法主要是从原型上继承的。
方法名 | 功能 | 备注 |
---|---|---|
hasOwnProperty() | 检测自有属性 | |
propertyIsEnumerable() | 检测自有属性且该属性的可枚举性为true时 | |
isPrototypeOf() | 测试一个对象是否存在于另一个对象的原型链上 | |
toString() | 返回表示调用这个方法的对象值的字符串 | |
toLocaleString() | 返回对象本地化的字符串 | |
toJSON() | 返回序列化的结果 | |
valueOf() | 将对象转换为原始值 |
var a = {name: 123};
a.toString(); // [object Object]
a.toLocaleString(); // [object Object]
a.valueOf(); // {name: 123}
var b = [1, 2, 3] // 1,2,3
b.toString(); // 1,2,3
b.toLocaleString();
b.valueOf(); // [1, 2, 3]
var d = new Date();
d.toString(); // Fri Aug 28 2020 11:29:42 GMT+0800 (中国标准时间)
d.toLocaleString(); // 2020/8/28 上午11:29:42
d.toJSON(); // 2020-08-28T03:29:42.460Z
d.valueof(); // 1598585382460
2
3
4
5
6
7
8
9
10
11
12
13
14
15
# Object 构造函数
# 属性
- Object.length:值为 1
- Object.prototype:可以为所有 Object 类型的对象添加属性
# 方法
ES5
方法名 | 功能 |
---|---|
Object.create() | 使用指定的原型对象和属性创建一个新对象 |
Object.defineProperty() | 给对象添加一个属性并指定该属性的配置 |
Object.defineProperties() | 给对象添加多个属性并分别指定它们的配置 |
Object.getOwnPropertyNames() | 返回一个数组,它包含了指定对象所有的可枚举或不可枚举的属性名 |
Object.getOwnPropertySymbols() | 返回一个数组,它包含了指定对象自身所有的符号属性 |
Object.getOwnPropertyDescriptor() | 返回对象指定的属性配置 |
Object.isExtensible() | 判断对象是否可扩展 |
Object.preventExtensions() | 防止对象的任何扩展 |
Object.seal() | 创建一个“密封”对象,实际上是调用Object.preventExtensions()把所有属性标记为configurable:false,仅可修改属性的值 |
Object.isSealed() | 判断对象是否已经密封 |
Object.freeze() | 创建一个“冻结”对象,实际上调用Object.seal(),并把所有属性标记为writable:false |
Object.isFrozen() | 判断对象是否已经冻结 |
ES6
方法名 | 功能 |
---|---|
Object.is() | 比较两个值是否相同。所有 NaN 值都相等(这与==和===不同) |
Object.assign() | 通过复制一个或多个对象来创建一个新的对象 |
Object.getOwnPropertyDescriptors() | 返回对象所有属性的描述对象 |
Object.setPrototypeOf() | 设置对象的原型(即内部 [[Prototype]] 属性) |
Object.getPrototypeOf() | 返回指定对象的原型对象 |
Object.keys() | 返回一个包含所有给定对象自身可枚举属性名称的数组 |
Object.values() | 返回给定对象自身可枚举值的数组 |
Object.entries() | 返回给定对象自身可枚举属性的 [key, value] 数组 |
Object.fromEntries() | Object.entries()的逆操作,用于将一个键值对数组转为对象 |