ECMAScript 6 要点整理

96
dadage456
2016.09.07 15:23* 字数 5600

一、let 和 const

let:变量声明, const:只读常量声明(声明的时候赋值)。

let 与 var 的区别:

  • let、const 作用于 块级作用域: 作用域在{}内,可作为局部变量(常量)、全局变量(常量)。

  • var 作用于 全局作用域:除非使用闭包实现局部变量。

  • var 声明的全局变量全局window对象的属性是等价的。let 声明的全局变量则不然。


二、变量的解构赋值(数组解构、对象解构)

数组解构赋值:用于解构函数返回多数据
对象解构赋值:用于解构JSON数据
函数参数的解构赋值,用到解构默认值

解构赋值特点:
  • 完全解构:等号两边模式匹配,如果解构不成功变量就等于undefined.

  • 不完全解构:左边的模式只匹配一部分等号右边的数据。

  • 解构赋值默认值:数据成员必须是undefined,默认值才能生效.

数组解构赋值注意:
  • 在使用数组解构赋值的时候,如果等号右边不是可遍历的结构(可实现Iterator),会报错。
对象解构赋值需要注意:
  • 注意解构时模式变量 的区分。只有变量才能获取赋值。

  • 需要解构赋值的变量已经提前声明了,需要在解构赋值表达式上加圆括号。(为解析器会将起首的大括号,理解成一个代码块,而不是赋值语句。)

    let foo;
    ({foo} = {foo: 1}); // 成功
    
用途
  • 交换变量的值

    [x,y] = [y,x]
    
  • 从函数返回多个值

  • 函数的参数定义、函数的参数的默认值

  • 提取JSON数据

  • 遍历Map结构

    var map = new Map();
    map.set('first', 'hello');
    map.set('second', 'world');
    
    for (let [key, value] of map) {
      console.log(key + " is " + value);
    }
    
  • 输入模块的指定方法

    const { SourceMapConsumer, SourceNode } = require("source-map");
    

ES6扩展

一、字符串的扩展

  1. Unicode表示法:\uxxxx

  2. 超过2字节存储的字符处理

JavaScript内部,字符以UTF-16的格式储存,每个字符固定为2个字节。对于那些需要4个字节储存的字符(Unicode码点大于0xFFFF的字符),JavaScript会认为它们是两个字符。

* `codePointAt()` 代替 `charCodeAt()`
* `String.fromCodePoint()` 代替`String.fromCharCode()`
* `for...of` 代替 `for(let i=0;i<length;i++)`
* `at()` 代替 `charAt()`
  1. 字符查找处理扩展

    传统上,JavaScript只有indexOf方法,可以用来确定一个字符串是否包含在另一个字符串中

    • includes():返回布尔值,表示是否找到了参数字符串
    • startsWith():返回布尔值,表示参数字符串是否在源字符串的头部
    • endsWith():返回布尔值,表示参数字符串是否在源字符串的尾部
  2. 模板字符串

    `Hello ${name}, how are you ${time}?`
    
  3. 字符串原样输出

    String.raw`Hi\\n`
    
  4. 标签模板

    模板字符串紧跟在一个函数名后面,该函数将被调用来处理这个模板字符串。这被称为标签模板功能
    标签模板的一个重要应用,就是过滤HTML字符串,防止用户输入恶意内容。

    tag`Hello ${ a + b } world ${ a * b }`;
    // 等同于
    tag(['Hello ', ' world ', ''], 15, 50);
    
    function tag(stringArr,value1,value2){
        ...
    }
    //等同于
    function tag(stringArr,...values){
        ...
    }
    
    
  5. 其他扩展

    • repeat():返回一个新字符串,表示将原字符串重复n次
    • 自动补全padStart()padEnd()
    'x'.padStart(4, 'ab') // 'abax'
    'x'.padEnd(4, 'ab') // 'xaba'
    

二、数值的扩展

  1. 二进制和八进制的表示:分别用前缀0b(或0B)和0o(或0O)表示。2.

如果要将0b和0o前缀的字符串数值转为十进制,要使用Number方法:Number(0b111)

  1. Number.isFinite()用来检查一个数值是否为有限的(finite)

  2. Number.isNaN()用来检查一个值是否为NaN。

  3. Number.isInteger()用来判断一个值是否为整数。

  4. Number.parseInt() , Number.parseFloat()

