this指向
in Javascript with 0 comment

this指向

in Javascript with 0 comment

普通函数:谁调用指向谁

普通函数是指由构造函数Function创建或者使用关键字function声明定义的函数。通常情况下,普通函数的this并不天生指向其定义所在的对象,而是指向它的调用者。

let obj = {
  a: 'obj',
  func () {
    console.log(this.a)
  }
}

let obj2 = {
  a: 'obj2',
  func: obj.func
}

obj.func() // this指向调用者obj,输出:obj1
obj2.func() // this指向调用者obj2,输出:obj2

let func = obj.func
window.a = 'window'
func() // 未指定调用者,this指向全局对象,输出:window
"use strict"
// 省略函数定义
let func = obj.func
window.a = 'window'
func() // 报错:Uncaught TypeError: Cannot read property 'a' of undefined

箭头函数中的this

箭头函数的this指向,和箭头函数定义所在上下文的this相同。对于普通函数,this在函数调用时才确定;而对于箭头函数,this在箭头函数定义时就已经确定了,并且不能再被修改,指向此时外层函数的this,如果没有外层函数则指向全局对象。

let obj = {
  A () {
    return () => {
      return this
    }
  },
  B () {
    return function () {
      return this
    }
  }
}

let func = obj.A()
console.log(func() === obj) // true

func = obj.B()
console.log(func() === obj) // false
console.log(func() === window) // true

构造函数中的this

JS里的普通函数可以使用new操作符来创建一个对象,此时该函数就是一个构造函数,箭头函数不能作为构造函数。执行new操作符,其实JS内部完成了以下事情:

  1. 创建一个空的简单JavaScript对象(即{});
  2. 将构造函数的prototype绑定为新对象的原型对象 ;
  3. 将步骤1新创建的对象作为this的上下文并执行函数 ;
  4. 如果该函数没有返回对象,则返回this。
function A () {
  this.a = 1
  this.func = () => {
    return this
  }
}

let obj = new A()
console.log(obj.a) // 1
console.log(obj.func() === obj) // true

使用apply和call动态改变函数this的指向

使用apply可以在调用函数时改变this的指向,当然,箭头的this已经不可改变,所以不能通过该方法修改this指向。

function dir (x, y) {
  this.x = x
  this.y = y
}

let obj = {
  a: 1,
  b: 2
}
dir.apply(obj, [3, 4])

console.log(JSON.stringify(obj)) // {"a":1,"b":2,"x":3,"y":4}

call函数和apply的作用都是更改this的指向,只是使用上稍微不同,call函数接收多个参数。

对箭头函数使用call或者apply将不能指定this,因为箭头函数的this在定义时已经确定:

let dir = (x, y) => {
  this.x = x
  this.y = y
}

let obj = {
  a: 1,
  b: 2
}
dir.apply(obj, [3, 4])

console.log(JSON.stringify(obj)) // {"a":1,"b":2}
console.log(window.x, window.y) // 3, 4

使用bind创建新函数并绑定this

bind() 方法会创建一个新函数,这个新函数的 this 被指定为 bind() 的第一个参数,而其余参数将作为新函数的参数,供调用时使用。bind方法有以下特点:

let test = function (b, c) {
  console.log(this.a, b, c)
}
let obj = { a: 'obj1-a' }
let obj2 = { a: 'obj2-a' }

let bindFunc = test.bind(obj, 'b')
bindFunc('c') // obj1-a, b, c

bindFunc.call(obj2, 'c') // 指定this无效,输出:obj1-a, b, c

test.call(obj2, 'b', 'c') // 原函数不被影响,输出:obj2-a, b, c

let bindFunc2 = bindFunc.bind(obj2, 'c-rebind') 
bindFunc2() // 不能再次重新绑定,输出:obj1-a, b, c-rebind

let bindFunc3 = test.bind(undefined, 'b')
window.a = 'window-a'
bindFunc3('c') // 非严格模式输出window-a, b, c

  以上是bind函数的常规用法,bind函数生成的函数与箭头函数不同,它仍然可以用于new操作符,不过这种情况下,bind的this对象会被忽略,this还是指向新生成的对象:

function A (b) {
  this.a = 'a'
  this.b = 'b'
}

let obj = {
  a: 'obj-a'
}

let B = A.bind(obj)

let newObj = new B()
console.log(newObj.a, obj.a) // a, obj-a

this指向规则总结

Responses