【前端面试】javascript中手写call,apply,bind函数
javascript中手写call,apply,bind函数callapplybindcallFunction.prototype.myCall = function (context, ...args) {// if(typeof this !== 'function'){ //不需要判断类型,因为myCall定义在Function.prototype上//throw new TypeError
·
javascript中手写call,apply,bind函数
call/apply/bind
都是JS的中改变函数里this的方法,面试中经常会考到手写,尤其是
bind
函数
call
步骤:
- 首先明确
call
函数是定义在Function.prototype
上的,为所有Function
类型的对象所共享。现自定义Function.prototype.myCall
。
传入的参数:
(1)context
表示谁来调用这个原函数,
(2)...args
用三点运算符直接将后面传入的参数解析成数组; - 确定由谁来调用函数,命名为
new_this
;若没有传入要绑定的对象, 默认绑定window
对象; - 把方法作为对象的属性绑定给
new_this
,但要注意,也许原有属性就有func
这个属性,为了避免冲突,这里用了symbol
; - 执行当前函数,并获取返回值;
- 删除我们绑定的的
Symbol(func)
属性,以免污染new_this
的属性; - 返回第3步得到的返回值;
Function.prototype.myCall = function (context, ...args) {
// if(typeof this !== 'function'){ //不需要判断类型,因为myCall定义在Function.prototype上
// throw new TypeError(`${this} is not a function!`)
// }
// 1. 确定要绑定的对象,即最终谁来调用函数,命名为new_this;若没有传入要绑定的对象, 默认绑定window对象
const new_this = context || window
// 2. 把方法作为对象的属性绑定给new_this,但要注意,也许原有属性就有func,为了避免冲突,这里用symbol
const func = Symbol('func')
//由于这里func是Symbol变量不再是字符串,所以不能再用new_this.func而是要用中括号获取属性
new_this[func] = this
// 3. 执行当前函数,并获取返回值
const res = new_this[func](...args)
// 4. 删除我们绑定的的Symbol(func)属性,以免污染new_this的属性
delete new_this[func]
// 5. 返回第3步得到的返回值
return res
}
测试:
function func1(a, b, c) {
console.log(this)
return a + b + c
}
const res = func1.myCall({ x: 10 },1,2,3)
console.log(res);
效果如下:
注意,在绑定的函数func1里执行console.log(this)
语句,发现打印出来的内容带上了我们临时绑定的Symbol(func)方法
,但是用JS原生的call函数就没有这个问题,这是因为添加的Symbol(func)方法
是可枚举的属性
通过defineProperty可以避免这个问题
Object.defineProperty(new_this,func,{value:this,enumerable:false}) // 改用defineProperty,把enumerable设为false更干净
// new_this[func] = this
apply
步骤:
同call,不同的是apply只接收两个参数:要调用的函数和参数数组,因此函数形参略有不同,...args
改成args
,并且加一个类型判断,判断args
是否是Array
实例。
- 首先明确
apply
函数是定义在Function.prototype
上的,为所有Function
类型的对象所共享。现自定义Function.prototype.myApply
。
传入的参数:
(1)context
表示谁来调用这个原函数,
(2)args
表示参数数组; - 判断
args
的类型,如果不是Array
的实例,抛出一个TypeError
; - 确定由谁来调用函数,命名为
new_this
;若没有传入要绑定的对象, 默认绑定window
对象; - 把方法作为对象的属性绑定给
new_this
,但要注意,也许原有属性就有func
这个属性,为了避免冲突,这里用了symbol
; - 执行当前函数,并获取返回值;
- 删除我们绑定的的
Symbol(func)
属性,以免污染new_this
的属性; - 返回第3步得到的返回值;
Function.prototype.myApply = function (context, args) {
// 1. 判断args的类型,如果不是Array的实例,抛出一个TypeError;
if(!(args instanceof Array)){
throw new TypeError(`args is not an array!`)
}
// 2. 确定要绑定的对象,即最终谁来调用函数,命名为new_this;若没有传入要绑定的对象, 默认绑定window对象
const new_this = context || window
// 3. 把方法作为对象的属性绑定给new_this,但要注意,也许原有属性就有func,为了避免冲突,这里用symbol
const func = Symbol('func')
//由于这里func是Symbol变量不再是字符串,所以不能再用new_this.func而是要用中括号获取属性
new_this[func] = this
// 4. 执行当前函数,并获取返回值
const res = new_this[func](...args)
// 5. 删除我们绑定的的Symbol(func)属性,以免污染new_this的属性
delete new_this[func]
// 6. 返回第3步得到的返回值
return res
}
测试:
function func1(a, b, c) {
console.log(this)
return a + b + c
}
const res = func1.myApply({ x: 10 },[1,2,3])
console.log(res);
结果:
和上面是也一样的问题。
bind(使用apply实现)
步骤:
- 首先明确
bind
函数是定义在Function.prototype
上的,为所有Function
类型的对象所共享。现自定义Function.prototype.myBind
。
传入的参数:
(1)context
表示谁来调用这个原函数,
(2)...args
用三点运算符直接将后面传入的参数解析成数组; - 确定要绑定的对象,即最终谁来调用函数,命名为new_this;若没有传入要绑定的对象, 默认绑定window对象;
- (非必要)把原函数(即this)用一个fn变量保存一下,这样更能看出它表示一个函数 ;
- 返回一个新函数,新函数内部通过
apply
方法将原函数的this
指向new_this
;
Function.prototype.my_bind = function(context,...args){
// 1. 确定要绑定的对象,即最终谁来调用函数,命名为new_this;若没有传入要绑定的对象, 默认绑定window对象
const new_this = context || window
// 2. 把原函数(即this)用一个fn变量保存一下,这样更能看出它表示一个函数
const fn = this
// 3. 返回一个新函数
return function(){
//新函数内部通过apply方法将原函数的this指向new_this
return fn.apply(new_this,args)
}
}
测试
function func1(a,b,c){
console.log(this)
return a+b+c
}
// 用一个func2来接收func1.my_bind()传出来的新函数
const func2 = func1.my_bind({x:10},1,2,3)
// 输出func2()调用的返回值
console.log(func2());
结果:
更多推荐
已为社区贡献1条内容
所有评论(0)