this指向/四种高频面试题

1,342 阅读4分钟

this

一般情况下 在全局下面 this指向的就是window(globalObject) 在node环境中指向的一个{} 因为node环境中他会从module ->加载 ->编译 ->放到一个函数里面 ->执行这个函数的apply({}) 因为这个函数调用了apply 传入了一个空对象 全局中this 就是一个空对象

所有函数被调用的时候 都会创建一个执行上下文 **在这个上下文中记录着函数的调用栈 AO对象等 并且这个AO里面 this是动态绑定的(他是在代码运行的时候已经绑定好了

他有五种的绑定规则

重点:this指向是跟他所处的位置是没有关系的,而是跟这个函数被怎么调用有关系

1. 默认绑定

**独立的函数调用 这个就是全局的window ** foo() this:window

2. 隐式绑定

它是由某一个前提调用这个方法。是有一个调用者 obj.foo() this:obj

3. 显示绑定 (在执行函数的时候 可以绑定制定this值)

提供的三种显示绑定方法

  • call:sum.call('thisVal',10,20,30)它的参数是多个 是个参数列表
  • apply:sum.apply('thisVal,[20,30,40]')他的参数是一个数组
  • bind:let newSum-sum.bind('aaa') 它返回一个this为aaa的函数 foo 直接调用指向的是全局window 但是通过call,apply,bind三种方式可以指定this(这个也是跟prototype有关系) 如果传入是一个null或者undefined的话他在内部执行或进行一个忽略 他是指向window 默认绑定和显示绑定bind 起冲突了显示绑定优先级大于默认绑定

4. new绑定

我们通过一个new关键字调用一个函数时(构造器)这个时候 this是在调用这个构造器创建出来新对象 默认是一个{}

this =创造出来的对象{}
let thisObj={}
function foo (val){ 
console.log(this) // this=thisObj ->{} 
this.name=val
return thisObj
} 
let nerFoo=new foo('aa') 
console.log(nerFoo.name)
  1. 当用new调用函数时候 会在外部先创建一个全新的对象(thisObj)
  2. 这个对象会被绑定到原型上(prototype)
  3. 这个新对象会绑定到函数调用的this上(this绑定在这个步骤完成)
  4. 如果函数没有返回其他对象,则会默认返回this这个新对象

5.内置函数

  • 定时器
setTimeout(function(){
   console.log() window 相当于独立调用这个函数 (默认绑定)
)
  • 监听点击事件
const boxDiv = document.querySelector('.box')
boxDiv.onclick = function() {
  console.log(this)  // boxDiv这个元素
}
这个就相当于 boxDiv.onclick
  • 数组的函数调用(forEach/map/filter/find)
 var names = ["abc", "cba", "nba"]
    names.forEach(function(item) {
    console.log(item, this)    // 如果传了参数 就可以执行this 如果不传参数默认就是window
    }, "abc")
    names.map(function(item) {
    console.log(item, this)
    }, "cba")

规则优先级:默认绑定是最低的 显示绑定是高于隐形绑定 new高于隐式绑定

箭头函数

  • 他不会绑定this arguments属性
  • 构建函数不可以作为构造函数来使用(不可以和new一起使用)
  • 对象定义的函数 是没有作用域的
var obj = {
  data: [],
  getData: function() {
    // 发送网络请求, 将结果放到上面data属性中
    // 在箭头函数之前的解决方案
    // var _this = this
    // setTimeout(function() {
    //   var result = ["abc", "cba", "nba"]
    //   _this.data = result
    // }, 2000);
    // 箭头函数之后
    setTimeout(() => {
      var result = ["abc", "cba", "nba"]
      this.data = result
    }, 2000);
  }
}

四种高频面试题

1.基础的调用

let names = windosPerson
let person={
  names:'person',
  sayNamePerson(){
    console.log(this.names)
  }
 }
 
 function sayName(){
    let fun = person.sayNamePerson
    fun() // window ;独立函数调用
    person.sayNamePerson() // person ;隐式调用
    (person.sayNamePerson)() // person ;隐式调用
    (b=person.sayNamePerson)() // window ;赋值表达式(独立函数调用)
 }
 
 sayName()

2.嵌套箭头函数方式调用

var name = 'window'

var 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)
    }
  }
}

var person2 = { name: 'person2' }

person1.foo1() // person1 : 隐式调用
person1.foo1.call(person2) // person2 (显示绑定优先级高于隐式绑定)

person2.foo2() // window :箭头函数不绑定this指向 他的上层作用域是对象 没有作用域 在上层就是全局window
person1.foo2.call(person2); // window
person1.foo3 //window :因为返回了一个函数 独立函数调用
person1.foo3.call(person2)(); // window(独立函数调用)
person1.foo3().call(person2); // person2(最终调用返回函数,这个返回的函数调用了call使用的是显示绑定)
person1.foo4()(); // person1(箭头函数不绑定this, 上层作用域this是person1)
person1.foo4.call(person2)(); // person2( 先是给foo4这个方法绑定了一个this为person2  再次调用foo4()的时候为箭头函数 它的上层作用域被显示的绑定了一个person2)
person1.foo4().call(person2); //person1 : 因为先执行了foo4 返回的结果是一个箭头函数 箭头函数是不可以绑定this的 所以当调用这个箭头函数的时候 以及寻找上层作用域 (上层找到person1)

在构造方法里面的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 (上层作用域中的this是person1)
person1.foo2.call(person2) // person1 (上层作用域中的this是person1)

person1.foo3()() // window(独立函数调用)
person1.foo3.call(person2)() // window
person1.foo3().call(person2) // person2 直接给返回的函数绑定了person2

person1.foo4()() // person1
person1.foo4.call(person2)() // person2
person1.foo4().call(person2) // person1

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

person1.obj.foo2()() // obj
person1.obj.foo2.call(person2)() // person2
person1.obj.foo2().call(person2) // obj