bind
bind的基本用法
bind() 方法创建一个新的函数,在 bind() 被调用时,这个新函数的 this 被指定为 bind() 的第一个参数,而其余参数将作为新函数的参数,供调用时使用。——MDN
示例:
this.x = 9; // 在浏览器中,this 指向全局的 "window" 对象
var module = {
x: 81,
getX: function() { return this.x; }
};
module.getX(); // 81
var retrieveX = module.getX;
retrieveX();
// 返回 9 - 因为函数是在全局作用域中调用的
// 创建一个新函数,把 'this' 绑定到 module 对象
// 新手可能会将全局变量 x 与 module 的属性 x 混淆
var boundGetX = retrieveX.bind(module);
boundGetX(); // 81
我们使用bind的时候都是采用function.bind(obj)的方式,这样就能够让obj变成fn函数中的this,所以我们可以把obj当成call参数直接传给fn,不就可以实现bind了吗?
其次,fn.bind返回了一个函数,这样我们就可以采用下面这种方法传参和调用
fn.bind(obj,123)(456) //this是obj
上面是采用柯里化的方式,不但在绑定bind时可以传递参数,在调用后同样可以加参数。
了解了bind的基本用法,那么我们就可以动手实现
ES6实现
Function.prototype.likeBind=function (asThis,...args){
const fn=this //这里是把取到调用bind的fn
return (...args1)=>{
return fn.likeCall(asThis,...args,...args1)
}
}
上面要注意,因为要使用this取到调用bind的函数,所以不能使用箭头函数,不然就会让this变成window。
测试用例
import bind from "./hello";
function fn() {
return this;
}
console.assert(fn.bind === Function.prototype.bind, "fn的bind没有被替换");
fn.bind = bind; //把fn的原型上的bind改成自己写的bind
console.assert(fn.bind !== Function.prototype.bind, "fn的bind已经被替换了");
const obj = { name: "123" };
console.assert(fn.bind(obj)() === obj, "this通过");
function fn2(...args) {
return [this, ...args];
}
fn2.bind = bind;
console.assert(fn2.bind !== Function.prototype.bind, "fn2的bind已经被替换");
console.assert(fn2.bind(obj)(456)[0] === obj, "传参this通过");
console.assert(fn2.bind(obj)(456)[1] === 456, "参数通过");
console.assert(fn2.bind(obj, 123)(456)[1] === 123, "参数1通过");
console.assert(fn2.bind(obj, 123)(456)[2] === 456, "参数2通过");
在测试时要记得把bind放到Function.prototype上,上面的测试用例我是直接替换函数原型上的bind,因为TS不允许我直接修改Function.prototype.bind
ES5实现
下面采用ES5实现bind
Function.prototype.likeBind = function () {
var fn = this;
var args=Array.prototype.slice.call(arguments)
var asThis = args[0];//获取到要绑定的this
var args1 = args.slice(1);//获取到参数1
return function () {
var args2 = Array.prototype.slice.call(arguments);//获取到参数2
return fn.apply(asThis, args1.concat(args2));
};
};
采用ES5的思路是通过获取到arguments,已知第一个arguments的第一个参数为要绑定成this的对象,所以只要获取到它和其他参数,最后使用apply绑定就可以了。
call
call的用法
function func (a,b,c) {}
func.call(obj, 1,2,3)
// func 接收到的参数实际上是 1,2,3
func.call(obj, [1,2,3])
// func 接收到的参数实际上是 [1,2,3],undefined,undefined
手写call
call跟bind的区别就是call会根据传的第一个参变成fn的this,然后调用这个函数。
Function.prototype.call2=function(asThis,...rest){
const obj=asThis||window //有些人可能不会传参,那么就变成window
obj.fn=this //将调用call的函数绑在obj上,记得要删除
const result=obj.fn(...rest) //把结果记下来
delete obj.fn //删除
return result
}
测试用例
function fn() {
return this;
}
const obj={name:'123'}
console.assert(fn.call2(obj)===obj,'报错说明call2的this不对')
function fn2(x,y){
return [this,x,y]
}
console.assert(fn2.call2(obj,123)[0]===obj,'报错说明call2的this不对')
console.assert(fn2.call2(obj,123)[1]===123,'报错说明call2的传参无效')
console.assert(fn2.call2(obj,123,456)[1]===123,'报错说明call2的传参无效')
console.assert(fn2.call2(obj,123,456)[2]===456,'报错说明call2的传参2无效')
const obj2={
name:'qiuyanxi',
fn:fn2
}
console.assert(obj2.fn.call2(obj,123)[0]!==obj,'报错说明this是obj')
console.assert(obj2.fn.call2(obj,123)[1]!==123,'报错说明参数是123')
最终结果符合设想正确
apply
apply的用法
func.apply(obj, [1,2,3])
// func 接收到的参数实际上是 1,2,3
func.apply(obj, {
0: 1,
1: 2,
2: 3,
length: 3
})
// func 接收到的参数实际上是 1,2,3
手写apply
apply跟call就是传参不一样,实际效果都是一样的,
所以我们稍微改写一下就行
Function.prototype.call2=function(asThis,rest){
const obj=asThis||window //有些人可能不会传参,那么就变成window
obj.fn=this //将调用call的函数绑在obj上,记得要删除
const result=obj.fn(...rest) //就这里不一样
delete obj.fn //删除
return result
}