相信自从es6出来之后,你一定多少知道或者已经在项目中实践了部分的块级作用域,在函数或者类的内部命名变量已经在使用let了,但是你知道它真正的作用是什么吗?又是因为什么我们要用这个块级作用域,本文与你一起探讨块级作用域的场景以及所有细节问题。
特点1 :js只有函数级作用域以及全局两种 特点2 :不通过var声明的变量直接赋值也可以用并且可以访问,原理是直接赋值到了window对象属性变量下,两者如果同时定义,那么会覆盖使用,因为访问方式一样,两者都可以通过window属性值访问到。
//通过var 申明的
var a=13
console.log(window.a)//13
a=14
//通过定义到window下
b=13
console.log(b)//13
var b=14
console.log(a)//14
console.log(b)//14
特点 3:var定义的变量与直接window对象属性变量的区别,window属性变量可以通过delete(window.attrname)删除 ,而通过var申明变量只能内存回收,使用时永久有效。(具体文件报错还有显示报错可以自行尝试,没有定义和没有初始化还是有区别的)
var a
console.log(a)//打印undefined,没有初始化值,(申明过的不会直接文件报错)
a=12
delete(window.a) //删除失败 ,仍然可以访问
b = 12
console.log(b)//12
delete(window.b)//返回true,b不可以再被访问
console.log(b)//Uncaught ReferenceError 文件报错
特点4:语句块是没有局部作用域的,在下面的语句块中,与外部是完全一致的
var a=12
if(true){
var a=13
var b=13
console.log("a:"+a)//13
console.log("b:"+b)//13
c=14
console.log("c:"+c)//14
}
console.log("a:"+a)//13
console.log("b:"+b)//13
console.log("c:"+c)//13
特点5 :函数内有局部作用域,函数内可以访问外部作用域,而全局是不可以访问函数作用域内的变量或者方法的
var a=b=c=12
function demo(){
var z=13
console.log(b)//12
console.log(z)//13
}
demo()
console.log(z)//undefined
特点6:函数内部的变量对于其内部的函数或者对象都是可访问的
function demo(){
var z=13
return (function(){
console.log(z)
})()
}
demo()//13
特点7:如果函数内部不加var申明,并且全局定义过的,使用的是全局变量并且改变影响的也是全局变量;如果函数内部不加var申明,并且全局没有定义过的,会定义到window对象属性下并造成全局变量的污染;如果加了var申明 那么使用的是自己的变量,不会影响全局变量也不会造成全局变量污染;
var a=b=c=12
function demo(){
var a=13
b=13
z=15
console.log(a)//13
console.log(b)//13
console.log(c)//12
console.log(z)//15
}
demo()
console.log(a)//12
console.log(b)//13
console.log(c)//12
console.log(z)//15
为了解决特点5中暴露的问题,也就是外部环境不能使用函数内部变量,而我们实际的场景中是需要的,我们利用特点6可以解决,具体的方案就是闭包的方式。 * 代码案例
function package(){
var private_t=123
return (function(){
return private_t
})
}
var t=package()
console.log(t())
如果我们有一个遍历循环的绑定事件,并且需要把当前的指针绑定到对应方法中。
var arr=[]
for(var i=0;i<10;i++){
arr[i]=function(){
console.log(i)
}
}
arr[2]()//10
//不是你预想的2,原因是函数执行时,对应的i已经变成了10而不是函数定义时的2
//解决方案 1 闭包 将当时的变量i当做参数传入函数中
var arr=[]
for(var i=0;i<10;i++){
arr[i]=(function(temp){
return function(){
console.log(temp)
}
})(i)
}
arr[2]()//2
//解决方案 2 块级作用域 ,利用let块级作用域特性,区别就是定义变量时 i是块级变量,所以定义的函数中的变量也是当时的块级作用域,不随外面非块级元素值变化影响
var arr=[]
for(let i=0;i<10;i++){
arr[i]=function(){
console.log(i)
}
}
arr[2]()//1
console.log(i)//undefined
var name='window'
var demo={
name:'project',
sayname:function(){
return function(){
console.log(this.name)
}}
}
var demo2={
name:'project',
sayname:function(){
var that=this
return function(){
console.log(that.name)
}}
}
demo.sayname()()//window
demo2.sayname()()//project
我们经常会遇到经常性的用一个或者多个变量名重名导致的各种问题,包括在循环以及不同的语句块中。他们会互相影响,导致我们不得不重新命名或者细致的区分要不要使用原来的变量做某个事情。
if(true){
let i=2
console.log(i)//2
if(true){
let i=5
console.log(i)//5,使用的是自己区块变量
}
console.log(i)//2,仍然是自己区块变量,内部其他let同名不影响自己
}
if(true){
let i=3
console.log(i)//3,其他同级区块不影响自己
if(true){
console.log(i)//3,子区块可以链式得到父区块变量
i=4
console.log(i)//4 ,子块级直接修改父块级变量
}
console.log(i)//4 ,看到修改了的结果,已经变成4
}
a=12
console.log(a)
var a //这句申明会提前 ,在window环境下不会报错是因为赋值在了window下
//以上代码等效于
var a
a=12
console.log(b)//undefined
var b=12
var j=1;
function demo(){
console.log(j);//提示undefined ,因为布局有定义变量,先声明了变量,而赋值延迟
var j=3;
console.log(j);
}
demo()
建议大家尽量避免变量提升的问题,在变量使用前定义并初始化为你需要的值。