在javascript中,this 表示当前调用对象 ,在函数体内。本博客将介绍如何使用this
this 用法
this 是函数体内自带的一个对象指针,它始终指向调用对象。当函数被调用时,使用this可以访问调用对象,this关键字的使用范围局限于函数体内或调用范围内。
具体用法如下:
1 | this[.属性] |
如果this未包含属性,则传递的是当前对象
this 用法比较灵活,它可以存在与任何位置,它并不仅仅局限于对象的方法内,还可以被应用在全局域内、函数内、以及其它特殊上下文环境中
#
【示例1】 函数的引用和调用
函数的引用和调用分别表示不同的概念。虽然他们都无法改变函数的定义作用域。但是引用函数,却能改变函数的执行作用域,而调用函数是不会改变函数的执行作用域的
1 | var o = { |
在上面实例中,函数中的this所代表的是当前执行域对象o1:
1 | var who = o.ol.me(); |
如果把对象o1的me属性值改为函数调用:
1 | o.o1 = { |
则函数中this 所代表的是定义函数时所在的作用域对象o
1 | var who = o.o1.me; |
【示例2】 使用call ( )和apply( )
call()和apply()方法可以直接改变被执行函数的作用域,使其作用域指向所传递的参数对象,因此,函数中包含的this关键字也指向参数对象
1 |
|
在上面示例中,直接调用函数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 | function f(){ |
【示例3】原型继承
javascript 通过原型模式实现类的延续和继承,如果在父类的成员中包含了this关键字,当子类继承了父类的这些成员时,this的指向就变得很迷惑人
在一般情况下,子类继承父类的方法后,this就会指向子类的实例对象,但是也可能指向子类的原型对象,而不是子类的实例对象
1 | function Base(){ //基类 |
在上面示例中,基类Base包含4个成员 其中成员b和c以不同方式引用当前作用域内m(),
而成员a存储当前作用域内方法m()的调用值。当这些成员继承给子类F后,其中m,b和c成为原型对象的方法,而a成为原型对象的属性。但是,c的值为一个闭包体,当在子类的实例中调用时,实际上它的返回值已经成为实例对象的成员,也就是说,闭包体在哪被调用,则其中包含的this就会指向哪。所以,你会看到f.c()中的this指向实例对象,而不是F类的原型对象。
为了避免因继承关系而影响父类中this所代表的对象,除了通过上面介绍的方法,把方法的引用传递给父类的成员外,我们还可以为父类定义私有函数,然后在把他的引用传递给其他父类成员,这样就避免了因为函数闭包的原因,而改变this的值。
1 | function Base(){ |
这样基类的私有函数_m()就具有完全隐私性,外界其他任何对象都无法直接访问基类的私有函数 _m() 。所以 在一般情况下 定义方法的时候,对于相互依赖的方法 ,可以把它定义私有函数,并以引用方法的当时对外公开,这样就避免了外界对了依赖方法的影响。
【示例4】异步调用之事件处理函数
异步调用就是通过事件机制或计时器来延迟函数的调用时间和时机。通过调用函数的执行作用域不再是原来的定义作用域,所以函数中的this 总是指向引发该事件的对象
1 | <input type='button' value='Button' > |
这里的方法f()所包含的this 不再指向对象o ,而是指向按钮button 因为它是被传递 给按钮 的事件处理函数之后 ,在被调用执行的。函数的执行作用域发生了变化,所以不在指向定义方法时所指定的对象
如果使用DOM 2级标准为按钮注册事件 处理函数:
1 | if(window.attachEvent){//兼容Ie |
函数调用模式
在javascript 中,共有4种函数调用模式:方法调用模式、函数调用模式、构造器调用模式和apply调用模式。这些模式在如何初始化this上存在差异。
提示
1 | 调用运算符是小括号,小括号内可以包含零个或多个用逗号隔开的表达式。每个表达式产生一个参数值。每个参数值被赋予函数声明是定义的形参。当实参的个数与形参的个数不匹配是不会导致运行时错误。如果实参值过多,超出的参数会被忽略。如果实参值过少,缺失的值将会被替换u为undefined 。 不会对参数值进行类型检查,任何类型的值都可以被传递给参数 |
【示例1】方法调用模式
当一个函数被保存为对象的一个属性值时,将称之为一个方法。当一个方法被调用时,this被绑定到当前调用对象
1 | var obj = { |
在上面代码中创建了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 | var obj = { |
【示例3】构造器调用模式
javascript 是基于原型继承的语言,对象可以直接从其他对象继承属性。当今大多数语言都是基于类的语言,虽然原型继承有着强大的表现力,但他偏离可主流用法,并不被广泛理解。javascript为了能够兼容基于类语言的编写风格,提供了一套基于类似类语言的对象构建语法。
如果在一个函数前面加上new 来进行调用,那么创建一个隐藏链接到该函数的prototype
原型对象的新示例对象,同时this将会被绑定到这个新实例对象上。注意:new 也会改变return语句的行为。
1 | var F = function (string){ |
上面代码创建一个名为F的构造函数,此函数构建了一个带有status属性的对象。然后,为F所有实例提供一个名为get 的公共方法。最后,创建一个实例对象,并调用get方法,以读取status属性的值。
结合new前缀调用的函数被称为构造函数,按照约定,构造函数应该保存在以大写格式命名的变量中。如果有调用构造函数时没有在前面加上new,可能会发生非常糟糕的事情,即没有编译时警告,也没有运行时警告,所以大写约定非常重要。
【示例4】apply调用模式
javascript 是函数式的面向对象编程语言,函数可以拥有方法。apply就是函数的一个基本方法,使用这个方法可以调用函数,并修改函数体内的this值。apply 方法包括两个参数 第1个参数设置绑定给this的值,第2个参数是包含函数参数的数组。
1 | var array = [5,4]; |
上面代码构建了一个包含两个数字的数组,然后用apply方法调用add()函数,将数组array中的元素值相加。
1 | var F = function(string){ |
上面的代码构建了一个构造函数F,为该函数定义了一个原型方法get,该方法能够读取当前对象的status属性的值。然后定义一个obj对象,该对象包含了一个status属性,使用apply方法在obj对象上调用构造函数F的get方法,将会返回obj对象的status属性值