this使用

在javascript中,this 表示当前调用对象 ,在函数体内。本博客将介绍如何使用this

this 用法

this 是函数体内自带的一个对象指针,它始终指向调用对象。当函数被调用时,使用this可以访问调用对象,this关键字的使用范围局限于函数体内或调用范围内。

具体用法如下:

1
this[.属性]

如果this未包含属性,则传递的是当前对象

this 用法比较灵活,它可以存在与任何位置,它并不仅仅局限于对象的方法内,还可以被应用在全局域内、函数内、以及其它特殊上下文环境中

#

【示例1】 函数的引用和调用

函数的引用和调用分别表示不同的概念。虽然他们都无法改变函数的定义作用域。但是引用函数,却能改变函数的执行作用域,而调用函数是不会改变函数的执行作用域的

1
2
3
4
5
6
7
8
9
10
var o = {
name: "对象 o ",
f: function(){
return this;
}
}
o.o1 = {
name:"对象 o1",
me:o.f //引用对象o的方法f
}

在上面实例中,函数中的this所代表的是当前执行域对象o1:

1
2
var who = o.ol.me();
alert(who.name); //返回字符串"对象o1",说明当前this代表对象o1

如果把对象o1的me属性值改为函数调用:

1
2
3
4
o.o1 = {
name:"对象O1",
me:o.f() //调用 对象 o的方法f
}

则函数中this 所代表的是定义函数时所在的作用域对象o

1
2
var who = o.o1.me;
alert(who.name); //返回字符串"对象O" 说明当前this代表对象o

【示例2】 使用call ( )和apply( )

call()和apply()方法可以直接改变被执行函数的作用域,使其作用域指向所传递的参数对象,因此,函数中包含的this关键字也指向参数对象

1
2
3
4
5
6
7
8
9
10
11
12
13
14

function f(){
// 如果当前执行域对象的构造函数等于当前函数,则表示this为实例对象
if(this.constructor == arguments.callee) alert("this==实例对象");
// 如果当前执行域对象等于window 则表示this为window对象
else if (this == window)
alert("this == window 对象");
// 如果当前执行域对象为其他对象 则表示this为其他对象
else
alert("this == 其他对象 \nthis.constructor = " + this.constructor);
}
f(); //this 指向window对象
new f(); //this 指向实例对象
f.call(1); //this指向数值实例对象

在上面示例中,直接调用函数f()时,函数的执行作用域为全局域,所以this代表window。当使用new 调用函数时 ,将创建一个新的实例对象,函数的执行作用域为实例对象所在的上下文,所以this就指向这个新创建的实例对象。而使用call()方法执行函数f()时,call会把函数f()的作用域强制修改为参数对象所在的上下文。由于call()方法的参数值为数字1,则javascript 解释器会把数字1强制封装为数值对象,此时this就会指向这个数值对象。

​ 在以下示例中,call()方法把函数f()强制转换为对象o的一个方法并执行,这样函数f()中this就指向对象o,所以this.x的值就等于1,而this.y的值就等于2 结果就返回3

1
2
3
4
5
6
7
8
function f(){
alert (this.x + this.y);
}
var o = {
x:1,
y:2
}
f.call(o); // 执行函数f() 返回值3

【示例3】原型继承

javascript 通过原型模式实现类的延续和继承,如果在父类的成员中包含了this关键字,当子类继承了父类的这些成员时,this的指向就变得很迷惑人

在一般情况下,子类继承父类的方法后,this就会指向子类的实例对象,但是也可能指向子类的原型对象,而不是子类的实例对象
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
function Base(){ //基类
this.m = function(){ // 基类的方法m()
return "Base";
};
this.a = this.m(); //基类的属性a, 调用当前作用域中m()方法
this.b = this.m; // 基类的属性b,引用当前作用域中m()方法
this.c = function(){
//基类方法c()以闭包结构调用当前作用域中m()方法
return this.m();

}
}
function F(){//子类
this.m = function (){ //子类的方法m()
return "F"
}

}
F.prototype = new Base(); //继承基类
var f = new F(); //实例化子类
alert(f.a); //返回字符串"Base" 说明this.m()中this指向F的原型对象
alert(f.b());//返回字符串"Base",说明this.m()中this指向F的原型对象
alert(f.c());//返回字符串"F" 说明this.m()中this指向 F的实例对象

