99

我刚刚阅读了Ben Cherry 撰写的一篇关于 JavaScript 作用域和提升的精彩文章,他在其中给出了以下示例:

var a = 1;

function b() {
    a = 10;
    return;

    function a() {}
}
b();
alert(a);

使用上面的代码,浏览器会提示“1”。

我仍然不确定为什么它返回“1”。他说的一些事情让我想起了:所有的函数声明都被提升到了顶部。您可以使用函数作用域变量。仍然没有为我点击。

4

18 回答 18

131

函数提升意味着函数被移动到其作用域的顶部。那是,

function b() {  
   a = 10;  
   return;  
   function a() {} 
} 

将由interpeter重写为此

function b() {
  function a() {}
  a = 10;
  return;
}

很奇怪,嗯?

此外,在这种情况下,

function a() {}

表现得与

var a = function () {};

所以,本质上,这就是代码正在做的事情:

var a = 1;                 //defines "a" in global scope
function b() {  
   var a = function () {}; //defines "a" in local scope 
   a = 10;                 //overwrites local variable "a"
   return;      
}       
b();       
alert(a);                 //alerts global variable "a"
于 2011-09-21T21:33:14.163 回答
6

您必须记住的是,它会解析整个函数并在执行之前解析所有变量声明。所以....

function a() {} 

真的变成

var a = function () {}

var a强制它进入局部作用域,变量作用域是整个函数,所以全局变量 a 仍然是 1,因为你已经将 a 声明为局部作用域,使其成为一个函数。

于 2011-09-21T21:27:31.883 回答
5

该函数a在函数内部被提升b

var a = 1; 
function b() { 
   function a() {} 
   a = 10; 
   return;
} 
b(); 
alert(a);

这几乎就像使用var

var a = 1; 
function b() { 
   var a = function () {};
   a = 10; 
   return;
} 
b(); 
alert(a);

该函数是在本地声明的,并且设置a只发生在本地范围内,而不是全局变量。

于 2011-09-21T21:30:52.277 回答
3
  1. 函数声明function a(){}首先被提升,它的行为类似于var a = function () {};,因此在本地范围内a创建。
  2. 如果您有两个同名变量(一个在全局另一个在本地),则局部变量始终优先于全局变量。
  3. 设置时a=10,您设置的是局部变量a,而不是全局变量。

因此,全局变量的值保持不变,你会得到,alerted 1

于 2014-05-17T13:43:39.553 回答
1

function a() { }是一个函数语句,它创建一个函数a局部变量b
解析函数时会创建变量,无论是否执行var或函数语句。

a = 10设置这个局部变量。

于 2011-09-21T21:29:46.197 回答
1

吊装是为我们设计的一个概念,让我们更容易理解。实际发生的是声明首先针对其范围完成,然后分配将发生(而不是同时)。

当声明发生时var a,然后function b在该b范围内function a声明。

此函数 a 将隐藏来自全局范围的变量 a。

声明完成后,将开始赋值,全局a将获取值1,内部function b将获取10。当你这样做时alert(a),它将调用实际的全局范围变量。对代码的这个小改动会让它更清晰

        var a = 1;

    function b() {
        a = 10;
        return a;

        function a() { }
    }

    alert(b());
    alert(a);
于 2015-04-14T14:40:13.637 回答
1

这段小代码中争论的焦点是什么?

情况1:

function a(){}在正文中包含定义function b如下。logs value of a = 1

var a = 1;
function b() {
  a = 10;
  return;

  function a() {}
}
b();
console.log(a); // logs a = 1

案例2

排除function a(){}体内的定义function b如下。logs value of a = 10

var a = 1;
function b() {
  a = 10;  // overwrites the value of global 'var a'
  return;
}
b();
console.log(a); // logs a = 10

观察将帮助您意识到该语句console.log(a)记录了以下值。

案例 1: a = 1

案例 2: a = 10

职位

  1. var a已在全局范围内以词法方式定义和声明。
  2. a=10该语句将值重新分配给 10,它在词法上位于函数 b 内。

两种情况的解释

因为function definition with name propertya 与variable a. 内部variable a成为function body b局部变量。上一行暗示 a 的全局值保持不变,而 a 的局部值更新为 10。

所以,我们想说的是下面的代码

var a = 1;
function b() {
  a = 10;
  return;

  function a() {}
}
b();
console.log(a); // logs a = 1

它由 JS 解释器解释如下。

var a = 1;
function b() {
  function a() {}
  a = 10;
  return;


}
b();
console.log(a); // logs a = 1

