前端es6知识点总结10bet:

来源:http://www.chinese-glasses.com 作者:Web前端 人气:64 发布时间:2020-04-07
摘要:时间: 2019-10-14阅读: 106标签: 总结1.var、let、const iterator ES6 推荐在函数中使用 let 定义变量const 用来声明一个常量(值类似值不能改变,引用类型地址不能改变)let 和 const只在最近的一个块

时间: 2019-10-14阅读: 106标签: 总结1.var、let、const

iterator

ES6 推荐在函数中使用 let 定义变量const 用来声明一个常量 (值类似值不能改变,引用类型地址不能改变)let 和 const 只在最近的一个块中(花括号中)有效

遍历器iterator

makeIterator是个遍历器,生成遍历器对象it

var it = makeIterator(['a', 'b']);

it.next() // { value: "a", done: false }
it.next() // { value: "b", done: false }
it.next() // { value: undefined, done: true }

function makeIterator(array) {
    var nextIndex = 0;
    return {
        next: function() {
            return nextIndex < array.length ?
            {value: array[nextIndex++], done: false} :
            {value: undefined, done: true};
        }
    };
}
//1.let不能重复申明 var n = 10;var n = 100;console.log(n) //100let a=10let a=100console.log(a) // SyntaxError 'a' has already been declared//2.var能改变全局作用域(变量提升) let则不能var b =1{ var b=10}console.log(b) //10let c=1{ let c=10}console.log(c) //1//3.同作用域内不能重申明var d=1let d=2console.log(d) // SyntaxError'd' has already been declaredlet d=1var d=2console.log(d) //SyntaxError 'd' has already been declared//4.var不管作用域对于同个变量赋值则更改var e=1{ let e=2}console.log(e) //1let f=1{ var f=2}console.log(f) //SyntaxError 'f' has already been declared//5.常量不能重新赋值const A = [1,2];A.push = 3;console.log(A); //[1,2,3]A = 10; //Error 这里地址改变了

iterable接口

对象具有Symbol.iterator方法就是部署了iterable接口,但如果Symbol.iterator返回的不是遍历对象,会报错。下面对象部署了iterable接口

var obj = {
    value : 1,
    [Symbol.iterator](){
        let me = this
        return {
            next(){
                return {
                    value: me.value++,
                    done: me.value >= 10
                }
            }
        }
    }
}

10bet,iterable接口为数据结构提供统一的数据访问机制,具有线性访问特点。

原生部署的有:数组、类似数组对象、Set和Map,数组、Set、Map的entries、keys、values方法的返回。
类似数组对象如 arguments对象 DOM NodeList对象;

对象部署Iterator接口:

function* entries(obj){
     for(let key of Object.keys(obj)){
          yield [key, obj[key]]
     }
}

2.箭头函数、this

部署了iterable接口的数据结构使用场景

解构赋值、扩展运算符、yield *、for…of、Array.from、Map()、Set() Promise.all Promise.trace

ES6箭头函数内的this指向的是函数定义时所在的对象,而不是函数执行时所在的对象。箭头函数背部没有自己的this,this总是指向上一层的this,层层递上,直到找到有自己的this函数为止。ES5函数里的this总是指向函数执行时所在的对象,尤其在非严格模式下,this有时候会指向全局对象。

for…of 对比for…in

  • for…in

    获取键名,无序的,循环对象所有可枚举的属性,作用数组时,返回的key是字符串

  • for…of

    of后面跟遍历器对象,或者部署了遍历器接口的数据类型。返回有序元素,相对与forEach可以break、continue

    如果跟makeIterator,会报错

    for(let v of [1,2,3]){console.log(v)}
    for(let v of [1,2,3][Symbol.iterator]()){console.log(v)}//两结果一样
    

b.箭头函数不能用构造函数,因为它没有自己的this,无法实例化。

其它

c.箭头函数没有自己的this,所以函数内也不存在arguments对象。这里就可以ES6的扩展运算符来代替。

遍历器对象的return方法

一般用在for...of循环中提前退出break时或者throw,会执行定义在遍历器中return方法,return方法必须返回一个对象

