js 中 this 的指向问题

高程上的大前提:

1、this 对象是在运行时基于函数的执行环境绑定的:在全局函数中,this 等于window,而当函数被作为某个对象的方法调用时,this 等于那个对象;不过,匿名函数的执行环境具有全局性,因此其this 对象通常指向window。

2、每个函数在被调用时都会自动取得两个特殊变量:this 和arguments。内部函数在搜索这两个变量时,只会搜索到其活动对象为止,因此永远不可能直接访问外部函数中的这两个变量;

总结:

1、在事件处理程序中,事件绑定在哪个元素身上,this 就指向谁;分析戳这里;

2、在方法调用(如果某个对象的属性是函数,这个属性就叫方法,调用这个属性,就叫方法调用)中,执行函数体的时候,作为属性访问主体的对象和数组便是其调用方法内 this 的指向。(通俗的说,调用谁的方法 this 就指向谁;)

3、普通函数执行里的 this 都是 全局对象,非严格模式下浏览器端指的是 window(严格模式下 undefined,因为全局变量对象里没有定义this),因为都是在全局环境下执行的;

4、任意函数里如果嵌套了 函数,那这个时候 嵌套函数里的 this 在未指定的情况下,应该指向的是 window 对象(浏览器中非严格模式下,严格模式下返回 undefined ,因为嵌套函数里活动对象未指定 this);如果想在嵌套函数中继续用原来的 this 指向,需要保存下,比如 var self = this ,然后在嵌套函数中使用 self 变量,因为 this 不会传递;

这里加个案例:

var arr = [1, 2, 3];
arr.map(function() {
    console.log(this);
});

你觉得这里 this 是 arr 对象么?

不是的,全局环境下,输出三个 window,你可能以为上面的理论错了,其实不是,map 作为 arr 对象的方法,在 map 函数里访问 this 当然是 arr 对象,而这里相当于在 arr.map 函数里又嵌套了一层函数,没指定的情况下,this 当然是 window,这里容易混淆,注意理解;

这里重写了默认的 map 方法,方便理解:

var arr = [1, 2, 3];

arr.map = function(fn) {
    console.log(this); //输出 arr 对象
    fn();    
};

arr.map(function() {
    console.log(this);//输出 window ,全局环境下
});

如果要把 map 里的 this 保存到嵌套的函数里:

var arr = [1, 2, 3];

arr.map = function(fn) {    
    fn.call(this);    // 法一:把 this 作为参数传进去,替换了原来 fn 中的 this
    var self=this; // 法二:保存下来,在嵌套函数里用
    (function () {
        console.log(self);
    }());

};

arr.map(function() {
    console.log(this);
});

这样就输出两次 arr 对象了,call 不会用点这里

5、通过构造函数创建对象,在调用构造函数之前就已经创建了新对象,并将构造函数的作用域赋给了新对象,因此构造函数里的 this 指向的就是这个新对象;

6、with 语句并没有改变 this 的指向,不过表现确实有点奇怪,看截图:

image

7、一些特殊情况,如:逗号运算符,赋值运算符

 1 var name = "The Window";
 2 var object = {
 3     name: "My Object",
 4     getName: function() {
 5         return this.name;
 6     }
 7 };
 8 object.getName(); //"My Object"
 9 (object.getName)(); //"My Object"
10 (object.getName = object.getName)(); //"The Window",在非严格模式下
11 (object.getName, object.getName)(); //"The Window",在非严格模式下

第9行代码解释:

虽然加上括号之后,就好像只是在引用一个函数,但this 的值得到了维持,因为object.getName 和(object.getName)的定义是相同的。

根据 MDN ,圆括号运算符() 用来控制表达式中的运算优先级。所以书上说,没有改变 this,我觉得是说的过去的;

第10行代码解释:

代码先执行了一条赋值语句,然后再调用赋值后的结果。因为这个赋值表达式的值是函数本身,所以this 的值不能得到维持,结果就返回了"The Window"。

我的理解是,赋值表达式返回了内存中的一个值,也就是函数本身,而不是原来方法的引用,所以 this 没有得到维持,结果返回了 window;

第11行代码解释:

同上,因为根据 MDN ,逗号操作符 也是返回最后一个操作数的值,也是函数本身,所以和上面一样;

按照这个逻辑的话,所有返回值操作的,都有可能去改变上面代码中的 this 值,都需要谨慎,譬如:

var a = {
    func: function() {
        return this;
    }
};

function f(fn) {
    console.log(fn());
}

f(a.func);//window

因为函数中的参数也是按照值传递的,在向参数传递引用类型的值时,会把这个值在内存中的地址复制给一个局部变量。

参考资料:

JavaScript权威指南-第6版

JavaScript高级程序设计-第3版

posted @ 2017-12-24 18:16  xianshenglu  阅读(288)  评论(0编辑  收藏  举报