三、数组的扩展

  1. Array.from()

将两类对象转为真正的数组:类似数组的对象(array-like object)和可遍历(iterable)的对象(包括ES6新增的数据结构Set和Map)

* 为什么要转换成真正的数组:可以使用forEach方法

* Array.from还可以接受第二个参数,作用类似于数组的map方法

* 计算包含 `超过2个字节的字符` 的字符串的长度:`Array.from(string).length;`
  1. Array.of()

方法用于将一组值,转换为数组 Array.of(3, 11, 8,'a') // [3,11,8,'a']

  1. 数组实例的find()findIndex()

    • find()用于找出第一个符合条件的数组成员,没有则返回undefined
    • findIndex()第一个符合条件的数组成员的位置,如果所有成员都不符合条件,则返回-1
  2. 数组实例的fill()

    ['a', 'b', 'c'].fill(7)
    // [7, 7, 7]
    
    new Array(3).fill(7)
    // [7, 7, 7]
    
    ['a', 'b', 'c'].fill(7, 1, 2)
    // ['a', 7, 'c'] 第二个和第三个参数,用于指定填充的起始位置和结束位置
    
  3. 数组实例的entries()keys()values()

    for (let index of ['a', 'b'].keys()) {
      console.log(index);
    }
    // 0
    // 1
    
    for (let elem of ['a', 'b'].values()) {
      console.log(elem);
    }
    // 'a'
    // 'b'
    
    for (let [index, elem] of ['a', 'b'].entries()) {
      console.log(index, elem);
    }
    // 0 "a"
    // 1 "b"
    
  4. 数组实例的includes()(属于ES7,Babel支持)

    返回一个布尔值,表示某个数组是否包含给定的值,字符串的includes方法类似

  5. 数组的空位 : Array(3) // [, , ,]

    • ES6则是明确将空位转为undefined
    • ES5 对空位的处理 ,大多数情况下会忽略空位
  6. 数组实例的copyWithin()

在当前数组内部,将指定位置的成员复制到其他位置(会覆盖原有成员),然后返回当前数组。

Array.prototype.copyWithin(target, start = 0, end = this.length)

[1, 2, 3, 4, 5].copyWithin(0, 3)
// [4, 5, 3, 4, 5]

// 将3号位复制到0号位
[1, 2, 3, 4, 5].copyWithin(0, 3, 4)
// [4, 2, 3, 4, 5]

// -2相当于3号位,-1相当于4号位
[1, 2, 3, 4, 5].copyWithin(0, -2, -1)
// [4, 2, 3, 4, 5]

// 将2号位到数组结束,复制到0号位
var i32a = new Int32Array([1, 2, 3, 4, 5]);
i32a.copyWithin(0, 2);
// Int32Array [3, 4, 5, 4, 5]


三、函数的扩展

  1. 参数的默认值

    • 默认值参数
    • 解构赋值默认值 作为参数
    • 默认值的参数位置:末尾
    • 函数的length属性:参数的个数,制定默认值后失真
    • 作用域:如果参数默认值是一个变量,先是当前函数的作用域,然后才是全局作用域
  2. rest参数...

  3. name属性:函数的函数名

  4. 箭头函数=>

    • 函数体内的this对象,就是定义时所在的对象,而不是使用时所在的对象(所在的函数体内)。
    • 不可以当作构造函数,也就是说,不可以使用new命令,否则会抛出一个错误。
    • 不可以使用arguments对象,该对象在函数体内不存在。如果要用,可以用Rest参数代替。
    • 不可以使用yield命令,因此箭头函数不能用作Generator函数。
  5. 函数绑定 (ES7)

    :: 取代callapplybind this的绑定。

    foo::bar;
    // 等同于
    bar.bind(foo);
    
    foo::bar(...arguments);
    // 等同于
    bar.apply(foo, arguments);
    
    ---------------------------------
    
    var method = obj::obj.foo;
    // 等同于
    var method = ::obj.foo;
    
    ------------------------------------
    
    采用链式写法:
    let { find, html } = jake;
    
    document.querySelectorAll("div.myClass")
    ::find("p")
    ::html("hahaha");
    
    