但是,当我们删除函数 bfunction a(){} definition之外value of 'a'声明和定义的 时,该值被覆盖,并且在情况 2 中更改为 10。该值被覆盖,因为a=10引用了全局声明,如果要在本地声明,我们必须有写var a = 10;的。

var a = 1;
function b() {
  var a = 10; // here var a is declared and defined locally because it uses a var keyword. 
  return;
}
b();
console.log(a); // logs a = 1

name property我们可以通过将in更改function a(){} definition为其他名称来进一步澄清我们的疑问'a'

var a = 1;
function b() {
  a = 10; // here var a is declared and defined locally because it uses a var keyword. 
  return;

  function foo() {}
}
b();
console.log(a); // logs a = 1
于 2017-04-20T16:26:29.890 回答
1

令人惊讶的是,这里的答案都没有提到范围链中执行上下文的相关性。

JavaScript 引擎将当前正在执行的代码包装在一个执行上下文中。基本执行上下文是全局执行上下文。每次调用新函数时,都会创建一个新的执行上下文并将其放入执行堆栈。想想其他编程语言中位于调用堆栈上的堆栈框架。后进先出。现在每个执行上下文在 JavaScript 中都有自己的变量环境和外部环境。

我将使用下面的示例作为演示。

1)首先,我们进入全局执行上下文的创建阶段。创建了词法环境的外部环境和变量环境。全局对象被设置并放置在内存中,特殊变量“this”指向它。函数 a 及其代码以及具有未定义值的变量 myVar 被放置在全局变量环境的内存中。重要的是要注意函数 a 的代码没有被执行。它只是用函数 a 放在内存中。

2)其次,是执行上下文的执行阶段。myVar 不再是未定义的值。它初始化为 1,存储在全局变量环境中。调用函数 a 并创建新的执行上下文。

3)在函数a的Execution Context中,经历了自己的Execution Context的Creation and Execution Phase。它有自己的外部环境和变量环境,因此,有自己的词法环境。函数 b 和变量 myVar 存储在其变量环境中。此变量环境不同于全局变量环境。由于函数 a 在词法上(在代码中物理上)与全局执行上下文处于同一级别,因此它的外部环境是全局执行上下文。因此,如果函数 a 要引用一个不在其变量环境中的变量,它将搜索作用域链并尝试在全局执行上下文的变量环境中查找该变量。

4) 函数 b 在函数 a 中被调用。创建了一个新的执行上下文。由于它在词汇上位于函数 a 中,因此它的外部环境是 a。所以当它引用 myVar 时,由于 myVar 不在函数 b 的变量环境中,它会在函数 a 的变量环境中查找。它在那里找到它并且 console.log 打印 2。但是如果变量不在函数 a 的变量环境中,那么由于函数 a 的外部环境是全局执行上下文,那么作用域链将继续在那里搜索。

5) 函数 b 和 a 执行完毕后,从执行栈中弹出。单线程 JavaScript 引擎在全局执行上下文中继续执行。它调用 b 函数。但是全局变量环境中没有 b 函数,并且在全局执行上下文中没有其他外部环境可以搜索。因此 JavaScript 引擎会引发异常。

function a(){
  function b(){
    console.log(myVar);
  }

  var myVar = 2;
  b();
}

var myVar = 1;
a();
b();
> 2
> Uncaught ReferenceError: b is not defined

下面的示例显示了作用域链的作用。在函数b的执行上下文的变量环境中,没有myVar。所以它搜索它的外部环境,也就是函数a。函数 a 在其变量环境中也没有 myVar。所以 Engine 搜索函数 a 的 Outer Environment,也就是全局 Execution Context 的 Outer Environment,myVar 就是在里面定义的。因此,console.log 打印 1。

function a(){
  function b(){
    console.log(myVar);
  }

  b();
}

var myVar = 1;
a();
> 1

关于执行上下文和与之相关的词法环境,包括外部环境和变量环境,启用 JavaScript 中变量的作用域。即使您多次调用同一个函数,对于每次调用,它都会创建自己的执行上下文。因此,每个执行上下文都会在其变量环境中拥有自己的变量副本。没有共享变量。

于 2019-02-13T15:51:11.220 回答
0

发生这种情况是因为变量名称与函数名称的意思是“a”相同。因此,由于 Javascript 提升,它试图解决命名冲突,它将返回 a = 1。

我也对此感到困惑,直到我读到这篇关于“JavaScript Hoisting”的帖子http://www.ufthelp.com/2014/11/JavaScript-Hoisting.html

希望能帮助到你。

于 2014-11-16T12:55:54.893 回答
0

这是我对答案的回顾,其中包含更多注释和一个伴奏的小提琴。

// hoisting_example.js

