脚本宝典收集整理的这篇文章主要介绍了模拟实现bind方法,脚本宝典觉得挺不错的,现在分享给大家,也给大家做个参考。
前言
最近接触到bind方法,为了加深理解,自己模拟实现一下。
首先,谈一下bind()方法的特点:
- 把调用bind()的函数(以后统称为初试函数)中的this指向绑定到某个对象上,bind的第二个及其以后的参数作为作为 初始函数的 参数,bind()执行完返回一个新的函数。
- 新返回的函数如果用 new 字符来调用的话,那么之前this指向绑定到某个对象上 将会失效,并且初始函数中的this指向绑定到 new调用的 函数实例上。函数实例上还可以调用初始函数的原型上的方法
那么先实现第一个特点
// 因为需要所有函数都能执行,所以绑定到Function.prototype上
Function.prototype.bindFn=function(thisArg) {
if(typeof this !== 'function') {
throw new TypeError(this+' is not a function');
}
// 调用bindFn方法的函数的引用
var self = this;
// 以数组形式保存第二个及其以后的参数
var beforeArg = [].slice.call(arguments,1);
var bound = function() {
// 以数组形式保存着当前函数的所有参数
var afterArg = [].slice.call(arguments);
// bindFn第二个及其以后的参数和当前函数所有参数的 集合
// 全部传到self函数的参数里
var finalArgs = beforeArg.concat(afterArg);
// self中的this指向绑定到 thisArg
return self.apply(thisArg, finalArgs);
}
return bound;
}
var obj1={sex:23};
function m1(a,b) {
console.log(a+b); // 6
console.log(this); // {sex:23}
}
var bound=m1.bindFn(obj1,2);
var result=bound(4);
可见m1中的this指向了obj1对象,2,4分别传给了形参a,b。
进一步完善bindFn,实现第二个特点
// 因为需要所有函数都能执行,所以绑定到Function.prototype上
Function.prototype.bindFn=function(thisArg) {
if(typeof this !== 'function') {
throw new TypeError(this +' is not a function');
}
// 调用bindFn方法的函数的引用
var self = this;
// 以数组形式保存第二个及其以后的参数
var beforeArg = [].slice.call(arguments,1);
var bound = function() {
// 以数组形式保存着当前函数的所有参数
var afterArg = [].slice.call(arguments);
// bindFn第二个及其以后的参数和当前函数所有参数的 集合
// 全部传到self函数的参数里
var finalArgs = beforeArg.concat(afterArg);
// 如果new调用的话,this指向实例对象
// 否则this指向需要绑定的对象
// this instanceof bound并不准确,可以用es6中的new.target来解决
return self.apply(this instanceof bound ? this : thisArg, finalArgs);
}
// new调用的时候有用
// 避免es6中的箭头函数
// 箭头函数没有prototype
if(this.prototype) {
// 避免修改 bound.prototype 污染到 this.prototype
function a(){}
a.prototype=this.prototype;
bound.prototype=new a();
bound.prototype.constructor=bound;
}
return bound;
}
var obj1={sex:23};
function m1(a,b) {
this.name='m1';
console.log(a+b); // 6
console.log(this); // {name:'m1'}
}
m1.prototype.allKey=function() {
console.log('this is allkey');
}
var bound=m1.bindFn(obj1,2);
var result=new bound(4);
result.allKey(); // this is allkey
可见,m1中的this指向了result这个实例对象,并且result 通过原型链 继承了m1.prototype上的方法。
那么我们看下官方bind的使用:
// 关键部分
function m1(a,b) {
this.name='m1';
console.log(a+b); // 6
console.log(this); // {name:'m1'}
}
var bound=m1.bind(obj1,2);
var result=new bound(4);
result.allKey(); // this is allkey
由上图可见,虽然原型链上有所不同,但我们模拟的方法还是实现了bind的功能。我们模拟的方法bindFn参考mdn上的Polyfill。基本上关于模拟bind的实现就是这样。
现在我们在延伸一下关于原型链的知识,如果刚才的bound.prototype完全重写在了bound构造函数内,又会怎样?
// 因为需要所有函数都能执行,所以绑定到Function.prototype上
Function.prototype.bindFn=function(thisArg) {
if(typeof this !== 'function') {
throw new TypeError(this +': is not a function');
}
// 调用bindFn方法的函数的引用
var self = this;
// 以数组形式保存第二个及其以后的参数
var beforeArg = [].slice.call(arguments,1);
var bound = function() {
// 以数组形式保存着当前函数的所有参数
var afterArg = [].slice.call(arguments);
// bindFn第二个及其以后的参数和当前函数所有参数的 集合
// 全部传到self函数的参数里
var finalArgs = beforeArg.concat(afterArg);
// 用new 方式调用
if(this instanceof bound) {
// 避免es6箭头函数
// 箭头函数没有prototype
if(self.prototype) {
// 避免修改 bound.prototype 污染到 self.prototype
function a(){}
a.prototype=self.prototype;
bound.prototype=new a();
bound.prototype.constructor=bound;
}
// self中的this指向 new生成的实例对象
return self.apply(this,finalArgs);
}
// 无new 调用方式, self中的this指向thisArg
return self.apply(thisArg, finalArgs);
}
bound.prototype.getKey=function() {
}
return bound;
};
var obj1={sex:23};
function m1(a,b) {
this.name='m1';
console.log(a+b); // 6
console.log(this);
}
m1.prototype.allKey=function() {
console.log('this is allkey');
}
var bound=m1.bindFn(obj1,2);
var result=new bound(4);
result.allKey(); // 报错 result.allKey is not a function
按理说 result原型链上应该有allKey方法的,结果却报错了,我们把m1中的this打印一下看看
这里我们看到原型链上并没有m1.prototype, 所以在构造函数内部 完全重写 构造函数原型对象的话,原型链会与预想的不同。
那么图上的bound.prototype打个 ? ,因为并不确定这个是bound.prototype,如果真是bound.prototype的话,那么下面的话如何解释?
console.log(result.__proto__ === bound.prototype ) // false
按理说 result.__proto__ 指向的是bound.prototype,结果却为false。所以不能肯定刚才的就是bound.prototype
总结: 完全重写构造函数原型对象 要放在构造函数后面,如果放在构造函数里面,可能原型链会有错误。
以上是脚本宝典为你收集整理的模拟实现bind方法全部内容,希望文章能够帮你解决模拟实现bind方法所遇到的问题。
本图文内容来源于网友网络收集整理提供,作为学习参考使用,版权属于原作者。
如您有任何意见或建议可联系处理。小编QQ:384754419,请注明来意。