09 | (...x):不是表达式、语句、函数,但它却能执行
09 | (...x):不是表达式、语句、函数,但它却能执行
讲述:周爱民
时长20:27大小18.72M
递归与迭代
迭代对执行过程的重造和使用
展开语法
如何做到呢?
内部迭代过程
异常处理
是谁的退出与异常?
知识回顾
思考题
赞 4
提建议
精选留言(11)
- leslee2019-12-261. 他可以手动调用..., 在you dont know js 里面说过这个 .throw 可以委托异常. 2. 这是由他的语义决定的, 如果他是一个语句, 那他就不能跟表达式连用, 如果他是一个表达式, 那他的返回值又显得有点多余. 3. ```js function foo4(x = 5) { return { next: () => { return { done: !x, value: x && x-- }; }, "return": () => { return { done: true, value: '恭喜发财' } }, "throw": () => { console.log("THROW!") } } } let x = new Object; x[Symbol.iterator] = foo4 aaa: for (let item of x) { console.log(item, 'w') for (let item of x) { console.log(item, 'i') continue aaa; } } // echo 'return' x2 // 当return 函数返回undefined 的时候会报这个错 Iterator result undefined is not an object ``` 被我试出来了, 当return函数返回undefined , 且嵌套循环且continue带有外层循环语句的标签的时候, 他会触发两次return, 缺一个条件都不行. 当return函数返回一个正常的迭代器对象`{done:true, value: 'xxx'}`, 他会输出5个return, 这个return应该由内层的forof 发出, 因为内层的循环直接被打断了, 继续下去的是外层循环, 单层循环不行是因为 continue 的语义并不是打断/结束, 是这样理解么老师, 这里面还有其他的豆子么,老师. 4. 把x展开, 返回迭代值 , 如果 没有 * 返回的将是迭代器函数, .展开
作者回复: 关于1和3,老实说,这个东西确实灰常灰常麻烦,你得自己去体会tor.return和tor.throw的含义与用法。 我只能提示说:谁创建了tor,那么就该是谁去调用tor.return和tor.throw。这两个方法不是事件触发,而是“tor的持有者”调用。 关于2,你的答案没有对或不对的问题,但我觉得还有深思的余地。不妨多思考一下,不着急有结论。另外,这个问题没有标准答案。 关于4,你“可能”是对的,但似乎表达得不太清楚。^^.
3 - HoSalt2020-05-18老师,为什么fn(...args)怎么变成了一个个的参数,而在{...args}怎么变成了对象属性的一部分,这是谁控制的?
作者回复: ...x 这个称为“展开语法”,是在语法分析阶段就被替换成了硬代码的。所以简单的说,在执行期的时候就是一个循环/迭代处理,并且根据上下文的不同,来决定是展开成参数,还是属性。 类似于用字符串替换重写了代码,硬写入的。^^.
1 - Smallfly2020-01-16// 迭代函数 function foo(x = 5) { return { next: () => { return {done: !x, value: x && x--}; } } } const tor = foo(); const names = Object.getOwnPropertyNames(tor.constructor.prototype); console.log(names); /* [ 'constructor', '__defineGetter__', '__defineSetter__', 'hasOwnProperty', '__lookupGetter__', '__lookupSetter__', 'isPrototypeOf', 'propertyIsEnumerable', 'toString', 'valueOf', '__proto__', 'toLocaleString' ] */ 请问老师我用 node 执行上面的代码,为什么跟文中的以下输出不一致呢。 > Object.getOwnPropertyNames(tor.constructor.prototype) [ 'constructor', 'next', 'return', 'throw' ]展开
作者回复: 这里的foo()是一个模拟生成器函数的界面的,所以它当然得不到“真的”生成器。 你需要的示例是这样: ``` function* foo() {} tor = foo(); names = Object.getOwnPropertyNames(tor.constructor.prototype); ```
共 2 条评论1 - weineel2019-12-02四个思考题,都没能找到很好的答案。。。共 1 条评论1
- Sam2021-08-12根据周老师的指点,谁创建谁调用。以下是我想到一个办法调用throw方法(不知理解是否合理): function* foo() { yield 1 yield 2 yield 3 } foo.prototype.throw = function(e) { console.log("Test Error " + e) } let tor = foo() for(let item of tor) { console.log(item) tor.throw(new Error('迭代器循环休内触发')) }展开
作者回复: 看起来这样是能用的,并且事实上通常也需要这样来使用。但是,总之,在这里throw()的原则与应用都是令人困惑的。——我的意思是js语言在这里的设计原本就很令人困惑。由于篇幅, 这一讲并没有关于这个部分的更多讨论,而仅仅是开了个头。如果你有兴趣了解更深的话,建议参阅《javascript语言精髓与编程实践》的5.4.4.3和5.4.5的内容。
共 2 条评论 - 吉法师2021-04-06迭代感觉用不着啊……
- 油菜2020-11-13"用户在这里设计异常处理过程,那么 foo2() 中的 touch(x) 管理和涉及的资源都无法处理。" 是指在“console.log() 调用或 for…of 循环中”处理异常么? touch(x)和涉及的资源无法处理,是什么意思呢? 是指touch(x)内部抛出异常,但catch不到异常么? ------------------------------------- function touch(x) { try{ if (x==2) throw new Error("hard break"); }catch(c){ console.log('c:'+ c); } } // 迭代函数 function foo2(x = 5) { return { next: () => { touch(x); // some process methods return {done: !x, value: x && x--}; }, "return": () => console.log("RETURN!"), "throw": () => console.log("THROW!") } } // 示例 var x = new Object; x[Symbol.iterator] = foo2; // default `x` is try{ console.log(...x); }catch(d){ console.log('d:'+ d); } 结果:touch(x)可以处理异常 c:Error: hard break 5 4 3 2 1展开
作者回复: 以for..of语句为例,在那个例子中发生异常时,在`console.log(i)`位置是无法捕获异常的;在for..of外层可以加一层异常,但是无法处理touch()过程所分配的(或者所"碰(touch)"过的)资源。例如,如果touch()过程打开了一个文件,那么当异常发生时,在哪里去关闭文件呢?
- 油菜2020-11-131 语言设计者有考虑到异常处理,但功能上还不够完善。类似写了个todo 2 使用者或设计者需要一种语法,能够接收所有参数。例如剩余参数 function foo(...x){console.log(x)}, 数组x可以接收所有入参。“为什么 ECMAScript 不提供一个表达式 / 语句之外的概念来指代它”(老师可能是想说,之内的概念,文章标题已指出(...x)既不是表达式,也不是语句),可能是和现有的编译逻辑冲突。例如我们写程序,如果出现分支情况,最简单的做法是写个if判断,区分分支。表达式,语句,语法,可能是不同分支处理。 3 本人简单测试,以下几种都会触发tor.return。 for(var i of x ) {console.log(i);break}; for(var i of x ) {console.log(i); throw new Error('test')} for(var i of x ) {console.log(i);continue}; 4 只知其然,不知其所以然 // 迭代函数 function foo6(x = 5) { return { // foo2()中的next next: () => { return {done: !x, value: x && x--}; }, // foo3()中的return和throw "return": () => console.log("RETURN!"), "throw": () => console.log("THROW!") } } var b = foo6(); b.next();// { done: false, value: 5 } b.next();// { done: false, value: 4 } //yield用法 var x = new Object; x[Symbol.iterator] = foo6; function* g1() { yield* x}; var a = g1(); a.next(); // { done: false, value: 5 } a.next(); // { done: false, value: 4 }展开
- Elmer2020-07-07这个贴近语义的语法和解析为可执行结构的关系是什么呢
作者回复: 这个这个,你的这句话我读不太明白意思。 如果你的意思是“语法与可执行结构”的关系,那么可以简单地说:在语法解析之后,会在相应的位置上插入一段硬代码,以支持对应的可执行结构的实际语义。 例如如果是“展开...x作为参数”,那么对应的位置上的代码,其实就是把数组x添加到参数列表的指定位置。这是一段确定的逻辑。
- 亦枫丶2020-04-03看了好久,对于第二个思考题恍然大悟,我想可以用文章开头的 **“换而言之,可以通过更显式的、特指的或与应用概念更贴合的语法来表达新的语义。”** 这句话来解释吧,它只是“展开”逻辑的一种实现,叫它展开语法更为显式,与应用概念更贴合吧。展开
- antimony2020-02-09老师,请问一下这个迭代和递归的关系您是从sicp中了解到的吗?
作者回复: 不是。^^. 循环与递归的关系可能源出于SICP。但我是在读SICP之前听一个朋友谈及的,后来意识到根本问题是抽象模型内在的一致性。所以才有了《程序原本》中说“本质上相同的抽象系统,其解集的抽象本质上也是相同的”。基于此,再观察论述迭代与递归的关系,就是显而易见的事情了。 《程序原本》: https://github.com/aimingoo/my-ebooks