YDKJS 作用域和闭包(四)变量提升

先有鸡还是先有蛋?

如下代码:

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

很多开发者可能会认为结果会输出 undefined,因为 var a 在 a = 2 后面,好像变量似乎被重新定义了,所以结果会是默认值 undefined。

然而,正确结果是 2.

下面这段代码,a 在 定义之前被引用。不会抛出错误,也不会输出 2 。

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

结果输出 undefined。

编译器

回忆一下第(一)节中的内容,引擎在解释执行之前会编译你的代码。

所有的声明,包括函数和变量,都会在代码执行之前被处理。

你看到 var a = 2;是一个语句,实际上 JavaScript 会认为这是两个语句,第一个声明 var a 在编译期间被处理,第二个语句赋值操作,留在原处等待执行。

所以上面的第一段代码会被处理成两部分

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

第二段代码实际上也被处理成:

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

这个过程,可以看成是变量和函数的声明被提升到代码的头部,叫做变量提升(Hoisting)

也就是说变量声明在赋值语句之前被执行。

注:只有声明被提升,赋值和其它的操作被留在原处。

foo();

function foo() {
    console.log( a ); // undefined

    var a = 2;
}

上面代码中的函数 foo 的声明被提前,所以这样的调用是可以被执行的。

提升发生在每个作用域内部,所有变量 a 的声明被提升到 foo 的顶部。相当于:

function foo() {
    var a;

    console.log( a ); // undefined

    a = 2;
}

foo();

函数声明被提升了,但是函数表达式不会被提升:

foo(); // not ReferenceError, but TypeError!

var foo = function bar() {
    // ...
};

这里的变量 foo 的声明被提升了,所以对 foo 的 RHS 查询不会报错。但这个时候 foo 的值还是 undefined,所有会报 TypeError 错误。

就算是有命名的函数表达式,这个名称标识也不会被外部作用域访问:

foo(); // TypeError
bar(); // ReferenceError

var foo = function bar() {
    // ...
};

上面这段代码会被解释为类似下面的:

var foo;

foo(); // TypeError
bar(); // ReferenceError

foo = function() {
    var bar = ...self...
    // ...
}

函数优先

函数和变量的声明都会被提升,但是一个细微的区别就是函数声明在前面。

考虑下面的代码:

foo(); // 1

var foo;

function foo() {
    console.log( 1 );
}

foo = function() {
    console.log( 2 );
};

输出结果为1

因为这段代码会被解释为:

function foo() {
    console.log( 1 );
}

foo(); // 1

foo = function() {
    console.log( 2 );
};

var foo 是一个重复的声明,所有被忽略掉了。即时它在函数声明的前面编写,因为函数声明的提升要先于变量声明的提升。

和函数名称重复的变量声明就会被忽略,但如果有多个同名的函数声明,那么后面的声明会覆盖掉前面的:

foo(); // 3

function foo() {
    console.log( 1 );
}

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

function foo() {
    console.log( 3 );
}

在普通的代码块中出现的函数声明也会被提升到外部作用域的顶部。

foo(); // "b"

var a = true;
if (a) {
   function foo() { console.log( "a" ); }
}
else {
   function foo() { console.log( "b" ); }
}

但这种行为并不靠谱,因为有可能将来 JavaScript 的版本会改变这个机制(比如在块中声明的函数将会绑定在块作用域中),所以要避免在块中声明函数。

小结

变量声明提升,函数声明提升,其它操作不变

函数表达式不会被提升

函数声明优先于变量声明

后声明的会覆盖先声明的

原文地址:https://www.cnblogs.com/xiyouchen/p/10315867.html

时间: 2024-07-29 05:31:15

YDKJS 作用域和闭包(四)变量提升的相关文章

初识javascript 闭包和变量提升

先上一小段代码: 1 function outFun(){ 2 var num = 2; 3 function inFun(){ 4 console.log(num); 5 } 6 return inFun; 7 } 8 var out = outFun(); 9 out();//2,这里能够访问,其实是把num这个变量往上面一层提升了一下,out()往里面执行了一层.刚刚好在同一层. 10 console.log(num);//浏览器报错,不能访问outFun函数里面的局部变量num 敲黑板,

YDKJS:作用域与闭包