// top of scope ie. global var a = 1
var a = 1;

// new scope due to js' functional (not block) level scope
function b() {
    a = 10; // if the function 'a' didn't exist in this scope, global a = 10
  return; // the return illustrates that function 'a' is hoisted to top
  function a(){}; // 'a' will be hoisted to top as var a = function(){};
}

// exec 'b' and you would expect to see a = 10 in subsequent alert
// but the interpreter acutally 'hoisted' the function 'a' within 'b' 
// and in doing so, created a new named variable 'a' 
// which is a function within b's scope
b();

// a will alert 1, see comment above
alert(a);

https://jsfiddle.net/adjavaherian/fffpxjx7/

于 2016-05-09T18:41:35.223 回答
0

scpope & 闭包 & 提升 (var/function)

  1. scpope:全局变量可以在任何地方(整个文件范围)访问,本地变量只能被本地范围(函数/块范围)访问!
    注意:如果局部变量在函数中不使用 var 关键字,它将成为全局变量!
  2. 闭包:另一个函数内部的函数,可以访问本地范围(父函数)和全局范围,但是它的vars不能被其他人访问!除非,您将其作为返回值返回!
  3. 提升:将所有声明/取消声明的变量/函数移动到范围顶部,而不是分配值或 null!
    注意:它只是移动声明,而不是移动值!

var a = 1;                
//"a" is global scope
function b() {  
   var a = function () {}; 
   //"a" is local scope 
   var x = 12; 
   //"x" is local scope 
   a = 10;
   //global variable "a" was overwrited by the local variable "a"  
   console.log("local a =" + a);
   return console.log("local x = " + x);
}       
b();
// local a =10
// local x = 12
console.log("global a = " + a);
// global a = 1
console.log("can't access local x = \n");
// can't access local x = 
console.log(x);
// ReferenceError: x is not defined

于 2016-12-17T14:25:22.120 回答
0

长帖!

但它会清除空气!

Java Script 的工作方式是它涉及两步过程:

  1. 编译(可以这么说) - 这一步注册变量和函数声明及其各自的范围。它不涉及评估函数表达式:var a = function(){}或变量表达式(例如分配给3x在这种情况下var x =3;只不过是对 RHS 部分的评估。)

  2. 解释器:这是执行/评估部分。

检查以下代码的输出以了解:

//b() can be called here!
//c() cannot be called.
console.log("a is " + a);
console.log("b is " + b);
console.log("c is " + c);
var a = 1;
console.log("Now, a is " + a);
var c = function() {};
console.log("Now c is " + c);

function b() {
  //cannot write the below line:
  //console.log(e); 
  //since e is not declared.
  e = 10; //Java script interpreter after traversing from this function scope chain to global scope, is unable to find this variable and eventually initialises it with value 10 in global scope.
  console.log("e is " + e) //  works!
  console.log("f is " + f);
  var f = 7;
  console.log("Now f is " + f);
  console.log("d is " + d);
  return;

  function d() {}
}
b();
console.log(a);

让我们打破它:

  1. 在编译阶段,“a”将在全局范围内注册,值为“ undefined”。' c' 也是如此,此时它的值将是 ' undefined' 而不是 ' function()'。' b' 将被注册为全局范围内的函数。在b' 范围内,' f' 将被注册为此时未定义的变量,并且函数 ' d' 将被注册。

  2. function()当解释器运行时,可以在解释器到达实际的表达式行之前访问声明的变量和(而不是表达式)。因此,变量将被打印 ' undefined' 并且可以更早地调用声明的匿名函数。但是,在表达式初始化之前尝试访问未声明的变量会导致如下错误:

console.log(e)
e = 3;

现在,当你有同名的变量和函数声明时会发生什么。

答案是- 函数总是在之前被提升,如果声明了同名变量,它被视为重复并被忽略。请记住,顺序并不重要。函数总是优先的。但是在评估阶段,您可以将变量引用更改为任何内容(它存储上次分配的内容)看看下面的代码:

var a = 1;
console.log("a is " + a);

function b() {
  console.log("a inside the function b is " + a); //interpreter finds                                'a' as function() in current scope. No need to go outside the scope to find 'a'.
  a = 3; //a changed
  console.log("Now a is " + a);
  return;

  function a() {}
}
var a; //treated as duplicate and ignored.
b();
console.log("a is still " + a + " in global scope"); //This is global scope a.

于 2017-04-21T04:29:54.230 回答
0

提升在 JavaScript 中意味着,变量声明在执行任何代码之前在整个程序中执行。因此,在代码的任何地方声明一个变量就相当于在开头声明它。

于 2018-02-14T22:13:24.973 回答
0

