创建简单对象
1,使用对象字面量
1 | var person = { |
2,使用 new 运算符
new 运算符可以创建用户自定义的对象类型或具有构造函数的内置对象类型的实例。
有两种情况:
- 函数 return 的是对象,则新对象接收该对象。
- 函数没有 return 或者 return 的不是对象,则把该函数的 this(上下文)指定给新对象。
1 | var person = new Object(); |
对象的属性
1,内部属性和 [[prototype]]
通常情况下,内部属性不能直接访问,用两对方括号表示,例如:[[prototype]] 。
每一个 JavaScript 对象都有一个名为 [[prototype]] 的内部属性,用来存放该对象指向的原型对象,称为原型链。
1)检查原型链:
Object.prototype.isPrototypeOf() 方法
语法: prototype.isPrototypeOf(object)
检测 prototype 对象 是否在 object 对象的原型链上。
1 | function Person(name) { |
另一个检查原型链的办法:
instanceof 运算符
语法: object instanceof constructor
用来检查 constructor.prototype 是否存在于 object 的原型链上。
注意:
isPrototypeOf 和 instanceof 云算法的作用完全不同。
在表达式 object instanceof constructor 中,检测的是 constructor.prototype 是否在 object 的原型链上,而不是检测 constructor 本身。
2)获取原型:
语法: Object.getPrototypeOf(object)
获取对象的 [[Prototype]] 内部属性。
1 | Object.getPrototypeOf(person) === Object.prototype // true |
在 ES5 中,如果参数不是一个对象类型,将抛出一个 TypeError 异常。
在 ES6 中,参数被强制转换为 Object。
3)设置原型:
__proto__ 属性 和 Object.setPrototypeOf() 方法
ES6 之前,可以通过 __proto__ 属性设置,ES6 开始,可以用 Object.setPrototypeOf() 方法来设置。
4)应用:
添加原型链
通过 Object.getPrototypeOf() 和 Object.setPrototypeOf() 的组合,可以给一个新的原型对象添加完整的原型链:
1 | /** |
2,Object.prototype.constructor 属性
函数在创建时会带有 prototype 属性,称为原型对象,
该对象有一个不可枚举且可修改的 constructor 属性,其值默认指向该函数。
对象自身并没有 constructor 属性,该属性都继承自其原型对象。
知乎:JavaScript 中对象的 constructor 属性的作用是什么?
反之,如果需要隐藏构造函数的原型对象,可修改实例的 constructor 属性,
所以,依赖一个对象的 constructor 属性并不安全。
3,属性
1)获取属性
获取自身属性
返回一个由给定对象的所有可枚举自身属性的属性名组成的数组。
Object.getOwnPropertyNames() 方法
返回一个由指定对象的所有自身属性(包括不可枚举)的属性名组成的数组。
获取所有属性
for…in 语句
遍历对象上可枚举的属性(包括自身属性和继承属性)。
2)设置属性
直接赋值
1 | person.name = 'bell'; |
通过方法赋值
语法: Object.defineProperty(obj, prop, descriptor)
通过定义或修改 属性描述对象 来给属性赋值,并返回该对象。
语法: Object.defineProperties(obj, props)
在一个对象上添加或修改一个或者多个自身属性,并返回该对象。
3)删除属性
delete 操作符
用来删除一个对象的自身属性。
在严格模式下,如果属性是一个不可配置(non-configurable)属性,删除时会抛出异常。
非严格模式下,返回 false。
其他情况都返回 true(如果删除的属性不存在也会返回 true)。
delete 操作符可以删除一个隐式声明的全局变量。
一些对象的属性不能被delete。 ECMA 262 规范中把这些属性标记为 DontDelete。
1 | var y = 2; |
当使用 delete 删除一个数组元素时,数组的 length 属性并不会变小,被删除的元素已经不属于该数组。
1 | var trees = ["redwood","bay","cedar","oak","maple"]; |
4)检查属性
Object.prototype.hasOwnProperty() 方法
使用 hasOwnProperty() 方法来判断某个对象是否含有指定的自身属性,
和 in 运算符不同,该方法会忽略掉那些从原型链上继承到的属性。
注意:
hasOwnProperty 方法有可能被遮蔽
如果一个对象拥有自己的 hasOwnProperty 方法, 则原型链上的同名方法会被遮蔽(shadowed):
1 | var foo = { |
in 运算符
检查指定的属性是否存在于指定的对象中,包括从原型链上继承的属性。
只有 delete 的属性才会返回 false,值为 undefined 的属性会返回 true。
注意:
数组是一个特殊的对象,在对数组做属性检查时,其实检查的是数组的下标。
4,属性赋值的流程
分两种情况:
1)对象有该自身属性
1,是否是访问器属性(是否有 [[set]] 函数),是则调用。
2,是否可写(writable:false),是则静默失败或抛出 TypeError 异常。
3,如果都不是,则把该值设置为属性的值。
2)对象没有该自身属性
1,如果原型链上也没有该属性,则该属性会被添加到对象上成为自身属性。
2,如果原型链上存在该属性,则会有三种情况。
如果原型链上的属性为数据属性,且 writable:true,则该属性会被添加到对象上,称为屏蔽属性。
如果原型链上的属性为数据属性,且 writable:false,则创建屏蔽属性静默失败,或者抛出 TypeError 异常。
如果原型链上的属性为访问器属性,则会调用原型链上的 [[set]] 函数,如果函数中存在通过 this 存放的过渡属性,则会在对象上也创建一个过渡属性。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18var obj1 = {};
Object.defineProperty(obj1, 'age', {
set: function (val) {
this._age_ = val;
},
get: function () {
return this._age_ || 10;
}
});
var obj2 = Object.create(obj1);
obj1.age = 20;
obj2.age = 30;
obj1.age // 20
obj2.age // 30
obj2 // Object {_age_: 30}
如果想在第二种和第三种情况下也实现屏蔽属性,则需要使用 Object.defineProperty() 方法。
5,属性描述对象
Object.getOwnPropertyDescriptor() 方法返回指定对象上一个自身属性的属性描述对象。
(自身属性指的是直接赋予该对象的属性,不需要从原型链上进行查找的属性。)
1)数据属性
[[value]]:属性的值。
[[writable]]:属性的值是否可修改。
[[enumerable]]:属性是否可枚举。
[[configurable]]:属性是否可配置,
设置为 false 后,该属性不可使用 delete 删除,不可使用 Object.defineProperty() 等方法修改。
注意:
当修改 configurable:false 后,属性只接受 writable:false 的修改,
其他修改都会抛出 TypeError 的错误,或静默失败。
1 | Object.defineProperty(person, 'name', {configurable:false}); |
2)访问器属性
当给一个属性定义 [[get]],[[set]] 时,该属性会被定义为访问器属性。
访问器属性会忽略 value 和 writable 特性。
[[enumerable]]:同数据属性。
[[configurable]]:同数据属性。
[[get]]:读取属性值的函数,只有该函数则意味着不能写入属性值。
[[set]]:写入属性值的函数,只有该函数则意味着不能读取属性值。
6,默认的属性描述对象
1)直接赋值
1 | person.name = 'bell'; |
2)通过 Object.defineProperty() 等方法赋值
1 | Object.defineProperty(person, 'age', {}); |
7,不变性的应用
1)对象常量
使用 writable:false 和 configurable:false 创建一个常量属性
1 | var myObject = {}; |
2)禁止扩展
使用 Object.preventExtensions() 方法让一个对象变的不可扩展,并且返回原对象。
Object.preventExtensions() 只能阻止一个对象不能再添加新的自身属性,仍然可以为该对象的原型添加属性。
使用 Object.isExtensible() 方法判断一个对象是否是可扩展的(是否可以在它上面添加新的属性)。
密封、冻结对象均为不可扩展。
3)密封
使用 Object.seal() 方法让一个对象密封,并返回被密封后的对象。
密封一个对象会让这个对象变的不能添加新属性,且所有已有自身属性会变的不可配置,
但属性的值仍然可以修改,且不会影响从原型链上继承的属性。
使用 Object.isSealed() 方法判断一个对象是否是密封的。
密封对象是指那些不可扩展的,且所有自身属性都不可配置的对象。
4)冻结
使用 Object.freeze() 方法可以冻结一个对象。
冻结对象的所有自身属性都不可能以任何方式被修改,包括数据属性和访问器属性。
如果一个属性的值是个对象,则这个对象中的属性是可以修改的,除非它本身也是个冻结对象。
使用 Object.isFrozen() 方法判断一个对象是否被冻结的。