美文网首页JavaScriptWeb前端之路程序员
你不知道的JavaScript之this篇(一)

你不知道的JavaScript之this篇(一)

作者: 2f1b6dfcc208 | 来源:发表于2017-07-21 11:22 被阅读40次

    From 《你不知道的JavaScript》上卷

      this关键字是JavaScript中非常复杂的机制之一,它是一个很特别的关键字,被自动定义在所有函数的作用域中,我们平时会经常用到它,但是往往不求甚解,以致于在很多地方滥用this甚至根本弄不清this的真正含义。

    • 为什么要用this
    function identify(context){
        return context.name.toUpperCase();
    }
    function speak(context){
        var greeting = "hello,I'm "+ identify(context);
        console.log(greeting);
    }
    var me = { name:'ht'}
    var you = {name:'xm'}
    identity(you);//XM
    speak(me);//hello,I'm HT
    

    下面使用this来重写这两个函数

    function identify(){
        return this.name.toUpperCase();
    }
    function speak(){
        var greeting = "hello,I'm "+ identify.calll(this);
        console.log(greeting);
    }
    identify.call(me);//HT
    identify.call(you);//XM
    speak.call(me);//hello,I'm HT
    speak.call(you);hello,I'm XM
    

    通过使用this我们可以在不同的上下文对象中重复使用identify()和speak(),不需要再显示的传入一个上下文对象,也许这个例子太简单了,让你看不出太多的差别,但随着代码的复杂度增加,显示传递上下文会让js代码变得越来越复杂,如果函数可以自动引用合适的上下文对象,我们可以将API设计得更加简洁并且易于复用。

    • 误解
      有两种常见的对于this的解释,但它们都是错误的。
      (1)、指向函数对象自身。人们很容易把this理解成指向函数自身,因为在js中,所有的函数都可以看作对象,错误的认为this便指向当前函数对象,如下
    function foo(num){
        console.log("foo:" + num);
        this.count ++;
    }
    foo.count = 0;
    for(let i=0;i<10;i++){
        if(i>5){
            foo(i);
        }
    };
    //foo:6
    //foo:7
    //foo:8
    //foo:9
    console.log(foo.count);//0
    

    很显然,这里的foo(...)被调用了4次,但是foo.count的值并不是预期的4,而是0,说明了这里的this并不是指向函数自身,显然从字面意思来理解this是错误的。执行 foo.count=0时,的确向函数对象添加了一个属性count,但是函数内部的this.count并不是指向当前函数对象的count属性,虽然属性名相同,根对象却不相同,实际上这里的this指向全局对象。
    如果并不想深究为什么而只是想要解决问题,可以使用其它方式

    function foo(num){
        console.log('foo:'+num);
        data.count++;
    }
    var data = { count:0 }
    for(let i=0;i<10;i++){
        if(i>5){
            foo(i);
        }
    };
    console.log(data.count);//4    
    

    上面使用了词法作用域来解决问题,显然这是有效的,但它避免了使用this,并没有真正理解this的含义和工作原理。如果要从函数对象内部引用它自身,那只使用this是不够的,一般来说需要通过一个指向函数对象的词法标识符(变量)来引用它。

    function foo(num){
        console.log('foo:'+num);
        foo.count++;
    }
    foo.count=0;
    for(let i=0;i<10;i++){
        if(i>5){
             foo(i);
        }
    };
    console.log(foo.count);//4
    

    然而这种方法同样回避了this的问题,并且完全依赖于变量foo的词法作用域。其实还有一种方法是强制this指向foo函数对象。

    function foo(num){
        console.log('foo:' + num);
        this.count++;
    }
    foo.count=0;
    for(let i=0;i<10;i++){
        if(i>5){
            foo.call(foo,i);
        }
    }
    console.log(foo.count);//4
    

    (2)、指向它的作用域。第二种常见的误解是,this指向函数的作用域。这个问题有点复杂,因为在某种情况下它表现是正确的,但是在其它情况下它却是错误的。需要明确的是,this在任何情况下都不指向函数的词法作用域。在JavaScript内部,作用域确实和对象类似,可见的标识符都是它的属性。但是作用域“对象”无法通过JavaScript代码访问,它存在于JavaScript引擎内部。

    • this到底是什么
      this其实是在运行时绑定的,并不是在编写时绑定,它的上下文取决于函数调用时的各种条件。this的绑定和函数声明的位置没有任何关系,只取决于函数的调用方式。
      当一个函数被调用时,会创建一个活动记录(有时候也被称为执行上下文)。这个记录会包含函数在哪里被调用(调用栈)、函数的调用方式、传入的参数等信息,this就是这个记录的一个属性,会在函数执行的过程中用到。总之,this实际上是在函数被调用时发生的绑定,它指向什么完全取决于函数在哪里被调用。

    突然想说一句,js真的是门坑爹的语言~

    相关文章

      网友评论

        本文标题:你不知道的JavaScript之this篇(一)

        本文链接:https://www.haomeiwen.com/subject/ixvskxtx.html