首先,啥是作用域,作用域就是一个变量可被访问的范围,也就是说,你定义了一个变量,我在哪里可以访问到它,哪里就是它的作用域,全局可以访问就是全局,函数体内可以访问就是函数体内。
然后,es6新加了let和const 我们先不讨论,因为这俩的作用域很好判断,我们讨论var,也就是这个坑最多,面试经常问的东西
我们要明确,var是没有块级作用域的!
var scope="global";
function t(){
console.log(scope);
var scope="local"
console.log(scope);}
猜猜输出啥,输出undefine,那么为啥输出这个呢,中间经过了什么呢
这就要涉及到第一个知识点
var声明的变量会有变量提升,就是会把声明提升到函数内部最前面,赋值操作在原位置
然后就引出了第二个概念函数作用域
就是说,函数内部的变量,在函数体内任何位置都是有定义的!但是只有赋值之后才有值,懂了吧,
我们可以看到,由于函数作用域的特性,局部变量在整个函数体始终是由定义的,我们可以将变量声明”提前“到函数体顶部,同时变量初始化还在原来位置。
为什么说Js没有块级作用域呢,有以下代码为证:
var name="global";
if(true){
var name="local";
console.log(name)
}
console.log(name)
说到这里,有一个非常需要注意的问题,快作用域和函数作用域
var a=10;
function fun()
{
var a=100;
a++;
console.log(a);
}
fun();//101
console.log(a);//10
观察,为什么第一个修改了全局里面的name,但是第二个没有修改全局里面的a呢,因为第一个使if大括号,第二个是函数大括号,有函数作用域,里面定义的变量是独立于函数体外的
你以为加一个大括号就是块级作用域了吗,错,只有放在函数体内,才会实现块级作用域的功能
function t(flag){
if(flag){
s="ifscope";
for(var i=0;i<2;i++)
;
}
console.log(i);
}
t(true);
console.log(s);
将声明s中的var去掉。
程序会报错还是输出“ifscope"呢?
让我揭开谜底吧,会输出:”ifscope"
这主要是Js中没有用var声明的变量都是全局变量,而且是顶层对象的属性。
当使用var声明一个变量时,创建的这个属性是不可配置的,也就是说无法通过delete运算符删除
var name=1 ->不可删除
sex=”girl“ ->可删除
this.age=22 ->可删除
三:作用域链
name="jack";
function t(){
var name="mary";
function show(){
var name="tom";
console.log(name);
}
function shows(){
console.log(name);
}
show();
shows();
}
t();
分析:
当调用t函数show指向时,将自己的函数环境压入栈中,该函数内部有name所以不需要上级索引,直接输出
当调用shows函数时,函数环境入栈,但是本函数里面没有name,所以将上一级函数环境入栈,也就是t函数,使用里面的name,注意不是show函数,而是父函数
这就是作用域链
下面看一个很容易犯错的例子:
Button1 Button2 Button3 当文档加载完毕,给几个按钮注册点击事件,当我们点击按钮时,会弹出什么提示框呢? 很容易犯错,对是的,三个按钮都是弹出:"Button4",你答对了吗?当注册事件结束后,i的值为4,当点击按钮时,事件函数即function(){ alert(“Button”+i);}这个匿名函数中没有i,根据作用域链,所以到buttonInit函数中找,此时i的值为4,
所以弹出”button4“。
四:with语句
说到作用域链,不得不说with语句。with语句主要用来临时扩展作用域链,将语句中的对象添加到作用域的头部。
看下面代码
person={name:“yhb”,age:22,height:175,wife:{name:“lwy”,age:21}};
with(person.wife){
console.log(name);
}
with语句将person.wife添加到当前作用域链的头部,所以输出的就是:“lwy".
with语句结束后,作用域链恢复正常。