Giter VIP home page Giter VIP logo

Comments (30)

gentlelius avatar gentlelius commented on May 2, 2024 6

如下场景:

// 尾调用
function f(x){
    return g(x);
}

在chrome中跑了下,那个控制台 -> Sources -> Call Stack中观察到,f(x) 和g(x)都在stack中,并没有出现如下这种情况啊

// 伪代码
ECStack.push(<f> functionContext);

ECStack.pop();

ECStack.push(<g> functionContext);

ECStack.pop();

from blog.

xiaolan92 avatar xiaolan92 commented on May 2, 2024 4

@cikeyin 这不是尾递归,这是闭包.朋友,基础很重要啊!!!

from blog.

scutuyu avatar scutuyu commented on May 2, 2024 3

终于明白了尾递归,没白来

from blog.

hazxy avatar hazxy commented on May 2, 2024 2

@mqyqingfeng 如果真的能够写一个算法系列,那真是大大的好啊!

from blog.

iversonwool avatar iversonwool commented on May 2, 2024 2

关于尾调用刚开始还以为作者说错了 所以又仔细看了看阮老师的http://es6.ruanyifeng.com/#docs/function#%E5%B0%BE%E8%B0%83%E7%94%A8%E4%BC%98%E5%8C%96
还有尾调用优化的问题 是否跟是否使用严格模式有关系

from blog.

iversonwool avatar iversonwool commented on May 2, 2024 1
function f(x){
    return g(x);
}
// 非尾调用
function f(x){
    return g(x) + 1;
}

谁能帮我解释下这俩的执行上下文栈有啥区别么

@tancgo 可以看下这个文章开头部分的解释
https://es6.ruanyifeng.com/#docs/function#%E5%B0%BE%E8%B0%83%E7%94%A8%E4%BC%98%E5%8C%96

函数调用会产生调用栈 call stack,用于保存函数调用的一些信息,比如局部变量等。非尾调用g(x) + 1,需要留存住这个 + 1,但是尾调用g(x),不需要保留任何信息,直接用当前call stack 取代之前call stack 节省了空间。言语很拙劣,不知道讲清楚了没,😂。

from blog.

SilenceZeng avatar SilenceZeng commented on May 2, 2024

阶乘函数优化那节,factorial中的factorial多打了个2,newFactorial(5) // 24这个也手误了,newFactorial函数用的也不是你链接中的curry,而是partial

from blog.

mqyqingfeng avatar mqyqingfeng commented on May 2, 2024

@SilenceZeng 非常感谢指出~ 已经修改,以后写文章的时候会更加严谨一些~

from blog.

jasonzhangdong avatar jasonzhangdong commented on May 2, 2024

@coderLius 怎么观察的,我怎么看不到?

from blog.

mqyqingfeng avatar mqyqingfeng commented on May 2, 2024

@coderLius 很抱歉之前没有看到这个问题,V8 并没有部署尾递归优化,所以其实从 Call Stack 中看不到期望的效果

from blog.

mqyqingfeng avatar mqyqingfeng commented on May 2, 2024

@jasonzhangdong 在这里

default

from blog.

qujsh avatar qujsh commented on May 2, 2024

看了你这张截图,然后试着把数据跑出来,看着数据的变化,感觉更能理解这儿的this,执行上下文了。
然后我的第一反应是,在js深入讲解的时候,你应该就把截图操作的放上去做下举例;第二反应是,涨姿势了,然后希望你能更多的简单讲解下(甚至只是一张截图),这些工具在你们手上是怎么发挥作用的。

from blog.

mqyqingfeng avatar mqyqingfeng commented on May 2, 2024

@qujsh 感谢建议~ 以后可以写一个工具系列~ 哈哈

from blog.

mqyqingfeng avatar mqyqingfeng commented on May 2, 2024

@hazxy 算法系列真是任重而道远呐~

from blog.

iiicon avatar iiicon commented on May 2, 2024

执行上下文栈和函数调用栈是一个东西吧

from blog.

Tan90Qian avatar Tan90Qian commented on May 2, 2024

@mqyqingfeng V8 并没有部署尾递归优化那就是说,尾递归能否对性能有改进,取决于运行的平台、环境咯?那么是否会存在一些平台反而是常规的递归性能比尾递归更好呢?

function factorial(n, res) {
    if (n == 1) return res;
    return factorial(n - 1, n * res)
}

function factorial2(n) {
    if (n == 1) return n;
    return n * factorial(n - 1)
}

console.time('尾递归');
factorial(10000, 1)
console.timeEnd('尾递归');

console.time('正常递归');
factorial2(10000)
console.timeEnd('正常递归');

在chrome下,尾递归的性能通常较好,偶尔性能劣于正常的递归;
在safari和firefox下,尾递归的性能通常较差,甚至在firefox下经常出现4、5倍的耗时差距;

from blog.

mqyqingfeng avatar mqyqingfeng commented on May 2, 2024

@Tan90Qian 代码写错了哈……

function factorial(n, res) {
    if (n == 1) return res;
    return factorial(n - 1, n * res)
}

function factorial2(n) {
    if (n == 1) return n;
    // 这里应该是 factorial2
    return n * factorial2(n - 1)
}

console.time('尾递归');
factorial(10000, 1)
console.timeEnd('尾递归');

console.time('正常递归');
factorial2(10000)
console.timeEnd('正常递归');

from blog.

cikeyin avatar cikeyin commented on May 2, 2024

default
@mqyqingfeng 你好!有个疑问:你在《JavaScript深入之执行上下文栈》 中举的这个例子不也是尾调用吗?为什么执行上下文栈的变化和本章的尾调用不同?

