js块级作用域和let,const,var区别
1. 块作用域{ }
JS中作用域有:全局作用域、函数作用域。没有块作用域的概念。ECMAScript 6(简称ES6)中新增了块级作用域。
块作用域由 { } 包括,if语句和for语句里面的{ }也属于块作用域。
我们都知道在javascript里是没有块级作用域的,而ES6添加了块级作用域,块级作用域能带来什么好处呢?为什么会添加这个功能呢?那就得了解ES5没有块级作用域时出现了哪些问题。
ES5在没有块级作用域的情况下出现的问题:
一。在if或者for循环中声明的变量会泄露成全局变量
for(var i=0;i<=5;i++){ console.log("hello"); } console.log(i); //5
二。内层变量可能会覆盖外层变量
var temp = new Date(); function f(){ console.log(temp); if(false){ var temp = "hello"; } } f(); //undefined
不管最后是否执行if语句,都会输出undefined,因为temp会提升到函数顶部,因此覆盖了外部的变量temp。
上一篇介绍的let和const命令,它们所声明的变量只在所在的代码块内有效,即为js添加了块级作用域。
【1】允许块级作用域任意嵌套;
{{{let tmp = "hello world"}}}
【2】外层作用域无法读取内层作用域的变量;
{{{ {let tmp = "hello world";} console.log(tmp); //error }}}
【3】内层作用域可以定义外层作用域的同名变量
{{{ let tmp = "hello world"; {let tmp = "hello world";} }}}
【4】函数本身的作用域在其所在的块级作用域之内。
function f(){ console.log("outside"); } (function(){ if(false){ function f(){ console.log("inside"); } } f(); }());
这段代码如果是在ES5中运行,那么会输出inside,因为在ES5中,函数会提升到作用域的顶部,如果是在ES6中运行,则会输出outside,因为在ES6中函数无法提升,所以访问到的f()是外层的f()。
【5】在ES5中,因为没有块级作用域,获得广泛运用的是立即执行函数。现在ES6增加了块级作用域,那么立即执行函数就不再必要了。ES6以前变量的作用域是函数范围,有时在函数内局部需要一些临时变量,因为没有块级作用域,所以就会将局部代码封装到IIEF(立即执行函数)中,这样达到了想要的效果又不引入多余的临时变量。而块作用域引入后,IIEF当然就不必要了!临时变量被封装在IIFE中,就不会污染上层函数;而有块级作用域,就不用封装成IIEF,直接放到一个块级中就好。更简单的说法是,立即执行匿名函数的目的是建立一个块级作用域,那么现在已经有了真正的块级作用域,所以立即执行匿名函数就不需要了。
//立即执行函数 (function(){ var temp = "hello world"; }()); //块级作用域 { var temp = "hello world"; }
2. var、let、const的区别
- var定义的变量,没有块的概念,可以跨块访问, 不能跨函数访问,有变量提升。
- let定义的变量,只能在块作用域里访问,不能跨块访问,也不能跨函数访问,无变量提升,不可以重复声明。
- const用来定义常量,使用时必须初始化(即必须赋值),只能在块作用域里访问,而且不能修改,无变量提升,不可以重复声明。注意:const常量,指的是常量对应的内存地址不得改变,而不是对应的值不得改变,所有把应用类型的数据设置为常量,其内部的值是可以改变的,例如:const a={}; a.b=13;//不会报错 const arr=[]; arr.push(123);//不会报错
-
let 声明的变量只在块级作用域内有效
'use strict'; function func(args){ if(true){ let i = 6; console.log('inside: ' + i); //不报错 } console.log('outside: ' + i); // 报错 "i is not defined" }; func();
不存在变量提升,而是“绑定”在暂时性死区
// 不存在变量提升 'use strict'; function func(){ console.log(i); let i; }; func(); // 报错
在let声明变量前,使用该变量,它是会报错的,而不是像var那样会‘变量提升’。
其实说let没有‘变量提升’的特性,不太对。或者说它提升了,但是ES6规定了在let声明变量前不能使用该变量。'use strict'; var test = 1; function func(){ console.log(test); let test = 2; }; func(); // 报错
如果let声明的变量没有变量提升,应该打印’1’(func函数外的test);而它却报错,说明它是提升了的,只是规定了不能在其声明之前使用而已。我们称这特性叫“暂时性死区(temporal dead zone)”。且这一特性,仅对遵循‘块级作用域’的命令有效(let、const)。
let使用经典案例:let命令代替闭包功能
闭包实现:var arr = []; for(var i = 0; i < 2; i++){ arr[i] = (function(i){ return function(){ console.log(i); }; }(i)); }; arr[1](); let 实现: 'use strict'; var arr = []; for(let i = 0; i < 2; i++){ arr[i] = function(){ console.log(i); }; }; arr[1]();
剩下 const 命令了!
const 与 let 的使用规范一样,与之不同的是:
const 声明的是一个常量,且这个常量必须赋值,否则会报错。'use strict'; function func(){ const PI; PI = 3.14; console.log(PI); }; func(); // 报错“Missing initializer in const declaration”