[TOC]
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}
参考资料:
区别:
- 【重复性】
- set: value不重复 //通过此特性,可用来去重
- array: value 可重复
Map对象保存键值对,任务值(对象或原始值)都可以做为一个键或一个值
区别(Maps的优点):
- 【key值类型】
- Map:key可以是对象
- Objects: key只能是一个string 或是 symbol
- 【键的顺序】
- Map: 有序
- Objects: 无序
- 【size】
- -Map: 增加size属性,直接获取
- Objects: 只能依靠手动计算
- 【键名冲突】
- Map: 默认不包含任何键,只包含显式插入的键
- Object: object都有自己的原型,原型链上的键名有可能和你自己对象上的设置的键名产生冲突 (ES5开始可用Object.create(null)来创建一个没原原型的对象,但这种用法不常见)
- 【性能】
- Map: 在频繁增删键值对的场景下表现更好
- Object: 在频繁添加/删除键值对的场景下未作出优化
- 默认绑定外层this
- 不能使用call方法修改里面的this
- -原因:函数的this可以用call方法来手动指定,而为了减少this的复杂性,箭头函数无法用call方法来指定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'
参考资料:
特点:
- 【严谨】对语法要求更规范,消除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 关键词,直接定义
参考资料:
Decorator 就是一种动态地往一个类中添加新的行为的设计模式,它可以在类时,扩展一个类的功能,并且 去修改类本身的属性和方法,使其可以在不同类之间更灵活的共用一些属性和方法;
修饰模式(Decortaor),是面向对象编程领域中,一种动态地往一个类中添加新的行为的设计模式。修饰模式相比生成子类更加灵活,这样可以给某个对象而不是整个类添加一些功能
用法:
- 装饰类
@FooDecorator
class Foo {
}
function FooDecorator(target){
// target 就是这个 class 的原型对象
}
- 装饰属性
//给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()和{}的区别
let,const: 变量,常量 变量的解构赋值: promise:处理异步 字符串扩展: for..of字符串遍历接口,repeat将一个新字符串重量N次,模板字符串$(baseUrl) 数值扩展:math.trunc()去掉小数部分,sign判断一个数是正/负/零,指数运算符**, 函数扩展:箭头函数(不绑定自己的this) 数组扩展:填充数组(fill),复制数据const a2=[...a1] 对象扩展: 正则的扩展: 增加async函数: 对异步的处理
常用的模块化方法:
模块名 | 导出模块 | 引入模块 | 加载方式 | 说明 |
---|---|---|---|---|
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有一个独立的模块依赖的解析阶段
/** 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();
}
});
相同点:都是定义变量的,都可以修改
let 优点:
-
不能重复声明:var可以重复声明,避免变量被覆盖
-
不存在变量提升:let 不存在变量提升,一定要先定义再使用。未定义的变量会报
// var中存在变量提升 console.log(a); // undefined var a=1; // let中不存在变量提升,更容易发现错误 console.log(b); // 引用错误:初始化之前不能使用 let b=1;
-
存在暂时性死区: 只要块级作用域内存在let/const命令,它所声明的变量便绑定在这个区域,不再受外部影响
-
拥有块级作用域:ES5中只有全局和函数作用域,没有块级作用域,let会产生块级作用域。
-
- var没有块级作用域的一些问题
-
-
- 内层变量会覆盖外层变量;
-
- 用来计数的循环变量泄露为全局变量;
-
箭头函数(不绑定自己的this),解构赋值,spread展开,块级作用域(let,const),promise异步解决方案,set(没有重复的值)/map数据结构,proxy拦截器
重要属性:class类(类继承extends,constructor,super)
es7: async(async,await)异步操作,返回promise对象,可以接着用.then方法来连接;
在 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
原生对象的key只能是string类型的,map的key可以是任意值(object,array,function,undefind...)
map是一种更完善的hash结构实现;
map的适用场景,把不同的事件关联起来;、
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属性名。
Proxy的字面意思是代理器,任何对对象(对象,数组,函数等都可以)的操作(包括:访问,写入等操作)都会进入这一层的拦截。可以在这一层添加一些自定义的操作。
比始拦截后的一些操作,方法复写与增强
- 增加日志记录
- 提供友好提示和阻止特定操作 // 读取不存在的属性时,自定义报错信息。不能删除特定的属性等。
- 读取负索引的值
- 设置真正的私有属性,私有的定义:不能访问,不能修改,不能遍历。 // 一般约定以 _ 开头key为私有属性
参考资料
生成对象的模板
主要组成:
- constructor:构造方法
- 默认属性,也可手动添加
- 直接指向“类”的本身,和es5的行为一致 //Person.prototype.constructor === Person
- 实例方法
- 静态方法
- 私有方法、私有属性
- new.target属性
特点:
- 类内部定义的方法,都是不可枚举的(non-enumeerable),和构造函数不一样
//es6的类,可看作是构造函数的另一种写法
class Person{}
typeof Person //"function"
Person===Person.prototype.constructor //true
参考资料:
class本质使用prototype的原型链,只是一种语法糖;
在语法上更加贴合面向对象的语法,在实现继承上更新易读、易理解;
区别:
- 【变量提升】
- 类:没有
- 构造函数:有
- 【调用】
- 类:必须使用new调用,否则报错
- 构造函数:可直接调用
- 【严格模式】
- 类:类和模块的内部,默认是严格模式,无需使用 use strict 关键词
- 构造函数:无强制要求
//类不存在变量提升
new Person{}; //ReferenceError
class Person{}
es6语法 中,super指代父类的构造函数;
react里面就是指代 React.Component 的构造函数
在调用super() 之前,无法在构造函数中使用this;在es2015中,子类必须在 constructor中调用super(),传递props给super() 的原因是便于能在constructor访问this.props;
参考资料:
本质:async是Generator的语法糖;
async改变有如下几个方法
- 更好的语义化:async相当于*,await 相当于yield;
- 返回值是promise,generator返回的是Iterator对象,更方便的使用then来操作
- 内置执行器:async自带执行器,Generator需要依靠执行器(每次都是执行g.next()方法);
- 更广的适用性:async函数的await后台可以是promise对象,也可以是原始类型的值;
- 同步写法优雅:使摆脱了then的链式调用带来的阅读负担
- 获取返回值方便:promise传递中间传非常麻烦,而async/await几乎是同步的写法,更优雅
- 错误处理友好:async/await可使用成熟的 try/catch,promise的错误捕获非常冗余
- 调试友好:promise中的then使用调试器的步进(step-over)功能,调试器并不会进入后续的then代码块,因为调试器只能跟踪同步代码的【每一步】
Promise是es6引入的一个新的对象,用来解决js中异步回调地狱的写法,并不是什么突破性的api,只是封装了异步回调函数;
使得异步写的更加优雅, 可读性更高,而且支持链式操作;
Promise一共有三种状态
1.初始化,状态:pending
2.当调用resolve(成功),状态:pengding=>fulfilled
3.当调用reject(失败),状态:pending=>rejected
Promise/A+规范
- pending:表示初始状态,可以转移到 fullfilled 或者 rejected 状态
- fulfilled:表示操作成功,不可转移状态
- rejected:表示操作失败,不可转移状态
- 必须有一个 then 异步执行方法,then 接受两个参数且必须返回一个promise
实现思路
我们定义Promise1对象,在对象内部创建status、reason、fullfilledCallbacks、rejectedCallbacks这四个属性,这些属性分别表示的意义为:
- reason:保存当前promise实例状态
- value:保存fullfilled之后的值
- reason:保存rejected后的原因
- fullfilledCallbacks: fullfilled回调队列
- rejectedCallbacks:rejected回调队列
我们定义resolve和reject方法用于处理传进来的executor函数。在当前实例调用then方法时候去返回新的Promise1实例,并判断当前实例状态是否pendding,如果pendding,将传入的成功和失败回调函数加入队列,在外部调用resolve或者reject时候,再次判断当前状态是否pendding,如果是,则修改当前实例状态为fullfilled或者rejected,并批量执行回调队列中的回调函数。
promise 状态流转过程
参考资料:
## promise
function asyncTast(url){
return new Promise(resolve,reject) => {
}
}
## async
try{
}catch(e){
console.log(e);
}
总结 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方法是异步执行的
const promise = new Promise((resolve, reject) => {
console.log(1)
resolve()
console.log(2)
})
promise.then(() => {
console.log(3)
})
console.log(4)
参考资料:
Promise 构造函数是同步执行还是异步执行,那么 then 方法呢?
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对象允许你存储任何类型的唯一值,无论是原始值或者是对象引用
属性:
- 长度: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: 无返回值