学习ES6后的心得总结-上

let const 和 var

他们之间的区别以及应用场景:

  1. let是ES6新出的命令,用于声明变量,会形成块级作用域,用其声明的变量,只在let命令所在的代码块内有效,并不再受外部的影响;
  2. let不存在变量提升,即变量在声明之前不能使用,这样的设计是为了让大家养成良好的编程习惯,变量一定要在声明之后使用;
  3. let会造成暂时性死区,即一进入到let声明的块级作用域,使用let声明的变量就会认为已经存在,但是由于变量不可提升,只有等到声明变量的那一行代码出现,才可以获取和使用该变量,在声明之前获取使用该变量就会报错
  4. let在相同作用域内,不允许重复声明同一个变量;
  5. const声明一个只读的常量,即用const声明的常量不得改变值,一旦声明变量,就必须立即初始化,不得留到以后赋值
  6. const对于基本数据类型的常量声明,值是不可改变的,但是对于引用数据类型的常量,例如obj 更改某个属性的值,是可以更改的;
  7. 如果真的想将obj冻结(属性值也不可改变),可以应使用Object.freeze方法,如果属性值也是一个引用类型数据的话,可以使用递归的方式 ,深层次遍历调用该方法,将obj完全冻结,如下:
var constantize = (obj) => {
  Object.freeze(obj);
  Object.keys(obj).forEach( (key, i) => {
    if ( typeof obj[key] === 'object' ) {
      constantize( obj[key] );
    }
  });
};
  1. ES6 的块级作用域必须有大括号,如果没有大括号,JavaScript 引擎就认为不存在块级作用域。块级作用域的存在可以用来解决 内层变量覆盖外层变量、或者循环计数时的变量泄露为全局变量等问题;

Set和Map

  1. ES6提供的新的数据结构-Set ,类似数组,可以通过new Set()的方式生成Set数据结构
  2. Set 构造函数可以接受一个数组作为参数,还可通过add()添加成员,但不会添加重复的值,故可用于数组去重,去重后返回的是Set结构,可使用扩展运算符(…)或者Array.from()转化为真实的数组
  3. .ES6提供的新的数据结构-Map ,类似对象,可以通过new Map()的方式生成Set数据结构。传统的对象只能使用字符串当作键名,Map的键值可以是各种类型的值(包括对象),我们扩展别人的库的时候,如果使用对象作为键名,就不用担心自己的属性与原作者的属性同名。
  4. Map构造函数接受任何具有 Iterator 接口的数据结构作为参数,以数组为例,数组内每个成员也要是双元素的数组哦
    [[key,value],[key,value]]

Symbol

  1. Symbol是ES6 引入的一种新的原始数据类型,表示独一无二的值。ES5 的对象属性名都是字符串,这容易造成属性名的冲突,Symbol保证每个属性的名字都是独一无二,从根本上防止属性名的冲突;
  2. Symbol 值通过Symbol函数生成,不需要new操作符。因为生成的 Symbol 是一个原始类型的值,不是对象。
  3. typeof运算符的结果是Symbol
  4. Symbol函数接受一个字符串作为参数,如果参数是obj,则会调用该对象的toString方法转为字符串。参数是为了对实例进行描述,为了输出的时候就能够分清,到底是哪一个值

