原创

其实js中的this没那么诡异

戎码一生
1 条评论
18 人喜欢
1032 次阅读
全文共 1217 预计阅读时长 5 分钟

记得初学js的时候,就是被标题中的this搞得头大,一会儿this指向父级Object,一会儿又蛋疼地指向window了

自己也是查阅了一些资料,总结了一些关于this的判断的方法,想分享一下

预热知识

先说一下相关知识点吧

函数调用一般分为四种方式:

  • 简单调用:最为基本的函数调用,函数名前没有任何引导内容

      func()
  • 方法调用:函数作为一个对象的属性时

      obj.func()
  • call/apply:利用call/apply方法改变函数的运行上下文(Context)

      //将func函数里的this指向obj
      func.apply(obj)
      func.call(obj)

    至于call和apply的区别,其实就是接受的参数不一样

  • 构造调用:函数被当作构造器,使用new关键字来创建对象

      var obj = new Func()

How

那么怎么去判断this的值呢?

其实很简单

  1. 如果函数调用为方法调用,则函数内的this为调用该方法的对象

  2. 如果为简单调用,则this为全局对象(global object)

    strict mode下this为undefined
    browser环境下this为window对象,node环境下为global对象

  3. 如果是call.apply方式调用,this为call/apply的第一个参数(eg: obj)

  4. 如果是构造调用,则原构造函数内的this变更为new出的对象

    其实利用构造函数新建对象可以分解为以下三个步骤

     var obj = new Func()
     //等同于
     var obj = {}
    
     //将obj的原型链上游赋值为Func的原型,这是为了obj可以访问到Func的原型上的方法以及属性
     obj.__proto__ = Func.prototype
    
     //利用call/apply改变this,将Func里的this指向obj
     //这样做是目的是如果Func内给this定义了一些属性
     //通过将this指向obj,使得obj也具有与Func相同的属性
     Func.call(obj) //或者Func.apply(obj)

    但是如果构造函数renturn了一个对象,那么new Func()和普通的调用没区别了,例如

     var Func = function(){
         this.name = "BubblyPoker";
         return {
             name: "zhuzhiyang"
         }
     }
     Func.prototype.getName = function(){
         return this.name;
     }
    
     var func = new Func()
     func.name // zhuzhiyang
     func.getName() //TypeError: func.getName is not a function

    可以看到func的name并不是BubblyPoker,这和func = Func()的结果是一样的
    既然是简单调用,那么func自然就没有继承自Func了,那么func的原型链上游就不是Func的原型了,而是Object的原型

通过这四步,可以很轻松地应对有关this的场景。

哦对了,还有一点很重要

this是在函数调用时才设定的,而不是在写代码时就YY出来的

有趣的例子

对于上面提到的all this一文,很全面的从多个方面列出了this的应用场景

  • global
  • function
  • prototype
  • object
  • dom事件
  • html中的事件
  • eval
  • width
  • jquery

其中,有两个例子比较有趣,可以拿来说道一番

  1. prototype this中有这么一段code
function Thing() {
}
Thing.prototype.foo = "bar";
Thing.prototype.logFoo = function () {
    var info = "attempting to log this.foo:";
    function doIt() {
        console.log(info, this.foo);
    }
    doIt();
}

var thing = new Thing();
thing.logFoo();  //logs "attempting to log this.foo: undefined"
  1. prototype this中的另一段code
function Thing() {
}
Thing.prototype.foo = "bar";
Thing.prototype.logFoo = function () {
    var self = this;
    function doIt() {
        console.log(self.foo);
    }
    doIt();
}

function doItIndirectly(method) {
    method();
}

var thing = new Thing();
thing.logFoo(); //logs "bar"
doItIndirectly(thing.logFoo); //logs undefined

我第一眼看到这两个例子时,没有考虑就得出两个code的结果都是logs bar,但是稍加一考虑就发现问题了。

两段代码中的函数调用都是简单调用,所以函数内的this指向的是window(strict mode下是undefined)

对于第一段来讲是很容易判别的,那么对于第二段,你可能有疑问了

doItIndirectly函数传入的参数不是thing.logFoo吗?logFoo应该是是方法调用呀,为什么是简单调用

其实仔细思考一下的话,原因是这样的

函数传参都是值传递,而thing.logFoo的值就是logFoo这个方法在内存中的地址,所以形参method其实
是logFoo方法的地址,所以method()调用时,并没有给这个方法指定上下文,依旧是其调用时简单调用,
所以它内部的this还是指向window或者undefined(strict mode)的

总结

好吧,说了这么多,可以看出其实js中this也是很好判别的嘛,掌握了上述的四种,在绝大部分情况下都可以说得通,对于复杂一些的代码,唯一需要的就是细心+耐心地站在函数调用的角度去分析

以上就是我个人查阅了一些资料后对于js中this的使用判别的总结,这些都是基于我所见过的情况,对于在那些我暂未遇见过的场景中不能使用上述方法判断出this的情况,可以在下方留言或者直接联系我,希望这篇文章能帮助到你。

参考资料

  1. this | MDN
  2. all this
  3. Some of this
相关文章
1 条评论
中国  -  北京市 Mac OS X Chrome | 70

(≧▽≦)/

回复