* apply 、 call 、bind 三者都是用来改变函数的this对象的指向的;
* apply 、 call 、bind 三者第一个参数都是this要指向的对象,也就是想指定的上下文;
* apply 、 call 、bind 三者都可以利用后续参数传参;
* bind 是返回对应函数,便于稍后调用;apply 、call 则是立即调用 。
  1. 扩展运算符 ...

    • 合并数组
    • 与结构赋值结合
    • 将字符串转为真正的数组 能够正确识别32位的Unicode字符
    • 任何Iterator接口的对象,都可以用扩展运算符转为真正的数组(Map和Set结构,Generator函数)
  2. 尾调用优化、尾部递归优化

    调用栈太多,造成溢出,那么只要减少调用栈,就不会溢出。


Symbol

凡是属性名属于Symbol类型,就都是独一无二的,可以保证不会与其他属性名产生冲突。

ES6有7种原始数据类型:

 Undefined 、Null、布尔值(Boolen) 、字符串(String) 、数值(Number) 、对象(Object) 、Symbol

一、基本

  1. 可以显示的转为字符串

    var sym = Symbol('My symbol');
    
    String(sym) // 'Symbol(My symbol)'
    sym.toString() // 'Symbol(My symbol)'
    
  2. 可以转为布尔值,不能转换为数值

    var sym = Symbol();
    Boolean(sym) // true
    !sym  // false
    
    if (sym) {
      // ...
    }
    
    Number(sym) // TypeError
    sym + 2 // TypeError
    

二、Symbol类型的变量作为属性的名

  1. Symbol类型的变量作为属性名的使用

    var mySymbol = Symbol();
    
    // 第一种写法
    var a = {};
    a[mySymbol] = 'Hello!';
    
    // 第二种写法
    var a = {  [mySymbol] :  'Hello!'  };
    // 第三种写法
    var a = {};
    Object.defineProperty(a, mySymbol, { value: 'Hello!' });
    
    // 以上写法都得到同样结果
    a[mySymbol] // "Hello!"
    
    注意:注意,Symbol值作为对象属性名时,不能用点运算符。
    a.mySymbol 相当于 a['mySymbol']        //属性名为字符串
    
    
  2. Symbol类型的变量作为属性名,遍历属性名

    • Symbol类型的变量作为属性名,该属性不会出现在for...infor...of中。

    • 不会被Object.keys()Object.getOwnPropertyNames()JSON.stringify()返回.

    • Object.getOwnPropertySymbols()方法,可以获取指定对象的所有Symbol属性名.

三、Symbol.for(),Symbol.keyFor()

  1. Symbol.for()

    • 使用同一个Symbol值。
    • 不会每次调用返回新的Symbol类型的值,会先检查给定的key是否已经存在,如果不存在才回建立一个新的值.
    • Symbol.for()为Symbol值登记的名字,是全局环境的。可以在不同的frame或service worker中取同一个值。
  2. Symbol.keyFor()

    • 返回一个已登记的Symbol类型值的key

四、用途

  1. 实现类似enum的功能,内部的值不重复。
  2. 模块的Singleton模式
    // mod.js
    const FOO_KEY = Symbol.for('foo');
    
    function A() {
      this.foo = 'hello';
    }
    
    if (!global[FOO_KEY]) {
      global[FOO_KEY] = new A();
    }
    
    module.exports = global[FOO_KEY];
    
    
    注意:const FOO_KEY = Symbol('foo'); 如果多次执行这个脚本,每次得到的FOO_KEY都是不一样的。

五、ES6内置了11个Symbol值

指向语言内部使用的方法

  1. Symbol.hasInstance
    • foo instanceof Foo 相当于 Foo[Symbol.hasInstance](foo)
  2. Symbol.isConcatSpreadable 使用Array.prototype.concat()时,是否可以展开
  3. Symbol.species
  4. Symbol.match: 相当于str.match(myObject)
  5. Symbol.replace :相当于 String.prototype.replace(searchValue, replaceValue)
  6. Symbol.search : 相当于 String.prototype.search
  7. Symbol.split : 相当于 String.prototype.split
  8. Symbol.iterator :指向该对象默认的遍历器方法
  9. Symbol.toPrimitive : 该对象被转为原始类型的值时,会调用这个方法,返回该对象对应的原始类型值。
    • Number:该场合需要转成数值
    • String:该场合需要转成字符串
    • Default:该场合可以转成数值,也可以转成字符串
  10. Symbol.toStringTag : 相当于 Object.prototype.toString
  11. Symbol.unscopables : 该对象指定了使用with关键字时,哪些属性会被with环境排除。

