JavaScript - this、call、apply、bind
this
this
主要分为以下几种.
1、函数调用
- 直接调用一个函数,就是默认绑定,this 在非严格模式下指向的为全局对象
window
javascript
1234function fn() {console.log(this) // window}fn()
javascript
123456789101112131415161718const obj = {fn: () => {console.log(this) // window},fn1() {return function () {console.log(this) // window}},fn2() {console.log(this) // window},}obj.fn()obj.fn1()()const Fn = obj.fn2Fn()
2、对象函数调用
- 函数 fn 作为一个对象的方法来调用时,this 指向这个对象
javascript
123456const obj = {fn() {console.log(this) // obj},}obj.fn()
3、构造器调用
- 使用 new 调用时,函数执行前会新创建一个对象,this 指向这个新创建的对象。
javascript
12345function fn() {console.log(this) // fn}let obj = new fn()
javascript
123456789101112131415161718function fn() {}fn.prototype = {func: function () {console.log(this) // fn},}let FN = new fn()FN.func()const obj = {name: 'j',}FN.func.call(obj) // objFN.func.apply(obj) // obj// 在使用 call,apply,bind 改变 this,这个优先级仅次于 new
call
Function.prototype.call(thisArg, arg1, arg2, ...)
call()
方法使用一个指定的this
值和单独给出的一个或多个参数来调用一个函数。
使用
javascript
12345function method(a, b) {console.log(this, a, b)}method.myCall(null, 1, 2)
具体实现
- 不传入第一个参数,那么默认为
window
- 改变了 this 指向,让新的对象可以执行该函数。那么思路可以变成给新的对象添加一个函数,然后在执行完以后删除
javascript
123456789101112131415Function.prototype.myCall = function (context, ...args) {context = context === null || context === undefined ? globalThis : Object(context)// 使用Symbol确保不会出现命名冲突let key = Symbol('_key')// 使用defineProperty创建一个属性存放 thisObject.defineProperty(context, key, { value: this })// 运行方法let result = context[key](...args)// 删除方法delete context[key]return result}
apply
Function.prototype.apply(thisArg, [argsArray])
apply()
方法调用一个具有给定this
值的函数,以及以一个数组(或一个类数组对象)的形式提供的参数。
使用
javascript
1234function method(a, b) {console.log(this, a, b)}method.apply({}, [2, 3])
具体实现
apply
的实现和call
类似
javascript
12345678910111213141516Function.prototype.myApply = function (context) {context = context === null || context === undefined ? globalThis : Object(context)// 使用Symbol确保不会出现命名冲突let key = Symbol('_key')// 使用defineProperty创建一个属性存放 thisObject.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()
的第一个参数,而其余参数将作为新函数的参数,供调用时使用。
使用
javascript
123456function method(a, b) {console.log(this, a, b)}let newMethod = method.bind({}) //this指向改变为obj2newMethod(3, 4)
具体实现
bind
和其他两个方法作用也是一致的,只是该方法会返回一个函数。并且我们可以通过bind
实现柯里化。
javascript
123456789101112131415Function.prototype.myBind = function (context) {if (typeof this !== 'function') {throw new TypeError('Error')}let _this = thislet 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))}}