Skip to content

Latest commit

 

History

History
928 lines (480 loc) · 23.3 KB

ES6+.md

File metadata and controls

928 lines (480 loc) · 23.3 KB

ES6+

[TOC]

介绍下Generator?

Generator函数是ES6提供的一种 异步编程解决方案,Generator函数是分段执行的,yield表达式是暂停执行的标记,而next方法可以恢复执行

Generator函数是一个状态机,封装了多个内部状态。执行Generator 函数会返回一个遍历器对象,可以依次遍历Generator函数内部的每个状态。

Generator与普通函数的区别:

  • 【不执行】调用Generator函数后,该函数并不执行
  • 【返回指针】返回的是一个指向内部状态的指针对象,而不是函数运行结果(遍历器对象 Iterator Object)
  • 【继续运行】必须调用遍历器对象的next方法,使得指针移向下一个状态,每次调用next方法,就继续执行,直到遇到下一个yield表达 式(或return语句)为止。

Generator API:

  • next(): 返回一个由yield 表达式生成的值
  • return(): 返回给定的值并结束生成器
  • throw(): 向生成器抛出一个错误
function* helloWorldGenerator(){
	yield 'hello';   //遇到yield 暂停执行
	yield 'world';
	return 'ending';
}

let hw = helloWorldGenerator();  //调用不执行函数,只是返回一个遍历器对象
hw.next();  //{value:'hello',done:false}  调用next方法继续执行,直到遇到yield或return表达式
hw.next(); //{value:'world',done:false}
hw.next(); //{value:'ending',done:true}
hw.next(); //{value:undefind,donw:true}  

参考资料:

Generator 函数的语法

ES6 Set 和 Array 的区别?

区别:

  • 【重复性】
    • set: value不重复 //通过此特性,可用来去重
    • array: value 可重复

ES6 Map和Objects的区别?

Map对象保存键值对,任务值(对象或原始值)都可以做为一个键或一个值

区别(Maps的优点):

  • 【key值类型】
    • Map:key可以是对象
    • Objects: key只能是一个string 或是 symbol
  • 【键的顺序】
    • Map: 有序
    • Objects: 无序
  • 【size】
    • -Map: 增加size属性,直接获取
    • Objects: 只能依靠手动计算
  • 【键名冲突】
    • Map: 默认不包含任何键,只包含显式插入的键
    • Object: object都有自己的原型,原型链上的键名有可能和你自己对象上的设置的键名产生冲突 (ES5开始可用Object.create(null)来创建一个没原原型的对象,但这种用法不常见)
  • 【性能】
    • Map: 在频繁增删键值对的场景下表现更好
    • Object: 在频繁添加/删除键值对的场景下未作出优化

img

箭头函数的this 指向哪里?

  • 默认绑定外层this
  • 不能使用call方法修改里面的this
    • -原因:函数的this可以用call方法来手动指定,而为了减少this的复杂性,箭头函数无法用call方法来指定this

参考资料:

JS中的箭头函数与this

箭头函数与普通函数区别?

箭头函数的优点

  • 语法更简洁
    • 不用写function 更简洁 // let sum=()=>{console.log(11);}
    • 只有一个参数的情况下,不用括号,直接写参数 // let sum = num1= > num1*2
    • 返回体只有一句的情况下,可以省去大括号 // let sum=(num1,num2)=> num1+num2
    • 如果箭头函数的函数体只有一条语句,并且 需要返回值,可以给这条语句前加一个 void // let fn=()=> void donotReturn()
  • 箭头函数不会创建自己的this (它只会从自己的作用域上一层继承this)
    • 定义时所处外层如执行环境的上下文,并继承这个 this,之后永远不会修改。避免this指向不确定的问题,更方便的管理作用域

箭头函数其他的一些特性:

  • 不能作为构造函数:不能使用new关键词创建构造函数,因为它没有自己的this。
  • 没有自己的 arguments对象:但是可以访问外部的arguments对象
  • 没有原型属性:箭头函数没有 prototype属性,所以不能用作构造函数
var id = 'Global';

function fun1() {
    // setTimeout中使用普通函数
    setTimeout(function(){
        console.log(this.id);
    }, 2000);
}

function fun2() {
    // setTimeout中使用箭头函数
    setTimeout(() => {
        console.log(this.id);
    }, 2000)
}

