ES6深入学习(一)块级作用域详解

栏目: JavaScript · 发布时间: 5年前

内容简介:JavaScript 中,函数及变量的声明都将被提升到函数的最顶部。 注意:只有声明本身被提升,而任何赋值或者其他执行逻辑都被留在原处。 函数声明会被提升,但函数表达式不会函数声明和变量声明都会被提升,但(可以拥有多个“重复”声明的代码中出现)是,函数会优先被提升。 考虑这段代码:注意var foo2是一个重复的(因此被无视),即使它出现在function foo2()...声明之前。因为函数声明是在普通变量之前被提升的。但后续的声明会覆盖前一个。

JavaScript 中,函数及变量的声明都将被提升到函数的最顶部。 注意:只有声明本身被提升,而任何赋值或者其他执行逻辑都被留在原处。 函数声明会被提升,但函数表达式不会

foo() //Function foo
function foo(){
    console.log('Function foo')
}

foo2() //TypeError: foo2 is not a function
var foo2 = function() {
    console.log('Function foo2')
}
复制代码

函数声明和变量声明都会被提升,但(可以拥有多个“重复”声明的代码中出现)是,函数会优先被提升。 考虑这段代码:

foo2() //2
var foo2 = function() {
console.log(1)
}
function foo2(){
console.log(2)
}
foo2()//1
复制代码

注意var foo2是一个重复的(因此被无视),即使它出现在function foo2()...声明之前。因为函数声明是在普通变量之前被提升的。但后续的声明会覆盖前一个。

var声明及变量提升(Hoisting)机制 在函数作用域或全局作用域中,通过关键字var声明的变量,无论实际上在哪声明的,都会被当成在当前作用域顶部声明的变量。

function getValue(flag){
    //var value,变量value的声明被提升至函数顶部,而初始化操作停留在原处执行
    if(flag){
        var value = "true";  
    }else{
        return null
    }
    //此处可以访问变量value,值为undefined
}
复制代码

块级声明

块级作用域(也被称为词法作用域)存在于函数内部、块中({}之间的区域)

  • let声明的用法与var相同,用let代替var来声明可以把变量的作用域限制在当前代码块中。let声明不会被提升,假设作用域中已经存在某个标识符,在使用let关键字声明则会抛出错误。
var a = 1;
let a = 2;//Uncaught SyntaxError

var b = 3;
if(true){
    let b = 4;//不会抛出错误
}
复制代码
  • const声明一个常量,其值一旦被设定后不可更改,因此通过const声明的常量必须进行初始化。

  • 对于复合类型的变量,const声明不允许修改绑定,但允许修改值。变量名不指向数据,而是指向数据所在的地址,const命令只是保证变量名指向的地址不变,并不能保证该地址的数据不变,急可以修改该对象的属性值。

const b = 'b';
b = 'c';//Uncaught SyntaxError: 

const obj = {};
obj.a = 'a';
console.log(obj);//{a:'a'}
复制代码

临时死区(Temporal Dead Zone)与var不同,let和const声明的变量不会被提升到作用域顶部,如果在声明之前访问这些变量,即使相对安全的typeof操作符也会触发引用错误

if(true){
    console.log(typeof a)//Uncaught ReferenceError
    let a = 1;
}
复制代码

由于console.log(typeof a)语句会抛出错误,因此用let定义并初始化变量a的语句不会被执行,此时a还位于所谓的临时死区(TDZ)中;将let换成const会有相同的效果。 JavaScript引擎在运行代码是发现变量声明时,要么将它们提升至作用域顶部(var声明),要么将声明放到TDZ中(let和const声明)。访问TDZ中的变量会触发运行是的错误,只有执行过变量声明语句后,变量才会从TDZ中移出,然后方可正常访问。

循环中的块作用域绑定

for(var i = 0;i<3;i++){
    console.log(i)//0,1,2
}
console.log(i)//3
//由于var声明提升,变量i在循环结束后仍可以访问。如果换用let声明,循环结束后访问i则会抛出一个错误
复制代码

循环中的函数:

var data = [];
for(var i = 0;i<10;i++){
    data[i] = function(){
        console.log(i)
        return i
    }
}
data[1]();//10
复制代码

**循环里的每次迭代同时共享着变量i,循环内部创建的函数保留了对相同变量的引用,循环结束时变量i的值为10,所以每次调用console.log(i)时就会输出是在10。

为解决这个问题,通常会使用立即调用函数表达式(IIFE),以强制生成计数器变量的副本

var data = [];
for(var i = 0;i<10;i++){
    data[i] = function(){
        console.log(i);
        return i
    }()
}
data[5];//5
复制代码

循环中的let声明let声明模仿上述示例中IIFE所做的一切来简化循环过程,每次迭代都会创建一个新变量,并以之前迭代中同名变量的值将其初始化(即删除IIFE之后仍可得到预期的效果)

var arr = [];
for(let i = 0;i<10;i++) {
    data[i] = function(){
        return i
    }
}
data[1]();//1
复制代码

全局作用域绑定

let和const与var的另一个区别是它们在全局作用域中的行为,当var被用于全局作用域时,会创建一个新的全局变量作为全局对象(浏览器中的window对象)的属性,这意味着var可能会无意覆盖一个已经存在的全局属性

//浏览器中
var RegExp = "hello";
console.log(window.RegExp);//hello
复制代码

即使全局对象RegExp定义在window上,也不能幸免于var声明覆盖。如果使用let或const,会在全局作用一下创建一个新的绑定,但该绑定不会添加为全局对象的属性,即不会覆盖全局变量,只能遮蔽它。

let RegExp = "hello";
console.log(RegExp)//"hello"
console.log(window.RegExp === RegExp);//false,不会破坏全局作用域
复制代码

以上所述就是小编给大家介绍的《ES6深入学习(一)块级作用域详解》,希望对大家有所帮助,如果大家有任何疑问请给我留言,小编会及时回复大家的。在此也非常感谢大家对 码农网 的支持!

查看所有标签

猜你喜欢:

本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们

用户体验要素

用户体验要素

Jesse James Garrett / 范晓燕 / 机械工业出版社 / 2011-7-1 / 39.00元

《用户体验要素:以用户为中心的产品设计(原书第2版)》是AJAX之父Jesse James Garrett的经典之作。本书用简洁的语言系统化地诠释了设计、技术和商业融合是最重要的发展趋势。全书共8章,包括关于用户体验以及为什么它如此重要、认识这些要素、战略层、范围层、结构层、框架层、表现层以及要素的应用。 《用户体验要素:以用户为中心的产品设计(原书第2版)》用清晰的说明和生动的图形分析了以......一起来看看 《用户体验要素》 这本书的介绍吧!

JS 压缩/解压工具
JS 压缩/解压工具

在线压缩/解压 JS 代码

XML 在线格式化
XML 在线格式化

在线 XML 格式化压缩工具

html转js在线工具
html转js在线工具

html转js在线工具