1. Iterator 和 for…of 循环
1.1 可迭代协议 与 迭代器协议
https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Iteration_protocols
1.2 为什么要有两个协议
不可能知道一个特定的对象是否实现了迭代器协议,然而创造一个同时满足迭代器协议和可迭代协议的对象是很容易的
(就像下面的example所示)。这样做允许一个迭代器能被不同希望迭代的语法方式所使用。 因此,很少只实现迭代器协议而不实现可迭代协议。
1.3 都有哪些语法或特性,使用或实现了可迭代协议与迭代器协议
for...of / ... / Array.from 使用了迭代器协议,自制???
[] / Set / Map / generators 实现了Iterators
2. Generator函数与异步应用
1.yield 使生成器函数执行暂停,yield关键字后面的表达式的值返回给生成器的调用者(返回)
2.基于生成器的版本的return关键字
3.yield关键字实际返回一个IteratorResult(迭代器)对象,它有两个属性,value和done,分别代表返回值和是否完成。
4.yield无法单独工作,需要配合generator(生成器)的其他函数,如next,懒汉式操作,展现强大的主动控制特性。
function* 定义生成器函数
1.生成器函数能减少系统存储空间,因为要一次数据,生成器才会生成一次数据,不要不生成。
2.[GeneratorFunction: yie]
2.1 基本用法
function* yie(){
let list = [1,3,6,9];
for (let i = 0;i<list.length;i++){
yield list[i];
}
}
const myArr = yie();
/*
1.函数next()是个迭代器对象,传参可以缺省,默认调用函数 。
2.两个属性{value,done}
*/
console.log(myArr.next(yie()));//{ value: 1, done: false }
console.log(myArr.next());//{ value: 3, done: false }
console.log(myArr.next());//{ value: 6, done: false }
console.log(myArr.next());//{ value: 9, done: false }
console.log(myArr.next());//{ value: undefined, done: true }
2.2 next传递参数
next()函数及参数
- next()可以带一个参数,该参数会被认为是上一个yield整体的返回值
- 意义:可以在不同阶段从外部直接向内部注入不同的值来调整函数的行为
- 第几个next() 就获得第几个yield 的返回对象
function* mygenerator() {
for (let i = 1; true; i++) {
let reset = yield i;
if (reset) {
console.log(`yield 返回值被改变,函数行为发送变化`);
i = -1;
};
}
}
const myArr = mygenerator();
console.log(myArr.next());//{ value: 1, done: false }
console.log(myArr.next());//{ value: 2, done: false }
console.log(myArr.next());//{ value: 3, done: false }
console.log(myArr.next());//{ value: 4, done: false }
console.log(myArr.next());//{ value: 5, done: false }
// yield 返回的值被赋值为true
console.log(myArr.next(true)); //yield 返回值被改变,函数行为发送变化 { value: 0, done: false }
next() 传参是对yield整体的传参 (把yield也包括进去),否则yield类似于return
function* test(x){
console.log(`x:${x}`)
let y = 2*(yield (x+1));
console.log(`y:${y}`);
let z = yield (y/3);
console.log(`z:${z}`);
return x+y+z;
}
let a = test(5);
console.log(a.next());//x:5 { value: 6, done: false }
console.log(a.next());//y:NaN { value: NaN, done: false }
console.log(a.next());//z:undefined { value: NaN, done: true } //value 是 return的值
分析:
1. x恒为5,所以第一次调用传空没问题,可得到对应的第一个yield返回值:yield (x + 1)
2. 第二次调用,无参数传入,所以y为(2* undefined) => NaN,自然得不到z
3. 第三次调用同上分析
let a = test(5);
console.log(a.next()); //x:5 { value: 6, done: false }
console.log(a.next(12));//y:24 { value: 8, done: false }
console.log(a.next(13));//z:13 { value: 42, done: true }
分析:
1. x恒为5,所以第一次调用传空没问题,可得到对应的第一个yield返回值:yield (x + 1)
2. 第二次调用,传入12,所以y为24 (yield (x + 1) = 入参),得到第二个yield: yield (y / 3) = 8
3. 第三次调用同上分析,得到最后的z值并 return = 42
2.3 用for…of迭代generators
function* liangpiGenerators() {
console.log('和面之前');
window.status = yield '和面';
console.log('和完面了');
var b = yield '蒸';
console.log('蒸完了');
var c = yield '切';
}
for (let item of liangpiGenerators()) {
console.log('item:', item);
}
2.4 generators处理异步
function buy(name, cb) {
setTimeout(() => {
cb && cb(null, name);//cb为真执行cb函数
}, 5);
}
function buyPromise(name) {
return new Promise((resolve, reject) => {
buy(name, (err, content) => {
if (err) reject();
else resolve(content);
})
})
}
function* buyAmountGenerators() {
var food1 = yield buyPromise('炸鸡');
var food2 = yield buyPromise('汉堡' + food1);
var food3 = yield buyPromise('可乐' + food2);
var food4 = yield buyPromise('烤鸭' + food3);
return food4;
}
let buyAmount = buyAmountGenerators();
// 用递归实现 1.异步执行无法直接输出返回值 使用回调函数
function myBuyAmount(food,callBack) {
let a = buyAmount.next(food);
if (a.done) return callBack(food);
a.value.then(res => {
// console.log("foods:", res);
myBuyAmount(res,callBack);
})
}
myBuyAmount(null,food=>{
console.log(food);//烤鸭可乐汉堡炸鸡
});
//使用Promise
function myBuyAmount() {
return new Promise((resolve, reject) => {
(function buy(food) {
let a = buyAmount.next(food);
if (a.done) {
resolve(food);
return
};
a.value.then(res => {
// console.log("foods:", res);
buy(res);
})
})()
})
}
myBuyAmount().then(res => {
console.log(res)
})
//使用co第三方库
var co = require('co');
//自动执行next并将promise的返回值作为下一个next的参数,返回结果是return的结果
co(buyAmountGenerators).then(res =>{
console.log(res)
})
2.5 封装异步处理函数
类似于co中的使用方式
const co = require('co');
const fetch = require('node-fetch');
co(function *() {
const res = yield fetch('https://mcs.snssdk.com/v1/list?rdn=0.5130960891765854');
const jsonRes = yield res.json();
console.log("jsonRes:", jsonRes);
});
3. async函数
3.1 基本用法
封装隐式的Promise行为,趋近于同步行为,实则为语法糖
async function buyAmountGenerators() {
var caiContent = await buyPromise('cai');
var paomianContent = await buyPromise('paomian' + caiContent);
var shuanghuanglianContent = await buyPromise('shuanghuanglian' + paomianContent);
return shuanghuanglianContent;
}
3.2 如何封装旧的函数以适应async/await语法
将error first风格的函数,封装为Promise形式的函数即可
function buy(name, cb) {
setTimeout(() => {
cb && cb(null, 'content:' + name);
}, 5);
}
function buyPromise(name) {
return new Promise((resolve, reject) => {
buy(name, function (err, content) {
if (err) {
reject();
}
resolve(content);
});
});
}
async function buyAmountAsync() {
var caiContent = await buyPromise('cai');
var paomianContent = await buyPromise('paomian' + caiContent);
var shuanghuanglianContent = await buyPromise('shuanghuanglian' + paomianContent);
return shuanghuanglianContent;
}
buyAmountAsync().then(content => {
console.log('content:::::::', content);
});
3.3 babel编译下的generators/async/await
看编译后的源码
generators -> 代码切割
async/await的原理还是老一套(generators)
3.4 优势
比Promise优势
axios()
.then(function () {
new Promise(() => {
reject();
});
})
.then(function () {
})
.catch(function () {
// 统一???!!!
});
- 对于流的控制,更加精细化
- 直接简单的try-catch体验
- 同步的书写体验
4. Proxy与Reflect用法
4.1 基本用法
// obj.name
// obj.money
// obj.money = 100000000;
var obj = {};
//拦截属性对象属性读取和设置
Object.defineProperty(obj, 'money', {
get(key) {
console.log('get a attr');
return obj[key];
},
set(key, value) {
console.log('set a attr');
return obj[key] = value
}
});
// obj.money = 100000000;
var obj = {};
var proxiedObj = new Proxy({}, {
//target:原始对象 key:拦截的属性 receiver:发起调用的对象
get(target, key, receiver) {
console.log('key:', key);
},
//value:设置的值
set(target, key, value, receiver) {
console.log('key:', key, value);
}
});
proxiedObj.asdasd = 1;
4.2 可撤销对象
var {proxy, revoke} = Proxy.revocable({}, {
get(target, key, receiver) {
console.log('key:', key);
},
set(target, key, value, receiver) {
console.log('key:', key, value);
}
});
//删除拦截
revoke();
4.3 Reflect基本用法
4.4 在Vue3.0中的应用
代理对象与处理对象部分的源码,使用的是Proxy,虽然使用的是TS,但是和ES6中的Proxy与Reflect一致
5. Decorators用法与注意事项
5.3 如何装饰类与方法
//装饰一个类
const itemFy = (target) => {
console.log('target::::', target);
target.prototype.click = function (){
console.log("click");
}
return target;
};
//装饰一个类方法 方法定义在类内,函数写在外头
const renderShell = (targetPrototype,propName)=>{
}
@itemFy //=== itemFy(MyComponent)
//class 是语法糖 === function MyComponent(){}
class MyComponent {
render() {
return '<div>内容</div>';
}
}
5.4 babel编译下的Decorators
5.5 decorators与proxy的联系与区别
- Decorators会更改原始对象,装饰是对于原有对象的修改
- Proxy注重于“代理”,产生新的对象,而非对原始的修改
6. class语法
第二节课(面向对象)讲过,同学们可以自行回顾复习
7. babel配置与插件书写
7.1 babel中的术语
Presets
一系列配置的集合
polyfill(腻子)
补丁,用于兼容各个浏览器对于语法支持程度的不同,补充的方法或属性集合
plugin
现在,Babel 虽然开箱即用,但是什么动作都不做。它基本上类似于 const babel = code => code; ,将代码解析之后再输出同样的代码。如果想要 Babel 做一些实际的工作,就需要为其添加插件
7.2 babel-loader在webpack中的注意事项
webpack loader
babel-loader
babel-loader 6.0
babel-plugin-proposal-decorators
babel-preset-env
babel-loader 7.0 @babel/plugin-proposal-decorators
module: {
use: {
test: /\.js$/,
loader: 'babel-loader'
}
}