深入JavaScript的this指向(打通任督二脉)

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

以上就是全部内容,可能不是很完善,欢迎大家多多交流指正。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值