【js基础修炼之路】- 手把手教你实现bind
程序员文章站
2022-04-03 07:56:44
手写bind前我们先回顾一下bind有哪些特性,以便更好的理解bind和实现bind。 bind的特性 可以看出: bind是函数的方法,只有函数可以调用 bind的第一个参数是this指向,剩下的参数作为调用者的参数 bind方法返回的是一个函数,需要再次调用才能执行 从上面可以看出,new之后t ......
手写bind前我们先回顾一下bind有哪些特性,以便更好的理解bind和实现bind。
bind的特性
var obj = { a: 100, say(one, two) { console.log(this.a, one, two); } } var obj2 = { a: 300 } var res = obj.say.bind(obj2, 1, 2); res(); //300 1 2
可以看出:
bind是函数的方法,只有函数可以调用
bind的第一个参数是this指向,剩下的参数作为调用者的参数
bind方法返回的是一个函数,需要再次调用才能执行
function test(){ this.a = 10, this.b = 20 }; var foo = { a:200 } var res = test.bind(foo); var res2 = new res(); console.log(res2); //test {a: 10, b: 20}
从上面可以看出,new之后this执行不跟随bind的第一个参数了,知道new是怎么实现的小伙伴一定知道为什么(不知道的可以看一下这篇文章的末尾 ) 此时的this指向了res2。
知道了bind的特性,下面我们来实现一下bind
手把手教你实现bind
我们知道bind返回的是一个函数,调用者也是一个函数,并且bind改变了this指向,而且bind还可以传参,下面我们来实现一下这些功能:
function.prototype.bind = function(othis) { // 判断调用者是不是函数 if(typeof this != 'function'){ throw new typeerror('function.prototype.bind - what is trying to be bound is not callable'); } //保存this(this指向调用bind者) var ftobind = this; //获取传入bind函数的第二个及其后面的参数(除去this参数) var aargs = array.prototype.slice.call(arguments,1); var fbound = function(){ //this instanceof fbound === true时,说明返回的fbound被当做new的构造函数调用 //== false的时候说明当做了普通函数来调用,this为bind的第一个参数 return ftobind.apply(this instanceof fbound ? this : othis,aargs.concat(array.prototype.slice.call(arguments))); } // 返回新函数 return fbound; }
上面的代码实现了一个基本的bind,但是还有一些问题,例如上面只是绑定了this,但是原函数的原型新函数并没有继承,所以我们需要再次继承一下原型:
function.prototype.bind = function(othis) { // 判断调用者是不是函数 if(typeof this != 'function'){ throw new typeerror('function.prototype.bind - what is trying to be bound is not callable'); } //保存this(this指向调用bind者) var ftobind = this; //获取传入bind函数的第二个及其后面的参数(除去this参数) var aargs = array.prototype.slice.call(arguments,1); var fbound = function(){ //this instanceof fbound === true时,说明返回的fbound被当做new的构造函数调用 //== false的时候说明当做了普通函数来调用,this为bind的第一个参数 return ftobind.apply(this instanceof fbound ? this : othis,aargs.concat(array.prototype.slice.call(arguments))); } //绑定原型 fbound.prototype = this.prototype; // 返回新函数 return fbound; }
本以为大功告成,但是还有一个问题,看下面的例子:
function bar() {} var bindfoo = bar.bind(null); bindfoo.prototype.value = 1; console.log(bar.prototype.value) // 1
我只改变了bindfoo的原型,bar的为什么也跟着变了,因为在写bind的时候把bar的原型赋给了bindfoo,所以导致了这种情况,下面我们用一个中转的函数来解决这个问题:
function.prototype.bind = function(othis) { // 判断调用者是不是函数 if(typeof this != 'function'){ throw new typeerror('function.prototype.bind - what is trying to be bound is not callable'); } //保存this(this指向调用bind者) var ftobind = this; //获取传入bind函数的第二个及其后面的参数(除去this参数) var aargs = array.prototype.slice.call(arguments,1); var fnop = function() {}; var fbound = function(){ //this instanceof fbound === true时,说明返回的fbound被当做new的构造函数调用 //== false的时候说明当做了普通函数来调用,this为bind的第一个参数 return ftobind.apply(this instanceof fbound ? this : othis,aargs.concat(array.prototype.slice.call(arguments))); } // 为了让 fbound 构造的实例能够继承绑定函数的原型中的值 if (this.prototype) { fnop.prototype = this.prototype; } // 下行的代码使fbound.prototype是fnop的实例,因此 // 返回的fbound若作为new的构造函数,new生成的新对象作为this传入fbound,新对象的__proto__就是fnop的实例 fbound.prototype = new fnop(); // 返回新函数 return fbound; }
对于代码有疑问的小伙伴可以留言或者看注释!!!
上一篇: 漫画爱情图片,爱情与战争,谁输谁赢?
下一篇: 武则天的四大男宠是谁?他们最后结局如何?