1
0
mirror of https://github.com/ruanyf/es6tutorial.git synced 2025-05-25 03:02:21 +00:00
es6tutorial/docs/object.md
2014-05-23 22:41:49 +08:00

350 lines
8.3 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# 对象的扩展
## Object.is()
Object.is()用来比较两个值是否严格相等。它与严格比较运算符(===)的行为基本一致,不同之处只有两个:一是+0不等于-0二是NaN等于自身。
```javascript
+0 === -0 //true
NaN === NaN // false
Object.is(+0, -0) // false
Object.is(NaN, NaN) // true
```
## Object.assign()
Object.assign方法用来将源对象source的所有可枚举属性复制到目标对象target。它至少需要两个对象作为参数第一个参数是目标对象后面的参数都是源对象。只要有一个参数不是对象就会抛出TypeError错误。
```javascript
var target = { a: 1 };
var source1 = { b: 2 };
var source2 = { c: 3 };
Object.assign(target, source1, source2);
target // {a:1, b:2, c:3}
```
注意,如果目标对象与源对象有同名属性,或多个源对象有同名属性,则后面的属性会覆盖前面的属性。
```javascript
var target = { a: 1, b: 1 };
var source1 = { b: 2, c: 2 };
var source2 = { c: 3 };
Object.assign(target, source1, source2);
target // {a:1, b:2, c:3}
```
## __proto__属性Object.setPrototypeOf()Object.getPrototypeOf()
**1__proto__属性**
__proto__属性用来读取或设置当前对象的prototype对象。该属性一度被正式写入ES6草案但后来又被移除。目前所有浏览器包括IE11都部署了这个属性。
```javascript
var obj = {
__proto__: someOtherObj,
method: function() { ... }
}
```
有了这个属性实际上已经不需要通过Object.create()来生成新对象了。
**2Object.setPrototypeOf()**
Object.setPrototypeOf方法的作用与__proto__相同用来设置一个对象的prototype对象。
```javascript
// 格式
Object.setPrototypeOf(object, prototype)
// 用法
var o = Object.setPrototypeOf({}, null);
```
该方法等同于下面的函数。
```javascript
function (obj, proto) {
obj.__proto__ = proto;
return obj;
}
```
**3Object.getPrototypeOf()**
该方法与setPrototypeOf方法配套用于读取一个对象的prototype对象。
```javascript
Object.getPrototypeOf(obj)
```
## 增强的对象写法
ES6允许直接写入变量和函数作为对象的属性和方法。这样的书写更加简洁。
```javascript
var Person = {
name: '张三',
//等同于birth: birth
birth,
// 等同于hello: function ()...
hello() { console.log('我的名字是', this.name); }
};
```
这种写法用于函数的返回值,将会非常方便。
```javascript
function getPoint() {
var x = 1;
var y = 10;
return {x, y};
}
getPoint()
// {x:1, y:10}
```
## 属性名表达式
ES6允许定义对象时用表达式作为对象的属性名。在写法上要把表达式放在方括号内。
```javascript
var lastWord = "last word";
var a = {
"first word": "hello",
[lastWord]: "world"
};
a["first word"] // "hello"
a[lastWord] // "world"
a["last word"] // "world"
```
上面代码中对象a的属性名lastWord是一个变量。
下面是一个将字符串的加法表达式作为属性名的例子。
```javascript
var suffix = " word";
var a = {
["first" + suffix]: "hello",
["last" + suffix]: "world"
};
a["first word"] // "hello"
a["last word"] // "world"
```
## symbols
ES6引入了一种新的原始数据类型symbol。它通过Symbol函数生成。
```javascript
var mySymbol = Symbol('Test');
mySymbol.name
// Test
typeof mySymbol
// "symbol"
```
上面代码表示Symbol函数接受一个字符串作为参数用来指定生成的symbol的名称可以通过name属性读取。typeof运算符的结果表明Symbol是一种单独的数据类型。
注意Symbol函数前不能使用new命令否则会报错。这是因为生成的symbol是一个原始类型的值不是对象。
symbol的最大特点就是每一个symbol都是不相等的保证产生一个独一无二的值。
```javascript
let w1 = Symbol();
let w2 = Symbol();
let w3 = Symbol();
function f(w) {
switch (w) {
case w1:
...
case w2:
...
case w3:
...
}
}
```
上面代码中w1、w2、w3三个变量都等于`Symbol()`,但是它们的值是不相等的。
由于这种特点Symbol类型适合作为标识符用于对象的属性名保证了属性名之间不会发生冲突。如果一个对象由多个模块构成这样就不会出现同名的属性。
Symbol类型作为属性名可以被遍历Object.getOwnPropertySymbols()和Object.getOwnPropertyKeys()都可以获取该属性。
```javascript
var a = {};
var mySymbol = Symbol();
a[mySymbol] = 'Hello!';
//另一种写法
Object.defineProperty(a, mySymbol, { value: 'Hello!' });
```
上面代码通过点结构和Object.defineProperty两种方法为对象增加一个属性。
下面的写法为Map结构添加了一个成员但是该成员永远无法被引用。
```javascript
let a = Map();
a.set(Symbol(), 'Noise');
a.size // 1
```
如果要在对象内部使用symbol属性名必须采用属性名表达式。
```javascript
let specialMethod = Symbol();
let obj = {
[specialMethod]: function (arg) {
...
}
};
obj[specialMethod](123);
```
## Proxy
所谓Proxy可以理解成在目标对象之前架设一层“拦截”外界对该对象的访问都必须先通过这层拦截可以被过滤和改写。
ES6原生提供Proxy构造函数用来生成proxy实例对象。
```javascript
var proxy = new Proxy({}, {
get: function(target, property) {
return 35;
}
});
proxy.time // 35
proxy.name // 35
proxy.title // 35
```
上面代码就是Proxy构造函数使用实例它接受两个参数第一个所要代理的目标对象上例是一个空对象第二个是拦截函数它有一个get方法用来拦截对目标对象的访问请求。get方法的两个参数分别是目标对象和所要访问的属性。可以看到由于拦截函数总是返回35所以访问任何属性都得到35。
下面是另一个拦截函数的例子。
```javascript
var person = {
name: "张三"
};
var proxy = new Proxy(person, {
get: function(target, property) {
if (property in target) {
return target[property];
} else {
throw new ReferenceError("Property \"" + property + "\" does not exist.");
}
}
});
proxy.name // "张三"
proxy.age // 抛出一个错误
```
上面代码表示如果访问目标对象不存在的属性会抛出一个错误。如果没有这个拦截函数访问不存在的属性只会返回undefined。
## Object.observe()Object.unobserve()
Object.observe方法用来监听对象的变化。一旦监听对象发生变化就会触发回调函数。
```javascript
var o = {};
function observer(changes){
changes.forEach(function(change) {
console.log('发生变动的属性:' + change.name);
console.log('变动前的值:' + change.oldValue);
console.log('变动后的值:' + change.object[change.name]);
console.log('变动类型:' + change.type);
});
}
Object.observe(o, observer);
```
上面代码中Object.observe方法监听一个空对象o一旦o发生变化比如新增或删除一个属性就会触发回调函数。
Object.observe方法指定的回调函数接受一个数组changes作为参数。该数组的成员与对象的变化一一对应也就是说对象发生多少个变化该数组就有多少个成员。每个成员是一个对象change它的name属性表示发生变化源对象的属性名oldValue属性表示发生变化前的值object属性指向变动后的源对象type属性表示变化的种类目前共支持六种变化add、update、delete、setPrototype、reconfigure属性的attributes对象发生变化、preventExtensions当一个对象变得不可扩展时也就不必再观察了
Object.observe方法还可以接受第三个参数用来指定监听的事件种类。
```javascript
Object.observe(o, observer, ['delete']);
```
上面的代码表示只在发生delete事件时才会调用回调函数。
Object.unobserve方法用来取消监听。
```javascript
Object.unobserve(o, observer);
```
注意Object.observe和Object.unobserve这两个方法不属于ES6而是属于ES7的一部分Chrome 36已经开始支持了。