10 | x = yield x:迭代过程的“函数式化”
10 | x = yield x:迭代过程的“函数式化”
讲述:周爱民
时长18:35大小17.01M
将迭代过程展开
逻辑的重现
有趣的.next() 方法
对传入参数的改造
知识回顾
思考题
赞 1
提建议
精选留言(10)
- 授人以摸鱼2020-03-26我忽然想明白为啥会有迭代器的next需要支持传入参数这样的功能了……以前一直没想明白来着…… 其实就是作为生成器函数的一个应用实例,co模块需要这个功能,需要把yield返回的promise like对象的then方法传回的值从next给生成器函数传回去,这个需求抽象一下,就成了“外部执行环境会需要根据yield传出的结果进行变换后用next传入”这样的通用需求了。 所以我同时也就理解了,为啥说async await是生成器函数的语法糖了,而且这糖真甜wwwww展开
作者回复: :) 赞的!又是大有所得~ ^^.
10 - 红白十万一只2020-03-08老师,最近遇到个问题 if (true) { a = 5 function a() { } a = 1 console.log(a) } console.log(a) 1 5 外部的a变成了5,内部的a变成了1 我查了资料说: function a() { }的函数提升只提升到了if的代码块顶部,但也有一个var声明被悬挂到了全局中值为undefined 之后在a=5时if内部的变量a从函数被修改为了5 function a() { }这步计算函数声明时,函数对象被分配给函数作用域变量。因为被修改为5,所以此时外部的a也被改变成了5 之后在a=1时if内部的变量a从5被修改为了1 最后输出1 5 我的理解是 函数提升只提升到代码块顶部,但是为了符合函数作用域的规则在最近的函数作用域创建了一个同名变量且值为undefined。 之后再执行到声明语句时将这个函数对象转换为值赋值给这个函数作用域同名变量。 老师有更详细的解释么?我找了ES规范没有发现对这里解释。展开
作者回复: 这个东西说起来,就有点历史了。呵呵 早期的JScript认为,只要在同一个作用域(函数/全局)内声明函数,那么同名函数是覆盖的,也就是后一个声明覆盖前一个。所以按照这个逻辑,对“同一个名字的”连续两次声明其实只有后一个有效,并且即使是在前一个之前访问它,也是一样的(因为有变量提升,所以这个域中能访问到的只有第二个声明)。 这个特性直到较晚一些的IE中都是如此,包括IE8或者IE9,但是IE10之后我就不知道了。然而Firefox/SpiderMonkey却不这么认为,在早期还没有块级作用域的时候,他们就搞了一个称为“条件声明”的东西出来,也就是在if的两个分支中如果声明了函数,只有执行到指定分支时,该分支中的声明才有效。——因为那时还没有“块级作用域”这么个概念,所以这个就算成了动态的、“执行期的”逻辑。 而你的这个例子,就是这个特性在起作用了。——在执行到function a()这个声明之前,会因为变量泄露而在全局创建标识符`a`;而在执行到function a()的时候,就在当前块中创建了函数a()。 这个特性呢,在标准的ECMAScript(的正文部分)中是没有的。因为对于Firefox来说,这是一个特殊的、自有的特性,不属于ECMAScript规范,但由于它一直存在于SpiderMonkey系的引擎中,所以在MDN中也一直有这一部分的解释。 后来在ECMAScript中,因为这是一个“通常应于浏览器环境”的特性,所以把它归在了“Web Legacy Compatibility”中,是作为扩展特性来介绍的,不在ECMAScript的正文中。这与两个主题有关: > https://tc39.es/ecma262/#sec-block-level-function-declarations-web-legacy-compatibility-semantics > https://tc39.es/ecma262/#sec-functiondeclarations-in-ifstatement-statement-clauses 你仔细读读就好了。至于MDN,现在也有关于这个内容的条目,并且也指出在不同的浏览器环境中并不一致(因为是ECMAScript扩展规范,并不强制引擎实现)。在这里: > https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/function#Conditionally_created_functions
共 3 条评论5 - 许童童2019-12-04如果遇到 yield* 就将当前的yield执行权交到 yield* 里面,yield* 里面return的值,将返回给外层的x = yield* xxx 中的x
作者回复: Yes. 赞的!+2
5 - .Alter2019-12-09老师好,我想问一下生成器这个挂起和调用栈移动的机制是协程吗?
作者回复: 不是。 不过实现上,可以是。OS真实线程的切换成本高,在实现上用协程来做是可以的。但这与具体引擎的选择有关。另外按照ECMAScript的约定,这里自己实现一个上下文的管理器也是可以的,与线程什么的,并没有关系。
共 2 条评论4 - 油菜2020-11-13老师,从返回结果比较,“迭代器函数”和“生成器函数”的作用是一样的,都是通过迭代器或生成器的.next()方法,一次调用获取一个值。
作者回复: 严格来说,并没有所谓的“迭代器函数”。所谓“迭代”是一组接口规范,它约定的是一种行为。——在《JavaScript语言精髓与编程实践》中称“(可迭代对象是)一种可循环处理的‘可计算对象’”,并称“处理这种对象的‘行为’为迭代”。 而“生成器(函数)”是一个实现。
2 - HoSalt2020-05-19「如果用户代码——通过任意的手段——试图挂起这惟一的执行上下文,那么也就意味着整个的 JavaScript 都停止了执行」 老师,这是什么意思?唯一的执行上下文指全局上下文?
作者回复: 不完全正确。 js的执行上下文是放在一个执行栈里面,而所谓的全局上下文只不过是在这个栈的栈底。上下文之间是通过类似arguments.caller的方式关联起来的(对的,在严格模式中没有,但内部机制上还是有的)。 所以“挂起这惟一的执行上下文”更确切的说法是“活动的(active)上下文”。活动上下文是通过栈顶的移入移出操作来切换的,如果用户代码“通过别的方法”使得这个上下文挂起了,那么整个上下文的栈就挂起了,没有办法来激活它。 js里面所谓的“单线程”,本质上就是“所有的上下文中有且仅有一个活动的(active)上下文”。如果引擎中存在多个这样的活动上下文,那么要么是多线程的,要么是多引擎的,要么是多虚拟机(vm)的。总之这些活动上下文会被隔离在不同的“全局”里面,做不到真并行。 在js的单一线程中,所谓的“(一般意义上的)全局上下文”是惟一的,但它不一定是“活动的(active)”,当然活动的上下文可能在一个函数中(当前函数),或者一个被launch起来的promise的reactions(就是.then里的回调)中,等等。但无论是哪种情况,它们“所处于的调用栈”的栈底,总会是一个全局环境(称为GlobalEnv,是用全局对象来作为词法环境的),但不一定是被称为“全局上下文”的那一个。——也就是说,所谓“一般意义上的全局上下文”,并不一定处于活动上下文(栈)的底部。
2 - 阿鑫2019-12-04我的理解就是 tor 这个句柄其实就是包含了这个迭代器的一切,包括上下文 context 和执行函数。每次执行 tor.next() 就是把 context 压入栈顶,然后执行执行函数?
作者回复: 确实,ECMAScript就是这样做的。 如果tor是一个生成器对象,那么它就会有[[GeneratorContext]]这个私有槽,而tor.next()方法就是从这个私有槽中取出上下文给塞回到栈上。
2 - 行问2019-12-04x = yield x 首先,yield 是向函数外发送 x 的值 其次,yield 接收外部传入的参数并赋值给 x 解惑了之前理解 yield 是一个“等待”的过程,没有往“挂起”去构思 时不时会用到 async await 来写并行的 Promise, 但 yield 只知其知识点和应用,还没有开发中实际使用过展开
作者回复: Promise比await要难用一点,但其实深刻理解promise对整个的语言学习提升很大很大,因为它提供了一种新的理解程序执行逻辑的模式。不过这些内容,会放到20讲之后才讨论,这一次的课程中是不包括的。:)
共 2 条评论1 - 静坐常思己过,闲谈莫...2020-09-28如果我能搞懂这些就不是初级前端了,学路漫漫感谢爱民老师
作者回复: ������ 谢谢������
- 潇潇雨歇2019-12-04先看看yield再来