本篇内容介绍了“怎么手写实现bind函数”的有关知识,在实际案例的操作过程中,不少人都会遇到这样的困境,接下来就让小编带领大家学习一下如何处理这些情况吧!希望大家仔细阅读,能够学有所成!
前面发了一篇文章,面试题目之原生实现call、apply、bind,这篇文章介绍了如何手动实现call、apply、bind,但是前不久重读这篇文章时发现了实现bind的代码不是很完善,我们看一段代码:
function Person(){
this.name="zs";
this.age=18;
this.gender="男"
}
var obj={
hobby:"看书"
}
// 将构造函数的this绑定为obj
var changePerson = Person.bind(obj);
// 直接调用构造函数,函数会操作obj对象,给其添加三个属性;
changePerson();
// 1、输出obj
console.log(obj);
// 用改变了this指向的构造函数,new一个实例出来
var p = new changePerson();
// 2、输出obj
console.log(p);
代码输出结果:
1、输出:
2、输出:
仔细观察上面的代码,再看输出结果。
我们对Person类使用了bind将其this指向obj,得到了changeperson函数,此处如果我们直接调用changeperson会改变obj,若用new调用changeperson会得到实例 p,并且其__proto__指向Person,我们发现bind失效了。
我们得到结论:用bind改变了this指向的函数,如果用new操作符来调用,bind将会失效。
再看我们这篇文章(面试题目之原生实现call、apply、bind)中bind实现的代码:
Function.prototype.myBind = function(ctx, ...argv1) {
return (...argv2) => {
return this.call(ctx, ...argv1, ...argv2)
}
}
如果看不太习惯,将其转化为es5的执行方式:
Function.prototype.mybind = function(){
// 1、保存函数
var _this = this;
// 2、保存目标对象
var context = arguments[0]||window;
// 3、保存目标对象之外的参数,将其转化为数组;
var rest = Array.prototype.slice.call(arguments,1);
// 4、返回一个待执行的函数
return function F(){
// 5、将二次传递的参数转化为数组;
var rest2 = Array.prototype.slice.call(arguments)
//6、用apply调用第一步保存的函数,并绑定this,传递合并的参数数组
_this.apply(context,rest.concat(rest2));
}
}
我们用自己实现的mybind函数,来实现文章最上面的例子,测试一下如果,用mybind函数改变了构造函数的this,然后用new来执行生成的新函数,能否得到和原生bind一样的效果,测试代码如下:
function Person(){
this.name="zs";
this.age=18;
this.gender="男"
}
var obj={
hobby:"看书"
}
// 将构造函数的this绑定为obj ,此处调用上面开发的mybind方法;
var changePerson = Person.mybind(obj);
// 直接调用构造函数,函数会操作obj对象,给其添加三个属性;
changePerson();
// 1、输出obj
console.log(obj);
// 用改变了this指向的构造函数,new一个实例出来
var p = new changePerson();
// 2、输出obj
console.log(p);
查看输出结果:
1、输出:
2、输出:
我们用上面实现的mybind改变函数的this,然后调用new方法,发现并未实现和原生bind一样的效果,我们实现的mybind方法和原生的bind实现的功能还有些差距,那么我们如何修正呢?
仔细观察代码,发现突破点再这里: new changeperson()。
这里我们只需要在调用 new changeperson()时候,判断一下,是否是通过new操作符调用的,如果是new 操作符调用的话,我们就用new直接调用未改变this之前的函数,并返回其结果。
那么如何判断是否是通过new操作符来调用一个函数呢?这里我们就要用到instanceof了,看看官方文档对其解释:
| instanceof运算符用于测试构造函数的prototype属性是否出现在对象的
| 原型链中的任何位置。
翻译成大白话,就是判断某个实例是否由某个类或者构造函数生成。
回归正文,我们知道,我们在用new操作符调用一个构造函数时,或者普通函数,都会在函数内部执行如下步骤:
1、生成一个空对象,
2、然后将this指向这个空对象,
3、最后将这个对象返回。
而这个对象就是这个构造函数的实例,那么只要在函数内部执行 this instanceof 构造函数 来判断其结果是否为true,就能判断函数是否是通过new操作符来调用了,若结果为true则是用new操作符调用的,代码修正如下:
Function.prototype.mybind = function(){
// 1、保存函数
var _this = this;
// 2、保存目标对象
var context = arguments[0]||window;
// 3、保存目标对象之外的参数,将其转化为数组;
var rest = Array.prototype.slice.call(arguments,1);
// 4、返回一个待执行的函数
return function F(){
// 5、将二次传递的参数转化为数组;
var rest2 = Array.prototype.slice.call(arguments)
if(this instanceof F){
// 6、若是用new操作符调用,则直接用new 调用原函数,并用扩展运算符传递参数
return new _this(...rest2)
}else{
//7、用apply调用第一步保存的函数,并绑定this,传递合并的参数数组
_this.apply(context,rest.concat(rest2));
}
}
}
此时,测试在运行上面的测试案例,打印结果为:
完美实现了和原生bind一样的效果,对一个知识点进行比较深入的研究确实不容易,越深入发现涉及的知识越广泛,就像这篇文章,虽然说得是bind的手动实现,但是其实涉及了new操作符调用的原理,instanceof 的用法。
“怎么手写实现bind函数”的内容就介绍到这里了,感谢大家的阅读。如果想了解更多行业相关的知识可以关注亿速云网站,小编将为大家输出更多高质量的实用文章!
免责声明:本站发布的内容(图片、视频和文字)以原创、转载和分享为主,文章观点不代表本网站立场,如果涉及侵权请联系站长邮箱:is@yisu.com进行举报,并提供相关证据,一经查实,将立刻删除涉嫌侵权内容。