fun1.call({id: 'Obj'});     // 'Global'

fun2.call({id: 'Obj'});     // 'Obj'

参考资料:

ES6 - 箭头函数、箭头函数与普通函数的区别

什么是严格模式(use strict)?

特点:

  • 【严谨】对语法要求更规范,消除js语法的一些不合理、不严谨之处,减少一些怪异行为
  • 【安全】消除代码运行的一些不安全之处
  • 【高效】提高编译器效率,增加运行速度
  • 【扩展性】为未来新版本js做好铺垫

调用方式:在代码最前面添加一行代码("use strice";)

  • 针对整个脚本文件
  • 针对单个函数

改变:

  • 全局变量显式声明:不能省略 var/const/let 关键词
  • 静态绑定:让属性和方法在编译阶段就确定指向哪个对象
    • 禁止使用with语句
    • 创设eval作用域
  • 增强字全措施:
    • 禁止this 指向全局对象
    • 禁止在函数内部遍历调用栈
  • 禁止删除变量(除非configurable 属性为true)
  • 显式报错:
    • 只读属性赋值
    • 对禁止扩展的对象添加新属性:
    • 删除一个不可删除的属性: delete object.prototype
  • 重名错误
  • 对象不能重名的属性(之前是覆盖)
  • 函数不能有重名的参数
  • 禁止八进制表示法
  • arguments对象的限制:arguments是函数的参数,对它的使用做了限制
    • 不允许对arguments赋值
    • arguments 不再追踪参数的变化
    • 禁止使用arguments.callee: 无法在匿名函数内部调用自身
  • 函数必须声明在顶层:不允许在非函数的代码块内声明函数(比如if,for),只允许在全局作用域或函数作用域的顶层声明函数
  • 保留字:新增了一些保留字,向将来js新版本过渡
    • let ,private,public,static ,package,yield,interface,protected,implements

js语法不规范的几个地方:

  • 【变量定义】可以不使用 var/const/let 关键词,直接定义

参考资料:

Javascript 严格模式详解

介绍下js的装饰器(decorators)?

Decorator 就是一种动态地往一个类中添加新的行为的设计模式,它可以在类时,扩展一个类的功能,并且 去修改类本身的属性和方法,使其可以在不同类之间更灵活的共用一些属性和方法;

修饰模式(Decortaor),是面向对象编程领域中,一种动态地往一个类中添加新的行为的设计模式。修饰模式相比生成子类更加灵活,这样可以给某个对象而不是整个类添加一些功能

用法:

  1. 装饰类
@FooDecorator
class Foo {

}

function FooDecorator(target){
    // target 就是这个 class 的原型对象
}
  1. 装饰属性
//给MyClass 所有实例的  getType 属性设置为仅为可读不可更改的属性 readonly
class MyClass {
	constructor(){
		this.type="myClass"
	}
	@readonly
	getType(){
		return this.type
	}
}

function readonly(target, key, discriptor){
	discriptor.writable = false
	return discriptor
}

3)多个装饰器

class MyClass {
	constructor(){
		this.type="myClass"
	}
	@readonly
	@logHello
	getType(){
		return this.type
	}
}

//装饰器让类的getType方法不可更改
function readonly(target, key, discriptor){
	discriptor.writable = false
	return discriptor
}

//让每次调用类中的getType方法会在控制台输出 hello
function logHello(target, key, discriptor){
	const oldFn = target[key]
    target[key] = function(...rest){
        console.log('Hello')
        return oldFn.call(this,...rest)
    }
    return target
}

参考资料:

JS 装饰器,一篇就够 学习 ES7 语法 decorator 装饰器

创建对象的三种方式?

  • 字面量
  • 构造函数
  • Object.create

参考资料:

Object.create()、new Object()和{}的区别

箭头函数?

ES6常用 API有哪些?

let,const: 变量,常量 变量的解构赋值: promise:处理异步 字符串扩展: for..of字符串遍历接口,repeat将一个新字符串重量N次,模板字符串$(baseUrl) 数值扩展:math.trunc()去掉小数部分,sign判断一个数是正/负/零,指数运算符**, 函数扩展:箭头函数(不绑定自己的this) 数组扩展:填充数组(fill),复制数据const a2=[...a1] 对象扩展: 正则的扩展: 增加async函数: 对异步的处理