Set和Map数据结构

一、Set

它类似于数组,但是成员的值都是唯一的,没有重复的值。

  1. Set结构的实例属性

    • Set.prototype.constructor:构造函数,默认就是Set函数
    • Set.prototype.size:返回Set实例的成员总数
  2. Set 的操作方法

    • add(value) : 添加某个值,返回Set结构本身。
    • delete(value) : 删除某个值,返回一个布尔值,表示删除是否成功。
    • has(value) : 返回一个布尔值,表示该值是否为Set的成员。
    • clear() :清除所有成员,没有返回值。
  3. Set 的遍历方法

    • keys()
    • values()
    • entries()
    • forEach()
  4. Set 的遍历实例

    let set = new Set(['red', 'green', 'blue']);
    
    for (let item of set.keys()) {
      console.log(item);
    }
    // red
    // green
    // blue
    
    for (let item of set.values()) {
      console.log(item);
    }
    // red
    // green
    // blue
    
    for (let item of set.entries()) {
      console.log(item);
    }
    // ["red", "red"]
    // ["green", "green"]
    // ["blue", "blue"]
    
    set.forEach((value,key) => console.log(value));
    //red
    //green
    //blue
    
    

二、WeakSet

  1. 与Set的区别

    • WeakSet的成员只能是对象,而不能是其他类型的值
    • WeakSet中的对象都是弱引用
    • WeakSet是不可遍历的
  2. WeakSet 的构造函数

    • WeakSet可以接受一个数组或类似数组的对象作为参数。
    • 实际上,任何具有iterable接口的对象,都可以作为WeakSet的参数。
    • 该数组的所有成员,都会自动成为WeakSet实例对象的成员。
  3. WeakSet 操作方法

    • WeakSet.prototype.add(value)
    • WeakSet.prototype.delete(value)
    • WeakSet.prototype.has(value)
  4. WeakSet没有遍历方法,没有size属性和forEach属性.

三、Map

  1. Map与Object的区别

    • Obejct结构提供 "字符串-值" 的对应
    • Map结构提供 "值-值"的对应
  2. 构造函数

    • Map接受一个数组作为参数,成员是一个个表示键值对的数组.
    var map = new Map([
     ['name', '张三'],
     ['title', 'Author']
    ]);
    
  3. Map的属性与操作方法

    • size : 返回Map结构的成员总数
    • set(key,value)
    • get(key) : 如果找不到key,返回undefined
    • has(key)
    • delete(key)
    • clear()
  4. Map的遍历方法,Map的顺序是插入顺序

    • keys() : 返回键名的遍历器
    • values() : 返回键值的遍历器
    • entries() : 返回所有成员的遍历器
    • forEach() : 遍历Map的所有成员
  5. Map与其他数据结构的互相转换

    • Map 转换为 数组

    • 数组 转换为 Map

    • Map 转换为 对象

    • 对象 转换为 Map

    • Map 转换为 JSON :JSON.stringify(value)

      • 所有键名为字符串: 先转换为 对象(Object),再转换为 JSON
      • 键名不为字符串: 先转换为 数组,在转换为JSON
    • JSON 转换为 Map :JSON.parse(value)

      • 所有键名为字符串: 先转换为 对象(Object),再转换为Map
      • 整个JSON是一个数组,每个数组成员有两个成员的数组:转换为 数组后,再转换为Map

四、WeakMap

  1. WeakMap 的操作方法

    • get()
    • set()
    • has()
    • delete()
  2. WeakMap 与 Map的区别

    • 没有遍历操作,也没有 size属性
    • 只接受 对象 作为键名。键名所指向的对象,不计入垃圾回收机制.
  3. 主要的应用场合,预防内存泄漏

    • DOM节点作为键名
    • 部署私有的属性
    let _counter = new WeakMap();
    let _action = new WeakMap();
    
    class Countdown {
      constructor(counter, action) {
        _counter.set(this, counter);
        _action.set(this, action);
      }
      dec() {
        let counter = _counter.get(this);
        if (counter < 1) return;
        counter--;
        _counter.set(this, counter);
        if (counter === 0) {
          _action.get(this)();
        }
      }
    }
    
    let c = new Countdown(2, () => console.log('DONE'));
    
    c.dec()
    c.dec()
    // DONE
    

