JavaScript中this全面解析

 

一、关于this

this关键字是JavaScript中最复杂的机制之一。它是一个很特别的关键字,被自动定义在所有函数的作用域中。

    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,I'm KYLE
speak.call(you); // Hello,I'm READER

上面代码中,可以在不同的上下文对象(me和you)中重复使用函数iedentify()和speak(),不用针对每个对象编写不同版本的函数。

    1.2 this的作用域

this在任何情况下都不指向函数的词法作用域。在JavaScript内部,作用域确实和对象类似,可见的标识符都是它的属性。但是作用域“对象”无法通过JavaScript代码访问,它存在于JavaScript引擎内部。

    1.3 this到底是什么?

this是在运行时进行绑定的,并不是在编写时绑定,它的上下文取决于函数调用时的各种条件。this的绑定和函数声明的位置没有任何关系,只取决于函数的调用方式。

当一个函数被调用时,会创建一个活动记录(有时候也成为执行上下文)。这个记录会包含函数在哪里被调用(调用栈)、函数的调用方法、传入的参数等信息。this就是记录的其中一个属性,会在函数执行的过程中用到。

二、this全面解析

    2.1 this的四种绑定规则

       1. 默认绑定

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

var a = 2;
foo(); // 2

在上面代码中,函数foo和变量a同在全局作用域中声明,调用函数foo时,直接使用不带任何修饰的函数引用进行调用的,因此只能使用默认绑定。当调用函数foo时,this.a被解析成了全局变量a。

        2. 隐式绑定

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

var obj = {
  a: 2,
  foo: foo
};

obj.foo(); // 2

上述代码中,函数foo被当作引用属性添加到obj中,foo不属于obj对象,但当foo()被调用时,它的落脚点确实指向obj对象。根据隐式绑定规则,会把函数调用中的this绑定到这个上下文对象。所以调用this.a得到的值等于obj.a。

        3. 显式绑定

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

var obj = {
  a: 2
};

foo.call(obj); // 2

上述代码中,通过foo.call(...),可以在调用foo时强制把它的this绑定到obj上。

        4. new绑定

使用new来调用函数,或者说发生构造函数调用时,会自动执行下面的操作:

    1.建一个全新的对象;

    2.这个新对象会被执行[[原型]]连接;

    3.这个新对象会绑定到函数调用的this;

    4.如果函数没有返回其他对象,那么new表达式中的函数调用会自动返回这个新对象;

下面来看一段代码:

function foo(a) {
  this.a = a;
}

var bar = new foo(2);
console.log(bar.a);

使用new来调用foo(...)时,我们会构造一个新对象并把它绑定到foo(...)调用中的this上。

    2.2 this词法(ES6箭头函数)

ES6中的箭头函数不使用this的四种标准规则,而是根据外层(函数或者全局)作用域来决定this。

下面我们来通过一段代码看看箭头函数的词法作用域:

function foo() {
  // 返回一个箭头函数
  return (a) => {
    // this继承自foo()
    console.log(this.a);
  };
}

var obj1 = {
  a: 2
};

var obj2 = {
  a: 3
};

var bar = foo.call(obj1);
bar.call(obj2); // 2

上述代码中,foo()内部创建的箭头函数会捕获调用时foo()的this。由于foo()的this绑定到obj1,bar(引用箭头函数)的this也会绑定到obj1,箭头函数的绑定无法被修改。

 

猜你喜欢

转载自blog.csdn.net/liya_nan/article/details/80843814