在上面示例中,基类Base包含4个成员 其中成员b和c以不同方式引用当前作用域内m(),

而成员a存储当前作用域内方法m()的调用值。当这些成员继承给子类F后,其中m,b和c成为原型对象的方法,而a成为原型对象的属性。但是,c的值为一个闭包体,当在子类的实例中调用时,实际上它的返回值已经成为实例对象的成员,也就是说,闭包体在哪被调用,则其中包含的this就会指向哪。所以,你会看到f.c()中的this指向实例对象,而不是F类的原型对象。

​ 为了避免因继承关系而影响父类中this所代表的对象,除了通过上面介绍的方法,把方法的引用传递给父类的成员外,我们还可以为父类定义私有函数,然后在把他的引用传递给其他父类成员,这样就避免了因为函数闭包的原因,而改变this的值。

1
2
3
4
5
6
7
8
function Base(){
var _m = function (){//定义基类的私有函数_m
return "Base";

};
this.a = _m;
this.b = _m();
}

​ 这样基类的私有函数_m()就具有完全隐私性,外界其他任何对象都无法直接访问基类的私有函数 _m() 。所以 在一般情况下 定义方法的时候,对于相互依赖的方法 ,可以把它定义私有函数,并以引用方法的当时对外公开,这样就避免了外界对了依赖方法的影响。

【示例4】异步调用之事件处理函数

​ 异步调用就是通过事件机制或计时器来延迟函数的调用时间和时机。通过调用函数的执行作用域不再是原来的定义作用域,所以函数中的this 总是指向引发该事件的对象

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
<input type='button' value='Button' >
<script language="javascript" type="text/javascript">
var button = document.getElementByTagName('input')[0];
var o = {};
o.f = function (){
if(this == o){
alert("this == 0");
}
if(this == window){
alert("this == window");
}
if(this == button){
alert("this == button");
}

}
button.onclick = o.f;
</script>

这里的方法f()所包含的this 不再指向对象o ,而是指向按钮button 因为它是被传递 给按钮 的事件处理函数之后 ,在被调用执行的。函数的执行作用域发生了变化,所以不在指向定义方法时所指定的对象

​ 如果使用DOM 2级标准为按钮注册事件 处理函数:

1
2
3
4
5
if(window.attachEvent){//兼容Ie
button.attachEvent("onclick",o.f);
}else{//兼容符合DOM 标准的浏览器
button.addEventListener("click",o.f,true);
}

函数调用模式

​ 在javascript 中,共有4种函数调用模式:方法调用模式、函数调用模式、构造器调用模式和apply调用模式。这些模式在如何初始化this上存在差异。

提示

1
调用运算符是小括号,小括号内可以包含零个或多个用逗号隔开的表达式。每个表达式产生一个参数值。每个参数值被赋予函数声明是定义的形参。当实参的个数与形参的个数不匹配是不会导致运行时错误。如果实参值过多,超出的参数会被忽略。如果实参值过少,缺失的值将会被替换u为undefined 。 不会对参数值进行类型检查,任何类型的值都可以被传递给参数

【示例1】方法调用模式

​ 当一个函数被保存为对象的一个属性值时,将称之为一个方法。当一个方法被调用时,this被绑定到当前调用对象

1
2
3
4
5
6
7
8
9
10
var obj = {
value : 0,
increment: function(inc){
this.value += typeof inc === 'number' ? inc: 1;
}
}
obj.increment();
document.writeln(obj.value);//1
obj.increment(2);
document.writeln(obj.value);//3