Iterator 和 for...of循环

一、Iterator

  1. Iterator接口的描述

    • 每次调用next方法,遍历返回{value:'',done:false},直到done为true
    • 使用 for...of可以直接遍历。
    • 一个数据结构只要具有Symbol.iterator属性,就可以认为是“可遍历的”(iterable)
  2. 原生具备Iterator接口的数据结构

    • 数组
    • 某些类似数组的对象(存在数值键名和length属性)
    • Set 和 Map

Generator函数

  1. Generator 基本示例

    function* testGenerator(){
        yield 'hello';
        let tmp = yield 'world';
        console.log(tmp);
        return 'ending';
    }
    
    let g = testGenerator();
    
    g.next();
    //{ value: 'hello', done: false }
    
    g.next();
    //{ value: 'world', done: false }
    
    g.next(123);
    //123
    //{ value: 'ending', done: true }
    
    g.next();
    //{ value: undefined, done: true }
    
    for(let item of testGenerator()){
        console.log(item);
    }
    //hello
    //world
    //undefined
    
    
  1. yield

    • yield不能用于普通的函数中,不用用于forEach函数中。

    • yield语句如果用在一个表达式中,必须放到圆括号里面。(就是调用下一次next,传递的值)

  2. 与Iterator接口的关系

Generator函数就是遍历器生成函数,可以把Generator赋值给对象的Symbol.iterator属性,是该对象具有Iterator接口

  1. next方法的参数

next方法可以带一个参数,该参数作为上一个yield语句的返回值

  1. for...fo循环

自动遍历Generator函数生成的Iterator对象,不需要调用next方法

  1. Generator.prototype.throw()

Generator函数返回的遍历器对象,都有一个throw方法.

实现函数体外抛出错误,Generator函数体内捕获. 主要用于 异步操作 的错误捕获。

也可以Generator体内抛出错误,体外捕获。

    var g = function* () {
      try {
        yield;
      } catch (e) {
        console.log('内部捕获', e);
      }
    };
    
    var i = g();
    i.next();
    
    try {
      i.throw('a');
      i.throw('b');
    } catch (e) {
      console.log('外部捕获', e);
    }
    // 内部捕获 a
    // 外部捕获 b
    
    
    注意:throw命令与i.throw方法是无关的
  1. Generator.prototype.return()

终结遍历Generator函数

  1. yield* 语句

用来在一个Generator函数里面执行另一个Generator函数。

```
function *foo() {
  yield 2;
  yield 3;
  return "foo";
}

function *bar() {
  yield 1;
  var v = yield *foo();
  console.log( "v: " + v );
  yield 4;
}

var it = bar();

it.next()
// {value: 1, done: false}
it.next()
// {value: 2, done: false}
it.next()
// {value: 3, done: false}
it.next();
// "v: foo"
// {value: 4, done: false}
it.next()
// {value: undefined, done: true}
```
  1. 作为对象属性的Generator函数

    let obj = {
      * myGeneratorMethod() {
        ···
      }
      
      或 
      
      myGeneratorMethod: function* () {
        // ···
      }
    };
    
    
  2. Generator函数的 this

让Generator函数返回一个正常的对象实例,既可以用next方法,也可以获得正常的this

```
function* F() {
  this.a = 1;
  yield this.b = 2;
  yield this.c = 3;
}
var obj = {};
var f = F.call(obj);    //统一成一个对象, F.call(F.prototype);

f.next();  // Object {value: 2, done: false}
f.next();  // Object {value: 3, done: false}
f.next();  // Object {value: undefined, done: true}

//两个对象
obj.a // 1
obj.b // 2
obj.c // 3

//统一成一个对象后
f.a //1
f.b //2
f.c //3
```
  1. Generator函数作为状态机

