文章目錄
  1. 1. 属性和方法的简写
  2. 2. 计算属性名
  3. 3. Object.assign()
  4. 4. 重复对象字面量属性
  5. 5. 改变原型
  6. 6. super引用

本文内容来自于:https://leanpub.com/understandinges6/read#leanpub-auto-objectis。内容有删减,有不懂的地方可以看原文哟。

ES6对对象也进行了很多扩展,比如属性和方法的简写方式、Object.assign()等。下面来一一介绍ES6中对象的扩展。

属性和方法的简写

ES5或者更早时,对象直接量其实就是简单的名-值对集合,这意味着当属性值被初始化时可能会有一些重复。例如:

1
2
3
4
5
6
function createPerson(name, age) {
return {
name: name,
age: age
};
}

createPerson()创建了一个对象,这个对象的属性名称和函数的参数名称是一样的。其结果是导致了nameage的复制,尽管每个表示一个过程的不同方面。

ES6中,当属性名和局部变量名是一样时,我们可以省略它后面的冒号和值。例如,上面的例子可以重写为:

1
2
3
4
5
6
function createPerson(name, age) {
return {
name,
age
};
}

当一个对象直接量的一个属性只有名称没有值时,JavaScript引擎会在周围作用域中寻找和属性名有相同名称的变量。如果找到,值就被赋给对象直接量中相同名的属性。所以在这个例子中,对象直接量的属性name被赋值为局部变量name的值。

除了属性可以简写外,方法也是可以简写的。在ES5或之前,我们定义方法必须像下面这样:

1
2
3
4
5
6
var person = {
name: "Nicholas",
sayName: function() {
console.log(this.name);
}
};

但在ES6中,通过省略冒号和function关键字,使语法变得更加简洁。你可以重写之前的例子:

1
2
3
4
5
6
var person = {
name: "Nicholas",
sayName() {
console.log(this.name);
}
};

计算属性名

JavaScript中,我们定义属性时,有两种方式:中括号[].的方式:

1
2
3
4
5
// 方法一
obj.foo = true;

// 方法二
obj['a'+'bc'] = 123;

.运算符具有很大的局限性,比如first name这种属性只能通过中括号的方式来定义。中括号的方式允许我们使用变量或者在使用标识符时会导致语法错误的字符串直接量来定义属性。例如:

1
2
3
4
5
6
7
8
var person = {},
lastName = "last name";

person["first name"] = "Nicholas";
person[lastName] = "Zakas";

console.log(person["first name"]); // "Nicholas"
console.log(person[lastName]); // "Zakas"

这两种方式只能通过中括号的方式来定义的。在ES5中,你可以在对象直接量中使用字符串直接量作为属性,例如:

1
2
3
4
5
var person = {
"first name": "Nicholas"
};

console.log(person["first name"]); // "Nicholas"

但是当我们的属性名存在一个变量中或者需要计算时,使用对象直接量是无法定义属性的。但是在ES6中计算属性名语法,同样是通过中括号的方式。例如:

1
2
3
4
5
6
7
8
9
var lastName = "last name";

var person = {
"first name": "Nicholas",
[lastName]: "Zakas"
};

console.log(person["first name"]); // "Nicholas"
console.log(person[lastName]); // "Zakas"

在对象直接量中的中括号表明属性名是需要被计算的,它的内容被计算为字符串。

Object.assign()

对于对象构成最流行的模式之一可能是mixin,一个对象从另一个对象中接收属性和方法。许多JavaScript库都有一个类似下面的mixin方法:

1
2
3
4
5
6
7
function mixin(receiver, supplier) {
Object.keys(supplier).forEach(function(key) {
receiver[key] = supplier[key];
});

return receiver;
}

mixin()方法遍历supplier对象的自有属性,并将其拷贝到receiver。这就使得receiver没有通过继承就获得了新的行为。例如:

1
2
3
4
5
6
7
8
9
10
11
function EventTarget() { /*...*/ }
EventTarget.prototype = {
constructor: EventTarget,
emit: function() { /*...*/ },
on: function() { /*...*/ }
};

var myObject = {};
mixin(myObject, EventTarget.prototype);

myObject.emit("somethingChanged");

在这个例子中,myObjectEventTarget.prototype接收了新的行为。

为此在ES6中添加了Object.assign(),它和mixin()的行为一样。但不同之处在于,mixin()使用赋值运算符=来拷贝,它不能拷贝访问属性accessor properties到接受者作为访问属性。Object.assign()是可以做到这点的。

我们可以使用Object.assign()重写上面的代码:

1
2
3
4
5
6
7
8
9
10
11
function EventTarget() { /*...*/ }
EventTarget.prototype = {
constructor: EventTarget,
emit: function() { /*...*/ },
on: function() { /*...*/ }
}

