bind会创建一个新的函数返回, 这个函数被调用的时候, this指向bind的第一个参数, 之后的一系列参数都会传入作为函数的参数
- 返回一个函数
- 指定这个函数的this
- 传入参数的实现
1. 实现返回函数和指定this
Function.prototype.bind = function(context){
return () => { // 返回新函数
return this.apply(context) // 新函数内部执行原函数, 并指定this, 将执行结果返回
}
}
2. 实现传参
bind的传参机制有点奇特, 它支持分两次传参, 也就是所需参数不用一次传完, 可以分两次传
var foo = {value: 1};
function bar(name, age) {
console.log(this.value);
console.log(name);
console.log(age);
}
var bindFoo = bar.bind(foo, 'daisy'); // 实现参数固化, 每次调用barFoo时, 原本所需的第一个参数固定了下来, 只用指定第二个
bindFoo('18');
// 1
// daisy
// 18
实现思路: 分别用arguments接收, 然后拼接起来
Function.prototype.bind = function(context){
let arg = Array.from(arguments).slice(1)
return () => { // 返回新函数
let arg2 = Array.from(arguments)
return this.apply(context, arg.concat(arg2)) // 拼接参数数组返回
}
}
以上就基本实现了bind, 但还差点东西:
- 如果调用bind的是一个构造函数, bind指定的this会失效
- 返回函数的prototype应指向原函数的prototype, 使原型链继承下来
3. 实现构造函数的判断
因为需要获取到实例的this, 所以这里就不用箭头函数了
Function.prototype.bind = function(context){
let _this = this // 这里的this指向调用bind的函数
let arg = Array.from(arguments).slice(1)
let bindFn = function(){
let arg2 = Array.from(arguments)
// 只需判断this的原型是否为返回函数.prototpye即可
// 如果返回函数被用作构造函数, 内部的this指向实例, this.__proto__ === bindFn.prototype, 下面三元表达式结果为true, 此时不改变this指向
// 如果返回函数被用作普通函数, 内部this指向window, this.__proto__ === Window.prototype, 下面三元表达式结果为false, 将this绑定为指定的context
return _this.apply(this instanceof bindFn ? this : context, arg.concat(arg2))
}
return bindFn
}
4. 保持函数的原型
因为调用bind的函数可能是用作构造函数, 所以原函数上可能有些属性需要提供给实例访问
举个例子, 上面代码, 原有函数bind之前, 原有函数new出来的实例的原型指向原函数.prototype
但bind之后, 返回了bindFn, new出来的实例的原型指向bindFn.prototype, 丢失了原函数.prototype这一原型
Function.prototype.bind = function(context){
let _this = this
let arg = Array.from(arguments).slice(1)
let bindFn = function(){
let arg2 = Array.from(arguments)
return _this.apply(this instanceof bindFn ? this : context, arg.concat(arg2))
}
// 这里不能直接写 bindFn.prototype = _this.prototype
// 如果这样写, 那 bindFn.prototype 和 原函数.prototype 会持有同一个对象的引用, 一旦我们修改了其中一个, 另一个也会改变
// 所以我们用一个函数中转
let bus = function(){}
bus.prototype = _this.prototype
bindFn.prototype = new bus() // 此时, new bindFn()实例的 __proto__ 指向这个new bus()实例, 这个new bus()实例的 __proto__ 又指向原函数.prototype, 原型链构建成功(*^▽^*)
return bindFn
}
5. 边界情况考虑
假如 bind 的不是一个函数呢?
拒绝。
if(typrof this !== "function"){
throw new Error("bind必须是函数调用")
return
}
最终代码:
Function.prototype.bind = function(context){
if(typrof this !== "function"){
throw new Error("bind必须是函数调用")
return
}
let _this = this
let arg = Array.from(arguments).slice(1)
let bindFn = function(){
let arg2 = Array.from(arguments)
return _this.apply(this instanceof bindFn ? this : context, arg.concat(arg2))
}
let bus = function(){}
bus.prototype = _this.prototype
bindFn.prototype = new bus()
return bindFn
}
网友评论