运行一次next,获取有序的下一个状态

  1. 应用

    • 异步操作的同步表达
      • 利用next传参数的方式,将异步返回的数据,返回到Generator函数中。
      • 然后进行下一个next的操作。
    • 控制流管理
      • 异步工作流,主要配合Promise使用.
    • 部署Iterator接口,Generator函数是遍历生成器。
    • 作为数据结构,可以看作是一个数组的结构

Promise对象

一、Promise基本简介

  1. Promise 特点

    • Promise对象的状态有三种:Pending(进行中)、Resolved(已完成)、Rejected(已失败)
    • Promise对象的状态改变有2种:从Pending----->Resolved、从Pending------>Rejected
    • 当Promise对象的状态已经改变后,再添加Promise对象的回调函数,也会立即执行回调。(不会因为错过它,再去监听,而得不到结果)
  2. Promise 缺点

    • 一旦新建它就会立即执行,不能中途取消
    • 如果不设回调函数,Promise内部抛出的错误,不能反应到外部
    • 当处于Pending中,无法得知进度。

二、Promise 使用

  1. Promise 创建与回调监听

    var promise = new Promise(function(resolve, reject) {
     // ... some code
    
     if (/* 异步操作成功 */){
       resolve(value);
     } else {
       reject(error);
     }
    });
    
    //回调处理
    promise.then(function(value) {
     // success
    }, function(error) {
     // failure
    });
    
  2. Promise.prototype.then()

    • then 返回一个Promise对象,所以可以采用链式写法
  3. Promise.prototype.catch()

    • Promise.prototype.catch 方法是 .then(null,rejection)的别名,用于指定发生错误时的回调函数
    • Promise对象的状态变为Rejected,就会调用catch方法指定的回调函数
    • Promise对象在运行中抛出异常,也会调用catch方法指定的回调函数
    • Promise对象的错误总是被下一个catch语句捕获。
    • 一般不要在then方法里面定义Rejected状态的回调函数,总是用catch方法
    • 如果没有下一个的catch方法,会导致错误不会被捕获,也不会传递到外层
    var promise = new Promise(function(resolve, reject) {
      throw new Error('test');
    });
    promise.catch(function(error) {
      console.log(error);
    });
    // Error: test
    
  4. Promise.all()

    • var p = Promise.all([p1, p2, p3]);
      • 只有p1p2p3的状态都变成 fulfilled,p的状态才会变成fulfilled.此时p1p2p3的返回值组成一个数组,传递给p的回调函数.

      • 只要p1p2p3之中有一个被rejected,p的状态就变成rejected.此时第一个被rejected的实例返回值,会传递给p的回调函数

    // 生成一个Promise对象的数组
    var promises = [2, 3, 5, 7, 11, 13].map(function (id) {
      return getJSON("/post/" + id + ".json");
    });
    
    Promise.all(promises).then(function (posts) {
      // ...
    }).catch(function(reason){
      // ...
    });
  1. Promise.race()

    • var p = Promise.race([p1,p2,p3]);
      • 要p1、p2、p3之中有一个实例率先改变状态,p的状态就跟着改变。那个率先改变的Promise实例的返回值,就传递给p的回调函数。
    • 应用场合:指定的某个时间内没有获取结果,就将Promise的状态变为reject.
  2. Promise.resolve()

    Promise.resolve('foo')
    // 等价于
    new Promise(resolve => resolve('foo'))
    
    • 参数是一个Promise实例
      • 原封不动返回这个实例
    • 参数是一个thenable对象
      • 首先将这个对象转换为Promise对象,然后立即执行thenable对象的then方法.
    • 参数不是具有then方法的对象,或根本不是对象,或不带任何参数
      • 直接返回一个Resolved状态的Promise对象,在本轮的“时间循环”结束时执行。
  1. Promise.reject()

    var p = Promise.reject('出错了');
    // 等同于
    var p = new Promise((resolve, reject) => reject('出错了'))
    
    
  2. Generator函数 与 Promise结合,可以实现异步操作的同步化实现。


Class

一、Class的基本使用

//定义类
class Point {
  constructor(x, y) {
    this.x = x;
    this.y = y;
  }

