发表于: 2017-04-15 20:32:17

0 1366


JS中this的指向

小课堂【郑州第八十二期】

分享人:王相博

1.背景介绍

在Java语言中,this关键字的含义是明确且具体的,表示当前对象。而在javascript中,this是动态绑定的,它可以是全局对象、当前对象或者任意对象,这完全取决于函数的调用方式。这就导致了this具备了多重含义,可以使得javascript更灵活的使用。但是,带来了灵活性的同时也会给我们初学者带来不少困惑。

2.知识剖析

全局环境中的this

                
                  console.log(this);                
                

总结:在全局作用域中它的 this 执行当前的全局对象(浏览器端是 Window,node 中是 global)

严格模式 ‘use strict’下的this

                
                  'use strict';
                  function test() {
                  console.log(this);
                  };
                  
                  test();
                  
                  // undefined                
                

原因:this 并不会指向全局,而是 undefined,这样的做法是为了消除 js 中一些不严谨的行为

在javascritp中,不一定只有对象方法的上下文中才有this, 全局函数调用和其他的几种不同的上下文中也有this指代。 它可以是全局对象、当前对象或者任意对象,这完全取决于函数的调用方式。JavaScript 中函数的调用有以下几种方式:作为对象方法调用,作为函数调用,作为构造函数调用,和使用 apply 或 call 调用。

1.作为对象方法调用:this 被自然绑定到该对象

                
                var obj = {
                    name: 'qiutc',
                    foo: function() {
                    console.log(this.name);
                    }
                }
                    obj.foo();
                    // 'qiutc'                
            

2.作为函数调用:this被绑定到全局对象

                
                  function test() {
                    console.log(this);
                    };
                    test();
                    // Window                
                

3.作为构造函数调用:this 绑定到新创建的对象上

                
                function Person(name) {
                    this.name = name;
                    console.log(this);
                    }

                    var p = new Person('dawa');

                    // Person {name: "dawa"}                
                

注:构造函数不使用new调用,则和普通函数一样。一般地,构造函数首字母大写

4.使用 apply 或 call 调用:在 JavaScript 中函数也是对象,对象则有方法,apply 和 call 就是函数对象的方法。

                
    function cat(){}
    cat.prototype={
        name:"tom",
        food:"fish",
        say: function(){
            console.log(this.name+" love "+this.food);
        }};
    var blackCat = new cat;
    blackCat.say();
    whiteDog = {name:"dog",food:"bone"};
    blackCat.say.call(whiteDog);
    bird = {food:"fly", name:"blue"};
    blackCat.say.apply(bird);                
                

3、常见问题

                
                  var obj = {
                   name: 'qiutc',
                   foo: function() {
                     console.log(this);
                   },
                   foo2: function() {
                     console.log(this);
                     setTimeout(this.foo, 1000);
                   }
                 }
                 
                 obj.foo2();                
                

现象:两次打印的this不一样

4、解决方案

可以这么这么解决:利用 闭包 的特性来处理

                var obj = {
                      name: 'qiutc',
                      foo: function() {
                        console.log(this);
                      },
                      foo2: function() {
                        console.log(this);
                        var _this = this;
                        setTimeout(function() {
                          console.log(this);  // Window
                    
                          console.log(_this);  // Object {name: "qiutc"}
                        }, 1000);
                      }
                    }
                    
                    obj.foo2();
                

可以看到直接用 this 仍然是 Window;因为 foo2 中的 this 是指向 obj,我们可以先用一个变量 _this 来储存,然后在回调函数中使用 _this,就可以指向当前的这个对象了

执行这段代码我们会发现两次打印出来的 this 是不一样的:
第一次是 foo2 中直接打印 this,这里指向 obj 这个对象,我们毋庸置疑;
但是在 setTimeout 中执行的 this.foo ,却指向了全局对象,这里不是把它当作函数的方法使用吗?这一点经常让很多初学者疑惑;
其实,setTimeout 也只是一个函数而已,函数必然有可能需要参数,我们把 this.foo 当作一个参数传给 setTimeout 这个函数,就像它需要一个 fun 参数,在传入参数的时候,其实做了个这样的操作 fun = this.foo,看到没有,这里我们直接把 fun 指向 this.foo 的引用;执行的时候其实是执行了 fun() 所以已经和 obj 无关了,它是被当作普通函数直接调用的,因此 this 指向全局对象。
这个问题是很多异步回调函数中普遍会碰到的;

5、编码实战

                
    var p = document.getElementsByTagName("p");
    function cat(){}
    cat.prototype={
        name:"tom",
        food:"fish",
        say: function(){
            console.log(this.name+" love "+this.food);
        }};
    var blackCat = new cat;
    blackCat.say();
    whiteDog = {name:"dog",food:"bone"};
    blackCat.say.call(whiteDog);
    bird = {food:"fly", name:"blue"};
    blackCat.say.apply(bird);
    function changeStyle(attr,value){
        this.style[attr] = value;
    }
    var box = document.getElementsByTagName("p");
    window.changeStyle.call(box[1],"borderTop","20px solid black");                
            

6.扩展思考

问题:如何理解this?

当一个函数被调用时,拥有它的object会作为this传入。在全局下,就是window or global,其他时候就是相应的object。 也可以看到,call和apply就是利用这一点实现更改this 值的

分享

                
                 'use strict';

                 function foo() {
                   console.log(this);
                 }

                 setTimeout(foo, 1);

                 // window                
                

现象:加了严格模式,foo 调用也没有指定 this,应该是出来undefined,但是这里仍然出现了全局对象

难道是严格模式失效了吗? 并不,即使在严格模式下,setTimeout 方法在调用传入函数的时候,如果这个函数没有指定了的 this,那么它会做一个隐式的操作—-自动地注入全局上下文,等同于调用 foo.apply(window) 而非 foo(); 当然,如果我们在传入函数的时候已经指定 this,那么就不会被注入全局对象,比如: setTimeout(foo.bind(obj), 1);;

7.参考文献

参考一: 深入浅出 JavaScript 中的 this

参考二: JavaScript 中的 this !

参考三: JavaScript中的this用法与指向

参考四: 如何理解 JavaScript 中的 this 关键字?

8、更多讨论

this含义为何如此丰富?

理解this的指向有何意义?

ppt地址:https://ptteng.github.io/PPT/PPT/js-02-js'this.html#/


返回列表 返回列表
评论

    分享到