JavaScript: this的指向

103 阅读2分钟

本文转自《JavaScript设计模式与开发实践》曾探著 第二章

this的指向大致可以分为以下4种

  1. 作为对象的方法调用
  2. 作为普通函数调用
  3. 构造器调用
  4. Function.prototype.call 或 Function.prototype.apply 调用

1.作为对象的方法调用

当函数作为对象的方法被调用时, this指执向该对象:

var obj = {
    a: 1,
    getA: function() {
        alert(this == obj)      //true
        alert(this.a)           //1
    },
}

obj.getA()

2.作为普通函数调用

当函数不作为对象的属性被调用时, 也就是我们常说的普通函数方式, 此时的this 总是指向全局对象。在浏览器的JavaScript里, 这个全局对象是window对象。

window.name = 'globaName'

var getName = function() {
    return this.name
}

console.log(getName())      //globaName

或者:

window.name = 'globaName'

var myObject = {
    name: 'sven',
    getName: function() {
        return this.name
    },
}

var getName = myObject.getName
console.log(getName())      //globaName

有时候我们会遇到一些困扰, 比如在div节点的事件函数内部, 有一个局部的callback方法,callback被作为普通函数调用时, callback内部的this指向了window, 但我们往往是想让它指向该div节点, 见如下代码:

<html>
    <body>
        <div id="div1"></div>
    </body>
    <script>
    window.id = 'window'
    document.getElementById('div1').onclick = function() {
        alert(this.id)      //'div1'
        var callback = function() {
            alert(this.id)  //'window'
        }
        callback()
    }
    </script>
</html>    

此时有一种简单的解决方案, 可以用一个变量保存div节点的引用:

        document.getElementById('div1').onclick = function() {
            var that = this     //保存div的引用
            var callback = function() {
                alert(that).id)  //'div1'
            }
            callback()
        }

3.构造器调用

JavaScript中没有类, 但是可以从构造器中创建对象, 同时也提供了new运算符, 使得构造器看起来更像一个类。 除了宿主提供的一些内置函数, 大部分JavaScript函数都可以作为构造器使用。构造器 的外表跟普通函数一模一样, 它们的区别在于被调用的方式。当用new运算符调用函数时, 该 函数总会返回一个对象, 通常情况下, 构造器里的this就指向返回的这个对象, 见如下代码:

var MyClass = function() {
    this.name = 'sven'
}
var obj = new MyClass()
alert(obj.name)     //sven

但用new调用构造器时, 还要注意一个问题, 如果构造器显式地返回了一个object类型 的对象, 那么此次运算结果最终会返回这个对象, 而不是我们之前期待的this:

var MyClass = function() {
    this.name = 'sven'
    return {        //显式地返回一个对象
        name: 'anne',
    }
}

var obj = new MyClass()
alert(obj.name)     //anne

如果构造器不显式地返回任何数据, 或者是返回一个非对象类型的数据, 就不会造成 上述问题:

var MyClass = function() {
    this.name = 'sven'
    return name: 'anne'
}

var obj = new MyClass()
alert(obj.name)     //sven

4.Function.prototype.call 或 Function.prototype.apply 调用

跟普通的函数调用相比, 用Function.prototype.call 或Function.prototype.apply 可以动态地改变传入函数的this:

var obj1 = {
    name: 'sven',
    getName: function() {
        return this.name
    },
}

var obj2 = {
    name: 'anne',
}

console.log(obj1.getName())  // sven
console.log(obj1.getName().call(obj2))   // anne

call和apply方法能够很好地体现JavaScript的函数式语言特性, 在JavaScript中, 几乎每一次编写函数式语言风格的代码, 都离不开call和apply。