ES6中新增了let命令,用来声明变量,和var类似但是也有一定的区别
只能在当前作用域内使用,各个作用域不能互相使用,否则会报错。
{
let a = 1;
var b = 1;
}
console.log(a); // 会报错
console.log(b); // 1
比如说一个函数外声明了一个变量count,在一个函数体内进行修改,然后在if语句中声明了一个相同的变量count。本意是修改外部count,内部使用新定义的count,但是会自动提升,就会发生变化。
var count = 1;
function f1 () {
console.log(count)
if (true) {
var count = 3;
}
}
f1();
代码执行时会转为
var count = 1;
function f1 () {
var count;
console.log(count);
if (true) {
var count = 3;
}
}
f1(); // undefined
当使用for进行循环时,定义变量i会在全局中也能获取到。
外层作用域不会读取内部作用域的变量,各个作用域之间也能定义相同的变量名
在ES5中函数声明只能存在顶级作用域和函数中。但是在ES6中支持在块级作用域内声明,本作用域外不能引用。
对于声明函数来说浏览器可以使用以下规则
如果确实需要在块级作用域内使用函数,可以使用函数表达式的形式,而不是函数声明语句。
使用var声明的变量会自动提升,会提升到函数、全局作用域的头部。意思就是说先使用后声明不会报错,变量值为undefined。
function f1 () {
console.log(num);
var num = 1;
}
// 当执行时代码会转化成
function f1 () {
var num;
console.log(num);
num = 1;
}
这样会导致打印出的结果为undefined,而使用let声明的变量没声明前使用会直接报错。
也就是因为变量不会自动提升,只要在当前作用域内使用let声明某个变量,就会在当前区域进行绑定,在该变量声明之前的区域称之为死区,不能使用,若使用会报错。
function f1 () {
console.log(num);
// 声明以上全部为死区
let num = 1;
}
否则会报错
块级作用域就是以一个花括号中的语句,将多个操作封装起来,但是没有返回值。
因为在块级作用域中,外层作用域是不能使用内部变量,可以使用do表达式返回一个结果
let x = do {
let t = 1;
}
这样变量x就能获取作用域变量t的值为1;
const声明的变量是只读一个常量,一旦声明就不能改变。因为不能改变也就得在声明的时候必须进行赋值,否则会报错。
具有的特点和let类似
const声明的变量并不是值不能改变,而是变量指向的那个内存地址是不能改变的,对于基本数据类型来说,内存地址存的是真实的值,所以对于基本数据来说声明后就不能更改。但是对于复杂数据类型来说内存地址保存的是一个指针,而指针指向的是真实的数据结构,所以只要指针不变,数据内容不管怎么变都行。
其实对于一个使用const声明的变量,假如想修改数组对象赋值成一个新对象时,按正常情况会报错,但是如果还是想赋值的话,可以使用Object.freeze()方法将对象冻结,只是冻结的是对象,如果要新增修改属性需要进一步将属性也冻结。
const foo = {};
// 冻结重新赋值为空对象
const foo = Object.freeze({});
// 常规模式下,不起作用
// 严格模式下,会报错
// 需要将对象的属性也冻结才能正常
foo.prop = 123;
将整个对象以及对象属性冻结
var constantize = (obj) => {
Object.freeze(obj);
Object.keys(obj).forEach((key, i) => {
if(typeof obj[key] === 'object') {
constantize(obj[key]);
}
})
}
在浏览器中的顶层对象指定是window对象,node中的顶层对象是global对象。
在ES5中,全局变量是与顶层对象的属性是等价的。
var a = 1;
console.log(window.a); // 1
但是window对象其实是指的是浏览器窗口对象,所以为了区别开,在ES6中规定,使用let、const声明的全局变量不等于顶层对象的属性,但是使用var声明的属性不变。