var myObject = {}
Object.assign(myObject, EventTarget.prototype);

myObject.emit("somethingChanged");

Object.assign()可以接受任意多个提供属性的对象,接收者则按顺序从提供者接收属性,这可能会导致第二个提供者会覆盖第一个提供者提供给接收者的属性。例如:

1
2
3
4
5
6
7
8
9
10
11
12
var receiver = {};

Object.assign(receiver, {
type: "js",
name: "file.js"
}, {
type: "css"
}
);

console.log(receiver.type); // "css"
console.log(receiver.name); // "file.js"

下面再看看Object.assign()用于访问属性的例子:

1
2
3
4
5
6
7
8
9
10
11
12
13
var receiver = {},
supplier = {
get name() {
return "file.js"
}
};

Object.assign(receiver, supplier);

var descriptor = Object.getOwnPropertyDescriptor(receiver, "name");

console.log(descriptor.value); // "file.js"
console.log(descriptor.get); // undefined

重复对象字面量属性

ES5的严格模式中,引入了一个对重复对象字面量属性的检查,它会抛出一个错误如果发现了重复属性。例如:

1
2
3
4
var person = {
name: "Nicholas",
name: "Greg" // syntax error in ES5 strict mode
};

但是在ES6中,重复属性检查已经被移除了。不管是strictnostrict模式都不会取检查重复属性,它会取给定名称的最后一个属性作为实际值:

1
2
3
4
5
6
var person = {
name: "Nicholas",
name: "Greg" // not an error in ES6
};

console.log(person.name); // "Greg"

在这个例子中,person.name的值为Greg,因为它是赋给该属性的最后一个值。

改变原型

原型是JavaScript继承时的基础,因此,ES6使得原型更强大。ES5中添加了Object.getPrototypeOf()方法来检索任何给定对象的原型。在ES6中添加了相反操作的方法,Object.setPrototypeOf(),它允许我们改变任何给定对象的原型。

ES6之前,我们无法在对象创建后来改变其原型,ES6Object.setPrototypeOf()打破了这一情况。Object.setPrototypeOf()接收两个参数,第一个参数为要改变原型的对象,第二个参数为被设置为第一个对象的原型的对象。例如:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
let person = {
getGreeting() {
return "Hello";
}
};

let dog = {
getGreeting() {
return "Woof";
}
};

// prototype is person
let friend = Object.create(person);
console.log(friend.getGreeting()); // "Hello"
console.log(Object.getPrototypeOf(friend) === person); // true

// set prototype to dog
Object.setPrototypeOf(friend, dog);
console.log(friend.getGreeting()); // "Woof"
console.log(Object.getPrototypeOf(friend) === dog); // true

这段代码中,我们有两个基本对象:persondog,两个对象都有一个getGreeting()的方法,对象friend首先从person中继承,意味着调用getGreeting()会输出Hello。当我们改变friend的原型为dog时,此时getGreeting()输出Woof

一个对象的原型的实际值是存储在一个内部属性[[Prototype]]中。Object.getPrototypeOf()方法返回存储在[[Prototype]]的值,而Object.setPrototypeOf()改变存储在[[Prototype]]上的值。

super引用

ES6中我们可以通过super引用来调用原型上的方法。例如,如果你想覆盖一个对象实例上的方法,但它同样需要去调用相同名字的原型方法,在ES5中我们可以通过以下方式来实现:

1
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
let person = {
getGreeting() {
return "Hello";
}
};

let dog = {
getGreeting() {
return "Woof";
}
};

// prototype is person
let friend = {
__proto__: person,
getGreeting() {
// same as this.__proto__.getGreeting.call(this)
return Object.getPrototypeOf(this).getGreeting.call(this) + ", hi!";
}
};

console.log(friend.getGreeting()); // "Hello, hi!"
console.log(Object.getPrototypeOf(friend) === person); // true
console.log(friend.__proto__ === person); // true

// set prototype to dog
friend.__proto__ = dog;
console.log(friend.getGreeting()); // "Woof, hi!"
console.log(friend.__proto__ === dog); // true
console.log(Object.getPrototypeOf(friend) === dog); // true

我们可以看到是通过Object.getPrototypeOf(this).getGreeting.call(this)的方式来实现的,但是在ES6中,我们不用这么复杂,只需要super就行了,下面是重写后的代码:

1
2
3
4
5
6
7
8
let friend = {
__proto__: person,
getGreeting() {
// same as Object.getPrototypeOf(this).getGreeting.call(this)
// or this.__proto__.getGreeting.call(this)
return super.getGreeting() + ", hi!";
}
};

以上就是本文的全部内容啦。

文章目錄
  1. 1. 属性和方法的简写
  2. 2. 计算属性名
  3. 3. Object.assign()
  4. 4. 重复对象字面量属性
  5. 5. 改变原型
  6. 6. super引用