一.作用域
当代码在一个环境中执行时,会创建由变量对象构成的一个作用域链,其作用就是保证对执行环境有权访问的所有变量和函数的有序访问。标示符解析是沿着作用域链一级一级的搜索标示符过程,搜索过程从作用域链的前端开始,然后逐级的向后回溯,直到找到标示符为止。
-----------------------------------------------------------------------------------------------
1.常见基本作用域
var color='red';
function firstColor(){
var aColor="blue";
function bColor(){
var xxColor=aColor;
aColor=color;
}
bColor()
}
firstColor();
这里有三个执行环境:全局环境、firstColor和bColor两个局部环境,局部环境中的变量只能在当前环境中访问,但是能访问更高一层的环境,firstColor环境以及全局环境不能访问xxColor 但是bColor能访问全局以及firstColor环境。
-------------------------------------------------------------------------------------------------
2.不易理解的误区
var name="d";
function aa(){
alert(name);
var name='cc';
alert(name);
}
aa();//underfind,cc;
分析:函数在执行时候先在内部作用域去寻找name的变量,由于函数执行是按照顺序执行,第一个alert时候name没有在aa函数找到就会向下接着找,在下面有name的定义,全局变量name=“d”但是执行第一个alert时候name只是就将该变量定义传给了第一个alert中的name,而该变量定义的值并没有传过去。相当于在第一个alert前面声明了一个变量name但是值没有定义。所以第一个alert出来的是underfind。假如替换成
var name="d";
function aa(){
alert(name);
}
aa();//d
在函数执行时候 函数内部作用域找不到name定义,于是就去最近的作用域去找该变量的定义,此时外部变量就将变量以及值都传给了alert所以结果会是"d"。
------------------------------------------------------------------------------------------------
3.改变作用域
利用call或者apply方法(以call举例):
var name="aaa";
var object={name:"bbb"};
function sayName(){
alert(this.name)
}
sayName();//aaa
sayName.call(object);//bbb
利用new实例化一个函数或者对象:
function changeName(name){
this.name=name;
this.sayName=function(){
alert(this.name);
}
}
如果直接changeName("dd").sayName()是会报错的,因为sayName此时是存在于windows全局的。
var newName=new changeName("xx");
newName.sayName()//xx
实例化一个对象(函数也是对象)就是讲构造函数(changeName)作用域赋给新对象(newName),此时this就指向了该新对象,而后在构造函数内为新对象创造属性,最后才返回newName新对象。
====================================================================
二.关键字this
书上说:“this对象是在运行时基于函数的执行环境绑定的”。也就是说单纯的this如果不调用则只代表一个符号,不代表任何对象,当调用时候才会产生一个内部对象,并且该对象的指针指向该实例(作用环境)。在全局函数中,this等于window,而当函数被作为某个对象的方法调用时候,this等于那个对象,匿名函数的执行环境具有全局性,因此this通常指向window(apply可以改变作用环境)
----------------------------------------------------------------------------------------------
var age="12";
var obj={
age:"22",
changeAge:function(){
return function(){
return this.age;
}
}
}
alert(obj.changeAge()())//12
每个函数在被调用时,其活动对象都会获得一个特殊变量this,内部函数在搜索这个变量时候只会搜索到其活动对象为止,因此永远不能直接访问外部函数中的this。这段代码中有一个匿名函数作为闭包想调用obj对象的age属性,而匿名函数活动对象只在changeAge内,因此this就指向了window,所以会输出的值为12.
两种方法可以改变此this作用域:
利用apply改变作用环境:
var age="12";
var obj={
age:"22",
changeAge:function(){
return function(){
return this.age;
}.apply(this)
}
}
alert(obj.changeAge())//22
这个时候应用apply在匿名函数并将匿名函数作用对象指向obj,此时的this.age就是22,该方式由于利用apply修改了匿名函数作用域,相当于被调用一次,所以匿名函数会被执行,因此在调用的时候就不用再去执行匿名函数了。
2. 将外部this保存为闭包能访问的变量:
var age="12";
var obj={
age:"22",
changeAge:function(){
var that=this;
return function(){
return that.age;
};
}
}
alert(obj.changeAge()())//22
这种方式在定义匿名函数之前,将this对象赋值给了一个名叫that的变量,而在定义了闭包之后,闭包也可以访问这个变量(因为它是我们在包含函数中特意声明的一个变量)即使在函数返回之后,that也仍然引用着obj,所以调用obj.changeAge()()也就返回了“22”
-----------------------------------------------------------------------------------------
this用处另一个地方就是js的面向对象以及继承过程中。this与作用域是无法分开的 在作用域的地方也提到了this;
var obj=function(){
this.name="first";
this.sayName=function(){
alert(this.name)
}
}
直接调用obj.sayName()会报错找不到sayName,原因是因为此时的name以及sayName()在实例化之前的this指针是指向window的.
如果直接调用window.sayName()会输出我们期望的结果。将其实例化之后再调用,var a=new obj();a.sayName()就没问题了。
obj.prototype.changeName=function(){ this.name="second"};
这里利用原型访问新建一个方法并改变原型中的name;
调用方法:
var a=new obj();
a.changeName();
alert(a.name)//"second";
delete a.name;
alert(a.name)//"first"
这里实例化原构造函数后,调用changeName相当于在实例函数对象中屏蔽了name的原始属性,而此属性只存在实例中,原型中的name属相并未改变。所以delete之后 a.name还是会顺着原型链找到构造函数中定义的name属性。