javascript函数内部作用域

javascript函数内部作用域,javascript,Javascript,考虑以下代码: function nepaliBuddha() { var a = 20; return function buddhaNepal() { console.log(a); } } var closure = nepaliBuddha(); closure(); // logs 20 现在,当我们调用闭包时,输出是20。这证明内部作用域属性[[scope]]已分配给定义它的内部函数,或者在声明时分配。如果在声明时未分配此属性,则无法

考虑以下代码:

function nepaliBuddha() {
    var a = 20;

    return function buddhaNepal() {
        console.log(a); 
    }
}

var closure = nepaliBuddha();

closure(); // logs 20
现在,当我们调用闭包时,输出是20。这证明内部作用域属性[[scope]]已分配给定义它的内部函数,或者在声明时分配。如果在声明时未分配此属性,则无法记录20,因为它在不同的上下文中被调用

调用闭包函数上下文的作用域链在函数调用时创建,由当前上下文的激活对象或VO以及该函数的内部[[scope]]属性组成

调用还创建[[scope]]属性,这意味着在声明和执行时都会创建内部作用域属性,不是吗

通常定义说[[scope]]属性是在运行时或函数调用时创建的,但事实并非如此,因为[[scope]]属性也已在声明时分配

我认为[[scope]]属性可能在函数执行后得到更新,是吗?请明确定义[[scope]]内部属性。如何以及何时在声明时、执行时或两者同时创建


哇,你不是完全糊涂了吗。好的,我将尽可能简单地解释闭包

首先,我们从作用域开始。有两种类型的作用域:

块范围 功能范围 块作用域在程序中出现时立即开始。另一方面,函数作用域在函数被调用之前不会开始。因此,对同一函数的多次调用会导致创建多个作用域

JavaScript没有块作用域。它只有函数作用域。因此,为了模拟块范围,我们需要创建一个函数表达式并立即执行它。此模式称为an,它看起来如下所示:

(function () {
    // this is the JS equivalent of a block scope
}());
除了块作用域和函数作用域之外,还有另一种对作用域进行分类的方法。因此,我们还有:

词汇范围 动态范围 这种区别只适用于函数作用域,因为块作用域始终是词汇作用域。JavaScript只有词法作用域

为了理解词法范围和动态范围之间的区别,我们需要理解自由变量和绑定变量之间的区别

自由变量是在函数中使用但未在该函数中声明的变量。 在函数中声明的变量称为绑定到该函数。 考虑以下计划:

function add(x, y) {
    return x + y; // x and y are bound to add
}
var count = 0;

function incrementCount() {
    return ++count;
}

(function () {
    var count = 100;
    alert(incrementCount()); // 1
}());
function getCounter() {
    var count = 0;

    return function () {
        return ++count;
    };
}

var counter = getCounter();

alert(counter()); // 1
alert(counter()); // 2
alert(counter()); // 3
在上面的程序中,变量x和y绑定到函数add,因为它们在add中声明

另一方面,以下程序中的变量x和y在函数add中是自由的,因为它们未在add中声明,但在add中使用:

现在自由变量是个问题。它们需要映射到某个值,但哪个值?这就是词法范围和动态范围出现的地方。我不谈主要细节,但你可以

作用域非常类似于原型继承。当一个新的作用域开始时,它从父作用域继承而来,形成一个作用域链,很像JavaScript中的原型链

词法作用域和动态作用域因新作用域继承表单的父作用域而异

在词法作用域中,新的函数作用域继承自定义该函数的作用域,即其词法环境。 在动态作用域中,新函数作用域继承自调用该函数的作用域,即调用作用域。 因为JavaScript只有词法范围,所以我们不需要动态范围。考虑下面的程序:

function add(x, y) {
    return x + y; // x and y are bound to add
}
var count = 0;

function incrementCount() {
    return ++count;
}

(function () {
    var count = 100;
    alert(incrementCount()); // 1
}());
function getCounter() {
    var count = 0;

    return function () {
        return ++count;
    };
}

var counter = getCounter();

alert(counter()); // 1
alert(counter()); // 2
alert(counter()); // 3
这里函数incrementCounter有一个自由变量-count。由于JavaScript具有词法作用域,因此计数将映射到全局变量计数,而不是IIFE中声明的局部计数。因此incrementCount返回1,而不是101

现在闭包只在具有词法范围的语言中起作用。考虑下面的程序:

function add(x, y) {
    return x + y; // x and y are bound to add
}
var count = 0;

function incrementCount() {
    return ++count;
}

(function () {
    var count = 100;
    alert(incrementCount()); // 1
}());
function getCounter() {
    var count = 0;

    return function () {
        return ++count;
    };
}

var counter = getCounter();

alert(counter()); // 1
alert(counter()); // 2
alert(counter()); // 3
在上述程序中,getCounter返回的函数是变量计数的闭包,因为:

变量计数在返回函数(即计数器)中是自由的。 函数被移到声明计数的范围之外。 这两个条件都是函数被称为闭包所必需的。有关更多信息,请阅读以下答案:

现在需要了解的重要一点是,函数计数器仍将被称为闭包,即使它可能永远不会被调用。闭包只是一个函数,它关闭一个变量,该变量称为闭包的upvalue

当我们调用getCounter时,我们创建一个新的作用域,让我们调用这个作用域a,每次调用getCounter返回的函数,即计数器,我们创建一个从作用域a继承的新作用域。仅此而已。没有创建新的闭包。

闭包是一种特殊类型的对象,它结合了两个方面:一是函数,二是它所在的环境 t函数被创建。环境由创建闭包时范围内的任何局部变量组成

现在调用makeFunc

在本例中,myFunc是一个包含displayName函数和创建闭包时存在的Mozilla字符串的闭包

所以,这个范围就是在我调用var myFunc=makeFunc时创建的;makeFunc调用的结果现在是一个闭包。那么,到第一线去哪里

闭包是一种特殊的对象,它结合了两个方面:一个 函数,以及创建该函数的环境

现在考虑这些

function nepaliBuddha() {
    var a = 1;
    return function buddhaNepal() {
        a = a+1;
        console.log(a);  
    }
}
var closure1 = nepaliBuddha(); // An individual scope
var closure2 = nepaliBuddha(); // An individual scope

closure1(); // 1 
closure1(); // 2

closure2(); // 1
closure2(); // 2
closure2(); // 3
也就是说,closure1和closure2都是闭包,它们都有自己的作用域/环境,一旦它们得到了自己的作用域,它们就可以访问自己的作用域。在这种情况下,每次调用nepalifound时,都是在创建闭包并将其交给/保存给变量

范围定义了功能、变量等可用的区域。因此,当您在NepaLiBudd外部函数中定义/声明函数buddhaNepal内部函数时,buddhaNepal内部函数仅与全局范围分离,没有其他内容。它不能访问全局范围内的任何内容,但它有自己的范围,就是这样。尼泊尔佛陀外部功能是佛心内部功能的边界,在这种情况下,尼泊尔佛陀外部功能的局部范围/环境是佛心内部功能的全局范围


在JavaScript中,这称为词法作用域,它定义了如何在嵌套函数中解析变量名。词法作用域的其他名称是静态作用域或闭包。这意味着内部函数的作用域包含父函数的作用域。

我认为闭包不会被创建两次。我认为从定义上讲,它不会被垃圾收集,直到引用它的函数也超出范围。如果你问第二次调用Nephali佛是否会创建另一个作用域,那么是的。它将独立于前面创建的问题。链接问题的可能重复与闭包有关,我要求的是内部范围属性,而不是闭包。您所要求的内容不太清楚。在您最初编写的内容中似乎有几个可能的问题。变量a绑定到nepaliBuddha中的作用域。但除此之外,@Maziere,您似乎正在讨论一些不影响JavaScript解释或执行方式的内部构件。在这种情况下,它可能依赖于实现。如果你不这么认为,为什么不提供一个测试来证明你的观点呢?让我问你一件事,当我们声明任何函数时,它会立即被分配内部作用域属性吗?如果是,内部作用域属性也在运行时或调用函数时分配。我的问题是内部作用域属性被创建了两次。那么我现在应该对我的STD说什么,内部作用域属性何时创建?否。当定义函数时,JavaScript会创建名为[[scope]]的内部属性在包含函数所属词法范围的函数上。例如,当我在全局范围内创建函数时,该函数的[[scope]]属性将设置为全局范围。当我调用一个函数时,JavaScript会创建一个新的作用域激活对象,该对象继承该函数的[[scope]]属性指向的任何对象。调用函数时不会修改[[scope]]属性。它只是用来确定要继承的范围from@Maizere阅读上面的评论。现在从逻辑上考虑一下。当您调用一个函数时,您正在创建一个新的作用域。此作用域必须从另一个作用域继承,但是哪一个?我们需要将函数的词法作用域存储在某个地方,以便在调用函数时可以正确设置新作用域的作用域链。每个函数的词法范围存储在其内部[[scope]]属性中。它在创建函数时只设置一次,从不修改。它是只读的。如果JavaScript具有动态作用域,那么就不需要[[scope]]属性=结束没有什么特别的。它就像其他JavaScript函数一样。然而,闭包比其他函数更有趣,因为闭包即使在其upvalue超出范围后仍保持其活动状态。我曾经认为闭包也很特殊,它们的处理方式与其他函数不同。然而事实并非如此——JavaScript解释器以相同的方式处理每个函数。欲了解更多信息,请阅读以下评论:@AaditMShah,是的,确实如此,闭包只是一个函数,但它位于不同的范围内。