AMD和CMD的区别,ES6模块与CommonJs 模块有什么区别?

常用的模块化方法:

模块名 导出模块 引入模块 加载方式 说明
ES6 export import 静态加载 输出的引用,静态引用,只读属性
commonjs module.exports require 动态引用 输出值的浅拷贝对象,动态加载,可读可写。nodejs中的标准
AMD(requirejs) define require 异步 依赖前置,提前执行(在模块定义的时候就要引入)
CMD(sea.js) define require(["jquery","math"],,()=>{}) 异步 依赖就近,延迟执行(用到的时候才引入)

CommonJs(require) 与 ES6(import)的区别:

  • require: 输出的是一个值的浅拷贝对象,import输出的是一个值 的引用(即es6 module 只存只读,不能改变其值,具体点就是指针指向不能变,类似const)
  • require是动态引入,import是静态加载了;动态引入的方式,引入的对象可以是一个变量,或者能通过计算出来的地址
  • require是同步加载模块,import命令是异步加载,require有一个独立的模块依赖的解析阶段

参考: https://es6.ruanyifeng.com/#docs/module-loader#ES6-%E6%A8%A1%E5%9D%97%E4%B8%8E-CommonJS-%E6%A8%A1%E5%9D%97%E7%9A%84%E5%B7%AE%E5%BC%82

/** AMD写法 **/

define(["a", "b", "c", "d", "e", "f"], function(a, b, c, d, e, f) {

​     // 等于在最前面声明并初始化了要用到的所有模块

​    a.doSomething();

​    if (false) {

​        // 即便没用到某个模块 b,但 b 还是提前执行了

​        b.doSomething()

​    }

});


/** CMD写法 **/

define(function(require, exports, module) {

​    var a = require('./a'); //在需要时申明

​    a.doSomething();

​    if (false) {

​        var b = require('./b');

​        b.doSomething();

​    }

});

js中let ,var 区别,let有什么优点?

相同点:都是定义变量的,都可以修改

let 优点:

  • 不能重复声明:var可以重复声明,避免变量被覆盖

  • 不存在变量提升:let 不存在变量提升,一定要先定义再使用。未定义的变量会报

    // var中存在变量提升
    console.log(a); // undefined
    var a=1;
    
    // let中不存在变量提升,更容易发现错误
    console.log(b); // 引用错误:初始化之前不能使用
    let b=1;
    
  • 存在暂时性死区: 只要块级作用域内存在let/const命令,它所声明的变量便绑定在这个区域,不再受外部影响

  • 拥有块级作用域:ES5中只有全局和函数作用域,没有块级作用域,let会产生块级作用域。

    • var没有块级作用域的一些问题
      1. 内层变量会覆盖外层变量;
      1. 用来计数的循环变量泄露为全局变量;

es6/7有什么新特性?

箭头函数(不绑定自己的this),解构赋值,spread展开,块级作用域(let,const),promise异步解决方案,set(没有重复的值)/map数据结构,proxy拦截器

重要属性:class类(类继承extends,constructor,super)

es7: async(async,await)异步操作,返回promise对象,可以接着用.then方法来连接;

es6临时死区?

在 ES6 中,let 和const 跟 var、class和function一样也会被提升,只是在进入作用域和被声明之间有一段时间不能访问它们,这段时间是临时死区(TDZ)

//console.log(aLet)  // would throw ReferenceError

let aLet;

console.log(aLet); // undefined

aLet = 10;

console.log(aLet); // 10

ES6中的map和原生对象有什么区别?

原生对象的key只能是string类型的,map的key可以是任意值(object,array,function,undefind...)

map是一种更完善的hash结构实现;

map的适用场景,把不同的事件关联起来;、

介绍下ES6中的Symbol,它有哪些运用场景?

symbol是ES6中新增的一种基本数据类型,通过typeof symbol()可以发现,输出的是symbol。

symbol最大的特点返回值的唯一性,即使传入相同的参数(参数主要有来在控制来做区分,备注用的)。

所以基于这个特性,Symbol 可以用在以下几个场景。

  • 作为对象的唯一值
  • switch作为唯一的枚举

