this,即当前执行代码的环境对象。

换句话说,执行的每个JavaScript函数都有对其当前执行上下文的引用,称为this

一、执行上下文

执行上下文代表函数的调用方式,大多数情况下,函数的调用方式决定了this的值。即,执行上下文决定了this的值。

要理解这个关键字this只需要this取决于函数的调用方式,跟函数声明以及声明位置没关系

比如看下面这段代码:

function person() {    console.log(this.name);}let name = "sueRimn";let one = {name: "八至", age: 22};let two = {name: "小九", age: 22};person();     // "sueRimn"one.person(); // "八至"two.person(); // "小九"

代码中函数person()的执行任务是打印出this.name,即打印当前执行上下文的name属性的值。

当函数person()没有被调用时,执行上下文也没有指定,默认为当前全局下,this.name就是全局变量的值"sueRimn"

one.person()是代表函数person()one调用,此时函数的执行上下文是one,即执行上下文this.name的值就是one.name的值,two.person()也是相同道理。

注意

  • this不能在函数执行期间被赋值
  • 函数每次被调用时this的值不同

二、this关键字的绑定规则

this的绑定分为:

  • 默认绑定与隐式绑定
  • 显示绑定与固定绑定

1、默认绑定和隐式绑定

  • 严格模式下:this的默认值是undefined
  • 非严格模式下:this默认指向全局对象
  • 在函数内部,this的值取决于函数被调用的方式
  • 当有一个作为方法调用的对象属性时,该对象是该方法的this对象或执行上下文对象,这是this关键字的隐式绑定

只要记住,在函数内部,this的值取决于函数被调用的方式,与函数声明时间、地点无关

//在浏览器中, window对象同时也是全局对象// this的默认绑定console.log(this === window);//trueconst AGE = 22;console.log(window.AGE); // 22this.b = "sueRimn";console.log(window.b); // "sueRimn"console.log(b); // "sueRimn"// this的隐式绑定let obj_1 = {    name: "sueRimn",    person: function () {        console.log(this.name);    }}let obj_2 = {name: "八至", person: obj_1.person};let name = "小九";let person = obj_1.person;person(); // "小九"obj_1.person(); // "sueRimn"obj_2.person(); // "八至"

2、显示绑定和固定绑定

要想把 this 的值从一个环境传到另一个,就要用 call 或者apply 方法。

call()

call()方法使用一个指定的 this` 值和单独给出的一个或多个参数来调用一个函数

fun.call(thisArg, arg1, arg2, ...)

apply()

apply() 方法调用一个具有给定this值的函数,以及作为一个数组(或类似数组对象)提供的参数

func.apply(thisArg, [argsArray])

区别

  • call() 方法接受的是一个参数列表
  • apply() 方法接受的是一个包含多个参数的数组
let obj = {a: 'sue'};let a = 'suerimn';function whatThis(arg) {    return this.a;//this的值取决于函数的调用方式}whatThis();// 'suerimn'whatThis.call(obj);//'sue'whatThis.apply(obj);//'sue'

如果我们使用call()apply()方法调用函数,需要把自己的第一个参数作为执行上下文。这就是this关键字的绑定。

注意:在显示绑定或固定绑定中,无论在哪里调用,可以强制this对象始终相同
function add(c, d) {    return this.a + this.b + c + d;}let o = {a: 1, b: 3};//第一个参数作为this使用的对象//后续参数作为参数传递给函数调用add.call(o, 2, 3);//1 + 3 + 2 + 3 = 9//第一个参数作为this使用的对象//第二个参数是一个数组,数组里的元素用作函数调用时的参数add.apply(o, [10, 20]);//1 + 3 + 10 + 20 = 34

上面代码中,add使用call()方法调用将执行上下文对象o作为第一个参数传递,将o分配给this对象并返回。这称为this关键字的显示绑定。

三、绑定this值的方法

(1)call()apply()

上文中已经提及。

(2)bind方法

ES5引入了bind方法来设置函数的this值,而不用考虑函数如何被调用的,调用f.bind(someObject)会创建一个与f具有相同函数体和作用域的函数,但是在这个新函数中,this将永久地被绑定到了bind的第一个参数,无论这个函数是如何被调用的。

function f() {    return this.a;}let g = f.bind({a: 'sueRimn'});console.log(g()); // sueRimnlet h = g.bind({a: 'sueRimn'}); // bind只生效一次console.log(h()); // sueRimnlet z = {a: 37, f: f, g: g, h: h};console.log(z.f(), z.g(), z.h()); // 37 'sueRimn' 'sueRimn'

(3)箭头函数

这是ES6新引入的,在箭头函数中,this与封闭词法环境的this保持一致。在全局代码中,它将被设置为全局对象。

let person = (name, age) => {    console.log(name, age);}

(4)new关键字

当函数作为对象里的方法被调用时,它们的this是调用该函数的对象。

任何函数前面的new关键字都会将函数调用转换为构造函数调用,并且当new关键字放在函数前面时会发生这些事情:

  • 创建一个全新的空对象
  • 新的空对象链接到该函数的prototype属性
  • 将相同的新空对象绑定为该函数调用的执行上下文
  • 如果该函数没有返回任何内容,则隐式返回此对象
function person() {    let name = "sueRimn";    this.age = 22;    console.log(this.name + "" + age); // undefined 23}let name = "八至";let age = 23;obj = new person();console.log(obj.age); // 22

在上面的代码中,使用new关键字调用person函数,它创建了一个新对象链接到函数原型链上,并绑定到该对象函数返回的对象上。

this.name

let fn = {    prop: 37,    f: function () {        return this.prop;    }};console.log(fn.f());//37

四、this关键字绑定的顺序

  1. 检查函数是否使用new关键字调用
  2. 检查函数是否使用call()或者apply()调用,因为这意味着显示绑定
  3. 检查函数是否通过上下文对象调用(隐式绑定)
  4. 默认全局对象(在严格模式下未定义)