JavaScript - this、call、apply、bind

this

this 主要分为以下几种.

1、在 ES5 中,其实 this 的指向,始终坚持一个原理:this 永远指向最后调用它的那个对象

js
123456
var a = 'window'
function fn() {
var a = 'fn'
console.log(this.a) // window
}
fn()
js
123456789101112
var a = 'window'
const obj = {
a: 'obj',
fn() {
var a = 'fn'
return function () {
const a = 'function'
console.log(this.a) // window
}
},
}
obj.fn()()
js
123456789
var a = 'window'
const obj = {
a: 'obj',
fn() {
var a = 'fn'
console.log(this.a) // obj
},
}
obj.fn()
js
1234567891011
var a = 'window'
const obj = {
a: 'obj',
fn() {
var a = 'fn'
console.log(this.a) // window
},
}
const Fn = obj.fn
Fn()
js
1234567891011
var a = 'window'
function fn() {
var a = 'fn'
console.log(this.a) // window
}
let obj = new foo()
obj.a = 'obj'
console.log(obj.a)
// 还有种就是利用 call,apply,bind 改变 this,这个优先级仅次于 new

2、在 ES6 中的箭头函数的 this

js
123456
var a = 'window'
const obj = {
a: 'obj',
fn: () => console.log(this.a), // window
}
obj.fn()

箭头函数其实是没有 this 的,这个函数中的 this 只取决于他外面的第一个不是箭头函数的函数的 this。在这个例子中,因为调用 a 符合前面代码中的第一个情况,所以 thiswindow。并且 this 一旦绑定了上下文,就不会被任何代码改变。

call

Function.prototype.call(thisArg,arg1, arg2, ...)
call() 方法使用一个指定的 this 值和单独给出的一个或多个参数来调用一个函数。

使用

js
12345
function method(a, b) {
console.log(this, a, b)
}
method.myCall(null, 1, 2)

具体实现

  • 不传入第一个参数,那么默认为 window
  • 改变了 this 指向,让新的对象可以执行该函数。那么思路可以变成给新的对象添加一个函数,然后在执行完以后删除
js
123456789101112131415
Function.prototype.myCall = function (context, ...args) {
context = context === null || context === undefined ? globalThis : Object(context)
// 使用Symbol确保不会出现命名冲突
let key = Symbol('_key')
// 使用defineProperty创建一个属性存放 this
Object.defineProperty(context, key, { value: this })
// 运行方法
let result = context[key](...args)
// 删除方法
delete context[key]
return result
}

apply

Function.prototype.apply(thisArg,[argsArray])
apply() 方法调用一个具有给定 this 值的函数,以及以一个数组(或一个类数组对象)的形式提供的参数。

使用

js
1234
function method(a, b) {
console.log(this, a, b)
}
method.apply({}, [2, 3])

具体实现

  • apply 的实现和 call 类似
js
12345678910111213141516
Function.prototype.myApply = function (context) {
context = context === null || context === undefined ? globalThis : Object(context)
// 使用Symbol确保不会出现命名冲突
let key = Symbol('_key')
// 使用defineProperty创建一个属性存放 this
Object.defineProperty(context, key, { value: this })
// 需要判断是否存储第二个参数
// 如果存在,就将第二个参数展开
let result = arguments[1] ? context[key](...arguments[1]) : context[key]()
// 删除方法
delete context[key]
return result
}

bind

Function.prototype.bind(thisArg,arg1, arg2, ...)
bind() 方法创建一个新的函数,在 bind() 被调用时,这个新函数的 this 被指定为 bind() 的第一个参数,而其余参数将作为新函数的参数,供调用时使用。

使用

js
123456
function method(a, b) {
console.log(this, a, b)
}
let newMethod = method.bind({}) //this指向改变为obj2
newMethod(3, 4)

具体实现

  • bind 和其他两个方法作用也是一致的,只是该方法会返回一个函数。并且我们可以通过 bind 实现柯里化。
js
123456789101112131415
Function.prototype.myBind = function (context) {
if (typeof this !== 'function') {
throw new TypeError('Error')
}
let _this = this
let args = [...arguments].slice(1)
// 返回一个函数
return function F() {
// 因为返回了一个函数,我们可以 new F(),所以需要判断
if (this instanceof F) {
return new _this(...args, ...arguments)
}
return _this.apply(context, args.concat(...arguments))
}
}