一起养成写作习惯!这是我参与「掘金日新计划 · 4 月更文挑战」的第19天,点击查看活动详情。
前言
今天继续来模拟一下bind()的实现,之前的一篇文章已经讲解了bind()模拟实现的一部分功能,其中包括了改变this的指向问题,参数的传递问题,可以点击这里跳转查看,本文来模拟bind()的另一个特点,那就是当bind()的返回函数当做构造函数的情况。
bind()的特性
- 改变this的指向
- 可以传递参数
- 当bind()的返回函数作为构造函数时,这时this指向会失效,但传递进去的参数依然生效
我们在下面的代码的基础上来来实现第三点功能
Function.prototype.myBind = function (context) {
var self = this; //这里的this是调用myBind的那个函数
// 获取myBind函数中从第二个参数到最后一个参数
var args = Array.prototype.slice.call(arguments, 1);
return function () {
// 这里的arguments是指bind返回的那个函数传入的参数
var returnFA = Array.prototype.slice.call(arguments);//这句话是把伪数组数组化
return self.call(context, ...args.concat(returnFA));
// return self.apply(context, args.concat(returnFA)); 如果想用apply改变this指向就这样写
}
}
在这里我们要知道的一点是当bind()当做构造函数被new的时候,这时候this的指向是实例化对象,关于this的指向问题可以点击这里查看。当知道了这点之后我们的主要工作就是改变this指向了。
只要将myBind中的return中改变this的指向即可,这时候就要考虑两种情况了,一种是当作为普通函数的情况,这是this应该指向context;另一种就是作为构造函数的情况,这时候就要指向this了(这里的this是实例化对象),所以上面的代码就要做一些更改了,如下
Function.prototype.myBind = function (context) {
var self = this;
// 获取myBind函数中从第二个参数到最后一个参数
var args = Array.prototype.slice.call(arguments, 1);
var fBound = function () {
// 这里的arguments是指bind返回的那个函数传入的参数
var returnFA = Array.prototype.slice.call(arguments);//这句话是把伪数组数组化
return self.call(this instanceof fBound ? this : context, ...args.concat(returnFA)); // 这里就要做区分了,不能单纯的写一个context
// return self.apply(this instanceof fBound ? this : context,, args.concat(returnFA)); 如果想用apply改变this指向就这样写
}
//把返回函数的原型指向调用函数(animal)的原型,这样实例就可以使用调用函数中的属性了
fBound.prototype = this.prototype;
return fBound
}
测试一下:
var dog = {
color: 'white'
};
function animal(name, age) {
console.log(this.color);
console.log(name);
console.log(age);
}
animal.prototype.eat = function () {
console.log('eat')
}
var bindFoo = animal.myBind(dog, '小白');
bindFoo()
var obj = new bindFoo('3');
obj.eat()
结果如下图:
从结果可以看到,当是普通调用的时候this指向是dog。当当作构造函数的时候this指向的是obj,所以this.color打印了undefined。
fBound.prototype = this.prototype;这个操作存在一定的隐患,因为当你修改 fBound.prototype的时候this.prototype也会改变,因此可以改为如下结构
Function.prototype.myBind = function (context) {
var self = this;
var args = Array.prototype.slice.call(arguments, 1);
var fBound = function () {
var returnFA = Array.prototype.slice.call(arguments);
return self.call(this instanceof fBound ? this : context, ...args.concat(returnFA));
// return self.apply(this instanceof fBound ? this : context,, args.concat(returnFA));
}
let fn =function (){}
fn.prototype = this.prototype
fBound.prototype = new fn()
//这里也可以这样写,原因是object.create()的实现就是这样的,如下代码
//fBound.prototype = Object.create(this.prototype)
return fBound
}
Object.create()的实现
object.create = function(obj){
functon fn(){}
fn.prototype = obj
return new fn()
}
总结
虽然call()、apply()、bind()都能改变this的指向,但bind()做的事情感觉更加的复杂,实现起来也更不容易,有空再来复习,gogogo