this指向一直都是JavaScript中比较难以理解的地方,就算是工作多年的人也有可能没有理解透彻this的指向,而this指向不管是在工作中还是面试中都是比较重要的知识点.
下面是我深入学习this指向的笔记希望能帮助大家理解并掌握this指向.
this的绑定规则
学习this指向我们必须先了解this的绑定的规则
- 默认绑定
- 隐式绑定
- 显示绑定
- new绑定
- 常见的this指向
- 箭头函数中的this指向
- 规则之外的绑定
了解到绑定规则之后,下面我们来敲一下代码
默认绑定
默认绑定就是函数的独立调用,而独立调用的函数this指向的是全局,在js中式window,在node中是一个空对象.
/**
* this的默认绑定(函数的独立调用)
*
*/
function foo() {
console.log(this) // window
}
foo()
隐式绑定
隐式绑定就是通过对象点方法的形式调用对象中的方法,那么这个方法中的this就是指向这个对象.如果将对象中的方法换成箭头函数的形式,那么里面的this将指向window
/**
* this的隐式绑定(通过对象调用方法)
*
*/
const obj = {
name: 'lhq',
bar: function() {
console.log(this) // {name: 'lhq', bar: ƒ} // 控制台输出结果
}
}
obj.bar()
显示绑定
显示绑定就是通过call(),apply(),bind().这些方法来改变函数内部的this指向
/**
* 显示绑定(通过call(), apply(), bind()),这些方法来改变this在函数中内部的指向
* 当call(),apply(), bind() 传入 null, undefined时,this指向window
*/
const obj1 = {
name: 'obj1',
foo: function() {
console.log(this.name)
}
}
const obj2 = {
name: 'obj2',
bar: function() {
console.log(this.name)
}
}
obj1.foo() // obj1
obj1.foo.call(obj2) // obj2
obj1.foo.apply(obj2) // obj2
const fn = obj1.foo.bind(obj2) // obj2
fn() // obj2
new 绑定
new绑定就是实例化一个对象,this会指向这个对象
/**
* new 绑定
* 通过new来调用构造函数,每次都会创建一个新的实例对象,且this指向这个对象
*/
function Person(name) {
this.name = name
this.student = function() {
console.log(this.name)
}
}
const person1 = new Person('lhq')
const person2 = new Person('hql')
person1.student() // lhq
person2.student() // hql
常见的this指向
定时器,事件绑定,数组中高阶函数的绑定
/**
* 定时器
*
*/
setTimeout(() => {
console.log(this) // window
})
/**
* DOM的事件绑定
*
*/
const box = document.querySelector('.box')
box.addEventListener('click', function() {
console.log(this) // 指向的是被点击的DOM节点
})
/**
* 数组中高阶函数
* 高阶函数的定义: 一个函数接受一个函数作为参数或返回一个函数称为高阶函数
*/
const arr = [10, 20, 30, 40, 50]
arr.forEach(function(item) {
console.log(this) // window
})
箭头函数中的this指向
箭头函数不绑定this,会使用上层作用域的this.
箭头函数因为内部没有绑定this指向,所以call(),apply(),bind()修改其内部的this指向是无效的
let name = 'lhq'
const obj = {
name: 'hql',
bar: () => {
console.log(this.name) // lhq
}
}
this绑定的面试题(附答案)
第一题
/**
* 面试题: 1
*/
const name = 'window'
const person = {
name: 'person',
sayName: function() {
console.log(this.name)
}
}
function sayName() {
const sss = person.sayName
sss() // 函数的独立调用 window
person.sayName(); // this的隐式绑定 person
(person.sayName)(); // this的隐式绑定 person
(b = person.sayName)() // 函数的独立调用 window
}
sayName()
第二题
/**
* 面试题: 2
*/
var name = 'window'
const person1 = {
name: 'person1',
foo1: function() {
console.log(this.name)
},
foo2: () => console.log(this.name),
foo3: function() {
return function() {
console.log(this.name)
}
},
foo4: function() {
return () => {
console.log(this.name)
}
}
}
const person2 = {
name: 'person2'
}
person1.foo1() // person1 隐式绑定
person1.foo1.call(person2) // person2 (显示绑定优先级大于隐式绑定)
person1.foo2() // window (不绑定作用域,上层作用域是全局)
person1.foo2.call(person2) // window 箭头函数不受显示绑定的限制,往上层作用域查找
person1.foo3()() // window(独立函数调用)
person1.foo3.call(person2)() // window(独立函数调用)
person1.foo3().call(person2) // person2(最终调用返回函数式,使用的是显示绑定)
person1.foo4()() // person1
person1.foo4.call(person2)() // person2(上层作用域被显示的绑定了一个person2)
person1.foo4().call(person2) // person1(上层找到person1)
第三题
/**
* 面试题: 3
*
* 对象没有作用域
* 函数有作用域
*
* new 一个构造函数会创建一个空对象,
* 构造函数的this会指向这个空对象
* 往这个对象中加入属性和方法
* 最终返回这个对象 称为实例对象
*/
var name = 'window'
function Person(name) {
this.name = name
this.foo1 = function() {
console.log(this.name)
}
this.foo2 = () => console.log(this.name)
this.foo3 = function() {
return function() {
console.log(this.name)
}
}
this.foo4 = function() {
return () => {
console.log(this.name)
}
}
}
var person1 = new Person('person1')
var person2 = new Person('person2')
person1.foo1() // person1
person1.foo1.call(person2) // person2 显示绑定高于隐式绑定
person1.foo2() // person1 上层作用域找
person1.foo2.call(person2) // person1 箭头函数不受显示绑定影响
person1.foo3()() // window 函数的独立调用
person1.foo3.call(person2)() // window 函数的独立调用
person1.foo3().call(person2) // person2 使用显示绑定改变this的指向
person1.foo4()() // person1 箭头函数上层作用域查找
person1.foo4.call(person2)() // person2 通过显示绑定改变了foo4的this指向, 返回的是箭头函数 往上层作用域查找this 找到person2
person1.foo4().call(person2) // person1 // 箭头函数不受显示绑定约束
const obj = {
fun: function() {
// 这里的this指向obj 上层是全局
}
}
function foo() {
// 这里的this是foo
function bar() {
// 这里的this是bar
}
}
第四题
/**
* 面试题: 4
*
*/
var name = 'window'
function Person(name) {
this.name = name
this.obj = {
name: 'obj',
foo1: function() {
return function () {
console.log(this.name)
}
},
foo2: function() {
return () => {
console.log(this.name)
}
}
}
}
var person1 = new Person('person1')
var person2 = new Person('person2')
person1.obj.foo1()() // window 函数的独立调用
person1.obj.foo1.call(person2)() // window 函数的独立调用
person1.obj.foo1().call(person2) // person2 通过显示绑定改变函数内部的this指向
person1.obj.foo2()() // obj 调用返回了一个箭头函数,箭头函数再进行调用,因为箭头函数内部没有this,会往上层作用域找,找到obj
person1.obj.foo2.call(person2)() // person2 通过call调用foo2方法,并修改了它内部的this指向为perosn2,并返回一个箭头函数,再次执行这个箭头函数,箭头函数内部无this,往上层作用域找,又因为上层作用域的this指向被修改为person2,所以最终的打印结果为person2
person1.obj.foo2().call(person2) // obj
以上就是全部内容,可能不是很完善,欢迎大家多多交流指正。