let、块级作用域、const定义变量和var定义变量区别及注意事项
分类:javascript
2.1 let与var定义变量的区别
- let声明变量不存在变量提升。即:变量在声明前使用会报错,使用var声明变量在使用前不会报错,打印为undefined;
console.log(a); //undefined
var a;
console.log(b);
let b;
报错:
index.html:19 Uncaught ReferenceError: Cannot access 'b' before initialization
- let声明变量会造成暂时性死区。即:在使用let声明变量前使用变量,会报错,直到使用let声明后会正常打印
var a = 0 ;
function fun(){
console.log(a);
let a = 3;
}
fun();
报错:在a变量声明前不可使用;这个例子中虽然使用var声明了全局变量a,但是只要在块级作用域中使用let声明了a,那么a就绑定了这个区域,不受外部的影响,在执行console打印之前let未声明a,所以会报错。
index.html:21 Uncaught ReferenceError: Cannot access 'a' before initialization
在块级作用域内,在let声明变量之前,都属于变量的“死区”,在“死区”里,使用typeof判断变量类型会抛出错误,在变量一定要声明后再使用!
- let不允许在相同作用域内重复声明同一个变量。
2.2 块级作用域
没有块级作用域会导致很多问题
- 函数内部变量覆盖函数外部变量。
在fun函数中,原意是要打印函数外部变量a的值,但是因为函数内部定义了a变量,变量提升导致fun函数内的a变量覆盖了外部的a变量,此时console打印的为还未声明的内部a变量。
var a="hello";
function fun() {
console.log(a); // undefined
if(true){
var a="world"
}
}
fun();
- for循环语句中循环变量泄露为全局变量,容易造成内存泄露
var a=[];
for(let i=0;i<6;i++){
a[i]=i;
}
console.log(i)
es6中新增块级作用域。
- 外层作用域无法读取内层作用域内变量。
- 内层作用于可以定义外层作用域的同名变量。
立即执行匿名函数写法:
写法一:
(function (a,b){console.log(a+b)})(3,4) //7
写法二:
(function(a,b){console.log(a+b)}(1,2)) //3
写法三:立即执行函数也可以有返回值
var mynum=(function(a,b){return a+b;}(1,1));
console.log(mynum); //2
一定是表达式才可以被执行符号执行,只要被() 括起来的都是表达式,如下:
(function(){} () )
(function(){})()
var test = function() {}()
立即执行函数常见用法:
function test(){
var arr=[];
for(var i=0;i<10;i++){
(function(j){
arr[j] = function(){
document.write(j+'')
}
})(i)
}
return arr;
}
var myArr = test();
for(var j=0; j<4;j++){
myArr[j]();
}
页面显示 0,1,2,3
在块级作用域中声明函数
function f(){console.log("outside")};
(function () {
if(false){
function f() { console.log("inside") }
}
f()
}())
不用环境运行后主要区别,以上例讲解:
- es5中,输出inside,因为if内声明的函数f提升到函数头部。
- es6中,输出outside,块级作用域内声明的函数类似于let,对作用域之外没有影响。真的在es6浏览器中会报错,因为es6附录中规定,浏览器可以不遵守上面的规定(仅对es6浏览器),使用自己的行为方式。如下:
- 允许块级作用域中声明函数
- 函数声明类似于var,提升到全局作用于或函数作用域头部
- 函数声明还会提升到所在块级作用域头部
es6浏览器中实际运行以下代码:
function f(){console.log("outside")};
(function () {
var f = undefined;
if(false){
function f() { console.log("inside") }
}
f()
}())
在块级作用域中声明函数,不同浏览器会出现不同的结果。所以应该避免在块级作用域内声明函数,如果确实需要,应该写成函数表达式形式。块级作用域中声明函数必须使用大括号 { }
function f(){console.log("outside")};
(function () {
if(false){
let a = function f() { console.log("inside") }
}
f()
}())
//outside
2.3 const声明变量
const声明一个只读的常量。使用const声明变量时必须立即初始化。
const a;
//VM213:1 Uncaught SyntaxError: Missing initializer in const declaration
const和let一样不存在变量提升、存在暂存性死区,只能在声明后使用。和let一样不可重复定义相同变量。
本质:并不是变量的值不能改动,而是变量指向的内存地址不得改动。
- 对于简单类型数据(数值、字符串、布尔值),值就存在变量指向的内存地址中,因此等同于一个常量。
- 对于复合型数据(数组、对象),变量指向的内存地址只是一个指针,const只能保证这个指针是固定的,它指向的数据结构是否可变,不能控制。在使用const定义对象时必须注意。
使用const定义对象,可以添加属性方法、但不可以更改指针地址,如下面obj = { }
const obj = {};
obj.name="hello";
obj={}
// Uncaught TypeError: Assignment to constant variable
冻结对象方法:Object.freeze
const obj = Object.freeze({})