Promise

  1. Promise 是异步编程的一种解决方案,有3种状态,2种结果。3种状态:pending、fulfilled、rejected,2种结果:fulfilled(已成功)、rejected(已失败)

  2. 状态不受外界影响,只有异步操作的结果,可以决定当前是哪一种状态,任何其他操作都无法改变这个状态。一旦状态改变,就不会再变

  3. Promise构造函数接受一个函数作为参数,该函数的两个参数分别是resolve和reject。它们是两个函数,由 JavaScript 引擎提供,不用自己部署;

  4. resolve函数的作用是,将Promise对象的状态从“pending”变为“fulfilled”(即从 进行中 变为 已成功),在异步操作成功时调用,并将异步操作的结果,作为参数传递出去;reject函数的作用是,将Promise对象的状态从“pending ”变为“rejected”(即从 进行中 变为 已失败),在异步操作失败时调用,并将异步操作报出的错误,作为参数传递出去;

  5. 使用new实例化一个 Promise对象之后,可以用then方法分别指定resolved状态和rejected状态的回调函数。then方法可以接受两个回调函数作为参数。第一个回调函数是Promise对象的状态变为resolved时调用,第二个回调函数是Promise对象的状态变为rejected时调用

  6. then方法的第二个参数可以忽略,使用catch方法捕获失败的状态

  7. then和catch方法返回的是一个新的Promise实例(注意,不是原来那个Promise实例)。因此可以采用链式写法;

  8. 以then为例,如果返回的是个promise对象,则后面的链式调用会等待该promise实例状态变化。如果返回的是个数值,则会生成一个新的prmise实例,并将该数值作为后面链式调用中回调函数的参数传入;

  9. Promise.all 、 Promise.race、Promise.allSettled、Prmise.any、Promise.resolve、Promise.reject、Promise.try 区别和用处
    Promise.all :
    接受一组Promise对象作为参数,返回一个新的Promise实例。参数中,所有的Promise对象状态都为fulfilled,新的promise实例状态为fulfilled,并将这一组Promise对象返回的结果按照顺序组成数组,传递给新Promise实例的回调函数。如果这一组Promise对象有一个状态为rejected,则新的Promise对象状态也会立即变为rejected,并将参数中第一个rejected的对象返回结果,传递给新Promise实例的回调函数。
    Promise.race :
    接受一组Promise对象作为参数,返回一个新的Promise实例。参数中有一个对象率先改变状态,新Promise实例状态即发生改变,并与前者的状态保持一致,参数中率先改变的 Promise 对象的返回值,也会传递给新Promise实例的回调函数。
    Promise.allSettled :
    接受一组Promise对象作为参数,返回一个新的Promise实例。不管参数中的这组Promise对象最后各自的状态是什么,总会等到这些实例都返回结果,新Promise实例状态才会改变,且状态总是fulfilled,不会变成rejected。新Promise实例的回调函数接收到的参数是一个数组,它的每个成员按照顺序分别对应传入Promise.allSettled()的 Promise 对象(包括最终状态和返回值) ,数组格式如下:

     [
    	{ status: 'fulfilled', value: 42 },
    	{ status: 'rejected', reason: -1 }
    ]
    

    Prmise.any :
    接受一组Promise对象作为参数,返回一个新的Promise实例。与Promise.all的情况相反,参数中如果有一个Promise对象状态先变为fulfilled,则新Promise实例状态为fulfilled。如果参数中的Promise对象状态都变为rejected,则新Promise实例状态变为rejected
    Promise.resolve :
    将现有对象转化为Promise对象。如果参数是一个Promise实例,则原封不动返回这个对象。如果参数是个原始值,则返回一个新的Promise实例,状态为fulfilled,原始值作为resolve回调的参数。如果参数是一个具有then属性方法的对象,则会把该对象转化为promise对象,并立即调用它的then方法
    Promise.reject :
    返回一个新的 Promise 实例,该实例的状态为rejected
    Promise.try :
    用于 同步函数同步执行,异步函数异步执行

