var
ES5时作用域分为全局和函数作用域,我们还在使用var定义变量,但var定义的变量会有些奇奇怪怪的现象。
变量提升
console.log(a); // undefined
var a = 1;
程序在定义前访问a变量,按照执行顺序程序应该报未找到变量的错误,但却输出undefined,这里涉及到var变量变量提升的概念,上面例子伪代码相当于
var a;
console.log(a); // undefined
a = 1;
var定义的变量会被提升到所在作用域的顶部
重复定义
var a = 1;
var a = 2;
console.log(a); // 2
var a = '3'
console.log(a); // '3'
可以使用var重复定义同一变量
绑定到window
var a = 1;
console.log(window.a); // 1
浏览器中,全局作用域var定义的变量会绑定到window对象上(Node环境不会绑定global对象),代码里没显示声明,但程序默默的把var变量添加到window对象上了(背地里搞事)
这里补充一下不用任何关键字定义变量的特点
-
不会提升变量
console.log(a); // ReferenceError: a is not defined a = 1 -
会绑定到全局作用域(浏览器和node)
// 浏览器环境 var a = 1; b = 2; console.log(window.a); // 1 console.log(window.b); // 2 // node环境 var a = 1; b = 2; console.log(global.a); // undefined console.log(global.b); // 2 -
可以使用
delete删除全局作用域下的变量,var定义的变量不可以// 浏览器环境 var a = 1; b = 2; console.log(Object.getOwnPropertyDescriptor(window, 'a')); // {value: 1, writable: true, enumerable: true, configurable: false} console.log(Object.getOwnPropertyDescriptor(window, 'b')); // {value: 2, writable: true, enumerable: true, configurable: true}浏览器中,
a、b变量都会绑定到全局作用域,a变量的数据描述符configurable为false,b变量则为true,意味着可以删除b变量,不能删除var定义的a变量var a = 1; b = 2; console.log(window.a); // 1 console.log(window.b); // 2 delete window.a; delete window.b; console.log(window.a); // 1 console.log(window.b); // undefined
let/const
ES6引入了let/const定义变量的关键字,使用这俩关键字定义变量更加符合我们对程序运行的预期。
不会提升变量
console.log(a); // ReferenceError: Cannot access 'a' before initialization
let a = 1;
不能重复定义
let a = 1;
let a = 2; // SyntaxError: Identifier 'a' has already been declared
不绑定全局作用域
// 浏览器环境
let a = 1;
console.log(window.a); // undefined
let、const没有var定义变量的特点,也就是消除了那些奇怪的现象。
引入块级作用域
if(false) {
var a = 1;
}
console.log(a); // undefined
// 伪代码
var a;
if(false) {
a = 1;
}
console.log(a); // undefined
var变量只有全局作用域和函数作用域,因为if不是函数, 又经过变量提升,相当于a定义在全局作用域中,console能访问到a
if(false) {
let a = 1;
}
console.log(a); // ReferenceError: a is not defined
由于块级作用域的隔离,全局作用域下console不能访问a
let和const的区别
-
let定义的变量可以修改,const用于定义常量,其值可以修改,绑定的内存不可以修改const obj = {}; obj.a = 1; // 可以修改值 obj = 1; // TypeError: Assignment to constant variable // 相当于obj指向的内存空间是不可修改的,内存空间内的值可以修改 -
不同的编程思想
这里有一个获取商品满减后价格的方法,用let和const可能会写成这样
// let function getReducePrice(originPrice) { // 原始价格减去店铺优惠 let reducePrice = reduceShopDiscounts(originPrice); // 店铺优惠基础上再减去活动优惠得到最终价格 reducePrice = reduceActivityDiscounts(reducePrice); return reducePrice; } // const function getReducePrice(originPrice) { const shopDiscountsPrice = reduceShopDiscounts(originPrice); const reducePrice = reduceActivityDiscounts(shopDiscountsPrice); return reducePrice; }使用
let时,我们一直在改变reducePrice,可以说这样方式是基于过程的,使用const可以说基于命名,通过命名了解方法整个过程。当然目前使用const命名越来越普遍,我们更加期望变量初始化后不再改变,这样就较容易减少由于变量变化造成bug的现象。