Symbol需要注意的点

  • Symbol()函数不能使用new命令,否则会报错。 //这个和其他基本类型都不一样,比始:String, Number, Boolean等
  • Symbol作为属性名时,不能通过以前的api直接获取,比如:Object.keys(), Object.getOwnPropertyNames()。甚至是JSON.stringify()。但是,它也不是私有属性,有一个Object.getOwnPropertySymbols方法,可以获取指定对象所有的Symbol属性名。

介绍下ES6中的Proxy对象,它有哪些使用场景?

Proxy的字面意思是代理器,任何对对象(对象,数组,函数等都可以)的操作(包括:访问,写入等操作)都会进入这一层的拦截。可以在这一层添加一些自定义的操作。

比始拦截后的一些操作,方法复写与增强

  • 增加日志记录
  • 提供友好提示和阻止特定操作 // 读取不存在的属性时,自定义报错信息。不能删除特定的属性等。
  • 读取负索引的值
  • 设置真正的私有属性,私有的定义:不能访问,不能修改,不能遍历。 // 一般约定以 _ 开头key为私有属性

参考资料

什么是可迭代对象,它有什么用?

for of有什么用,和for in有什么区别?

class类

class类的基本介绍?

生成对象的模板

主要组成:

  • constructor:构造方法
    • 默认属性,也可手动添加
    • 直接指向“类”的本身,和es5的行为一致 //Person.prototype.constructor === Person
  • 实例方法
  • 静态方法
  • 私有方法、私有属性
  • new.target属性

特点:

  • 类内部定义的方法,都是不可枚举的(non-enumeerable),和构造函数不一样
//es6的类,可看作是构造函数的另一种写法
class Person{}
typeof Person  //"function"
Person===Person.prototype.constructor //true

参考资料:

Class 的基本语法

class与普通构造函数有什么区别?

class本质使用prototype的原型链,只是一种语法糖;

在语法上更加贴合面向对象的语法,在实现继承上更新易读、易理解;

区别:

  • 【变量提升】
    • 类:没有
    • 构造函数:有
  • 【调用】
    • 类:必须使用new调用,否则报错
    • 构造函数:可直接调用
  • 【严格模式】
    • 类:类和模块的内部,默认是严格模式,无需使用 use strict 关键词
    • 构造函数:无强制要求
//类不存在变量提升
new Person{}; //ReferenceError
class Person{}

在构造函数中调用super(props)的目的是什么?

es6语法 中,super指代父类的构造函数;

react里面就是指代 React.Component 的构造函数

在调用super() 之前,无法在构造函数中使用this;在es2015中,子类必须在 constructor中调用super(),传递props给super() 的原因是便于能在constructor访问this.props;

参考资料:

React构造函数中为什么要写 super(props)

Async/Await

对async await的理解,内部原理?

本质:async是Generator的语法糖;

async改变有如下几个方法

  • 更好的语义化:async相当于*,await 相当于yield;
  • 返回值是promise,generator返回的是Iterator对象,更方便的使用then来操作
  • 内置执行器:async自带执行器,Generator需要依靠执行器(每次都是执行g.next()方法);
  • 更广的适用性:async函数的await后台可以是promise对象,也可以是原始类型的值;

async/await相比 promise的优势?

  • 同步写法优雅:使摆脱了then的链式调用带来的阅读负担
  • 获取返回值方便:promise传递中间传非常麻烦,而async/await几乎是同步的写法,更优雅
  • 错误处理友好:async/await可使用成熟的 try/catch,promise的错误捕获非常冗余
  • 调试友好:promise中的then使用调试器的步进(step-over)功能,调试器并不会进入后续的then代码块,因为调试器只能跟踪同步代码的【每一步】

promise

介绍下promise?

Promise是es6引入的一个新的对象,用来解决js中异步回调地狱的写法,并不是什么突破性的api,只是封装了异步回调函数;

使得异步写的更加优雅, 可读性更高,而且支持链式操作;

promise有几个状态?

Promise一共有三种状态

1.初始化,状态:pending

2.当调用resolve(成功),状态:pengding=>fulfilled

3.当调用reject(失败),状态:pending=>rejected

img

promise内部实现原理?

