属性初始化器的速记法

局部变量为对象同名属性赋值

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

方法简写

省略冒号与 function 关键字

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

使用方法简写速记法创建的方法,其 name 属性就是括号之前的名称。

可计算属性名

方括号允许指定变量或字符串字面量为属性名

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
// 第一种用法
var person = {},
lastName = "last name";

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

// 第二种用法
var lastName = "last name";
var person = {
"first name": "Nicholas",
[lastName]: "Zakas"
};

// 第三种用法
var suffix = " name";
var person = {
["first" + suffix]: "Nicholas",
["last" + suffix]: "Zakas"
};

新的方法

Object.is() 方法

绝大多数情况下, Object.is() 的结果与 === 运算符是相同的
仅有的例外是:它会认为+0 与 -0 不相等,而且 NaN 等于 NaN

Object.assign() 方法

重复的对象字面量属性

1
2
3
4
5
var person = {
name: "Nicholas",
name: "Greg" // 在 ES6 严格模式中不会出错
};
console.log(person.name); // "Greg"

自有属性的枚举顺序

Object.getOwnPropertyNames()

  1. 所有的数字类型键,按升序排列。
  2. 所有的字符串类型键,按被添加到对象的顺序排列。
  3. 所有的符号类型( 详见第六章) 键,也按添加顺序排列。
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    var obj = {
    a: 1,
    0: 1,
    c: 1,
    2: 1,
    b: 1,
    1: 1
    };
    obj.d = 1;
    console.log(Object.getOwnPropertyNames(obj).join("")); // "012acbd"

对于 for-in 循环,并非所有的 JS 引擎都采用相同的处理方式,其枚举顺序仍未被明确规定。而 Object.keys() 和 JSON.stringify() 也使用了与 for-in 一样的枚举顺
序。

更强大的原型

###修改对象的原型
对象原型的实际值被存储在一个内部属性 [[Prototype]] 上

  1. ES5 添加了 Object.getPrototypeOf() 方法来获取任意指定对象的原型
  2. Object.setPrototypeOf() 接受两个参数:需要被修改原型的对象,以及将会成为前者原型的对象
    ###使用 super 引用的简单原型访问
    1. 简单来说, super 是指向当前对象的原型的一个指针
      1
      2
      3
      4
      5
      6
      7
      let friend = {
      getGreeting() {
      // 这相当于上个例子中的:
      // Object.getPrototypeOf(this).getGreeting.call(this)
      return super.getGreeting() + ", hi!";
      }
      };
  1. 试图在方法简写之外的情况使用 super 会导致语法错误
    1
    2
    3
    4
    5
    6
    let friend = {
    getGreeting: function() {
    // 语法错误
    return super.getGreeting() + ", hi!";
    }
    };

正式的“方法”定义

  1. ES6 则正式将方法定义为:一个拥有 [[HomeObject]] 内部属性的函数,此内部属性指向该方法所属的对象。
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    let person = {
    // 方法
    getGreeting() {
    return "Hello";
    }
    };

    // 并非方法
    function shareGreeting() {
    return "Hi!";
    }

总结

第六章 符号与符号属性

创建符号值

  1. 使用全局 Symbol 函数来创建一个符号值

    1
    2
    3
    4
    let firstName = Symbol();
    let person = {};
    person[firstName] = "Nicholas";
    console.log(person[firstName]); // "Nicholas"
  2. Symbol 函数还可以接受一个额外的参数用于描述符号值,该描述并不能用来访问对应属性,但它能用于调试,例如:

    1
    2
    3
    4
    5
    6
    let firstName = Symbol("first name");
    let person = {};
    person[firstName] = "Nicholas";
    console.log("first name" in person); // false
    console.log(person[firstName]); // "Nicholas"
    console.log(firstName); // "Symbol(first name)"
  3. 由于符号值是基本类型的值,因此调用 new Symbol() 将会抛出错误

  4. 可以使用 typeof 运算符来判断一个变量是否为符号

    使用符号值

    你可以在任意能使用“可计算属性名”的场合使用符号。

    共享符号值

    符号值的转换

    检索符号属性

    使用知名符号暴露内部方法

    Symbol.hasInstance 属性

    Symbol.isConcatSpreadable

    Symbol.match 、 Symbol.replace 、Symbol.search 与 Symbol.split

    ###Symbol.toPrimitive

    Symbol.toStringTag

    识别问题的变通解决方法

    ES6 给出的答案

    Symbol.unscopables

    总结

第七章 Set与Map

第九章 JS的类

ES5 中的仿类结构

  1. 创建一个构造器,然后将方法指派到该构造器的原型上
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    function PersonType(name) {
    this.name = name;
    }
    PersonType.prototype.sayName = function() {
    console.log(this.name);
    };
    let person = new PersonType("Nicholas");
    person.sayName(); // 输出 "Nicholas"
    console.log(person instanceof PersonType); // true
    console.log(person instanceof Object); // true

类的声明

基本的类声明

  1. 类声明以 class 关键字开始,其后是类的名称;剩余部分的语法看起来就像对象字面量中的方法简写,并且在方法之间不需要使用逗号:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    class PersonClass {
    // 等价于 PersonType 构造器
    constructor(name) {
    this.name = name;
    }
    // 等价于 PersonType.prototype.sayName
    sayName() {
    console.log(this.name);
    }
    }
  2. 自有属性(Own properties ) :该属性出现在实例上而不是原型上,只能在类的构造器或方法内部进行创建。

    为何要使用类的语法

    尽管类与自定义类型之间有相似性,但仍然要记住一些重要的区别:

  3. 类声明不会被提升,这与函数定义不同。类声明的行为与 let 相似,因此在程序执行到声明处之前,类都会位于暂时性死区内。
  4. 类声明中的所有代码会自动运行并锁定在严格模式下
  5. 类的所有方法都是不可枚举的,这是对于自定义类型的显著变化,后者必须用 Object.defineProperty() 才能将方法改变为不可枚举。
  6. 类的所有方法内部都没有 [[Construct]] ,因此使用 new 来调用它们会抛出错误。
  7. 调用类构造器时不使用 new ,会抛出错误。
  8. 试图在类的方法内部重写类名,会抛出错误。
    1
    2
    3
    4
    5
    6
    7
    class Foo {
    constructor() {
    Foo = "bar"; // 执行时抛出错误
    }
    }
    // 但在类声明之后没问题
    Foo = "baz";

类表达式

基本的类表达式

1
2
3
4
5
6
7
8
9
10
let PersonClass = class {
// 等价于 PersonType 构造器
constructor(name) {
this.name = name;
}
// 等价于 PersonType.prototype.sayName
sayName() {
console.log(this.name);
}
};

具名类表达式

1
2
3
4
5
6
7
8
9
10
11
12
13
let PersonClass = class PersonClass2 {
// 等价于 PersonType 构造器
constructor(name) {
this.name = name;
}
// 等价于 PersonType.prototype.sayName
sayName() {
console.log(this.name);
}
};

console.log(typeof PersonClass); // "function"
console.log(typeof PersonClass2); // "undefined"

作为一等公民的类

访问器属性

可计算的成员名

生成器方法

静态成员

使用派生类进行继承

屏蔽类方法

继承静态成员

从表达式中派生类

继承内置对象

Symbol.species 属性

在类构造器中使用 new.target

总结