from blog.

fundatou avatar fundatou commented on May 2, 2024

@cikeyin 按照我的理解,这两个地方执行栈都没问题。一个是没有用尾调优化的栈,一个是使用了尾调用优化的栈(现在实际在chrome下跑也暂时还看不到尾调用优化的结果)。可能之前博主在写执行上下文栈的时候更多的重心是在执行上下文栈这块,现在写递归就顺带写了执行栈的优化。

from blog.

xsfxtsxxr avatar xsfxtsxxr commented on May 2, 2024

以前看阮一峰老师的ES6,看到尾调用这边云里雾里,后来看了冴羽大神写的执行环境栈运行原理,回过头看尾调用终于看懂了。
尾调用就是保证每次执行的时候,ECS里面只有一个函数执行上下文,这样在递归时候不会栈溢出。

from blog.

leonwens avatar leonwens commented on May 2, 2024

function f(x){ return g(x) + 1; }
我想问下为啥return g(x)和return g(x)+1,前者的执行上下文栈会先push再pop,后者就不会?

from blog.

yangtao2o avatar yangtao2o commented on May 2, 2024

尾递归学习总结:

// 求和
function sum(n, res = 0) {
  if (n < 1) return res;
  return sum(n - 1, n + res);
}
sum(5); // 15

// 斐波拉契数
function fibonacci(n, sum1 = 1, sum2 = 1) {
  if (n <= 2) return sum2;
  return fibonacci(n - 1, sum2, sum1 + sum2);
}
fibonacci(5); // 5

// 阶乘
function factorial(n, res = 1) {
  if (n <= 1) return res;
  return factorial(n - 1, n * res);
}

from blog.

conan1992 avatar conan1992 commented on May 2, 2024

function f(x){ return g(x) + 1; }
我想问下为啥return g(x)和return g(x)+1,前者的执行上下文栈会先push再pop,后者就不会?

ES6 的尾调用优化只在严格模式下开启,正常模式是无效的。博主讲的应该是在严格模式下

from blog.

puck1006 avatar puck1006 commented on May 2, 2024

function f(x){ return g(x) + 1; }
我想问下为啥return g(x)和return g(x)+1,前者的执行上下文栈会先push再pop,后者就不会?

return g(x) 的时候 f(x)已经结束了,所以f(x)push之后就pop了
return g(x)+1的时候. 因为return会返回计算结果. 所以先g(x)调用的返回值与1相加.那么就是f(x)push之后g(x)push紧接着g(x)调用完pop 再返回最终结果. f(x)也pop

from blog.

tancgo avatar tancgo commented on May 2, 2024
function f(x){
    return g(x);
}
// 非尾调用
function f(x){
    return g(x) + 1;
}

谁能帮我解释下这俩的执行上下文栈有啥区别么

from blog.

xsfxtsxxr avatar xsfxtsxxr commented on May 2, 2024

@tancgo 我理解的出入栈如下:

// 尾调用
function f(x){
    return g(x);
}
ECS:[] -> gloabalContext入栈 -> fContext入栈 -> fContext出栈 -> gContext入栈 -> gContext出栈
// 非尾调用
function f(x){
    return g(x) + 1;
}
ECS:[] -> gloabalContext入栈 -> fContext入栈 -> gContext入栈 -> gContext出栈 -> fContext出栈

from blog.

ZhangXinmiao avatar ZhangXinmiao commented on May 2, 2024
function f(x){
    return g(x);
}
// 非尾调用
function f(x){
    return g(x) + 1;
}

谁能帮我解释下这俩的执行上下文栈有啥区别么

@tancgo 可以看下这个文章开头部分的解释
https://es6.ruanyifeng.com/#docs/function#%E5%B0%BE%E8%B0%83%E7%94%A8%E4%BC%98%E5%8C%96

from blog.

PointerToNextPole avatar PointerToNextPole commented on May 2, 2024

关于尾递归,推荐一下文章:https://site.douban.com/196781/widget/notes/12161495/note/262014367/ 虽然有点老...
另外,文章中《数据结构与算法分析:C描述》中的尾递归的介绍在 3.3.3章节的最后(我这边的版本是 P61 )

from blog.

lh2218431632 avatar lh2218431632 commented on May 2, 2024

个人感觉递归最重要的一点是,函数的名字要具有语义化

from blog.

badmanbadman avatar badmanbadman commented on May 2, 2024

补充几个小点,可能更加容易理解:
1、尾调用的概念:某个函数的 最后一步是调用另外一个函数,即成为尾调用。
2、尾调用优化: 当函数最后一步调用另外一个函数(fn1)时,函数(fn1)内部么没有对父层函数的的任何变量有所依赖,或者说所有的依赖的计算都是在父函数中完成的,这时执行这个函数(fn1)时,父函数的执行上下文会被从栈内弹出,从而实现栈内只有 函数(fn1)的执行上下文,

*按照我的理解,只有进行了尾调用优化的尾调用才算是有实践意义的 ‘尾调用’;
举例说明:
1、没有进行尾调用
大佬文章的函数执行上下文栈中的例子
image
在这个例子中函数最后一步的确是调用 另外一个函数,从概念意义上讲,它是一个尾调用,但是由于调用的这个函数仍然依赖父层函数的变量scope ,所有并不会进行尾调用优化,所以它并不能称之为 有实践意义的 ‘尾调用’(可以理解为就是大佬文中所描述的那样的出入栈规则,)
另外 有些浏览器不支持尾调用优化,如chrome
image

from blog.

Related Issues (20)

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.