推荐答案
// www.javascriptcn.com code example
Function.prototype.myBind = function(context, ...args) {
if (typeof this !== 'function') {
throw new TypeError('Function.prototype.bind - what is trying to be bound is not callable');
}
const self = this; // 保存原函数引用
const fNOP = function() {}; // 空函数用于原型继承
const fBound = function(...boundArgs) {
// this instanceof fBound 判断是否是通过 new 调用
return self.apply(this instanceof fBound ? this : context, args.concat(boundArgs));
};
if(this.prototype){
fNOP.prototype = this.prototype
}
fBound.prototype = new fNOP(); // 处理原型链
return fBound;
};本题详细解读
1. bind 函数的作用
bind() 方法会创建一个新的函数,当这个新函数被调用时,它的 this 关键字会被设置为传入的第一个参数,后续的参数将作为新函数的参数传递。bind 函数本质上是改变了函数执行时的 this 指向。
2. 实现步骤分解
参数检查:
- 判断调用
myBind的是否是函数,如果不是,抛出TypeError错误。
- 判断调用
保存原函数和
context:- 用
self保存原函数this的引用,后续需要用apply调用。 context就是bind方法的第一个参数,它将作为新函数执行时的this上下文。
- 用
处理预置参数:
- 使用
...args获取bind时传入的预置参数。
- 使用
创建
fNOP空函数:- 用于原型继承,避免修改原函数的原型。
创建返回函数
fBound:- 这是
bind方法返回的新函数。 - 使用
...boundArgs获取新函数调用时传入的参数。 - 处理
this:- 判断是否使用
new调用:- 如果
this instanceof fBound为true, 说明是用new调用的,此时this就是要返回实例对象,不需要改变this。 - 否则就用
context作为this上下文。
- 如果
- 使用
apply方法调用原函数self,并合并预置参数args和新参数boundArgs。
- 判断是否使用
- 这是
处理原型链:
- 为了保证
new fBound()返回的对象能继承原函数的原型属性,需要进行原型链的连接。- 首先,判断原函数是否有
prototype,没有就忽略原型链继承。 - 将
fNOP的原型指向原函数的原型。 - 将
fBound的原型指向fNOP的实例。
- 首先,判断原函数是否有
- 为了保证
返回新函数
fBound:- 将新创建的
fBound返回,该函数的this和参数都被绑定了。
- 将新创建的
3. 为什么要使用 fNOP 空函数
如果直接让 fBound.prototype = this.prototype,会造成原型链的指向问题,当修改 fBound.prototype 时, this.prototype 也会被影响。
例如:
// www.javascriptcn.com code example
function Person(name){
this.name = name
}
Person.prototype.sayHello = function(){
console.log('hello')
}
const bindedPerson = Person.bind({a:1},'leo')
bindedPerson.prototype.sayBye = function(){
console.log('bye')
}
const p = new bindedPerson()
console.log(p) //会把bindedPerson的sayBye方法打印出来,并且 Person的prototype也改变了
console.log(Person.prototype) //可以看到Person的prototype改变了如果使用fNOP 空函数,则只改变了fBound的原型,不会影响原函数Person的原型。
// www.javascriptcn.com code example
function Person(name){
this.name = name
}
Person.prototype.sayHello = function(){
console.log('hello')
}
const bindedPerson = Person.myBind({a:1},'leo')
bindedPerson.prototype.sayBye = function(){
console.log('bye')
}
const p = new bindedPerson()
console.log(p) //只会输出sayHello方法,以及实例属性
console.log(Person.prototype) //可以看到Person的prototype没改变
4. 兼容 new 调用的情况
当使用 new 关键字调用 bind 返回的函数时, this 应该指向新创建的对象实例。因此需要判断是否使用 new 调用。
5. 核心要点
this指向的理解:- 普通函数调用:
this指向调用该函数的对象(在严格模式下,没有对象调用,this指向undefined)。 bind调用:this指向bind函数第一个参数。new调用:this指向新创建的对象实例。
- 普通函数调用:
- 使用
apply方法: 用于改变函数执行的this上下文,并传递参数。 - 原型链继承: 为了保证
new调用的情况下bind函数返回的新函数能够继承原函数的原型属性。