1、let命令用法:
a、let用来声明变量,类似var,但用let声明的变量,只能在其代码块中引用,相当于私有变量,不会被外界所获取:
function fn(){
let a = 1
console.log(a) 这里可以直接调用
}
console.log(a) 在函数外面访问输出就会报错
以后在for循环中比较适合用let:
var a = [];
for (var i = 0; i < 10; i++)
a[i] = function ();
console.log(i)
}
}
a[6]();这里会输出10
用var声明的i相当于全局变量,在全局范围内都有效,所以全局只有一个变量i
。每一次循环,变量i
的值都会发生改变,而循环内被赋给数组a
的函数内部的console.log(i)
,里面的i
指向的就是全局的i
。也就是说,所有数组a
的成员里面的i
,指向的都是同一个i
,导致运行时输出的是最后一轮的i
的值,也就是10。
把var换成let,最后输出的结果就是6
还有在fou循环中,设置循环的部分是父作用域,在循环体内是子作用域:
for ( let i = 0; i < 10; i++){
let i = 'aaa';
console.log(i)
}
最终输出10次'aaa',这是因为声明在不同块内,相互是不一样的。
b、不存在变量提升了
console.log(a) 输出结果是undefind
var a = 1;
console.log(b) 报错
let b = 1;
这是因为用var声明变量之前调用,因为变量提升的原因,即输出的时候,变量已经存在,但没有给a赋值,所以输出undefined,但用let声明变量只会在声明后存在,之前调用就会报错。
c、暂时性死区
let tem = 'abc';
if(true){
console.log(tem);
let tem;
}
这是就会报错,因为如果在一个块中存在let一个变量,那在let这个变量之前,不能出现这个变量!ES6明确规定,如果区块中存在let
和const
命令,这个区块对这些命令声明的变量,从一开始就形成了封闭作用域。凡是在声明之前就使用这些变量,就会报错。在代码块内,使用let
命令声明变量之前,该变量都是不可用的。这在语法上,称为“暂时性死区”(temporal dead zone,简称 TDZ)。
所以,在这个区域内使用tepyof就不太准确了。比如:
tepyof x;
let x;
这时就会报错,而没有声明的变量反而不会报错:
tepyof obj; undefined
所以一定要在声明变量再使用!!!
let x = x;
这样也是会报错的,使用let
声明变量时,只要变量在还没有声明完成前使用,就会报错。上面这行就属于这个情况,在变量x
的声明语句还没有执行完成前,就去取x
的值,导致报错”x 未定义“。
ES6 规定暂时性死区和let
、const
语句不出现变量提升,主要是为了减少运行时错误,防止在变量声明前就使用这个变量,从而导致意料之外的行为。这样的错误在 ES5 是很常见的,现在有了这种规定,避免此类错误就很容易了。
总之,暂时性死区的本质就是,只要一进入当前作用域,所要使用的变量就已经存在了,但是不可获取,只有等到声明变量的那一行代码出现,才可以获取和使用该变量。
d、不允许重复声明
function fn(){
let a = 1;
var a = 2;(或者let a = 3;)
} 报错
function fn2 (arg){
let arg = 2;
} 报错
这样的操作都会报错,在同一个作用域下,不允许重复声明一个变量,也不允许变量名和参数名相同
2、块级作用域
以前只有全局作用域和函数作用域,现在增加了块级作用域,为什么增加块级作用域?
原因一:内层变量会覆盖掉外层变量
var time = new Date();
function times () {
console.log(time);
if(false){
var time = 'hello';
}
}
times();
这时输出的是undefined;因为变量提升的原因,函数作用域中存在私有变量time,但没有赋值,所以就会覆盖掉全局变量。
原因二:用来计数的循环变量泄漏成全局变量
var str = 'hello';
for (var i = 0; i < str.length; i++){
console.log(str[i]);
}
console.log(i);
在外层也会访问到变量i;这样i就变成来全局变量。
let为javascript提供来块级作用域:
function fn(){
let n = 5;
if(true){
n = 10;
}
console.log(n);
}
这时输出的还是 5 ,虽然内层和外层都定义了n,但外层不受内层的影响。如果换成var,输出结果就是 10
ES6允许块级任意嵌套:
{{{{{let n = 1;}}}}} 这是5层块级作用域,外层的访问不到内层的;
{{{
console.log(n);
{let n = 2;}
}}} 这是就会报错。
{{{
let n = 1;
{let n = 2}
}}} 外层和内层允许存在相同的变量名,相互不受影响。
块级作用域的出现,使得自执行函数(IIFE)不再必要了:
IIFE:
(function () {...})()
块级作用域:
{let...}
do 表达式
本质上,块级作用域是一个语句,将多个操作封装在一起,没有返回值。
{ let t = f(); t = t * t + 1; }
上面代码中,块级作用域将两个语句封装在一起。但是,在块级作用域以外,没有办法得到t
的值,因为块级作用域不返回值,除非t
是全局变量。
现在有一个提案,使得块级作用域可以变为表达式,也就是说可以返回值,办法就是在块级作用域之前加上do
,使它变为do
表达式,然后就会返回内部最后执行的表达式的值。
let x = do { let t = f(); t * t + 1; };
上面代码中,变量x
会得到整个块级作用域的返回值(t * t + 1
)。
3、const基本用法:
const声明常量,一旦声明,其值将不能改变。所以也就意味着,一旦使用const声明变量就要及时赋值,不能先声明再赋值。
a、const和let一样,只在声明的作用域中起作用。
b、同样不提升,只能在声明后才能使用。
c、同样不能重复声明。
4、本质
const的本质就是引用地址不能变,但对于地址内部的信息是可以修改的,尤其是声明的对象和数组这样引用类型的:
const arr = [];
arr.lenght 可以执行;
arr.push(23) 可以执行
arr = ['abs'] 报错
const str = {};
str.prop = 'abc' 可执行
str = {} 指向另一个对象就会报错
如果真的要冻结对象,需要用到 Object.freeze方法
const str = Object.freeze({});
str.prop = 'abc' 在严格模式下就会报错,在非严格模式下不起作用。
除了将对象本身冻结,对象的属性也应该冻结。下面是一个将对象彻底冻结的函数。
var constantize = (obj) => {
Object.freeze(obj);
Object.keys(obj).forEach( (key, i) => {
if ( typeof obj[key] === 'object' ) {
constantize( obj[key] );
}
});
};
ES6除了以前的var和function声明变量外,新增了let和const声明变量,后面还有import和class,后期再补充。
来源:https://www.cnblogs.com/AlvinCs/p/7745168.html