手写call、apply、及bind
首先从以下几点来考虑如何实现这几个函数
- 不传入第一个参数,那么上下文默认为
window
- 改变了
this
指向,让新的对象可以执行该函数,并能接受参数
调用模板
1 2 3 4 5 6 7 8 9 10
| let a = { value:1 } function getValue(name,age){ console.log(name) console.log(age) console.log(this.value) } getValue.myCall(a,'ycy','22') getValue.myApply(a, ['ycy', '22'])
|
实现call
- 首先
context
为可选参数,如果不传的话,默认上下文为window
- 接下来
context
创建一个fn
属性,并将值设置为需要调用的函数
- 因为
call
可以传入多个参数作为调用函数的参数,所以需要将参数剥离出来
- 然后调用函数并将对象上的函数删除
1 2 3 4 5 6 7 8 9 10 11
| Function.prototype.myCall = function(context){ if(typeof this !== 'funcyion'){ throw new TypeError('Error') } context = context || window context.fn = this const args = [...arguments].slice(1) const result = context.fn(...args) delete context.fn return result }
|
实现apply
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
| Function.prototype.myAplly = function(context){ if(typeof this !== 'function'){ throw new TypeError('Error') } context = context || window context.fn = this let result if(arguments[1]){ result = context.fn(...arguments[1]) }else{ result = context.fn() } delete context.fn return result }
|
实现bind
bind的实现对比其他两个函数略微地复杂了一点,因为bind需要返回一个函数,需要判断一些边界问题
bind
返回了一个函数,对于函数来说有两种方式调用,一种是直接调用,一种是通过new
方式
- 对于直接调用来说,这里选择了
apply
的方式实现,但是对于参数需要注意以下情况:因为bind
可以实现类似这样的代码f.binde(obj,1)(2)
,所以我么需要将两边的参数拼接起来,于是就有了这样的实现args.contact(...arguments)
- 最后来说通过
new
的情况来说,不会被任何改变this
,所以对于这种情况我们需要忽略传入的this
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
| Function.prototype.myBind = function(context){ if(typeof this !== 'function'){ throw new TypeError('Error') } const _this=this const args = [...arguments].slice(1) return function F(){ if(this instanceof F){ return new _this(...args,...arguments) } return _this.apply(context,args.concat(...arguments)) } }
|