重学JS-7-一图掌握this关键字

219 阅读3分钟

一起养成写作习惯!这是我参与「掘金日新计划 · 4 月更文挑战」的第25天,点击查看活动详情

前端开发的第三年,突然发现,对于JS,我还有很多不懂的地方,趁着最近需求少,不如静下心来,从头把JS再学一遍,查漏补缺。

本系列以廖雪峰的《JavaScript教程》和《现代 JavaScript 教程》两个电子书作为线索,对其中需要进一步了解的知识,会阅读更多的文章,并作为扩展知识记录下来。

新手建议先阅读上面两个电子书,本系列更适合用来复习旧知识查漏补缺

思维导图

通过下面的思维导图,我们先对JavaScript的this关键字有一些基本的了解。

《重学JavaScript-this》思维导图

函数的“this”

我们在对象内部,想要访问对象的属性,就会需要用到this。

当然我们也可以直接使用对象变量名,但是变量名可以被重新赋值,直接使用变量名总是不保险的,而且也不方便阅读。

let user = {
  name: "John",
  age: 30,

  sayHi() {
    // "this" 指的是“当前的对象”
    alert(this.name);
    // alert(user.name);
  }

};

user.sayHi();

“this”指向调用方法的对象

JS的“this”有更高的灵活性。

我们知道JS的函数,也可以作为对象的属性,一个函数同时可以被赋值给多个对象。

这个时候,函数的this,是指向调用方法的对象。

let user = { name: "John" };
let admin = { name: "Admin" };

function sayHi() {
  alert( this.name );
}

// 在两个对象中使用相同的函数
user.f = sayHi;
admin.f = sayHi;

// 这两个调用有不同的 this 值
// 函数内部的 "this" 是“点符号前面”的那个对象
user.f(); // John(this == user)
admin.f(); // Admin(this == admin)

“this”绑定的规则

凭经验去判断this的指向,不太容易,我们可以总结出下面四条绑定规则,用来判断this的指向。

默认绑定

非严格模式下,没有对象时,this会是全局对象(比如浏览器中的window),这就是默认绑定

function sayHi(){ 
	console.log("Hi", this); 
} 
sayHi(); // Hi Window

严格模式下,没有对象时,this是undefined。

'use strict'; 
function sayHi(){ 
	console.log("Hi", this); 
}
sayHi(); // Hi undefined

隐式绑定

调用对象的方法时,进行隐式绑定,this指向它的调用者。

这是我们最常见也最容易理解的绑定规则。

let user = {
  name: "John",
  age: 30,

  sayHi() {
    // "this" 指的是“当前的对象”
    alert(this.name);
    // alert(user.name);
  }

};

user.sayHi();

硬绑定

与隐式绑定相对,我们显式得指定this的对象。

这里我们用到callapplybind

let obj1 = {
    name: 'obj1',
    sayHi() {
    	// "this" 指的是“当前的对象”
    	console.log(this.name);
  	}
}; 
let obj2 = {
    name: 'obj2'
}
let obj3 = {
    name: 'obj3'
}

var tmp = obj1.sayHi.bind(obj2)
tmp() // => 'obj2'
obj1.sayHi.call(obj3) // => 'obj3'

构造函数绑定

New绑定,构造函数的this指向对象实例。

function Person(){
	console.log('Hi', this);
}
let person = new Person(); // => Hi Person {}

规则优先级

先说结论:显式 or new > 隐式 > 默认

不存在同时使用显示绑定和new绑定的操作。

参考显示绑定时候的例子,可以验证下这个优先级。

let obj1 = {
    name: 'obj1',
    sayHi() {
    	// "this" 指的是“当前的对象”
    	console.log(this.name);
  	}
}; 
let obj2 = {
    name: 'obj2'
}
let obj3 = {
    name: 'obj3'
}

var tmp = obj1.sayHi.bind(obj2)
tmp() // => 'obj2'
obj1.sayHi.call(obj3) // => 'obj3'

箭头函数没有this

箭头函数的this,直接指向外层函数的this

所以箭头函数也不能用做构造函数。

let user = {
  firstName: "Ilya",
  sayHi() {
    let arrow = () => alert(this.firstName);
    arrow();
  }
};

user.sayHi(); // Ilya