1、let的基本用法以及let和var的区别
(1) let与var一样是用来声明变量的,与var的区别是let所声明的变量,只在let所在的代码块内有效
eg1:
{
let a = 10;
var b = 1;
}
eg2:
for(let i=0;i<5;i++){
console.log(i);//输出1,2,3,4
}
console.log(i);//i is not defined
可以看上面这段代码,也就是说let声明的变量只能在{ }里面获取,在{}外面是获取不到,但是var声明的变量在{}内外都能获取到
(2) let未先声明变量就用变量会编译出错,但是var不会,会输出undefined
另外let不允许在同一作用域下声明已经存在的变量,即在同一作用域下不可以声明两个变量名相同的变量,会直接报错,但是var不会,可见let比var更加规范化
console.log(a);//报错:a is not defined
let a=1;
console.log(a);//undefined
var a=1;
}
(3) let在for循环内作用域问题1
{
for (let i = 0 /* 作用域a */; i < 3; console.log("in for expression", i), i++) {
let i; //这里没有报错,就意味着这里跟作用域与上面a的作用域不同
console.log("in for block", i);
}
(4) let在for循环内作用域问题2
1)关于下面eg1这段代码为什么输出结果是10呢?因为for里面的i的作用域是整个外部区域,所以,当调用a6的时候,其实运行的是console.log(i),而此时因为跑完循环,i的值是10,所以输出10
2)关于eg2输出结果是6,因为在ES标准中,有一段是关于CreatePerIterationEnvironment,也就是for语句每次循环所要建立环境的步骤,里面有提及有关词法环境的相关步骤(LexicalEnvironment),这与使用let时会有关。所以,如果你使用了let而不是var,let的变量除了作用域是在for区块中,而且会为每次循环执行建立新的词法环境(LexicalEnvironment),拷贝所有的变量名称与值到下个循环执行。
eg1使用var:
var a = [];
for (var i = 0; i < 10; i++) {
// 作用域a
a[i] = function () {
// 作用域b
console.log(i);
};
}
a[6](); // 10
eg2使用let:
var a = [];
for (let i = 0; i < 10; i++) {
a[i] = function () {
console.log(i);
};
}
a[6](); // 6
eg2:里面的代码其实就相当于下面这段代码
var a = [];
{ let k;
for (k = 0; k < 10; k++) {
let i = k; //注意这里,每次循环都会创建一个新的i变量
a[i] = function () {
console.log(i);
};
}
}
a[6](); // 6
(5) let的暂时性死区:只要块级作用域内存在let命令,它所声明的变量就“绑定”(binding)这个区域,不再受外部的影响,
死区范围:在let命令声明变量tmp之前,都属于变量tmp的“死区”。
下面这段代码中,声明了一个全局对象tmp,并在if语句体对tmp进行重新赋值。正常来说,该语句是正确的,但是添加了“let tmp”后,运行该程序,就会报错,显示“ReferenceError”。大多数人往往会忽略到第一个声明的tmp变量作用域包裹了第二个tmp
例1:
var tmp = 123;
if (true) {
tmp = 'abc'; // ReferenceError
let tmp;
}
例2:
function bar(x = y, y = 2) {//y还没有定义的时候就使用y
return [x, y];
}
bar(); // 报错
例3:
// 不报错
var x = x;
// 报错
let x = x;
(6)块级作用域
ES6以前变量的作用域是函数范围,有时在函数内局部需要一些临时变量,因为没有块级作用域,所以就会将局部代码封装到IIEF中,这样达到了想要的效果又不引入多余的临时变量。而块作用域引入后,IIEF当然就不必要了!
function f(){
...
swap(var_a,var_b);
(function swap(a,b){
var tmp;
tmp = a;
a = b;
b=tmp;
})(var_a,var_b);
}
如上面的代码,tmp被封装在IIFE中,就不会污染上层函数;而有块级作用域,就不用封装成IIEF,直接放到一个块级中就好。
{
// 块级作用域写法
{
let tmp = ...;
...
}
}
(7)ES6对函数的改革
{
function add(x,y){return x+y};//这是es5中定义函数的写法
var add=(x,y)=>x+y;//es6中我们可以这么搞:
add(1,2);//3,正常运行
var fun1=x=>x+1;
fun1(3);//4,当参数为1个的时候 可以再简单一点,当然复杂函数还是需要括号与大括号的
var fun2=(...args)=>{
for(let arg of args){
console.log(arg)
}
};
fun2(1,2,3);//1,2,3配合rest参数,fun2()里面的实参1,2,3会传到args被当做args的值,...args是es6的扩展运算符,会把参数fun2()传递的实参作为一个数组
}
上面我们提到了扩展运算符,es6扩展运算符,也就是... ,作用是将一个数组转为用逗号分隔的参数序列。
那么问题就来了,我们为啥要用这么奇怪的东东涅,当然因为它能大大提高我们的开发效率。因此,我们可别小看这三个点。下面我们来总结两条这三个小点的用途:
(1)复制数组:
在es5时代,要想复制数组,最容易想到的是通过for循环一个一个push,或者来个slice()的,现在有了扩展运算符,直接一步就能搞定:
var arr = [1,2,3,4,5];
var copy = [...arr];
(2)将类似数组的对象转换为真正的数组
任何类似数组的对象可以用扩展运算符转换为真正的数组。比如:
var newNode= document.querySelectorAll('div');
var array = [...newNode];
Array.isArray(array) //true
var str = 'hello';
var aStr = [...str];
Array.isArray(aStr) //true
(8)关于es6函数声明的行为方式:
1允许在块级作用域内声明函数。
2函数声明类似于var,即会提升到全局作用域或函数作用域的头部。
3同时,函数声明还会提升到所在的块级作用域的头部。
上面的三条规则只对 ES6 的浏览器实现有效,其他环境的实现不用遵守,还是将块级作用域的函数声明当作let处理。
根据这三条规则,在浏览器的 ES6 环境中,块级作用域内声明的函数,行为类似于var声明的变量。
考虑到环境导致的行为差异太大,应该避免在块级作用域内声明函数。如果确实需要,也应该写成函数表达式,而不是函数声明语句。如下
// 函数声明语句
{
let a = 'secret';
function f() {
return a;
}
}
// 函数表达式
{
let a = 'secret';
let f = function () {
return a;
};
}
另外,还有一个需要注意的地方。ES6 的块级作用域允许声明函数的规则,只在使用大括号的情况下成立,如果没有使用大括号,就会报错。
// 不报错
'use strict';
if (true) {
function f() {}
}
// 报错
'use strict';
if (true)
function f() {}
总结:
1、通过var定义的变量,作用域是整个封闭函数,是全域的 。通过let定义的变量,作用域是在块级或是子块中。
2、使用 let 声明的变量,在声明前无法使用,否则将会导致错误,并且不允许重复声明。
3、如果未在 let 语句中初始化您的变量,则将自动为其分配 JavaScript 值 undefined。
4、变量提升:不论var声明的变量处于当前作用域的第几行,都会提升到作用域的头部。
-var 声明的变量会被提升到作用域的顶部并初始化为undefined,而let声明的变量在作用域的顶部未被初始化
5、暂时性死区,在代码块内使用let命令声明变量之前,该变量都是不可用的,不受外部变量影响;
网友评论