ES6新特性----面试


ES6新特性

文章参考:阮一峰老师的ECAMScript6入门

关键字

let关键字

与var相比:

  • 不存在变量提升,即声明的变量一定要在声明后使用;“变量提升”可以在声明之前使用,值为undefined;
  • 暂时性死区,在代码块内,使用let声明变量之前,该变量都是不可用的,把这种现象称为“暂时性死区”;
  • 不允许重复声明,不允许在相同作用域内,重复声明一个变量;
  • 块级作用域;全局作用域存在的问题:内层变量可能会覆盖外层变量;用来计数的循环变量泄露为全局变量;
const关键字

声明一个只读的常量,一旦声明,常量的值不能发生改变;

本质上来说,实际上并不是常量的值不能改变,而是指向的那个内存地址不能改变;对于简单数据类型来说,值就保存在变量所指向的那个内存地址中,因此就说值不能发生改变;但引用数据类型变量指向的是内存地址,保存的是一个指针,const只能保证这个指针是固定的。

解构赋值

变量的解构赋值

从数组和对象中提取值,对变量进行赋值,这种被称为解构;

数组的解构赋值
let a = 1;
let b = 2;
let c = 3;
let [a,b,c] = [1,2,3];

let [x,,y] = [1,2,3]
// x=1 y=3
对象的解构赋值

对象的解构与数组有一个重要的不同,数组的元素是按照次序排列的,变量的取值取决于它的位置;

而对象的属性是没有次序的,变量必须于属性同名;

let {foo, bar} = {foo:"aaa", bar:"bbb"}
foo//"aaa"   bar:"bbb"
字符串的解构赋值
const [a,b,c,d,e] = 'hello'
//a "h"  b "e" c "l" d "l" e "o"
数值和布尔值的解构赋值

解构赋值的规则:只要等号右边的值不是对象或者数组,就先将其转为对象。由于undefined和null无法转为对象,所以,对它们进行结构赋值都会报错。

let {toString:s} = 123;
s === Number.prototype.toString //true
函数参数的解构赋值
[[1,2],[3,4]].map(([a,b])=>a+b)
用途
  1. 交换变量的值
let x = 1;
let y = 2;
[x,y] = [y,x]
  1. 从函数返回多个值
//返回一个数组
function example(){
    return [1,2,3]
}
let [a,b,c] = example()
  1. 函数参数的定义
function f([x,y,z]){...}
f([1,2,3])
  1. 提取JSON数据
let jsonData = {
    id:42,
    status:'ok',
    data:[123,234]
}
let {id, status,data:number} = jsonData;
console.log(id,status,number)
// 42,"ok",[123,234]
  1. 遍历Map结构

任何部署了Iterator接口的对象,都可以使用for...of循环遍历。Map结构原生支持Iterator接口,配合变量的解构赋值,获取键名和键值非常方便

const map = new Map();
map.set('first','hello')
map.set('second','world')

for(let [key,value] of map){
    console.log(key+"is"+value)
}
// first is hello
// second is world
  1. 输入模块的指定方法

加载模块时,往往需要指定输入哪些方法,结构赋值使得语句清晰;

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

模板字符串