Iterator 和 for…of

  1. Iterator是遍历器的意思,为各种不同的数据结构提供统一的访问机制。主要供遍历命令for…of(ES6创造的新遍历命令)消费,因为使用for…of循环遍历某种数据结构时,该循环会自动去寻找 Iterator 接口。
  2. 遍历过程是:先创建一个指针对象,指向当前数据结构的起始位置,第一次调用调用指针对象的next方法,该指针指向数据结构的第一个成员,并返回当前成员的信息(一个包含value和done两个属性的对象。value属性是当前成员的值,done属性是一个布尔值,表示遍历是否结束),以此类推,不断调用指针对象的next方法,直到它指向数据结构的结束位置。Iterator其实就是个有next方法的对象,可参考例子:
    function makeIterator(array) {
      var nextIndex = 0;
      return {
        next: function() {
          return nextIndex < array.length ?
            {value: array[nextIndex++]} :
            {done: true};
        }
      };
    }
    
  3. 有些原生饿的数据结构是有默认的 Iterator 接口的,例如 set、Map、Array、String(字符串是一个类似数组的对象,也原生具有 Iterator 接口)、函数的 arguments 对象、NodeList 对象、TypedArray,但是obj没有,所以原生的obj不能使用for…of进行循环。如果想使用该遍历方式,可以给obj添加 Iterator 接口。
  4. Iterator 接口部署在数据结构的Symbol.iterator属性上,是一个成员方法,调用后,返回遍历器对象。注:由于属性名Symbol.iterator,它是一个表达式,添加或者自定义该方法时,不能使用. ,要放在中括号内,例如:
    const obj = {
      [Symbol.iterator] : function () {
        return {
          next: function () {
            return {
              value: 1,
              done: true
            };
          }
        };
      }
    };
    
  5. 一些类似的数据结构,其遍历器可以相互引用,例如类似数组的对象(存在数值键名和length属性),可以直接引用数组的 Iterator 接口
    NodeList.prototype[Symbol.iterator] = Array.prototype[Symbol.iterator];
    // 或者
    NodeList.prototype[Symbol.iterator] = [][Symbol.iterator];
    
  6. 如果Symbol.iterator方法对应的不是遍历器生成函数(即返回的是一个遍历器对象),解释引擎将会报错
  7. 除了for…of外,解构赋值、扩展运算符、yield*、Array.from()、Map(), Set()、Promise.all()、Promise.race()等,这些接受数组作为参数的场合,都会调用遍历器接口
  8. for…of循环可以自动遍历 Iterator对象,不需要手动调用next方法,且遍历过程中取到的值是next方法返回对象的value属性值
  9. for…of较for…in的优点:for…in循环遍历键名,还会遍历手动添加的其他键,甚至包括原型链上的键,适用于对象,不适用数组。
  10. for…of较forEach的优点:它可以与break、continue和return配合使用,例如使用break,跳出循环:
    for (var n of fibonacci) {
      if (n > 1000)
        break;
      console.log(n);
    }   
    