Promise/A+规范

  1. pending:表示初始状态,可以转移到 fullfilled 或者 rejected 状态
  2. fulfilled:表示操作成功,不可转移状态
  3. rejected:表示操作失败,不可转移状态
  4. 必须有一个 then 异步执行方法,then 接受两个参数且必须返回一个promise

实现思路

我们定义Promise1对象,在对象内部创建status、reason、fullfilledCallbacks、rejectedCallbacks这四个属性,这些属性分别表示的意义为:

  1. reason:保存当前promise实例状态
  2. value:保存fullfilled之后的值
  3. reason:保存rejected后的原因
  4. fullfilledCallbacks: fullfilled回调队列
  5. rejectedCallbacks:rejected回调队列

我们定义resolve和reject方法用于处理传进来的executor函数。在当前实例调用then方法时候去返回新的Promise1实例,并判断当前实例状态是否pendding,如果pendding,将传入的成功和失败回调函数加入队列,在外部调用resolve或者reject时候,再次判断当前状态是否pendding,如果是,则修改当前实例状态为fullfilled或者rejected,并批量执行回调队列中的回调函数。

promise 状态流转过程

img

参考资料:

今日头条: 介绍下Promise,内部实现(一面)

图解 Promise 实现原理(一)—— 基础实现

promise和async处理失败有什么区别?

## promise

function asyncTast(url){

   return new Promise(resolve,reject) => {

	}
}


## async

try{

}catch(e){

console.log(e);

}

如何设计promise.all?

总结 promise.all 的特点

1、接收一个 Promise 实例的数组或具有 Iterator 接口的对象,

2、如果元素不是 Promise 对象,则使用 Promise.resolve 转成 Promise 对象

3、如果全部成功,状态变为 resolved,返回值将组成一个数组传给回调

4、只要有一个失败,状态就变为 rejected,返回值将直接传递给回调 all() 的返回值也是新的 Promise 对象

实现思路:

function promiseAll(promises) {
  return new Promise(function(resolve, reject) {
    if (!isArray(promises)) {
      return reject(new TypeError('arguments must be an array'));
    }
    var resolvedCounter = 0;
    var promiseNum = promises.length;
    var resolvedValues = new Array(promiseNum);
    for (var i = 0; i < promiseNum; i++) {
      (function(i) {
        Promise.resolve(promises[i]).then(function(value) {
          resolvedCounter++
          resolvedValues[i] = value
          if (resolvedCounter == promiseNum) {
            return resolve(resolvedValues)
          }
        }, function(reason) {
          return reject(reason)
        })
      })(i)
    }
  })
}

Promise 构造函数是同步执行还是异步执行,那么 then 方法呢?

promise构造函数是同步执行的,then方法是异步执行的

const promise = new Promise((resolve, reject) => {
  console.log(1)
  resolve()
  console.log(2)
})

promise.then(() => {
  console.log(3)
})

console.log(4)

参考资料:

Promise 构造函数是同步执行还是异步执行,那么 then 方法呢?

API

Map api?

Map 保存键值对,并且能够记住键的原始插入顺序。任何值都可以作为一个或一个

  • Map的属性
    • size
  • Map的方法
    • 增:set({key:value}) //增加对象
    • 删:
      • -clear() //清空对象
      • delete(key) //删除对象
    • 改:
    • 查:
      • -get(key) //获取对象
      • has(key) //返回一个布尔值,判断Map实例是否包含键对应的值
    • 遍历:
      • forEach(callbackFn[,thisArg]) //删除对象
    • 其他:
      • keys() //返回类型:Iterator对象,获取keys列表
      • values() //返回类型:Iterator对象 ,返回一个新的Iteratror对象,它按插入顺序包含了Map对象中每个元素的值

Set api?

set对象允许你存储任何类型唯一值,无论是原始值或者是对象引用

属性:

  • 长度:size

实例方法:(Set.prototype)

  • 创建: new Set(); new Set([1,2]);
  • 增(只支持添加在尾部):
    • 尾:只接受一个参数,但可链式调用, mySet.add(1).add(2)
  • 删:
    • 指定item : delete(value) //Array(value=item),Object (value=object)
    • 清空: clear()
  • 查:
    • 判断值是否存在: has(value) //Array(value=item),Object (value=object)
  • 遍历:
    • for循环:for(const item of set1){console.log(item);}
    • forEach: 无返回值