​ 在上面代码中创建了obj对象,他有一个value属性和一个increment 方法。 increment方法接收一个可选的参数。如果该参数不是数字,那么默认使用数字1

​ increment方法可以使用this去访问对象,所以它能从对象中取值或修改该对象。this到对象的绑定发生在调用的时候。这个延迟绑定使函数可以对this高度复用。通过this可以取得increment方法所属对象的上下文的方法称为公共方法。

【示例2】函数调用模式

当一个函数不是一个对象的属性时,它将被当作一个函数来调用

1
var sum =add(3,4); //7

​ 当函数以此模式调用时,this被绑定到全局对象。这是语言设计上的一个缺陷。如果语言设计正确,当内部函数被调用时,this应该仍绑定到外部函数的this 变量。这个设计错误的后果是方法不能利用内部函数来帮助它工作,因为内部函数的this被绑定了错误的值,所以不能共享该方法对对象的访问权。

​ 解决方案:如果该方案定义一个变量并将它赋值为this ,那么内部函数就可以通过这个变量访问this。 按照约定,将这个变量命名为that

1
2
3
4
5
6
7
8
9
10
11
12
var obj = {
value:1,
doub:function(){
var that = this;
var helper =function(){
that.value = that.value*2
};
helper();
}
}
obj.doub();
document.writeln(obj.value); //2

【示例3】构造器调用模式

​ javascript 是基于原型继承的语言,对象可以直接从其他对象继承属性。当今大多数语言都是基于类的语言,虽然原型继承有着强大的表现力,但他偏离可主流用法,并不被广泛理解。javascript为了能够兼容基于类语言的编写风格,提供了一套基于类似类语言的对象构建语法。

​ 如果在一个函数前面加上new 来进行调用,那么创建一个隐藏链接到该函数的prototype

原型对象的新示例对象,同时this将会被绑定到这个新实例对象上。注意:new 也会改变return语句的行为。

1
2
3
4
5
6
7
8
var F = function (string){
this.status = string
};
F.prototype.get = function(){
return this.status;
};
var f = new F("new object");
document.writeln(f.get());//"new object"

​ 上面代码创建一个名为F的构造函数,此函数构建了一个带有status属性的对象。然后,为F所有实例提供一个名为get 的公共方法。最后,创建一个实例对象,并调用get方法,以读取status属性的值。

​ 结合new前缀调用的函数被称为构造函数,按照约定,构造函数应该保存在以大写格式命名的变量中。如果有调用构造函数时没有在前面加上new,可能会发生非常糟糕的事情,即没有编译时警告,也没有运行时警告,所以大写约定非常重要。

【示例4】apply调用模式

​ javascript 是函数式的面向对象编程语言,函数可以拥有方法。apply就是函数的一个基本方法,使用这个方法可以调用函数,并修改函数体内的this值。apply 方法包括两个参数 第1个参数设置绑定给this的值,第2个参数是包含函数参数的数组。

1
2
3
4
5
6
7
8
9
var array = [5,4];
var add = function (){
var i ,sum = 0;
for(i = 0;i<arguments.length;i+=1){
sum += arguments[i];
}
return sum;
};
var sum = add.apply({},array);

​ 上面代码构建了一个包含两个数字的数组,然后用apply方法调用add()函数,将数组array中的元素值相加。

1
2
3
4
5
6
7
8
9
10
11
var F = function(string){
this.status = string;

};
F.prototype.get = function(){
return this.status;
};
var obj = {
status:"obj"
};
var status = F.prototype.get.apply(obj);//"obj"

上面的代码构建了一个构造函数F,为该函数定义了一个原型方法get,该方法能够读取当前对象的status属性的值。然后定义一个obj对象,该对象包含了一个status属性,使用apply方法在obj对象上调用构造函数F的get方法,将会返回obj对象的status属性值