JavaScript - this、call、apply、bind
this
this
主要分为以下几种.
1、在 ES5 中,其实 this
的指向,始终坚持一个原理:this
永远指向最后调用它的那个对象
js
123456var a = 'window'function fn() { var a = 'fn' console.log(this.a) // window}fn()
js
123456789101112var a = 'window'const obj = { a: 'obj', fn() { var a = 'fn' return function () { const a = 'function' console.log(this.a) // window } },}obj.fn()()
js
123456789var a = 'window'const obj = { a: 'obj', fn() { var a = 'fn' console.log(this.a) // obj },}obj.fn()
js
1234567891011var a = 'window'const obj = { a: 'obj', fn() { var a = 'fn' console.log(this.a) // window },}
const Fn = obj.fnFn()
js
1234567891011var 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
123456var a = 'window'const obj = { a: 'obj', fn: () => console.log(this.a), // window}obj.fn()
箭头函数其实是没有
this
的,这个函数中的this
只取决于他外面的第一个不是箭头函数的函数的this
。在这个例子中,因为调用a
符合前面代码中的第一个情况,所以this
是window
。并且this
一旦绑定了上下文,就不会被任何代码改变。
call
Function.prototype.call(thisArg,arg1, arg2, ...)
call()
方法使用一个指定的this
值和单独给出的一个或多个参数来调用一个函数。
使用
js
12345function method(a, b) { console.log(this, a, b)}
method.myCall(null, 1, 2)
具体实现
- 不传入第一个参数,那么默认为
window
- 改变了 this 指向,让新的对象可以执行该函数。那么思路可以变成给新的对象添加一个函数,然后在执行完以后删除
js
123456789101112131415Function.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
1234function method(a, b) { console.log(this, a, b)}method.apply({}, [2, 3])
具体实现
apply
的实现和call
类似
js
12345678910111213141516Function.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
123456function method(a, b) { console.log(this, a, b)}
let newMethod = method.bind({}) //this指向改变为obj2newMethod(3, 4)
具体实现
bind
和其他两个方法作用也是一致的,只是该方法会返回一个函数。并且我们可以通过bind
实现柯里化。
js
123456789101112131415Function.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)) }}