JS基础面试题之手写bind

手写bind

bind()方法会创建一个新的函数。当这个函数被调用时,bind()的第一个参数将作为它的运行时的this,之后的一序列参数将会在传递的实参前传入作为它的参数。

由此,我们可以首先得出bind函数的两个特点:

  • 返回一个函数。
  • 可以传入参数。

返回函数的模拟实现

var foo = {
	value:1
};
function bar(){
	console.log(this.value);
}
//返回一个函数
var bindFoo = bar.bind(foo);

bindFoo();//1

关于指定this的指向,我们可以使用call或者apply实现。

//第一版
Function.prototype.bind2 = function (context){
	var self = this;
	//考虑到绑定函数可能是有返回值的,加上return
	return function(){
		return self.apply(context);
	}
}

传参的模拟实现

接下来,关于参数的传递:

var foo = {
	value:1
};
function bar(name,age){
	console.log(this.value);
	console.log(name);
	console.log(age);
}
var bindFoo = bar.bind(foo,'kin');
bindFoo('18');
//1
//kin
//18

当需要传name和age两个参数时,可以在bind的时候,只传一个name,在执行返回的函数的时候,再传另一个参数age。
这里如果不适用rest,使用arguments进行处理:

//第二版
Function.prototype.bind2 = function(context){
	var self = this;
	//获取bind2函数从第二个参数到最后一个参数
	var args = Array.prototype.slice.call(arguments,1);
	return function(){
		//这个时候的arguments是指bind返回的函数传入的参数
		var bindArgs = Array.prototype.slice.call(arguments);
		return self.apply(context,args.concat(bindArgs));
	}
}

构造函数效果的模拟实现

bind还有一个特点,就是

一个绑定函数也能使用new操作符创建对象:这种行为就像把原函数当成构造器。提供的this值被忽略,同时调用时的参数被提供给模拟函数。

也就是说当bind返回的函数作为构造函数的时候,bind指定的this值会失效,但传入的参数依然生效。举个例子:

var value = 2;
var foo = {
	value:1
};
function bar(name,age){
	this.habit = 'shopping';
	console.log(this.value,name,age);
}
bar.prototype.friend = 'ming';
var bindFoo = bar.bind(foo,'hong');
var obj = new bindFoo('18');
//undefined
//hong
//18
console.log(obj.habit);
console.log(obj.friend);
//shopping
//ming

尽管在全局和foo中都声明了value值,最后依然反悔了undefined,说明绑定的this失效了。

后文中new的模拟实现,就会知道这个时候的this已经指向了obj。

构造函数效果的优化实现

但是在这个写法中,我们直接将fBound.prototype = this.prototype,我们直接修改fBound.prototype的时候,也会直接修改绑定函数的prototype。这个时候,我们可以通过一个空函数来进行中转:

//第四版
Function.prototype.bind2 = function(context){
	var self = this;
	var args = Array.prototype.slice.call(arguments,1);
	var fNOP = function(){};
	var fBound = function(){
		var bindArgs = Array.prototype.slice.call(arguments);
		return self.apply(this instanceof fNOP ? this : context,args.concat(bindArgs));
	}
	fNOP.prototype = this.prototype;
	fBound.prototype = new fNOP();
	return fBound;
}

最终版

调用bind的不是函数时,提示错误:

if(typeof this !== "function"){
	throw new Error("Function.prototype.bind - what is trying to be bound is not callable");
}

最终代码为:

Function.prototype.bind2 = function (context){
	if(typeof this !== "function"){
		throw new Error("Function.prototype.bind - what is trying to be bound is not callable");
	}
	var self = this;
	var args = Array.prototype.slice.call(arguments,1);
	var fNOP = function(){};
	var fBound = function(){
		var bindArgs = Array.prototype.slice.call(arguments);
		return self.apply(this instanceof fNOP?this : context,args.concat(bindArgs));
	}
	fNOP.prototype = this.prototype;
	fBound.prototype = new fNOP();
	return fBound;
}

最简化版:

Function.prototype.myBind = function(context){
//判断是否是undefined和null
	if(typeof context === 'undefined' || context === null){
		context = window;
	}
	self = this;
	return function(...args){
		return self.apply(context,args);
	}
}

好啦!说实话,写到这里我也还有点蒙,我先消化一下~
欢迎大家留言讨论!一起消化!

在这里插入图片描述

  • 10
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值