简单基本类型(string、boolean、number、null、undefined)本身并不是对象,但是typeof null时会返回字符串‘object’。但实际上,null本身是基本类型。
原理是这样的。不同的对象在底层都表示二进制,在js中二进制前三位都为0的话会被判断为object,null的二进制表示全部为0,自然前三位也是0。所以执行typeof时会返回‘object’。
数组
数组也是对象,所以虽然每个下标都是整数,仍可以给数组添加属性
var myArr = ['foo', 42, 'bar']
myArr.baz = 'baz'
myArr.length // 3
myArr.baz // 'baz'
虽然添加了命名属性,数组的length值并未发生变化。
如果试图向数组添加一个属性,但是属性名‘看起来’像一个数字,那它会变成一个数值下标(因此会修改数组的内容而不是添加一个属性)
var myArr = ['foo', 42, 'bar']
myArr['3'] = 'baz'
myArr.length // 4
myArr[3] // 'baz'
属性描述符
var myObj = {a:2}
Object.getOwnPropertyDescriptor(myObj, 'a')
// {value: 2, writable: true, enumerable: true, configurable: true}
这个普通的对象属性对应的属性描述符不仅仅是一个2.它还包含另外三个特性:writable、enumerable和configurable。
在创建普通属性时属性描述符会使用默认值,可以使用Object.defineProperty()来添加一个新属性或者修改一个已有属性(如果是configurable)。
var myObj = {}
Object.defineProperty(myObj, 'a', {
value: 3,
configurable: true,
enumerable: true,
writable: true
})
myObj.a // 2
除了defineProperty外还有defineProperties方法,差别如下:
Object.defineProperty(obj, prop, descriptor)
Object.defineProperties(obj, props)
- writable决定是否可以修改属性的值
- configurable决定属性是否可配置,只要属性是可配置的,就可以使用defineProperty()方法来修改属性描述符(把configurable修改为false是单向操作,无法撤销);configurable: false 还会禁止删除这个属性
- enumerable决定的是该属性是否会出现在对象的属性枚举中。
不变性
- writable: false 和 configurable: false 就可以创建一个真正的常量属性。
- 若想禁止一个对象添加新属性并且保留已有属性,可以使用Object.preventExtensions()。
- Object.seal()会创建一个‘密封’的对象,这个方法实际上会在一个现有对象上调用Object.preventExtensions()并把所有现有属性标记为configurable: false。
- Object.freeze()会创建一个冻结对象,这个方法实际上会在一个现有对象上调用Object.seal()并把所有‘数据访问’属性标记为writable: false。
存在性
- in 会检查对象及其原型链
- hasOwnProperty 只会检查对象
- propertyIsEnumerable会检查对象 并且 满足enumerable: true
- Object.keys()会返回一个数组,包含所有可枚举属性
- Object.getOwnPropertyNames()会返回一个数组,包含所有属性,无论他们是否可枚举