这一切都取决于变量“a”的范围。让我通过将范围创建为图像来进行解释。

在这里,JavaScript 将创建 3 个作用域。

i) 全球范围。ii) 函数 b() 范围。iii) 函数 a() 范围。

在此处输入图像描述

当你调用“alert”方法时,它的作用域是属于全局的,所以它会从全局作用域中选择变量“a”的值,只有 1。

于 2018-02-28T15:51:06.843 回答
0

提升是 JavaScript 的行为概念。提升(比如移动)是解释变量应该如何以及在何处声明的概念。

在 JavaScript 中,变量可以在使用后声明,因为函数声明和变量声明总是被 JavaScript 解释器不可见地移动(“提升”)到其包含范围的顶部。

在大多数情况下,我们会遇到两种类型的吊装。

1.变量声明提升

让我们通过这段代码来理解这一点。

 a = 5; // Assign 5 to a
 elem = document.getElementById("demo"); // Find an element 
 elem.innerHTML = a;                     // Display a in the element
 var a; // Declare a
  //output-> 5

此处变量 a 的声明将在编译时由 javascript 解释器以不可见的方式托管到顶部。所以我们能够得到a的值。但是不推荐这种声明变量的方法,因为我们应该像这样声明变量到顶部。

 var a = 5; // Assign and declare 5 to a
 elem = document.getElementById("demo"); // Find an element 
 elem.innerHTML = a;                     // Display a in the element
  // output -> 5

考虑另一个例子。

  function foo() {
     console.log(x)
     var x = 1;
 }

实际上是这样解释的:

  function foo() {
     var x;
     console.log(x)
     x = 1;
  }

在这种情况下 x 将是未定义的

包含变量声明的代码是否已执行并不重要。考虑这个例子。

  function foo() {
     if (false) {
         var a = 1;
     }
     return;
     var b = 1;
  }

这个函数原来是这样的。

  function foo() {
      var a, b;
      if (false) {
        a = 1;
     }
     return;
     b = 1;
  }

在变量声明中,只有变量定义提升,而不是赋值。

  1. 函数声明提升

与变量提升不同,函数体或赋值也将被提升。考虑这段代码

 function demo() {
     foo(); // this will give error because it is variable hoisting
     bar(); // "this will run!" as it is function hoisting
     var foo = function () {
         alert("this would not run!!");
     }
     function bar() { 
         alert("this will run!!");
     }
 }
 demo();

现在我们了解了变量和函数提升,现在让我们了解这段代码。

var a = 1;
function b() {
  a = 10;
  return;
   function a() {}
}
b();
alert(a);

这段代码会变成这样。

var a = 1;                 //defines "a" in global scope
 function b() {  
   var a = function () {}; //defines "a" in local scope 
    a = 10;                 //overwrites local variable "a"
    return;      
 }       
 b();       
 alert(a); 

函数 a() 将在 b() 内具有局部作用域。a() 将在解释带有其定义的代码时移到顶部(仅在函数提升的情况下),因此 a now 将具有本地范围,因此不会影响 a 的全局范围,而在函数 b() 中有自己的范围.

于 2018-05-01T07:20:31.087 回答
0

据我所知,提升发生在变量声明和函数声明中,例如:

a = 7;
var a;
console.log(a) 

JavaScript 引擎内部发生了什么:

var a;
a = 7;
console.log(a);
// 7

或者:

console.log(square(7)); // Output: 49
function square(n) { return n * n; }

它会变成:

function square(n) { return n * n; }
console.log(square(7)); // 49

但是变量赋值、函数表达式赋值等赋值不会被提升:例如:

console.log(x);
var x = 7; // undefined

可能会变成这样:

var x;
console.log(x); // undefined
x = 7;
于 2019-08-16T15:14:40.343 回答
0

用一句话来描述 javascript 中的托管是将变量和函数提升到声明它们的范围的顶部。

在此处输入图像描述

我假设您是初学者,首先要正确理解提升,我们已经了解undefinedReferenceError之间的区别

 var v;
 console.log(v);
 console.log(abc);
/*
The output of the above codes are:
undefined
ReferenceError: abc is not defined*/

现在在下面的代码中我们看到了什么?变量和函数表达式被清除。

<script>
var totalAmo = 8;
var getSum = function(a, b){
      return a+b;
}
</script>

但是真实的图片证明变量和函数都被提升到了范围的顶部:

console.log(totalAmo);
console.log(getSum(8,9));
var totalAmo = 8;
var getSum = function(a, b){
      return a+b;
}
console.log(totalAmo);
console.log(getSum(9,7));

