我是目录
引言
对象很重要,是的,对象很重要。
对象
对象定义
在ECMAScript-262中,对象被定义为无序属性的集合,其属性可以包含基本值,对象或者函数。
也就是说,在JavaScript
中,对象无非就是由一些列无序的key-value
对组成。其中value
可以是基本值,对象或者函数。
从技术角度来说, 函数永远不会“ 属于” 一个对象,它们只是对于相同函数对象的多个引用。无论返回值是什么类型, 每次访问对象的属性就是属性访问。 如果属性访问返回的是一个函数, 那它也并不是一个“ 方法”。
内置对象
JavaScript
中还有一些对象子类型, 通常被称为内置对象。
- String
- Number
- Boolean
- Object
- Function
- Array
- Date
- RegExp
- Error
他们或许很像类,但是在 JavaScript 中, 它们实际上只是一些内置函数。 这些内置函数可以当作构造函数来使用, 从而可以构造一个对应子类型的新对象。
属性描述符
从 ES5 开始, 所有的属性都具备了属性描述符:
writable
决定是否可以修改属性的值: false为不可写,true为可写(不可修改属性)。configurable
只要属性是可配置的, 就可以使用defineProperty(..)
方法来修改属性描述符。configurable:false
还会禁止删除这个属性。enumerable
这个描述符控制的是属性是否会出现在对象的属性枚举中(可出现在对象属性遍历中)。如果把enumerable
设置成false
, 这个属性就不会出现在枚举中, 虽然仍然可以正常访问它。 相对地,设置成true
就会让它出现在枚举中。
怎样使对象不可变
以下所有的方法创建的不可变对象都是浅不变型, 也就是说, 它们只会影响目标对象和它的直接属性。 如果目标对象引用了其他对象(数组、 对象、 函数, 等), 其他对象的内容不受影响, 仍然是可变的。
- 对象常量
结合writable:false
和configurable:false
就可以创建一个真正的常量属性(不可修改、重定义或者删除)
var myObject = {
};
Object.defineProperty( myObject, "FAVORITE_NUMBER", {
value: 42,
writable: false,
configurable: false
});
- 禁止扩展:禁止添加新属性,保留原有属性
var myObject = {
a:2};
Object.preventExtensions( myObject );
myObject.b = 3;
myObject.b; // undefined
- 密封
Object.seal(..)
会创建一个“密封” 的对象, 这个方法实际上会在一个现有对象上调用Object.preventExtensions(..)
并把所有现有属性标记为configurable:false
。所以, 密封之后不仅不能添加新属性, 也不能重新配置或者删除任何现有属性(虽然可以修改属性的值)。 - 冻结
Object.freeze(..)
会创建一个冻结对象, 这个方法实际上会在一个现有对象上调用Object.seal(..)
并把所有“数据访问” 属性标记为writable:false
, 这样就无法修改它们的值。这个方法是你可以应用在对象上的级别最高的不可变性, 它会禁止对于对象本身及其任意直接属性的修改(不过就像我们之前说过的, 这个对象引用的其他对象是不受影响的)。
setter和getter
getter
和setter
只能应用在单个属性上。他们都是隐藏函数。当给一个属性定义 getter
、 setter
或者两者都有时, 这个属性会被定义为“ 访问描述符”( 和“数据描述符” 相对)。 对于访问描述符来说, JavaScript 会忽略它们的 value
和writable
特性, 取而代之的是关心 set
和 get
(还有 configurable
和 enumerable
) 特性。
var myObject = {
get a() {
return this._a_;
},
set a(val) {
this._a_ = val * 2;
}
}
myObject.a = 2;
myObject.a; //4
对象的[[Get]]和[[Put]]
对象默认的 [[Put]]
和 [[Get]]
可以理解为算法,这两种操作分别可以控制属性值的设置和获取。
Get
会在原型链上查找并获取属性值
Put
会检查:
- 属性是否是访问描述符? 如果是并且存在
setter
就调用setter
。 - 属性的数据描述符中
writable
是否是false
? 如果是, 在非严格模式下静默失败, 在严格模式下抛出TypeError
异常。 - 如果都不是, 将该值设置为属性的值。
对象的操作
对象属性判断:
var myObject = {
a: 2
}
('a' in myObject); //true
myObject.hasOwnProperty('a'); //true
in
操作符会检查属性是否在对象及其原型链上,hasOwnProperty
只会检查是否在对象上,不会检查原型链。
Object.create
Object.create(..)
会创建一个新对象并把它关联到我们指定的对象。
Object.create(null)
会 创 建 一 个 拥 有 空(或 者 说 null
) [[Prototype]]
链接的对象, 这个对象无法进行委托。 由于这个对象没有原型链, 所以instanceof
操作符( 之前解释过) 无法进行判断, 因此总是会返回 false
。这些特殊的空 [[Prototype]]
对象通常被称作“ 字典”, 它们完全不会受到原型链的干扰, 因此非常适合用来存储数据。
if (!Object.create) {
Object.create = function(o) {
function F(){
}
F.prototype = o;
return new F();
};
}
对象复制
对象复制要考虑浅复制还是深复制。
对于 JSON
安全的对象来说, 有一种巧