1、call 的源码实现
Function.prototype._call = function(){
let context = [].shift.call(arguments) // 类数组对象转数组,删除并返回第一个参数 this
let args = arguments // 剩余参数
context = context || window
context._this = this
context._this(...args) // 将类数组对象展开
delete context._this
}
2、apply的源码实现
Function.prototype._apply = function(){
let context = [].shift.call(arguments) // 类数组对象转数组,删除并返回第一个参数 this
let args = [].slice.call(...arguments) // 类数组对象转数组(剩余参数)
context = context || window
context._this = this
context._this(...args)
delete context._this
}
调用方式
3、bind的源码实现
Function.prototype._bind = function(){
let _this= this //原函数
let context = [].shift.call(arguments) // 类数组对象转数组,删除并返回第一个参数 this
let args = [].slice.call(arguments) // 类数组对象转数组(剩余参数)
return function(){
_this.apply(context, args)
}
}
4、调用方式及原理详解
var person = {
name: "林大大哟",
gender: "男",
work: function(enterprise, job) {
console.log(this.name + " , " + this.gender + " , 在" + enterprise + "做" + job);
}
}
// call 方式
person.work._call(person, '某某公司','前端开发'); // 林大大哟 , 男 ,在某某公司做前端开发
// apply 方式
person.work._apply(person, ['某某公司','前端开发']); // 林大大哟 , 男 ,在某某公司做前端开发
// bind 方式
person.work._bind(person)('某某公司','前端开发'); // 林大大哟 , 男 ,在某某公司做前端开发
person.work._bind(person, '某某公司','前端开发')(); // 林大大哟 , 男 ,在某某公司做前端开发
call和apply原理略解
call和apply都是第一参数为调用对象,唯一不同就是后续参数不同,apply为数组,call为任意参数,所以再重写这俩原型时,其实会发现,我并没有显示定义形参,而是通过arguments来获取参数(arguments为类数组对象,和直接数组不同,想要了解的可以自行搜索)。
下面两行是出现在喔代码里最频繁的,如何理解呢,其实就跟 Array.prototype.shift / slice 含义一致,将类数组对象转换成数组,细心点同学会发现,context我们三个重写函数都是一致的,但是args不一致,这是为什么呢?因为最后在调用时我们需要通过拓展符来讲参数展开(那么为什么要展开? 因为上面例子中的work函数形参是分开定义的喔~)所以意味着在最后调用时,必须将所有数组给展开,_apply 函数因为传实参时是传入的数组,然后经过 [].slice.call, 又封装了一层数组,就是二维数组了,所以[].slice.call(...arguments) 这里使用了一次拓展符,然后在调用时又使用了一次拓展符。
let context = [].shift.call(arguments) // 类数组对象转数组,删除并返回第一个参数 this
let args = [].slice.call(...arguments) // apply 重写 类数组对象转数组(剩余参数)
let args = arguments // call 重写 类数组对象转数组(剩余参数)
bind原理略解
bind其实可以看作call和apply的升阶版,因为bind直接返回当前的构造函数,所以我们在最外层调用时,需要再加一个括号才能执行bind函数,那么这部分有同学就会觉得,那这就是闭包啊,bingo,是滴,其实就是闭包的应用,那么既然涉及到闭包,肯定传参方式就不止一种了
person.work._bind(person)('某某公司','前端开发');
person.work._bind(person, '某某公司','前端开发')();
既可以将自定义参数,类似于call方式调用,也可以当作立即执行函数方式来调用