同一个做用域内不能重复声明同一个变量:数组
function func() { let a = 10; var a = 1; } func() // 报错 function func() { let a = 10; let a = 1; } func() // 报错
function func(arg) { let arg; } func() // 不能在函数内部从新声明参数!!!!!由于参数等同于在函数内部var声明的一个局部变量 function func(arg) { { let arg; } } func() // 该例子不报错,是由于在func里面增长{}等同于又新建了一个做用域,所以{}里的arg不是参数arg
let能够只声明不赋值,没有值的时候会输出undefined闭包
console.log(let1) // let1 is not defined let let1; console.log(let1) // undefined let1 = 1; console.log(let1) // 1
var a = []; for (var i = 0; i < 10; i++) { a[i] = function () { console.log(i); }; } a[6](); // 10
该demo的执行过程以下:函数
var a = []; // 全局变量spa
var i = 0; // 全局变量,第1次循环指针
a[0] = function(){ console.log(i) } // 此时的i不是0,是由于这里只是声明了这个函数,并无执行它,即没有建立这个函数的上下文,所以i不会沿着做用域链向外去找i的值code
var i = 1; // 第2次循环,由于是全局变量,所以i = 1会替换上一个i = 0对象
a[1] = function(){ console.log(i) } // 同a[0],全部该函数的执行的做用域是全局做用域blog
var i = 2 // 第3次循环,同理,i = 2会替换上一个i = 1内存
...作用域
var i = 9; // 第10次循环,i = 9会替换上一个i = 8
a[9] = function(){ console.log(i) }
直到var i = 10,10<10不知足循环条件,则跳出循环,继续向下执行全局做用域下面的语句:a[6]();
调用a[6]函数,并建立a[6]函数的函数上下文,执行该函数内部的console.log(i),这个函数中没有i,所以顺着做用域链向外去找i,而此时全局变量var i = 10,所以输出10
var a = []; for (let i = 0; i < 10; i++) { a[i] = function () { console.log(i); }; } a[6](); // 6
第二个demo的执行过程以下:
let a = []; // 全局变量
{ // 第1次循环
let i = 0; // let使得for循环变成一个块级做用域,则let i = 0是这个块级做用域下的局部变量
a[0] = function(){ console.log(i) } // 注意!!!因为let的声明建立了一个块级做用域,此时的a[0]这个函数就是一个闭包
}
{ // 第2次循环
let i = 1; // 注意!!!此时的let i = 1和let i = 0是属于两个不一样的块级做用域,所以二者的值并不会相互影响
a[1] = function(){ console.log(i) } // 同a[0]
}
...
{ // 第10次循环
let i = 9;
a[9] = function(){ console.log(i) } // 一样该函数也是一个闭包!
}
{
直到let i = 10,不知足循环条件,跳出循环,注意!!该代码块中不存在闭包所以,let i = 10在此次循环以后代码块随即被销毁
}
继续向下执行全局做用域下面的语句:a[6]();
调用a[6]函数,进入该块级做用域的代码环境,在该闭包内部找i值,若是没有则顺着做用域链向外去找i,而此时块级做用域内有let i = 6,所以输出6
此时闭包被调用,因此整个代码块中的变量i和a[6]函数被销毁。
在代码块内,凡是用let声明变量以前,该变量都是不可以使用的,这在语法上叫作“暂时性死区”(Temporal Dead Zone,简称TDZ)。因此,凡是在声明以前使用该变量,就会报错!
var tmp = 'ning'; if (true) { // TDZ开始 tmp = 'abc'; // ReferenceError console.log(tmp); // ReferenceError 去掉这两句才能继续向下执行代码并输出响应的结果 let tmp; // TDZ结束 console.log(tmp); // undefined tmp = 123; console.log(tmp); // 123 }
从上面的代码能够看出虽然全局有一个tmp变量,可是在if这个块级做用域下let声明了一个局部变量tmp,致使该局部变量tmp绑定(binding)了这个区域,因此在let声明以前使用它,都属于该tmp的死区,会报错!
在没有let以前,使用typeof是百分之百不会报错的,由于若是typeof一个未声明的变量,会输出'undefined'
可是,暂时性死区的出现使得typeof的使用须要当心!由于若是在let声明前typeof该变量则会报错!
typeof x; // ReferenceError let x;
const声明一个只读的常量。一旦声明其值就不能改变,而且一旦声明变量就必须当即初始化,只声明不赋值就会报错!
console.log(const1) // Missing initializer in const declaration const const1; console.log(const1) // Missing initializer in const declaration
cosnt也是在块级做用域内有效,例子以下:
if (true) { const MAX = 5; } console.log(MAX); // Uncaught ReferenceError: MAX is not defined
const命令声明的常量不能提高,只能在声明的位置以后使用const,例子以下:
if (true) { console.log(MAX); // Uncaught ReferenceError: MAX is not defined const MAX = 5; }
const一样不能重复声明!例子以下:
function func() { const a = 10; var a = 1; } func() // 报错 function func() { const a = 10; let a = 1; } func() // 报错
其实是保证的是变量指向的内存地址保存的数据不能改动!
对于基本数据类型而言,内存地址里保存的就是值,因此等同于常量
而对于引用类型而言(主要是对象和数组),变量指向的内存地址中保存的是 指向实际数据的指针,所以只要该指针是固定不变的,该指针指向的堆内存的数据是否变化const是不在乎的!所以,将一个对象声明为常量必须很是当心!!!!例子以下:
const obj = {}; obj.attr = 'ning'; console.log(obj.attr) // 'ning' obj = { attr: 'li' } console.log(obj.attr) // Uncaught TypeError: Assignment to constant variable.
常量obj存的是一个地址,指向一个对象,不可变的是这个地址,而对象自己里的内容是可变的!