Generator 函数

  1. Generator函数是异步编程的一种解决方案,是Symbol.iterator()方法的简单实现。特征:function关键字与函数名之间有一个星号、函数体内部使用yield表达式定义不同的内部状态,暂停执行、调用之后不会像普通函数那样立即执行,而是返回一个遍历器对象。

  2. Generator 函数是分段执行的,yield表达式是暂停执行的标记,而next方法可以恢复执行。调用遍历器对象的next方法,使得指针移向下一个状态,直到遇到下一个yield表达式(或return语句)为止。

  3. next方法返回的是一个对象,它的value属性表示当前的内部状态的值,也就是是yield表达式后面那个表达式的值;done属性是一个布尔值,表示是否遍历结束。和上述Iterator章调用next返回值格式相同

  4. yield表达式本身没有返回值,或者返回的是undefined,如果想将yield表达式赋值给某个变量,可以通过next方法传参,next方法内的参数,会被当作上一个yield表达式的返回值。如下:

    function* foo(x) {
        var y = yield (x + 1);
        var z=yield y;
        return z;
    }
    var a = foo(5);
    a.next(); // Object{value:6, done:false}
    a.next(); // Object{value:undefined, done:false}
    a.next(2); //Object{value:2, done:true}
    
  5. 常见的一个利用Generator函数和for…of实现斐波那契数列的例子

    function* fibonacci(){
        let [prev,cur]=[0,1];
        for(;;){
            yield prev;
            [prev,cur]=[cur,prev+cur]
        }
    }
    for(let n of fibonacci()){
        if(n>1000)break;
        console.log(n)
    }
    
  6. Generator 函数返回遍历器对象,都有一个throw方法,可以在函数体外抛出错误,然后在 Generator 函数体内捕获 ,如下:

    var g = function* () {
    	try {
    	   yield;
    	} catch (e) {
    	   console.log('内部捕获', e);
    	}
        yield  '自动执行一次next'
    };
    
    var i = g();
    i.next();
    
    try {
      i.throw('a'); //{value:'自动执行一次next',done:false}
      i.throw('b');
    } catch (e) {
      console.log('外部捕获', e);
    }
    //内部捕获 a
    //外部捕获 b
    

    如上代码,使用遍历器的throw之前必须至少执行过一次next方法,否则抛错在函数外部
    如果连续抛出2个错误,Generator函数体中的catch在第一个错误抛出时就已经捕获执行,那么第二个错误就不会再捕获,会被抛出到函数体外,被外部的catch捕获
    如果Generator 函数内部没有部署try…catch代码块,那么throw方法抛出的错误,将被外部try…catch代码块捕获
    如果 Generator 函数内部和外部,都没有部署try…catch代码块,那么程序将报错,直接中断执行
    遍历器的throw方法被捕获以后,会自动再执行一次next方法,也就是只要 Generator 函数内部部署了try…catch代码块,那么遍历器的throw方法抛出的错误,不影响下一次遍历

  7. Generator 函数体内抛出错误,且没有被内部捕获,就不会再执行下去了,js 引擎会认为这个 Generator 已经运行结束了。即使继续调用next,返回的也是value:undefined,done:true的结果

  8. throw命令与遍历器的throw方法是无关的,throw命令抛出的错误不会影响到遍历器的状态

  9. Generator 函数返回的遍历器对象,还有一个return方法,可以返回给定的值,并且终结遍历 Generator 函数

    function* gen() {
     yield 1;
     yield 2;
     yield 3;
    }
    var g = gen();
    g.next() //{value:1,done:false}
    g.return('foo') //{value:'foo',done:true}
    g.next() //{value:undefined,done:true}   
    

    return方法调用时,不提供参数,则返回值的value属性为undefined
    如果 Generator 函数内部有try…finally代码块,且正在执行try代码块,那么return方法会导致立刻进入finally代码块,执行完以后,整个函数才会结束,如下:

    function* numbers () {
      yield 1;
      try {
        yield 2;
        yield 3;
      } finally {
        yield 4;
        yield 5;
      }
      yield 6;
    }
    var g = numbers();
    g.next() //{value:1,done:false} 
    g.next() // {value:2,done:false} 
    g.return(7) //{value:4,done:false} 
    g.next() // {value:5,done:false} 
    g.next() //{value:7,done:true} 
    g.next() //{value:undefined,done:true}
    
  10. yield*表达式,用来在一个 Generator 函数里面执行另一个 Generator 函数,因为在 Generator 函数内部,调用另一个 Generator 函数。需要在前者的函数体内部,自己手动完成遍历。如下:

    function* foo() {
      yield 'a';
      yield 'b';
    }
    function* bar() {
      yield 'x';
      yield* foo();
      yield 'y';
    }
    //等同于
    function* bar() {
      yield 'x';
      yield 'a';
      yield 'b';
      yield 'y';
    }
    // 等同于
    function* bar() {
      yield 'x';
      for (let v of foo()) {
        yield v;
      }
      yield 'y';
    }
    

    任何数据结构只要有 Iterator 接口,就可以被yield*遍历

    	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();
    console.log(it.next()); //{value:1,done:false}
    console.log(it.next());//{value:2,done:false}
    console.log(it.next());//{value:3,done:false}
    console.log(it.next());
    //v:'foo' {value:4, done:false}
    console.log(it.next()); //{value:undefined, done:false}
    

    如上 如果被yield *遍历的foo函数内有return语句,则return的数据会被返回到bar函数内,并自动调用一次next方法

  11. Generator 函数也不能跟new命令一起用,会报错,因为它不是构造函数。返回的是一个遍历器对象,不是this对象哦。如果业务需要,可以通过call、apply等继承的方式,将Generator 函数内部的this绑定在自定义的空对象或者它的prototype对象上

