公司网站备案需要哪些百度竞价seo排名
Generator
种异步编程解决方案
- 函数会返回一个遍历器对象
- 语法上:Generator 函数是一个状态机,封装了多个内部状态。
- 形式上:Generator 函数是一个普通函数;
- 有两个特征。一是,
function
关键字与函数名之间有一个星号;二是,函数体内部使用yield
表达式,
function* helloWorldGenerator() {yield 'hello';yield 'world';return 'ending';
}
var hw = helloWorldGenerator();
它内部有两个
yield
表达式(hello
和world
),即该函数有三个状态:hello,world 和 return 语句(结束执行)。
- 调用 Generator 函数后,该函数并不执行,返回的也不是函数运行结果,而是一个指向内部状态的指针对象,也就是上一章介绍的遍历器对象(Iterator Object)。
总结一下
调用 Generator 函数,返回一个遍历器对象,代表 Generator 函数的内部指针。以后,每次调用遍历器对象的next
方法,就会返回一个有着value
和done
两个属性的对象。value
属性表示当前的内部状态的值,是yield
表达式后面那个表达式的值;done
属性是一个布尔值,表示是否遍历结束。
yield 表达式
遍历器对象的
next
方法的运行逻辑如下。
yield
表达式是暂停执行的标记,而next
方法可以恢复执行。
yield
表达式后面的表达式,只有当调用next
方法、内部指针指向该语句时才会执行;“惰性求值”
yield
表达式只能用在 Generator 函数里面,用在其他地方都会报错。
- 遇到
yield
表达式,就暂停执行后面的操作,并将紧跟在yield
后面的那个表达式的值,作为返回的对象的value
属性值。 - 下一次调用
next
方法时,再继续往下执行,直到遇到下一个yield
表达式。 - 如果没有再遇到新的
yield
表达式,就一直运行到函数结束,直到return
语句为止,并将return
语句后面的表达式的值,作为返回的对象的value
属性值。
yield
表达式如果用在另一个表达式之中,必须放在圆括号里面。
function* demo() {console.log('Hello' + (yield)); // OKconsole.log('Hello' + (yield 123)); // OK
}
yield
表达式用作函数参数或放在赋值表达式的右边,可以不加括号。
function* demo() {foo(yield 'a', yield 'b'); // OKlet input = yield; // OK
}
与 Iterator 接口的关系
由于 Generator 函数就是遍历器生成函数,因此可以把 Generator 赋值给对象的
Symbol.iterator
属性
含义:Generator 与状态机
没懂协程与子例程
next() 方法
yield
表达式本身没有返回值 undefined
next
方法可以带一个参数,该参数就会被当作上一个yield
表达式的返回值。
function* f() {let a = 2 * (yield 1) // 2 * undefindconsole.log('a:', a) //NaNlet b = 2 * (yield 2)console.log(a, b)//NaN, 20return 1
}
let a = f()
console.log(a.next())//{value: 1, done: false}
console.log(a.next())//{value: 2, done: false}
console.log(a.next(10))//{value: 1, done: true}
for…of 循环
yied*表达式
在 Generator 函数内部,调用另一个 Generator 函数。需要在前者的函数体内部,自己手动完成遍历。
function* foo() { yield 'a'; yield 'b'; }
function* bar() {yield 'x';for (let i of foo()) {yield i}; yield 'y';
}
for (let v of bar()){ console.log(v); }
- 在 Generator 函数内部,调用另一个 Generator 函数。需要在前者的函数体内部,自己手动完成遍历。
- 提供了
yield*
表达式,作为解决办法,用来在一个 Generator 函数里面执行另一个 Generator 函数。
function* bar() { yield 'x'; yield* foo(); yield 'y'; }
// 等同于
function* bar() { yield 'x'; yield 'a'; yield 'b'; yield 'y'; }
语法角度:如果
yield
表达式后面跟的是一个遍历器对象,需要在yield
表达式后面加上星号,表明它返回的是一个遍历器对象。这被称为yield*
表达式。
如果yield*
后面跟着一个数组,由于数组原生支持遍历器,因此就会遍历数组成员。
function* gen(){ yield* ["a", "b", "c"]}
gen().next() // { value:"a", done:false }
let read = (function* () {yield 'hello';yield* 'hello';
})();
read.next().value // "hello"
read.next().value // "h"
如果被代理的 Generator 函数有return
语句,结果是不一样的,‘
function* foo() { yield 'a'; yield 'b'; }
function* bar() {yield 'x';for (let i of foo()) {yield i}; yield 'y';
}
for (let v of bar()){ console.log(v); }
throw
throw指针对象的
throw
方法抛出的错误,可以被函数体内的try...catch
代码块捕获。
function* gen(x){try {var y = yield x + 2;} catch (e){console.log(e);}return y;
}
var g = gen(1);
g.next();
g.throw('出错了');
// 出错了
出错的代码与处理错误的代码,实现了时间和空间上的分离,这对于异步编程无疑是很重要的。
Thunk 函数
**传值调用:**即在进入函数体之前,就计算x + 5
的值
f(x + 5)
// 传值调用时,等同于
f(6)
传名调用:
f(x + 5)
// 传名调用时,等同于
(x + 5) * 2
两种比较
- 传值调用比较简单,但是对参数求值的时候,实际上还没用到这个参数,有可能造成性能损失。
Thunk 函数的含义
编译器的“传名调用”实现,往往是将参数放到一个临时函数之中,再将这个临时函数传入函数体。这个临时函数就叫做 Thunk 函数。
function f(m) {return m * 2;
}
f(x + 5);
// 等同于
var thunk = function () {return x + 5;
};
function f(thunk) {return thunk() * 2;
}
JavaScript 语言是传值调用,它的 Thunk 函数含义有所不同。在 JavaScript 语言中,Thunk 函数替换的不是表达式,而是多参数函数,将其替换成一个只接受回调函数作为参数的单参数函数。这个单参数版本,就叫做 Thunk 函数。
// 正常版本的readFile(多参数版本)
fs.readFile(fileName, callback);
// Thunk版本的readFile(单参数版本)
var Thunk = function (fileName) {return function (callback) {return fs.readFile(fileName, callback);};
};
var readFileThunk = Thunk(fileName);
readFileThunk(callback);
thunk函数
不会
co模块
不会