'use strict';
/**
 * node v6.9.0 chrome52下执行return
 * @type {{cur: number}}
 */
var obj = {
  cur: 1,
  next() {
    return {
      value: this.cur++,
      done: false
    }
  },
  return() {
    this.cur = 0
    console.log('execute iterator's method of return')
    return {
      done: true
    }
  },
  [Symbol.iterator]() { // 没有会报错
    return this;
  }
};
for (let v of obj[Symbol.iterator]()) {
  //throw new Error(); 也会执行return
  if (v > 10) {
    break;
  }
  if (v == 8) {
    continue;
  }
  console.log(v);
}

d.函数的默认赋值ES5,函数的形参是无法给默认值得,只能在函数内部通过变通方法实现。ES6,函数可以默认赋值。

generator函数

  • generator函数生成了多个返回值,每次执行返回一个状态。
  • generator函数作为异步编码的解决方案,提供函数执行时暂停功能。
var foo = function(){return 1;};//等价于let foo = () = 1;箭头函数中的 this 指的不是window,是对象本身。function aa(){ this.bb = 1; setTimeout(() = { this.bb++; //this指向aa console.log(this.bb); },500);}aa(); //2

generator函数相关知识点

  • yield右边的表达式是延迟执行的
  • 表达式中yield要用()包起来,但是函数参数,表达式右边的例外
  • generator函数执行后,返回遍历器对象g,该对象具有Symbol.iterator方法,执行后返回自身。
  • generator函数内部的return,执行到return,返回value等于return,done等于true
  • g.next(data) data参数会传到上一个yield停留地方,作为其返回值
  • g.return(data) 返回{value:data,done:true},nodeJs6.2.2支持。

    /**
     * return nodeJs暂不支持,调用会报错,nodeJs6.2.2支持
     */
    function* gen(){
        yield 1;
        try{
            yield 11;
            yield 22;
            yield 33;
        }finally{
            console.log('a')
            yield 21;
            yield 22;
        }
        yield 2;
    }
    let itr = gen()
    console.log(itr.next()) // { value: 1, done: false }
    console.log(itr.return(31)) // { value: 31, done: true }
    console.log(itr.next()) // { value: undefined, done: true }
    console.log(itr.next()) // { value: undefined, done: true }
    console.log(itr.next()) // { value: undefined, done: true }
    

3.字符串模板语法

generator抛错

g.throw()
  1. 遍历器对象可以执行throw,generator函数内部有try...catch,再被catch,直到运行结束或者执行时遇到下一个next。如果没被捕获,比如连续throw两次,会抛到generator外面。

    /**
     * 一开始没执行next,就执行throw,内部无法捕获。
     */
    function* gen(){
        try{
            console.log('start')
            yield 1;
        }catch(e){
            console.log('内部捕获',e)
        }
        yield 2;
    }
    var g = gen();
    try{
        //g.throw('a');
        console.log(g.next())
        console.log(g.throw('b'))
    }catch(e){
        console.log('外部捕获',e)
    }
    
    /**
     * 遍历器对象连续两次throw
     */

    function* errCnt() {
        try {
            yield 5 // { value: 5, done: false }
        } catch (e) {
            console.log('内部捕获', e)
            //throw new Error('cry')
        }
        yield 6
        yield 7
    }
    var itr = errCnt()
    console.log(itr.next())
    console.log(itr.throw('a')) // { value: 6, done: false }
    try {
        console.log(itr.throw('a'))
    } catch (e) {
        console.log('外部捕获', e)
        console.log(itr.next()) // { value: undefined, done: true }
    }
  1. 如果错误有没被捕获,程序中断执行。
  2. 遍历器对象throw,generator内部捕获后,会顺带执行下一个next。
  3. 没有执行过next,就执行throw,内部无法捕获。
  4. Generator函数体内报错,外部捕获后,下次还执行next,done为true,value undefined

    'use strict';
    
    function* foo() {
      let x = yield 3;
      let y = x.toUpperCase();
      yield y;
      yield 5;
    }
    
    var it = foo();
    
    console.log(it.next()); // { value:3, done:false }
    
    try {
      console.log(it.next(42));
    } catch (err) {
      console.log(err);
      console.log(it.next()); // {value: undefined, done: true}
    }
    
  5. Generator的遍历器对象抛错后,内部没捕获,外部捕获,下次执行next,同上

不使用模板字符串var name = 'Your name is' + first + '' + last + '.'使用模板字符串var name = Your name is ${first} ${last}.

var a = yield * AA

AA必须是部署了遍历器接口的数据结构,A如果是Generator生成的,且Generator有return,会将值赋值到a。

function* gen1(){
    yield 1;
    yield 2;
    return 3;
}
function* gen2(gen){
    yield 11;
    let rtn = yield* gen();
    console.log('rtn: ' + rtn)
    yield 22;
}
let itr1 = gen1()
console.log(itr1.next()) // { value: 1, done: false }
console.log(itr1.next()) // { value: 2, done: false }
console.log(itr1.next()) // { value: 3, done: true }

let itr2 = gen2(gen1)
console.log(itr2.next()) // { value: 11, done: false }
console.log(itr2.next()) // { value: 1, done: false }
console.log(itr2.next()) // { value: 2, done: false }
                         // rtn: 3
console.log(itr2.next()) // { value: 22, done: false }
console.log(itr2.next()) // { value: undefined, done: true }
for(let v of gen2(gen1)){
    console.log(v)
}

AA如果直接是Generator,会报错;for...of如果直接是Generator,也会报错。

4.class

this

function* gen(){
     yield 1;
     yield 2;
     yield this.name = ’sprying'
}  
gen.prototype.sayHi = () =>console.log(‘hi')
const g = gen()
g.next()
g.next()
g.next()

g.sayHi()
g.name // null

如果上面const g = gen()换成

const g = gen.apply(gen.prototype)

这时候g.name就有值
注意new gen会报错

Generator函数的原型时Generator.prototype(假设是ownProto);
ownProto原型是sharedProto,sharedProto.hasOwnProperty("next")
sharedProto的原型是iterProto,iterProto.hasOwnProperty(Symbol.iterator)

提供了关键字 class 定义类提供了关键字 extends 继承一个类super()执行父类的初始化函数提供了对象字面量, 定义一个函数

generator函数两大用途

class Animal { constructor(){ console.log('我是一个动物'); }}class Person extends Animal { constructor(){ super(); console.log('我是一个程序员'); }}let aa = new Person();//我是一个动物//我是一个程序员

作为异步编程的解决方案

函数遇到异步操作时,yield暂停,异步回调触发时,再继续执行。具体实现流程如下:

  1. 当执行到yield时暂停,执行yield右边表达式,后将结果和done状态返回调用者。
  2. 调用者根据结果判断是否继续执行,或者异步时等到什么时候执行,执行的时候传回什么给generator内部。
  3. 什么时候执行,传回什么,是通过异步编程协议来规范,比如promise、thinkify。

    • promise

      yield返回结果是promise对象,异步响应时,触发then,即

      // 业务代码
      function * gen(){
          yield new Promise((resolve, reject) => {
              setTimeout(()=>{
                  resolve({ok: true})
              }, 500)
          })
      }
      // 执行器背后的核心处理逻辑,执行next后,value是promise
      promise.then(function(data){
          g.next(data)
      })
      
    • thunkify

      yield返回参数是函数,传入回调,执行这个函数。异步响应时,执行回调,回调的参数是data,即。

      // 业务代码
      function * gen(){
          yield function(callback){
              setTimeout(()=>{
                  callback({ok: true})
              }, 500)
          }
      }
      
      // 执行器背后的核心处理逻辑
      hook((data) = >{
          g.next(data)
      })
      

      假设下面场景

      fs.readFile('/etc/passwd', (err, data) => {
        if (err) throw err;
        console.log(data);
      });
      
      yield function(callback){   
          fs.readFile('/etc/passwd', (err, data) => {
              if (err) throw err;
              callback(data)
          });
      }
      

      每次都这样写,是不是很麻烦?我们可以封装个函数

      function thunkify(callback){
          return function(...args){
              return function(hook){
                  callback(...[...args, hook])
              }
          }
      }
      
      // 下面就简洁了很多
      const readFile = thunkify(fs.readFile)
      function * gen(){
          yield   readFile(filename)
      } 
      

5.解构

生成具有iterable接口数据结构

function* gen(){
    yield 1; 
    yield 2; 
    yield 3; 
    return 4;
}
for(let v of gen()){
    console.log(v)
}//1,2,3

解构赋值是ES6中推出的一种高效、简洁的赋值方法

promise

//通常情况下var first = someArray[0];var second = someArray[1];var third = someArray[2];//解构赋值let [first, second, third] = someArray; //比上面简洁多了吧//还有下面例子let [,,third] = [1,2,3];console.log(third); //3let [first,...last] = [1,2,3];console.log(last); //[2,3]//对象解构let {name,age} = {name: "lisi", age: "20"};console.log(name); //lisiconsole.log(age); //20//注意let {ept1} = {};console.log(ept1); //undefinedlet {ept2} = {undefined};console.log(ept2); //undefinedlet {ept3} = {null};console.log(ept3); //null

promise知识点

  • 实例化Promise时,传入的函数立即执行,then在当前同步执行完时执行
  • resolve可以传入下一个promise实例

    /**
     * 注意执行顺序
     */
    setTimeout(()=>console.log(1),0)
    new Promise(function (resolve, reject) {
        console.log(2);
        x/2;
        resolve();
    }).then(function () {
        console.log(3)
        //throw new Error('err')
    },function(err){
        console.log(err)
    });
    console.log(4)
    

6.新增方法

promise错误

  • 捕获错误,可以在then第二参数回调中,但建议使用catch。
  • 运行中出现错误会触发报错
  • 状态已经resolve再throw错误,是无效的,如果前面情况是setTimeout再throw,错误会抛到外面。
  • catch后再then,如果then中再发生错误是无法被前面catch捕获的。
  • catch中报错,后面也没catch,导致无法捕获,也不会传递到外层。
  • 错误发生后,不管后面有几个catch,只会被第一个catch捕获执行,后面then还可以继续执行
  • 错误没被捕获,触发下面

    process.on('unhandledRejection', function (err, p) {})
    
    /**
     * reject未被catch,会被传递到unhandledRejection
     * Promise内部throw没被catch,会被传递到unhandledRejection,其它不做任何处理
     * throw后有catch,还有then,但是then应该是catch生成的默认resolve的promise
     */
    new Promise((resolve, reject) => {
        //reject(new Error('err'))
        //setTimeout(() => {
            throw new Error('err-1')
        //}, 0)
        //resolve('done')
    })
        .catch(err => console.log('err: ', err))
        .then((data) => {
            console.log('then: ' + data)
        })
    process.on('unhandledRejection', function (err, p) {
        //console.error(err.stack)
    });

    var someAsyncThing = function () {
        return new Promise(function (resolve, reject) {
            // 下面一行会报错,因为x没有声明
            resolve(x + 2);
        });
    };

    // 注意执行时间
    ///*
    someAsyncThing()
        .catch(function (error) {
            console.log('oh no', error);
        })
        .then(function () {
            console.log('carry on');
        });
        //*/

is()方法,比较两个目标对象,用来完善“===”方法。

Promise.all

参数是具有Iterator接口的对象,如果都是fulfilled,触发的then的参数回调的参数是数组。如果首先一个出来rejected,错误捕获的回调参数是首先出现错误那个。Promise.all对应的各个值如果不是Promise,调用Promise.resolve

NaN === NaN //falseObject.is(NaN,NaN); //true

Promise.race

谁先改变状态,结果和回调的参数就听谁

assign()方法,用于对象新增属性或者多个对象合并。

Promise.resolve

var jsPromise = Promise.resolve($.ajax('/whatever.json'));

参数是个thenable对象,立即执行thenable对象的then

参数是其它值,比如字符串

参数是空的

/**
 * Created by yingchun.fyc@alibaba-inc.com on 16/7/13.
 */
let thenable = {
    then(resolve, reject){
        resolve('thenable')
    }
}
Promise.resolve(thenable).then(res => console.log(res))


var p = Promise.resolve('hi')
p.then(res => console.log(res))


// 输出
// hi
// thenable
const target = {a:1};const source1 = {b:2};const source2 = {c:3};Object.assign(target,source1,source2);console.log(target); //{a:1,b:2,c:3}

Promise.reject

参数同上

ES6在原型上新增了Includes()方法,取代传统indexOf()查找字符串的方法。includes():找得到返回true,找不到返回false。indexOf():返回的是元素的所在下标,如果不存在则返回-1。此外,还新增了startsWith(),endsWith(),padStart(),padEnd(),repeat()等方法,可用于查找,补全字符串。

最新异步编程方式

let str = `xiao ming`;console.log(str.includes(`xiao`)); //true

Generator+(Promise/thunk)

generator函数允许执行暂停。当遇到yield时暂停,Js代码可以控制继续执行,比如控制yield后面的异步响应后,继续执行。暂停、和继续执行是我们写的Js代码控制,也就是,实现个执行器,让这些操作自动执行,就可以实现同步方式写异步代码。

执行器是个函数,返回promise。约定yield后面跟promise或者thunk函数。执行器执行到g.next(),返回obj,obj.value里的异步响应后,如果obj.done为false,继续执行,将响应的值传给g.next(data),如此循环。如果什么时候g.next,返回的done是true,那么等待value异步响应后,resolve响应的值,结束循环。

如果g.next时,报错,首先g.throw给generator处理,处理完继续next;处理失败,直接结束。如果异步响应出错,同样处理。thunk函数,异步响应时执行callback,规定第一参数传错误信息。

要并行处理异步时,yield后面跟数组、对象,执行器Promise.all下处理。

如果Generator函数,再套了一个Generator,放在yield后面。执行器里就再调一次执行器。

上面就是co的实现原理。

现在我们反思这样一个问题,同步方式写异步代码,遇到异步时,程序等待异步响应后,再继续执行,那么nodeJs异步处理优势就没了吗?一开始,我也是这么认为的,但是co执行Generator函数时,虽然内部yield暂停了,但是整个Generator并没有因此卡住,还是会继续执行co后续逻辑。所以nodeJs异步处理的优势还在。

7.Symbol类型

async

async属于ES7,不过引入babel的transform-async-to-generator就可以转码使用。await在ES6是保留字,ES5中使用它是合法。

普通函数中使用await会报错。

Async函数有多种使用形式。

// 函数声明
async function foo() {}

// 函数表达式
const foo = async function () {};

// 对象的方法
let obj = { async foo() {} };

// 箭头函数
const foo = async () => {};

async、await组合跟执行器co很像,只是它不再需要执行器了,像一般函数调用,返回的仍然是Promise。await相比co的yield,后面可以是基本数据类型。

至于错误处理机制,实际上跟co结果一样。不想结束,也是需要try...catch。或者promise后加catch。

至于并发执行,先都生成Promise实例,然后await。或者Promise.all([asyncMethod1(), asyncMethod2()])

/**
 * Created by yingchun.fyc@alibaba-inc.com on 16/9/12.
 */
async function f() {
  try {
    await Promise.reject('出错了');
  } catch(e) {
  }
  return await Promise.resolve('hello world');
}

f()
  .then(v => console.log(v))

async function dbFuc(fn) {
  let docs = [1,2,3];
  let promises = docs.map((doc) => fn(doc));

  let results = await Promise.all(promises);
  console.log(results);
}

// 或者使用下面的写法
// 两个运行实际差不多,因为都是先生成了promise,再调用await。await、yield,参数默认值都是延迟执行的。

async function dbFuc1(fn) {
  let docs = [4,5,6];
  let promises = docs.map((doc) => fn(doc));

  let results = [];
  for (let promise of promises) {
    results.push(await promise);
  }
  console.log(results);
}
var startTime = (new Date()).getTime()
dbFuc1((data) => {
  return new Promise((resolve, reject) => {
    setTimeout(()=>{
      resolve(data)
    }, 1000)
  })
}).then(() =>{
  console.log((new Date().getTime() - startTime))
})

Symbol是ES6引入的第七种原始数据类型,所有Symbol()生成的值都是独一无二的,可以从根本上解决对象属性太多导致属性名冲突覆盖的问题。Symbol值通过Symbol函数生成。对象的属性名现在可以有两种类型,一种是原来就有的字符串,另一种是新增的symbol类型,凡是属性名属于symbol类型,就都是独一无二的,可以保证不会与其他属性名产生冲突。Symbol 值可以显式转为字符串。另外,Symbol 值也可以转为布尔值,但是不能转为数值。

class

  • 关键字class定义类,方法之间没有,,方法名可以是变量[defineVar],定义的class只能new,否则报错。方法名不可枚举。构造函数在constructor方法中定义

  • 可以使用Object.assign新增方法。

  • class 不存在变量提升

  • class表达式

    let myClass = class Me {} //Me.name只能内部调用,Me也只能内部用
    myClass.name // Me
    
  • 实例的__proto__,指向原型对象

  • 私有方法

    使用Symbol定义方法名

    const bar = Symbol(“bar")
    
let s1 = Symbol('foo');let s2 = Symbol('foo');console.log(s1===s2) //falselet s1 = Symbol('foo');let s2 = Symbol('bar');console.log(s1,typeof(s1));console.log(s2);s1.toString() // "Symbol(foo)"s2.toString() // "Symbol(bar)"console.log(s1,typeof(s1.toString()))console.log(s2)

继承extends

继承重写constructor,要在constructor中先调用super(),否则会报错。

super在对象方法中都可以使用,但是奇怪的是,下面第一个正常,第二个会报错。

var obj = {
    toString() {
        return "MyObject: " + super.toString();
    }
};
var obj = {
    toString: function() {
        return "MyObject: " + super.toString();
    }
};

可以继承原生的构造函数,但是继承Object时,无法向父类的构造函数传入参数。

相比es5的继承不能继承原生的构造函数,即使es5继承了,也不具备原生的能力。

Object.getPrototypeOf获取类的父类

Object.getPrototypeOf(B) === A
Object.setPrototypeOf
Object.setPrototypeOf = function (obj, proto) {
    obj.__proto__ = proto;
    return obj;
}

8.Set

其它

方法名前可加set/get,并且是定义在descriptor上,Object.getOwnPropertyDescriptor

方法名前可加static

es7才支持属性名直接在类里定义

关于class具体使用例子,这里就不贴出来了。

类似Array的新的数据结构Set实例的成员都是唯一的,不重复的。这个特性可以轻松实现数组去重。

解构赋值destructuring

let array = [1,2,3,2,3];let set = new Set(array);console.log(set); //[1,2,3]let o1 = {age:11}let o2 = {age:12}let set = new Set([o1,o2]);console.log(set); //结果里面有两个对象吧,因为对象的内存地址不一样let o2 = o1;let set = new Set([o1,o2]);console.log(set); //结果里面有一个对象,对象的内存地址一样属性 .size 返回set集合的大小方法 .add()相当于数组的push() .delete(value) .has(value) .clear()清除数组

数组的解构赋值

支持嵌套,等号右边是可遍历(iterator)的结构

等号两边可以不完全匹配,或左边小,或右边没相应值(这时undefined)

支持默认值,默认值如果是表达式,表达式是惰性求值的

let [x=y,y=1] = [1,2]

let [a=b,b=1] = []// Uncaught ReferenceError: b is not defined(…)

var [a, , [b], c] = [5, null, [6]]; 
var [a, , [b], c] = [5, undefined, [6]];
var [a, , [b], c] = [5, , [6]];// a = 5; b = 6; c = undefined

var [a, b, c] = "ab"; // a = 'a'; b = 'b'; c = undefined

var [c] = "
	 

本文由10bet发布于Web前端,转载请注明出处:前端es6知识点总结10bet:

关键词:

最火资讯