Skip to content

This

JavaScript 得this指向是动态得,只有当你调用得时候才确定this得指向,这有时候很苦恼。

this

一句话:this指向调用它所处环境得上下文空间,也是指向最近调用它得对象。

在事件中,this 表示接收事件的 DOM 对象。

如下:调用obj.fnc()时,fnc函数所处得环境为obj对象,this就指向obj,函数执行打印0,执行fnc()时,由于fnc = obj.fnc操作,此时fnc变量指向函数本身,调用函数this就指向此时得环境即window,指向函数打印1

js
var a = 1
const obj = {
  a: 0,
  fnc: function () {
    console.log(this.a)
  }
}
const fnc = obj.fnc
obj.fnc() //0
fnc() //1

又一个例子。这里的this指向obj.b

js
const obj = {
  a: 0,
  b: {
    a: 1,
    fnc() {
      console.log(this.a) //1
    }
  }
}
obj.b.fnc()

改变 this

改变this指向有new,即构造函数,callapply,和bind,以及箭头函数。

优先级:new操作符>call方法等>箭头函数>其他

new

new操作会把this指向绑定到生成的对象。

js
function Fnc() {
  this.a = 0
}
const f = new Fnc()
console.log(f.a) //0

new操作做了什么?

  • 生成一个新对象
  • 链接原型
  • 绑定 this
  • 放回新对象

注意最后是返回一个对象。这个对象是默认返回的,如果你主动return一个对象,那么this指向你返回的那个对象。

js
function Fnc() {
  this.a = 0
  return {
    a: 1
  }
}
const f = new Fnc()
console.log(f.a) //1

提示

手动放回一个对象会导致原型指向改变。

js
Fnc.prototype.b = 10
console.log(f.b) //undefined

call 和 apply

callapply均可修改this,不同的是参数传递方式。可结合 es6rest参数使用。

js
const f = {
  a: 1,
  fnc: function (...res) {
    console.log(this.a + ',' + res)
  }
}
const obj = {
  a: 0
}
f.fnc.call(obj, 1, 2, 3) //0,1,2,3
f.fnc.apply(obj, [1, 2, 3]) //0,1,2,3

bind

bind的使用方法和call一样,不同的是它放回一个函数,

js
const f = {
  a: 1,
  fnc: function (...res) {
    console.log(this.a + ',' + res)
  }
}
const obj = {
  a: 0
}
const F = f.fnc.bind(obj, 1, 2, 3)
F() //0,1,2,3

箭头函数

箭头函数的this会指向原本指向的环境得上一层环境。

js
var a = 0
const f = {
  a: 1,
  fnc: (...res) => {
    console.log(this) //window
    console.log(this.a + ',' + res) //0,1,2,3
  }
}
f.fnc([1, 2, 3])

对于对象,无论嵌套多深都是指向最外层对象。

js
var a = -1
const obj = {
  a: 0,
  b: {
    a: 1,
    fnc: () => {
      return this.a
    }
  }
}
obj.b.fnc() //-1
const test = obj.b.fnc
test() //-1

手写 call 等

callapply一类。bind一类。

call 和 apply

call使用rest进行参数收集,这里只是非严格模式下的写法模拟。通过调用对象得方法来改变函数得this指向。

js
Function.prototype.myCall = function (context = window, ...res) {
  //参数传入模拟
  if (context === null) context = window
  else if (typeof context === 'number') context = new Number(context)
  else if (typeof context === 'string') context = new String(context)
  else if (typeof context === 'boolean') context = new Boolean(context)
  const symbol = Symbol('temporary')
  context[symbol] = this
  const result = context[symbol](...res)
  delete context[symbol]
  return result
}

使用:

js
const demo = {
  a: 1,
  fnc(...res) {
    console.log(this) //瑕疵   {a:0,Symbol(temporary): f}
    console.log(this.a + ',' + res) // 0,1,2
    return res
  }
}
const obj = {
  a: 0
}
var a = 100
const p = demo.fnc.myCall(obj, 1, 2)
console.log(...p) //1 2

apply使用数组传参,就不使用rest收集参数。

js
Function.prototype.myApply = function (context = window, res) {
  //代码一样
}
const p = demo.fnc.myApply(obj, [1, 2])

bind

bind放回一个函数,这个函数可以被调用,也可以作为构造函数使用。

js
Function.prototype.myBind = function (context, ...res) {
  const fnc = this
  return function F(...args) {
    if (this instanceof F) {
      return new fnc(...res, ...args)
    }
    return fnc.call(context, ...res, ...args)
  }
}

使用:

js
const demo = {
  a: 1,
  fnc(...res) {
    console.log(this) //{a:0}
    console.log(this.a + ',' + res) // 0,1,2,3
    return res
  }
}
const obj = {
  a: 0
}
var a = 100
const p = demo.fnc.myBind(obj, 1, 2)(3)
console.log(p) //[1,2,3]

Released under the MIT License.