Generator 函数的异步应用

  1. 异步编程解决方法:回调函数、promise、generator函数、thunk函数、co模块
  2. Generator函数能用解决异步是因为遇到yield命令就暂停,等到执行权返回,再从暂停的地方继续往后执行,处理一个异步如下:
    var fetch = require('node-fetch');
    function* gen(){
      var url = 'https://api.github.com/users/github';
      var result = yield fetch(url);
      console.log(result.bio);
    }
    var g = gen();
    var result = g.next();
    result.value.then(function(data){
      return data.json();
    }).then(function(data){
      g.next(data);
    });
    
  3. Thunk 函数在JS中的意义是将一个多参数函数,替换成一个只接受回调函数作为参数的单参数函数,如下:
    // 正常版本的readFile(多参数版本)
    fs.readFile(fileName, callback);
    
    // Thunk版本的readFile(单参数版本)
    var Thunk = function (fileName) {
      return function (callback) {
        return fs.readFile(fileName, callback);
      };
    };
    var readFileThunk = Thunk(fileName)(callback);
    
    Thunkify 模块是对thunk函数的封装,可以通过npm安装,不需要再自定义thunk函数,即可直接调用,使用方式如下:
    // npm install thunkify   完成安装
    var thunkify = require('thunkify');
    var fs = require('fs');
    var read = thunkify(fs.readFile);
    read('package.json')(function(err, str){});
    
    注:thunkify只允许回调函数执行一次
    先别慌 上边只是简单的描述了一下thunk函数是什么,接下来就要讲为什么thunk函数可以用于Generator 函数内异步编程的解决,并可自动执行Generator 函数。
  4. Geneartor函数可以通过遍历命令实现自执行,如果执行过程中有异步,且要求当前异步完成后才能进行下一步的话,Thunk 函数就派上了用场,如下:
    var fs = require('fs');
    var thunkify = require('thunkify');
    var readFileThunk = thunkify(fs.readFile);
    //定义generator函数
    var gen = function* (){
      var r1 = yield readFileThunk('/etc/fstab');
      console.log(r1.toString());
      var r2 = yield readFileThunk('/etc/shells');
      console.log(r2.toString());
    };
    //调用generator函数
    var g = gen();
    var r1 = g.next();
    r1.value(function (err, data) {
      if (err) throw err;
      var r2 = g.next(data);
      r2.value(function (err, data) {
        if (err) throw err;
        g.next(data);
      });
    });
    
    接下来实现自执行过程:
    function run(gen) {
      function next(err, data) {
        var result = gen.next(data);
        if (result.done) return;
        result.value(next);
      }
      next();
    }
    run(g);
    
  5. co 模块将thunk函数和Promise对象包装成了一个模块,用于 Generator 函数的自动执行,Generator 函数只要传入co函数,就会自动执行 。可以通过npm安装使用
    //定义generator函数
    function* gen() {
    	//...
    };
    //使用co实现自执行
    var co = require('co');
    co(gen);
    
    co函数返回一个Promise对象,可以用then方法添加回调函数。使用前提是Generator 函数的yield命令后面,只能是 Thunk 函数或 Promise 对象。如果数组或对象的成员,全部都是 Promise 对象,也可以使用 co
    co 支持并发的异步操作,即允许某些异步同时进行,等到它们全部完成,才进行下一步,写法如下:
    co(function* () {
      var res = yield [
        Promise.resolve(1),
        Promise.resolve(2)
      ];
      console.log(res);
    }).catch(onerror);
    // 或者
    co(function* () {
      var res = yield {
        1: Promise.resolve(1),
        2: Promise.resolve(2),
      };
      console.log(res);
    }).catch(onerror);
    
    要把并发的操作都放在数组或对象里面,跟在yield语句后面
  6. 使用Stream 模式读写数据 可以结合co模块和 Promise.race 实现

