《你不知道的JavaScript-上卷》第二部分-this和对象原型-笔记-1-关于this

65 阅读2分钟

1.1 为什么要用this

以在不同的上下文对象中重复使用函数,不用针对每个对象编写不同版本的函数。

示例代码

    function identify() {
      return this.name.toUpperCase();
    }
    function speak() {
      var greeting = "Hello, I'm " + identify.call(this);
      console.log(greeting);
    }
    var me = {
      name: "Kyle"
    };
    var you = {
      name: "Reader"
    };
    identify.call(me); // KYLE
    identify.call(you); // READER
    speak.call(me); // Hello, 我是 KYLE
    speak.call(you); // Hello, 我是 READER

不使用this的情况下,需要给函数传递一个上下文对象,比如

    function identify(context) {
      return context.name.toUpperCase();
    }
    function speak(context) {
      var greeting = "Hello, I'm " + identify(context);
      console.log(greeting);
    }
    var me = {
      name: "Kyle"
    };
    var you = {
      name: "Reader"
    };
    identify(you); // READER
    speak(me); //hello, 我是 KYLE

1.2 误解

1.2.1 指向自身

this 并不指向函数自身

    console.log(window.count) //undefined
    function foo(num) {
      console.log("foo: " + num);
      // 记录 foo 被调用的次数
      this.count++;
    }
    foo.count = 0; // 向foo函数本身添加一个属性count,但是调用时的this并不是指向foo函数
    var i;
    for (i = 0; i < 10; i++) {
      if (i > 5) {
        foo(i);
      }
    }
    // foo: 6
    // foo: 7
    // foo: 8
    // foo: 9
    // foo 被调用了多少次?
    console.log(foo.count); // 0 -- WTF?
    console.log(window.count)//NaN

要从函数内部指向或引用函数自己,必须使用函数自身的标识符

  • 具名函数可以通过名字指向自身
  • 匿名函数无法指向自身

function foo() {
    foo.count = 4; // foo 指向它自身
}

setTimeout( function(){
    // 匿名(没有名字的)函数无法指向自身
}, 10 );

所以,需要解决上面count的问题,有两种方式

一、使用 foo 标识符替代 this 来引用函数对象

function foo(num) {
    console.log( "foo: " + num );
    // 记录 foo 被调用的次数
    foo.count++;
}

foo.count=0
var i;
for (i=0; i<10; i++) {
    if (i > 5) {
        foo( i );
    }
}

// foo: 6
// foo: 7
// foo: 8
// foo: 9
// foo 被调用了多少次?
console.log( foo.count ); // 4

二、强制 this 指向 foo 函数对象,使用call函数

function foo(num) {
    console.log( "foo: " + num );
    // 记录 foo 被调用的次数
    // 注意,在当前的调用方式下(参见下方代码),this 确实指向 foo
    this.count++;
}

foo.count = 0;
var i;
for (i=0; i<10; i++) {
    if (i > 5) {
        // 使用 call(..) 可以确保 this 指向函数对象 foo 本身
        foo.call( foo, i );
    }
}

// foo: 6
// foo: 7
// foo: 8
// foo: 9
// foo 被调用了多少次?
console.log( foo.count ); // 4

1.2.2 它的作用域

需要明确的是,this 在任何情况下都不指向函数的词法作用域。

    function foo() {
      var a = 2;
      console.log(this, this.bar);//window对象,bar函数
      this.bar();
    }

    function bar() {
      console.log(this);//window对象
      console.log(this.a);//undefined
    }

    foo();  

这里和书的行为不一致

  • 书中说this.bar()不能成功,实际上Eage中成功调用了
  • 书中说foo()会抛出引用异常,实际在Eage中打印了undefined

1.3 this到底是什么

this 是在运行时进行绑定的,并不是在编写时绑定,它的上下文取决于函数调用时的各种条件。

  • this 的绑定和函数声明的位置没有任何关系,只取决于函数的调用方式
  • 当一个函数被调用时,会创建一个活动记录(有时候也称为执行上下文)。这个记录会包含函数在哪里被调用(调用栈)、函数的调用方法、传入的参数等信息。this 就是记录的其中一个属性,会在函数执行的过程中用到。