模拟实现Javascript中的bind函数

bind() 方法创建一个新的函数,在 bind() 被调用时,这个新函数的 this 被指定为 bind() 的第一个参数,而其余参数将作为新函数的参数,供调用时使用。

从MDN对于bind的描述来看:

  • 返回值是一个函数,而不是执行结果
  • this值会指向第一个参数
  • 其余参数会作为新函数的参数

看个例子:

function test(name, age) {
	console.log(this.name);
	this.name = name;
	this.age = age;
	console.log(this.name, this.age);
}

var obj = {
	name: 'Willem'
};

var bindFn = test.bind(obj, 'Wei');
bindFn(18);
// Willem
// Wei, 18

从上面的代码就可以看出来,bind函数执行之后,bindFn的this值指向了obj,并且在bind的时候传入的参数和在执行bindFn时传入的参数都成功的传入了test函数。

那代码就呼之欲出了啊。

Function.prototype.wbind = function() {
	var context = [].shift.call(arguments);
	var args = [].slice.call(arguments);
	var self = this;

	return function() {
		var innerArgs = [].slice.call(arguments);
		
		self.apply(context, args.concat(innerArgs));
	}
}

相关:模拟实现Javascript中的call和apply

既然bind返回的是一个函数,那我有一个大胆的想法,如果我把这个返回的函数作为一个构造函数会怎样呢?改造一下上面的那个例子:

function test(name, age) {
	console.log(this.name);
	this.name = name;
	this.age = age;
	console.log(this.name, this.age);
}

test.prototype.sayHi = function() {
	console.log('Hi, ' + this.name);
}

var obj = {
	name: 'Willem'
};

var bindFn = test.bind(obj, 'Wei');
var instance = new bindFn(18);
// undefined
// Wei,18
instance.sayHi(); // Hi, Wei
console.log(obj.name); // Willem

咦,obj对象里面明明是有name属性的啊,为啥第一次输出的是undfined呢?明明传入了name属性为"Wei",为啥obj.name还是"Willem"呢?

其实是因为this并没有指向obj了,而是指向了instance。总结一下,将返回的函数作为普通函数使用时,函数的this指向bind执行时传入的第一个参数;将返回的函数作为构造函数使用时,函数的this指向实例,并且该实例将会继承原函数原型上的属性和方法。

这时候,我们再来改一改wbind函数

Function.prototype.wbind = function() {
	var context = [].shift.call(arguments);
	var args = [].slice.call(arguments);
	var self = this;

	var fBound = function() {
		var innerArgs = [].slice.call(arguments);
		// 做构造函数时,this指向实例
		self.apply(this instanceof fBound ? this : context, args.concat(innerArgs));
	}
	
	// 实例需要继承原函数原型上的方法和属性
	// 使用fNOP中转一次是因为直接将this.prototype赋值到fNOP.prototype时
	// 当修改fNOP的prototype时,this.prototype也会被修改
	var fNOP = function() {}
	if (this.prototype) {
		fNOP.prototype = this.prototype;
	}
	
	// fBound.prototype = { __proto__: { this.prototype } }
	// 相当于是中间多了一个__proto__,因为原型链的缘故,所以多一层__proto__没有什么影响
	fBound.prototype = new fNOP(); 
	
	return fBound;
}

相关:
模拟实现js中的new操作符
简单说说原型和原型链
使用ts模拟实现js中的一些函数和属性

以上,就是bind的相关内容。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值