Async函数

  1. async函数也可用于处理异步编程,是对Generator函数的改进,自带执行器,与普通函数一样调用即执行
  2. async函数特征:函数名前加async表示函数里有异步操作,函数内使用await表示紧跟在后面的表达式需要等待结果,最终函数返回一个Promise对象,该Promise对象的状态要等内部所有await命令后面的异步操作执行完,才能发生变化,除非遇到return或者错误。
  3. await命令后面不限于thunk函数或Promise对象,原始类型的值(数值、字符串、布尔值)也可以,会自动转为resolved的Promise对象
  4. async函数内部return语句返回的值,会成为then方法回调函数的参数,如下:
    async function f() {
     return 'hello world';
    }
    f().then(v => console.log(v))   // "hello world"
    
  5. async函数内部抛出错误,会导致返回的 Promise 对象变为reject状态。抛出的错误对象会被catch方法回调函数接收到,如下:
    async function f() {
      throw new Error('出错了');
    }
    f().then(
      v => console.log('resolve', v),
      e => console.log('reject', e)
    )
    //reject Error: 出错了
    
  6. await命令有返回值,这也是与yield命令的一处不同。await后面如果是Promise对象,则返回的是该对象的结果;如果后面是一个thenable对象(定义了then方法的对象),那么会将其等同于 Promise 对象;如果是原始类型的值,则直接返回该值如下:
    async function f() {
      // 等同于
      // return 123;
      return await 123;
    }
    
    f().then(v => console.log(v))  // 123
    
  7. 只要await语句后面的 Promise 对象变为reject状态,那么整个async函数都会中断执行,且返回的Promise对象状态也变为reject状态。如果不想因为一个异步操作失败,就影响后面执行的话,可以使用try…catch捕获,这样不管这个异步操作是成功还是失败,都可以继续下一步操作了。
  8. await命令只能用在async函数之中,如果用在普通函数,就会报错。但允许在模块的顶层独立使用await命令,用于解决模块异步加载的问题。包含顶层await的模块,在加载时需要用import,因为require()是同步加载。如果加载多个包含顶层await命令的模块,加载命令是同步执行的。关于顶层await的使用,如下:
    // awaiting.js
    const data = fetch(url);
    export const output = await data ;
    // usage.js
    import { output } from "./awaiting.js";
    console.log(output );
    //awaiting模块的加载会等待依赖模块的异步操作完成,才执行后面的代码,有点像暂停在那里,总会得到正确的output 值
    
  9. async 函数可以保留运行堆栈