前两个日志的输出是未定义的并且TypeError: getSum is not a function因为 var totalAmogetSum都被提升到它们的范围顶部,如下所示

 <script>
        var totalAmo;
        var getSum;

        console.log(totalAmo);
        console.log(getSum(8,9));
        var totalAmo = 8;
        var getSum = function(a, b){
            return a+b;
        }
        console.log(totalAmo);
        console.log(getSum(9,7));
    </script>

但是对于函数声明,整个函数都被提升到其作用域的顶部。

console.log(getId());
function getId(){
   return 739373;
}
/* output: 739373, because the whole function hoisted on the top of the scope.*/

现在,同样的逻辑也适用于在函数范围内声明的变量、函数表达式和函数声明。关键点:它们不会被吊在文件的顶部

function functionScope(){
            var totalAmo;
            var getSum;

            console.log(totalAmo);
            console.log(getSum(8,9));
            var totalAmo = 8;
            var getSum = function(a, b){
                return a+b;
            }
        }

因此,当您使用var关键字时,变量和函数会在作用域(全局作用域和函数作用域)的顶部提升。letconst怎么样, const 和 let 仍然像 var 一样知道全局作用域和函数作用域,但是 const 和 let 变量也知道另一个称为阻塞作用域的作用域。只要有代码块,就存在块作用域,例如 for 循环、if else 语句、while 循环等。

当我们使用 const 和 let 在这些块范围内声明一个变量时,变量声明只会被提升到它所在的块的顶部,而不会被提升到父函数的顶部或它被提升的全局范围。

 function getTotal(){
            let total=0;
            for(var i = 0; i<10; i++){
                let valueToAdd = i;
                var multiplier = 2;
                total += valueToAdd*multiplier;
            }
            return total;
        }

abobe 示例中的变量将像下面一样被提升

 function getTotal(){
            let total;
            var multiplier;
            total = 0;
            for(var i = 0; i<10; i++){
                let valueToAdd;
                valueToAdd = i;
                multiplier = 2;
                total += valueToAdd*multiplier;
            }
            return total;
        }
于 2020-05-20T07:04:53.393 回答
0

ES5:函数提升&变量提升

function hoisting优先级greater高于variable hoisting

"use strict";

/**
 *
 * @author xgqfrms
 * @license MIT
 * @copyright xgqfrms
 * @created 2016-06-01
 * @modified
 *
 * @description function-hoisting.js
 * @augments
 * @example
 * @link
 *
 */

(function() {
  const log = console.log;

  var a = 1;
  function b() {
    a = 10;
    log(`local a`, a)
    return;
    // function hoisting priority is greater than variable hoisting
    function a() {}
  }
  b();
  log(`global a`, a);
  // local a 10
  // global a 1
})();



这等于

(function() {
  const log = console.log;

  // define "a" in global scope
  var a = 1;
  function b() {
    // define "a" in local scope
    var a ;
    // assign function to a
    a = function () {};
    // overwrites local variable "a"
    a = 10;
    log(`local a`, a);
    return;
  }

  b();
  // log global variable "a"
  log(`global a`, a);

  // local a 10
  // global a 1
})();

吊装的原因

var a = 1;                
//"a" is global scope
function b() {  
   var a = function () {}; 
   //"a" is local scope 
   var x = 12; 
   //"x" is local scope 
   a = 10;
   //global variable "a" was overwrited by the local variable "a"  
   console.log("local a =" + a);
   return console.log("local x = " + x);
}       
b();
// local a =10
// local x = 12
console.log("global a = " + a);
// global a = 1
console.log("can't access local x = \n");
// can't access local x = 
console.log(x);
// ReferenceError: x is not defined

/**
 *  scpope & closure & hoisting (var/function)
 *  
 * 1. scpope : the global var can be access in any place(the whole file scope), local var only can be accessed by the local scope(function/block scope)!
 * Note: if a local variable not using var keywords in a function, it will become a global variable!
 * 
 * 2. closure : a function inner the other function, which can access local scope(parent function) & global scope, howerver it's vars can't be accessed by others! unless, your return it as return value!
 * 
 * 3. hoisting : move all declare/undeclare vars/function to the scope top, than assign the value or null!
 * Note: it just move the declare, not move the value!
 * 
 */

ES6 let,const不存在提升

(() => {
  const log = console.log;
  log(a)
  // Error: Uncaught ReferenceError: Cannot access 'a' before initialization
  let a = 1;
})();



(() => {
  const log = console.log;
  log(b)
  // Error: Uncaught ReferenceError: Cannot access 'b' before initialization
  const b = 1;
})();

参考

https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/var

https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/let

https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/const

于 2020-06-01T13:21:12.080 回答