用反引号(`)标识;

模板字符串嵌入变量,需要将变量名写在${}之中

箭头函数

箭头函数省去了function关键字,采用=>来定义函数;

与普通函数的区别:

  • 从结构上来说,箭头函数更简洁
  • 箭头函数没有自己的this,它会捕获自己在定义时所处的外层执行环境的this,所以它的this指向在它被定义的时候就已经确定了,之后不会改变;
  • 无法使用call()apply()bind()等方法去改变this的指向;
  • 箭头函数不能作为构造函数使用;因为箭头函数里面没有this;
  • 箭头函数没有自己的arguments对象;
  • 不可以使用yield命令,因此箭头函数不能用作Generator函数;

扩展运算符(…)

数组的扩展

Array.from()

数组实例的find()、数组实例的includes()

对象的扩展

Object.assign()

基本数据类型Symbol

表示独一无二的值,通过Symbol函数生成,可以保证不会与其他属性名冲突

Set和Map数据结构

基本用法

ES6提供了新的数据结构Set,类似于数组,但成员的值是唯一的,没有重复的值;

set实例的属性和方法
  • add()
  • has()
  • delete()
  • clear()
遍历操作
  • eys() 返回键名的遍历器
  • values() 返回键值的遍历器
  • entries() 返回键值对的遍历器
  • forEach() 使用回调函数遍历每个成员
Weakset

与set结构类似,也是不重复的值的集合,但是与Set有两个区别:

  • WeakSet的成员只能是对象,而不能是其他类型的值
  • WeakSet中的对象都是弱引用,即垃圾回收机制不考虑WeakSet对该对象的引用,也就是如果其他对象都不再引用该对象,那么垃圾回收机制会自动回收该对象所占用的内存,不考虑该对象还存在于WeakSet之中。
Map

ES6提供了Map数据结构,它类似于对象,也是键值对的集合。

WeakMap

WeakMap结构与Map结构类似,也是用于生成键值对的集合;

区别:

  • WeakMap只接受对象作为键名(null除外),不接受其他类型的值作为键名;
  • WaekMap的键名所指向的对象,不计入垃圾回收机制;

Promise对象

概念
  • promise是异步编程的一种解决方案,较好的解决了回调地狱的问题,允许将回调函数嵌套改成链式调用;

回调函数本身没有什么问题,问题是多个回调函数的多层嵌套,代码朝横向发展,不易于维护;

多个异步操作形成了强耦合,只要有一个操作需要修改,它的上层回调函数和下层回调函数,可能都需要跟着修改,这种情况就称为"回调地狱";

结构:

fs.readFile(fileA,'utf-8',function(err,data){
  fs.readFile(fileB,'utf-8',function(err,data){
      // ...
  })
})

使用Promise后:

var readFile = require('fs-redfile-promise');
readFile(fileA)
.then(function (data){
    console.log(data.toString())
})
.then(function(){
    return readFile(fileB)
})
.then(function (data){
    console.log(data.toString())
})
.catch(function(err){
    console.log(err)
})
  • 通过new Promise()的方法,创建一个Promise实例,Promise构造函数接收一个函数作为参数,这个函数有两个参数分别为rejectresolve
  • resolve函数的作用:将Promise的状态从“未完成”变成“成功”(即从pending变为resolved),在异步操作成功时调用,并将异步操作的结果,作为参数传递出去;
  • reject函数的作用:将Promise的状态从“未完成”变成“失败”(即从pending变为rejected),在异步操作失败时调用,并将异步操作报出的错误作为参数传递出去;
  • then()方法:
    • Promise实例生成以后,可以用then方法分别指定resolved状态和rejected状态的回调函数;
    • then()方法可以接收两个回调函数作为参数,第一个回调函数是Promise对象的装填变为resolved时调用,第二个回调函数是Promise对象的状态变为rejected时调用。这两个函数都接二手Promise对象传出的值作为参数。
    • then方法返回的是一个新的Promise实例(注意:不是原来的那个Promise实例),因此可以采用链式法则,即then犯法后面再调用另一个then方法。
状态
  • promise有三种状态:pending(进行中)、fulfilled(已成功)、rejected(已失败);

  • Promise状态改变只有两种可能:从pending变为fulfilled和从pending变为rejected;

  • 而且一单状态发生改变,就不会再变了。

方法

Promise实例的方法

  • Promise.prototype.then()
  • Promise.prototype.catch()

Promise

  • Promise.all()

    • const p = Promise.all([p1,p2,p3])
      //其中p1 p2 p3都是Promise实例
      
    • p的状态,由p1,p2,p3决定

      • 只有p1、p2、p3的状态都变成fulfilled,p的状态才会变成fulfilled
      • 只要p1、p2、p3中有一个被rejected,p的状态就会变成rejected,此时第一个被rejected的实例的返回值,会传递给p的回调函数
  • Promise.race()

    • const p = Promise.race([p1,p2,p3])
      
    • 只要p1、p2、p3中有一个实例率先改变状态,p的状态就会跟着改变,那个率先改变的Promise实例返回值,就传递给p的回调函数

  • Promise.resolve()

  • Promise.reject()

  • Promise.try()

缺点
  • 首先,无法取消promise,一旦创建会立即执行,无法中途取消;
  • 其次,如果不设置回调函数,Promise内部抛出的错误,不会反应到外部;
  • 第三,如果当前处于pending状态时,无法得知目前进展到哪一个阶段(刚刚开始还是即将完成)

Generator函数

概念
  • ES6提供的一种异步编程解决方案;
  • 最大的特点是可以交出函数的执行权(即暂停执行)
  • Generator函数可以把它理解为一个状态机,封装了多个内部状态,执行该函数会返回一个遍历器对象,返回的遍历器对象可以依次遍历Generator函数内部的每一个状态;
function *helloWorldGenerator(){
    yield 'hello';
    yield 'world';
    return 'ending';
}
var hw = helloWorldGenerator()
//该函数有三个状态:hello、world和ending
特征
  • function关键字与函数名之间有一个星号(*);
  • 函数体内部使用yield表达式,定义不同的内部状态
调用过程
  • 调用generator函数后,该函数并不执行,返回的也不是函数的运行结果,而是一个遍历器对象(Iterator Object)代表Generator函数的内部指针,只有调用next方法才会遍历下一个内部状态;
next()
  • 每次调用next方法,内存指针就从函数头部或者上一次停下来的地方开始执行,直到遇到下一个yield表达式(或return语句)为止。也就是说Generator是分段执行的,yield表达式是暂停执行的标记,而next方法可以恢复执行;
  • 每次调用遍历器对象的next方法,就会返回一个有着value和done两个属性的对象,value属性表示当前的内部状态的值,是yield表达式后面的那个值;done属性是一个布尔值,表示是否遍历结束。
hw.next()
// {value:'hello',done:false}
hw.next()
// {value:'world',done:false}
hw.next()
// {value:undefined,done:true}
yield表达式

yield 表达式就是暂停标志;

遇到yield 表达式,就会暂停执行后面的操作,并将紧跟在yield 后面的那个表达式的值,作为返回的对象的属性值;

下一次调用next方法时,再继续往下执行,直到遇到下一个yield 表达式;

yield 与return的区别

**相同点:**都能返回紧跟在语句后面的那个表达式的值;

不同点:

  • 每次遇到yield ,函数暂停执行,下一次再从该位置继续向后执行,而return语句不具备位置记忆的功能;

  • 正常函数只能返回一个值,因为只能执行一次return;Generator函数可以返回一系列的值,因为可以有任意多个yield ;

Async函数

概念

Generator的语法糖;将函数的星号(*)替换成async,将yield替换成await;

与Generator相比的改进:

  • 内置执行器:Generator函数,需要调用next方法,才能真正执行,得到最后的结果;而async函数自带执行器;
  • 更好的语义:aysnc和await,比起星号和yield,语义更清楚了。async表示函数里有异步操作,await表示紧跟在后面的表达式需要等待结果;
  • 更广的适用性:yield命令后面只能是Thunk函数或者Promise对象,而async函数的await命令后面,可以是Promise对象和原始类型的值(数值、字符串和布尔值,但这时等同于同步操作);
  • 返回的值是Promise:async函数的返回值是Promise对象,比Generator函数的返回值是Iterator对象方便多了,可以用then方法指定下一步操作;
基本用法

async函数返回一个Promise对象,可以使用then方法添加回调函数,当函数执行的时候,一旦遇到await就会先返回,等到异步操作完成,再接着执行函数体后面的语句;

async function getStockPriceByName(name){
    const symbol = await getStockSymbol(name);
    const stockPrice = await getStockPrice(symbol);
    return stockPrices;
}
getStockPriceByName('goog').then(function(result){
   console.log(result); 
});
错误处理

如果await后面的异步操作出错,async函数返回的Promise对象被reject,会抛出一个错误的对象,导致catch方法的回调函数被调用,它的参数就是抛出的错误对象。

将函数放在try…catch代码块之中

async functin f(){
    try{
        await new Promise(function(resolve,reject){
            throw new Error('出错了')
        })
    }catch(e){
    }
    return await('hello world')
}

如果有多个await命令。可以统一放在try…catch结构中。

for…of函数

for…of循环可以使用的范围包括数组:Set和Map结构、某些类似数组的对象(比如说arguments对象、DOM NodeList对象)、Generator对象,以及字符串。

JavaScript原有的for…in循环,只能获得对象的键名,不能直接获取键值。ES6提供for…of循环,允许遍历获得键值

var arr = ['a','b','c','d']
for(let a in arr){
    console.log(a); // 0 1 2 3
}
for(let a in arr){
    console.log(a); //a b c d
}

Class

定义类

ES6的class可以看做只是一个语法糖,它的绝大部分功能,ES5都可以做到

function point(x,y){
    this.x = x;
    this.y = y;
}
function.prototype.toString = function(){
    return '('+this.x+','+this.y+')';
}
var p = new Point(1,2)

class改写

class Point{
    constructor(x,y){
        this.x = x;
        this.y = y;
    }
    toString(){
        return '('+this.x+','+this.y+')';
    }
}

事实上,类的所有方法都定义在类的prototype属性上面;

在类的实例上面调用方法,其实就是调用原型上的方法;

class B{}
let b = new B()
b.constructor === B.prototype.constructor //true
constructor方法

constructor是类的默认方法,通过new命令生成对象实例时,自动调用该方法。一个类必须有constructor方法,如果没有显式定义,一个空的constructor方法会被默认添加。

class Point(){}
//等同于
class Point{
    constructor(){}
}
类的实例对象

与ES5一样,实例的属性除非显示的定义在其本身(即定义在this对象上),否则都是定义在原型上(即定义在class上)

class Point{
    constructor(x,y){
        this.x = x;
        this.y = y;
    }
    toString(){
        return '('+this.x+','+this.y+')';
    }
}
var point = new Point();
point.toString() //(2,3)

point.hasOwnProperty('x') //true
point.hasOwnProperty('y') //true
point.hasOwnProperty('toString') //false
point.__proto__.hasOwnProperty('toString') //true                                                                                                                                                                                                                                                   
class的继承
class ColorPoint extends Point{
    constructor(x,y,color){
        super(x,y);
        this.color = color;
    }
    toString(){
        return this.color+' '+super.toString()//调用父类的toString()
    }
}
super关键字

在子类的构造函数中,只有调用super之后,才能使用this关键字,否则会报错。这是因为子类实例的构建,是基于对父类实例加工,只有super方法才能返回父类实例。

super作为函数

super(),代表父类的构造函数,但是返回的子类B的实例,即super内部的this指的是B;

作为函数时,super()只能用在子类的构造函数之中,用在其他地方会报错;

class A{}
class B extend A{
    constructor(){
        super();
    }
}

super作为对象

class A{
    p(){
        return 2;
    }
}
class B extends A{
    constructor(){
        super();
        console.log(super.p()); //2
    }
}

上面代码中,子类B当中super.p(),就是将super当作一个对象使用,这时,super在普通方法之中,指向A.prototype,所以super.p()就相当于A.prototype.p();

注意,由于super指向父类的原型对象,所以定义在父类实例上的方法或属性,是无法通过super调用的。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值