解析和整理this的指向问题

    本篇主要总结了this指针的指向问题。如果把问题细分,就可以更容易的得到判断。


    以普通函数的方式调用(Invocation as a function)

    1
    2
    3
    4
    function test(){
    console.log(this);
    }
    test();

    结果是window。结论是当我们以一个函数的方式调用的时候,this就是指向window全局作用域。
    定义在全局的函数, 函数的所有者就是当前页面, 也就是window对象。
    我们可用通过函数名直接调用, 也可用通过window.方法名来调用, 这个时候, 方法中的this关键字指向它的所有者:window对象.

    1
    2
    3
    4
    5
    function a() {
    function b() { console.log(this); }
    b();
    }
    a();

    试试这一个,可能大家跟我一样,如果不仔细看,就会觉得结果是b。但实际上打印出来的还是window对象。

    When invoked in this manner, the function context is the global context—the window object.


    以对象方法来调用(Invocation as a method)

    函数还可以作为某个对象的方法调用,这时this就指这个上级对象。

    1
    2
    3
    4
    5
    6
    var obj = {
    fun : function(){
    console.log(this);
    }
    };
    obj.fun();

    结果是object。结论是当我们以一个对象的方法的形式来调用一个函数,这个时候的上下文就变成了这个对象,this指向的也就是这个对象。

    When we invoke the function as the method of an object, that object becomes the function context and is available within the function via the this parameter.


    以构造函数的形式(Invocation as a constructor)

    通过构造函数可以生成一个新对象,this就指向这个新对象。

    1
    2
    3
    4
    5
    function creep(){
    console.log(this);
    return this;
    }
    new creep();

    结果是creep{}。当我们以构造函数的形式唤醒一个函数的时候,就会发生下面这些事情:一个新的对象会首先被创建,被创建的对象会成为当前构造函数的上下文。

    Invoking a function as a constructor is a powerful feature of JavaScript, because when a constructor is invoked, the following special actions take place:

    * A new empty object is created.
    * This object is passed to the constructor as the this parameter, and thus becomes the constructor’s function context.
    * In the absence of any explicit return value, the new object is returned as the constructor's value.
    

    Call和Apply改变this指向(Invocation with the apply() and call() methods)

    apply(),Apply()是函数对象的一个方法,它的作用是改变函数的调用对象,切换函数执行的上下文环境(context),即 this 绑定的对象。
    Call和Apply的区别在于,apply传递的是数组,call传递的是直接参数量。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    function juggle() {
    var result = 0;
    for (var n = 0; n < arguments.length; n++) {
    result += arguments[n];
    }
    this.result = result;
    }
    var ninja1 = {};
    var ninja2 = {};
    juggle.apply(ninja1,[1,2,3,4]);
    juggle.call(ninja2,5,6,7,8);
    assert(ninja1.result === 10,"juggled via apply");
    assert(ninja2.result === 26,"juggled via call");

    To invoke a function using its apply() method, we pass two parameters to apply(): the object to be used as the function context, and an array of values to be used as the invocation arguments. The call() method is used in a similar manner, except that the arguments are passed directly in the argument list rather than as an array.


    以事件处理函数的方式

    1
    2
    3
    4
    function test(){
    console.log(this.value);
    }
    document.getElementById("ele").onblur = test;

    这样是可以准确的得到input里面的值的,但是为啥这里this不是window对象呢?test是再全局作用域下定义的吗?为啥可以成功的获取到DOM的值呢?
    问题出在onclick事件绑定的时候,在对onclick绑定处理器的时候, 其实是对id为name的输入框Dom对象的onclick属性赋值。
    比如说我们把这个onblur对象打印出来:console.log(document.getElementById(“ele”).onblur);

    this值
    可以看到这个onblur底下是有test函数的。

    比如说下面这个可以得到正确的答案吗?

    1
    2
    3
    4
    function test() {
    alert(this.value);
    }
    <input id="name" type="text" value="test" onclick="test()"/>

    这个时候的alert出来的是undefined。所以这里的this指向全局对象window。

    嵌套函数中的this

    嵌套函数中的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
    24
    25
    26
    27
    28
    //例子1
    var name = "outer";
    var foo3 = {
    name: "inner",
    sayHello: function() {
    console.log(this.name);
    var subSayHello = function() {
    console.log(this.name);
    }
    subSayHello();
    }
    }
    foo3.sayHello(); //先输出inner,后输出outer
    //例子2
    var name = "outer";
    var foo3 = {
    name: "inner",
    sayHello: function() {
    var that = this;
    console.log(that.name);
    var subSayHello = function() {
    console.log(that.name);
    }
    subSayHello();
    }
    }
    foo3.sayHello(); // 都输出inner

    严格模式下的this

    ECMAScript 5 引入了 strict mode ,现在已经被大多浏览器实现(包括IE10. 会使web浏览器更容易的解析代码(只需要添加 “use strict”; 在源码的最上面), 由现有的代码到严格模式的过渡需要一些事做.

    在普通的函数调用f()中,this的值会指向全局对象.在严格模式中,this的值会指向undefined.
    当函数通过call和apply调用时,如果传入的thisvalue参数是一个null和undefined除外的原始值(字符串,数字,布尔值),则this的值会成为那个原始值对应的包装对象,如果thisvalue参数的值是undefined或null,则this的值会指向全局对象.在严格模式中,this的值就是thisvalue参数的值,没有任何类型转换.


    总结

    问题来啦,调用者是谁this就指向谁,这句话对吗???!!!