在函数的初始化和调用之间重新定义函数

Redefine function between its initialization and invocation

本文关键字:函数 新定义 定义 之间 初始化 调用      更新时间:2023-09-26

为什么警告Initial而不是Redefined:

var inner = function() { alert('Initial'); }
var outer = function() {
    setTimeout(function() { 
       inner = function() { alert('Redefined'); }
    }, 0);
    return inner;
}
var f = outer();
setTimeout(f, 1000);

将超时设置为0并不一定意味着回调将立即运行。控制立即进行到下一行,在那里你返回inner。在这个时间点上,它还没有被重新定义,f指向旧的inner,这就是为什么你看到"Initial"。

把你的代码改成:

var inner = function() { console.log('Initial'); }
var outer = function() {
    setTimeout(function() { 
       inner = function() { console.log('Redefined'); }
    }, 0);
    return inner;
}
outer();
setTimeout(function() { 
    inner(); 
}, 1000);

现在你会看到"Redefined",因为我们调用outer,它将在将来的某个时候重新定义inner。当我们在超时处理程序中调用inner()时,它指向重新定义的版本,因此它将打印" redefined "。

你可能想知道为什么我们不能这样做:

var inner = function() { console.log('Initial'); }
var outer = function() {
    setTimeout(function() { 
       inner = function() { console.log('Redefined'); }
    }, 0);
    return inner;
}
outer();
setTimeout(inner, 1000);

但是你也会因为同样的原因在这里得到"Initial";当setTimeout被调用时,inner仍然指向原始版本

这是因为inner的值是在所有其他JS执行之后设置的。该函数被发送到其他堆栈,并从main堆栈中删除。即使将定时器设置为0 ms;setTimeout函数将在所有其他JS执行时执行。下面是程序的执行方式:

  1. 初始化innerouter
  2. 执行outer,用返回值初始化f

但是outer()是如何被执行的?方法如下:

  1. 命中setTimeout Web API,该API有一个函数应该在0毫秒后执行,但它将被发送到事件循环,并将在稍后执行。其余代码将被执行。
  2. 点击return语句。它将访问inner的值。现在,请记住setTimeout函数还没有执行。因此,该函数将向上移动到原型链并找到innerinner是在Global上下文中定义的。

  3. 最后,outer()返回一个函数给f,该函数由另一个setTimeout执行。setTimeout中的函数也会被发送到事件循环

现在,事件循环有两个函数要调用!其中一个将重置inner的值,另一个将警告"Initial",但是inner的值将立即改变。