  toString() {
    return '(' + this.x + ', ' + this.y + ')';
  }
}
  1. ES6定义类的特性

    • ES6的类,完全看做构造函数的另一种写法
      Point.prototype.constructor === Point // true
    • 类所有的方法,都定义在类的prototype属性上.
    • 类实例调用的方法,其实都是调用原型上的方法
  2. 类的实例对象

    • 与ES 5一样,实例的属性除非显示的定义在其本身(即定义在this对象上),否则都定义在原型上 (prototype上)。
    • 与ES 5一样,类的所有实例共享一个原型对象
    • 需要首先定义类后,才能使用类。
  3. 私有方法

    • 命名约定,方法名前加下划线。
    • ES6 利用Symbol值的唯一性,将私有的方法的名字命名为一个Symbol值。
    const bar = Symbol('bar');
    const snaf = Symbol('snaf');
    
    export default class myClass{
    
      // 公有方法
      foo(baz) {
        this[bar](baz);
      }
    
      // 私有方法
      [bar](baz) {
        return this[snaf] = baz;
      }
    
      // ...
    };
    
    
  4. this的指向

类的方法内部如果含有this,它默认指向类的实例。

如果单独取出类的实例的一个方法,来单独使用,会因为找不到this指定的方法或属性(也就不在一个上下文)报错。

* 在构造方法中绑定`this`
* 使用箭头函数,会自动绑定 `this`
* 使用`Proxy`,获取方法的时候,自动绑定`this`

二、Class的继承

class ColorPoint extends Point {
  constructor(x, y, color) {
    super(x, y); // 调用父类的constructor(x, y)
    this.color = color;
  }

  toString() {
    return this.color + ' ' + super.toString(); // 调用父类的toString()
  }
}
  1. 子类必须在constructor方法中调用super方法,否则新建实例时会报错。

  2. 在子类的constructor构造函数中,只有调用super后,才能调用this关键字.

三、 类的prototype属性和proto属性

每个对象都有__proto__属性,指向对应的构造函数的prototype属性.

Class作为构造函数,同时有prototype属性和__proto__属性.

  1. 子类的__proto__属性,表示构造函数的继承,总是指向父类。
  2. 子类的prototype属性的__proto__属性,总是指向父类的prototype属性。
class A {
}

class B extends A {
}

B.__proto__ === A // true
B.prototype.__proto__ === A.prototype // true

四、super 关键字

  1. super作为函数,代表父类的构造函数

  2. super作为对象时,指向父类的原型对象.

    • 由于super指向父类的原型对象,所以定义在父类实例上的方法或属性,无法通过super调用。
  3. ES6有一个特别的规定,通过super调用父类的方法时,super会绑定子类的this.

五、Class的取值函数(getter) 和 存值函数(setter)

class MyClass {
  constructor() {
    // ...
  }
  get prop() {
    return 'getter';
  }
  set prop(value) {
    console.log('setter: '+value);
  }
}

六、Class的静态方法

父类的静态方法,可以被子类继承.

静态方法也是可以从super对象上调用的.

class Foo {
  static classMethod() {
    return 'hello';
  }
}

class Bar extends Foo {
  static classMethod() {
    return super.classMethod() + ', too';
  }
}

Bar.classMethod();

七、ES7的Class的静态属性和实例属性

ES6明确规定,Class内部只有静态方法,没有静态属性。

  1. 类的实例属性

    class MyClass {
      myProp = 42;
    
      constructor() {
        console.log(this.myProp); // 42
      }
    }
    
  2. 类的静态属性

    class MyClass {
      static myStaticProp = 42;
    
      constructor() {
        console.log(MyClass.myProp); // 42
      }
    }
    

八、new.target属性

  • 返回new命令作用于的那个构造函数

  • 如果构造函数不是通过new命令调用的,new.target会返回undefined

  • Class内部调用new.target,返回当前Class。

  • 子类继承父类时,new.target会返回子类。


ES7 类的修饰Decorator


动态语言特性

  • Object.assign
  • Object.keys
  • Object.getOwnPropertyNames
  • Object.create
  • Object.name
  • Object.setPrototypeOf
  • Object.getPrototypeOf() 从子类获取父类
  • Object.getOwnPropertyDescriptor
  • Object.prototype.hasOwnProperty
  • Object.prototype.proto
IOS开发