Class及其继承

  1. 传统方式是通过构造函数生成一个对象,有类的概念后,就可以先通过class关键字声明一个类,在通过new的方式实例化一个对象。class写法只是让对象原型的写法更加清晰、更像面向对象编程的语法而已
  2. 类中必须有一个constructor方法(即构造方法),如果没有显式定义,一个空的constructor方法会被默认添加,通过new命令生成对象实例时,自动调用该方法。还可自定义别的方法,不需要加function关键字,方法中间也不要用逗号分隔。且类的所有方法都是定义在类的prototype属性上面。如下:
    class B {}
    let b = new B();
    b.constructor === B.prototype.constructor // true
    
  3. 类的数据类型就是函数,类本身就指向构造函数
    class Point {
     // ...
    }
    typeof Point // "function"
    Point === Point.prototype.constructor // true
    
  4. constructor方法默认返回实例对象(即this),也可以指定对象返回,如下
    class Foo {
      constructor() {
        return Object.create(null);
      }
    }
    
    new Foo() instanceof Foo // false
    
  5. 传统的构造函数不适用new也可以执行,但是类必须要用new调用,否则报错
  6. 实例的属性除非显式定义在this对象(即自身)上,否则都是定义在class上(即原型)
  7. 在类的内部可以使用get和set关键字,对某个属性设置存值函数和取值函数,拦截该属性的存取行为。这俩函数都是设置在属性的 Descriptor 对象上
  8. class关键字也算是声明变量的一种方式,用它声明的类 不存在提升
  9. 类有name属性,返回紧跟在class关键字后面的类名,如下:
    class Point {}
    Point.name // "Point"
    
  10. 在类内部定义方法时,如果加上static关键字,就表示该方法不会被实例继承,只能通过类来调用,这就称为“静态方法”。静态方法如果包含this关键字,这个this指的是类,而不是实例。静态方法还可以与非静态方法重名哦
  11. 实例的属性除了定义在constructor()中的this上,还可以定义在类内部的顶层,如下:
    class Point{
        count=666;
        get value() {
            return this.count;
        }
    }
    let poniter=new Point();
    console.log(poniter.value); //666
    
    这种写法的好处是看上去比较整齐,一眼就能看出这个类有哪些实例属性。如果前面加上关键字static,则会被声明为类的静态属性,和静态方法一样,只能通过类调用,不能通过实例使用哦,如下:
    class Point{
        static count1=666;
        count2=888;
    }
    let poniter=new Point();
    console.log(poniter.count1); //undefined
    console.log(poniter.count2); //888
    console.log(Point.count1); //666
    console.log(Point.count2); //undefined
    
  12. ES6 不提供私有方法和私有属性(只能在类的内部访问的方法和属性),业务需要的话,可以通过变通方法模拟实现
    私有方法:
    (1) 命名上 通过方法名前加_区别 (外部仍可访问)
    (2) 将私有方法移到类外,内部通过继承,例如call调用 (外部仍可访问)
    (3) 将私有方法的名字命名为symbol值(利用其唯一性(外部仍可访问)
    (4) 方法名前加#表示 (外部不可访问,只能在类的内部使用)
    私有属性
    (1) 属性名之前,加#表示(外部不可访问,只能在类的内部使用)
    使用方法this.#XXX (#是属性名的一部分,使用时必须带有#一起使用)
    只要是在类的内部,私有属性的引用不限于this,还可以通过实例引用
    class Point{
        #count=666;
        play(){
            console.log(this.#count);
        }
        #play2(){
            console.log(this.#count);
        }
        play3(){
            this.#play2();
        }
        play4(){
            console.log(Point.prototype);
        }
    }
    let pointer=new Point();
    console.log(pointer.#count) //报错
    pointer.play();  //666
    pointer.#play2(); // 报错
    pointer.play3() //666
    pointer.play4(); //constructor play play3 play4
    
    私有属性和私有方法前面,也可以加上static关键字,表示这是一个静态的私有属性或私有方法
  13. new.target用来确定构造函数是怎么调用的。用于构造函数内,返回new命令作用的构造函数,如果构造函数不是通过new调用的,则new.target返回undefined。同理Class 内部调用new.target,返回new作用的Class,如果子类继承父类时,new.target返回的是子类
  14. Class 的继承通过extends关键字实现 。在子类中,使用super作为函数调用时,只可用在子类的构造函数中,代表父类的构造函数。使用super作为对象时,在普通方法中,指向父类的原型对象(定义在父类实例上的方法或属性,无法调用);在静态方法中,指向父类(另注:父类的静态方法,也会被子类继承哦)。使用super的时候,必须显式指定是作为函数、还是作为对象使用,否则会报错。
  15. 若子类要显示定义自己的constructor方法,必须在该构造函数内先调用super方法,否则报错。这一步目的是调用父类的constructor,先将父类实例对象的属性和方法,加到this上面,然后再用子类的构造函数加工修饰this。如下:
    class Point { /* ... */ }
    class ColorPoint extends Point {
      constructor() {
      }
    }
    let cp = new ColorPoint(); // ReferenceError
    
    如子类没有定义constructor方法,这个方法会被默认添加的
  16. 在子类普通方法中通过super调用父类的方法时,方法内部的this指向当前的子类实例。由此还可解释另一个现象:普通方法内,通过super对某个属性赋值,super相当于this,赋值的属性会变成子类实例的属性。如下:
    class A {
      constructor() {
        this.x = 1;
      }
    }
    class B extends A {
      constructor() {
        super();
        this.x = 2;
        super.x = 3; // this.x=3;
        console.log(super.x); //A.prototype.x 值为undefined
        console.log(this.x); //3
      }
    }
    let b = new B();
    
  17. 在子类的静态方法中通过super调用父类的方法时,方法内部的this指向当前的子类,而不是子类的实例
  18. 子类的__proto__属性,表示构造函数的继承,总是指向父类
    子类prototype属性的__proto__属性,表示方法的继承,总是指向父类的prototype属性
    class A{}
    class B extends A {}
    let a=new A();
    let b=new B();
    b.__proto__.__proto__=B.prototype.__proto__==A.prototype==a.__proto__
    
  19. ES6允许继承原生构造函数定义子类,在此之前 原生构造函数是无法继承的,因为子构造函数无法获得原生构造函数的内部属性,通过apply()或者添加到原型对象都不行。原生构造函数会忽略apply方法传入的this,也就是说,原生构造函数的this无法绑定,导致拿不到内部属性
  • 3
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值