作用域与闭包 什么是作用域 编译器 理解作用域 嵌套的作用域 词法作用域 词法分析时 欺骗词法作用域 函数与块作用域 函数中的作用域 隐藏标识符于普通作用域 函数作为作用域 块作为作用域 提升 先有鸡还是先有蛋? 编译器再次袭来 函数优先 作用域闭包 启蒙 事实真相 循环 + 闭包 模块 什么是作用域 作用域是一组定义在何处储存变量以及如何访问变量的规则. 编译器 javascript 是编译型语言.但是与传统编译型语言不同,它是边编译边执行的.编译型语言一般从源码到执行会经历三个步骤: 分词/

javascript闭包、变量提升

一.add(2)(3)求和函数(闭包) 1 function add(x) { 2 var a = x; 3 return function(b) { 4 sum = a + b; 5 console.log(sum); 6 } 7 } 8 add(2)(3); //5 二.变量提升 1 //第一种情况 2 3 var a = 100; //全局变量 4 5 function test() { 6 alert(a); //向上搜索全局变量 -->100 7 a = 10; //没有var修饰,所

作用域、变量提升、函数提升、数据类型

一.作用域分类 (作用域范围内定义的变量,整个作用域都可以访问) 1. 全局作用域 使用var声明(或者不严格模式下没有声明)且在函数外定义的变量,其作用域范围是全局的,称其为全局作用域. 2. 函数作用域 使用var声明且在函数内部定义的变量,其作用域范围是整个函数,称其为函数作用域. 3. 块作用域(ES6) 使用let(或者const 常量)声明且在一个花括号(非函数)里面,其作用域范围就是这个花括号以内,称其为块作用域. 二.变量提升 (变量声明会提升至函数或者语句的最前面,位置还在其作

js 作用域链&内存回收&变量&闭包

闭包主要涉及到js的几个其他的特性:作用域链,垃圾(内存)回收机制,函数嵌套,等等 一.作用域链:函数在定义的时候创建的,用于寻找使用到的变量的值的一个索引,而他内部的规则是,把函数自身的本地变量放在最前面,把自身的父级函数中的变量放在其次,把再高一级函数中的变量放在更后面,以此类推直至全局对象为止.当函数中需要查询一个变量的值的时候,js解释器会去作用域链去查找,从最前面的本地变量中先找,如果没有找到对应的变量,则到下一级的链上找,一旦找到了变量,则不再继续.如果找到最后也没找到需要的变量,则

js最基础知识回顾3(字符串拼接,数据类型,变量类型,变量作用域和闭包,运算符,流程控制,)

一.javaScript组成     1.ECMAScript:解释器.翻译 ---------------------------------------------------------几乎没有兼容性问题     2.DOM:Document Object Model --------操作HTML的能力----document--------有一些兼容性问题     3.BOM:Browser Object Model -------------浏览器---------------wind

一篇文章带你了解JavaScript中的函数表达式,递归,闭包,变量,this对象,模块作用域

作者 | Jeskson 来源 | 达达前端小酒馆 定义函数的方式: 第一种为 函数声明: 第二种为 函数表达式. 语法: function functionName(arg0, arg1, arg2) { // 函数体 } 在Firefox,Safari,Chrome和Opera有效: 就是通过这个属性可以访问到这个函数指定的名字. console.log(functionName.name); // 'functionName' 函数声明: 它的一个重要特点就是:函数声明提升,就是在执行代码

JavaScript变量提升及作用域

今天在知乎看前端面试题的时候,看到这样的问题,发现自己懂的真的是太少了,看了给的例子,所以写一下自己的理解. 首先放一段代码: var v= "hello JavaScript"; alert(v); 很明显,这样的是会弹出对话框: 将alert(v)写到一个函数中: var v= "hello JavaScript"; function test(){ alert(v); }; test(); 这样弹出的结果肯定也是和第一个一样:那么下面这种方式输出的结果是什么?

js变量作用域--变量提升

1.JS作用域 在ES5中,js只有两种形式的作用域:全局作用域和函数作用域,在ES6中,新增了一个块级作用域(最近的大括号涵盖的范围),但是仅限于let方式申明的变量. 2.变量声明 1 var x; //变量声明 2 var x=1; //变量声明并赋值 3 x = 1; // 定义全局变量并赋值 3.函数声明 function fn(){}; //函数声明并定义 var fn = function(){}; // 实际上是定义了一个局部变量fn和一个匿名函数,然后把这个匿名函数赋值给了fn