前言
看了两篇文章,对于其中难以理解的部分使用es6的扩展运算符稍作修改,并且进行了一些扩展,总结得到这篇文章
https://github.com/lin-xin/blog/issues/7
https://blog.csdn.net/lovefengruoqing/article/details/80186401
1 实现bind
- 1 bind创建了一个新的函数
- 2 在 bind() 被调用时,这个新函数的 this 被指定为 bind() 的第一个参数,而其余参数将作为新函数的参数,供调用时使用。
- 3 所有的函数都是Function的实例,所以均可以访问Function原型上的方法和属性,所以将binding方法定义在其原型上
Function.prototype.binding = function () {
var self = this; // this就是调用binding方法的函数自身
// 第一个参数作为上下文保存
var context = [...arguments].shift();
// 如果binding的第一个参数时undefined或者Null,则令其为window
if(context === undefined || context === null){
context = 'window'; //或者globalThis es11中引入了globalThis,在nodejs12中也认可了这个对象指代全局对象global
}
// 保存剩余的参数
var args = [...arguments].slice(1);
return function () {
//
return self.apply(context, [...args, ...arguments]);
};
};
2 注意点
2.1 arguments是类数组对象
arguments是一个对象,但是它不是Array的实例,它只是一个类数组对象
typeof arguments // "object"
arguments instanceof Array // false
下面是输出的一个例子:
所以如果我们需要使用数组的方法对其进行操作就需要将其转换为数组,es6中提供的扩展运算符可以很方便地将类数组对象转换为数组对象。
需要注意的是:[...arguments]
等同于开辟了一个新的堆内存,产生了一个新数组对象,然后对该新对象执行shift操作,所以它不会修改arguments
的结构,在保存剩余参数时[...arguments]
仍然包含全部的输入实参,所以需要使用slice(1)
,保存除了第一个参数外的剩余实参
// 第一个参数作为上下文保存
var context = [...arguments].shift();
// 保存剩余的参数
var args = [...arguments].slice(1);
2.2 参数的传递
调用bind产生新函数时除了传递指定的上下文对象外,还可以传递其他参数args1
,供新函数调用时使用
新函数调用时同样可以传递参数args2
,但是这些参数一定是在args1
的后面的
如果args1
占据了所有的形参位,则args2
不会对参数进行覆盖
var obj = { a: 100};
var a = 1000;
function f(a,b,c){
return this.a+b+c;
}
console.log(f(1,2,3)); // 1005
var f1 = f.binding(obj,10,20);
console.log(f1(30,40,50)); // 150
上例中,实际传入f1中的参数有10,20,30,40,50
,但是形参位只有3个,所以a=10,b=20,c=30
,而由于this指向obj,所以this.a+b+c = 150
2.3 上下文对象为undefined或者null时将其指定为全局对象window
es11中引入了globalThis
指代全局对象window,在nodejs12中也认可了这个对象指代全局对象global
if(context === undefined || context === null){
context = 'window'; // globalThis
}
3 不借助于apply方法实现
Function.prototype.binding = function () {
var self = this; // this就是调用binding方法的函数自身
// 第一个参数作为上下文保存
var context = [...arguments].shift();
if(context === undefined || context === null){
context = 'window'; // globalThis
}
// 保存剩余的参数
var args = [...arguments].slice(1);
return function () {
// 这也是自实现apply方法的方式
// 将原函数绑定到预定的上下文对象上
context[self] = self;
// 使用预定的上下文对象.原函数的方式调用,则内部this就会指向该对象
var res = context[self](...args, ...arguments);
// 删除为该对象添加的方法
delete context[self];
// 返回结果
return res;
};
};
4 apply自实现
apply方法总共传入两个参数,一个指定上下文对象,一个是参数组成的数组,所以直接使用arguments[0],arguments[1]
获取即可
Function.prototype.apply = function () {
var self = this; // this就是调用binding方法的函数自身
// 第一个参数作为上下文保存
var context = arguments[0];
if (context === undefined || context === null) {
context = "window"; // globalThis
}
// 保存剩余的参数
var args = arguments[1];
context[self] = self;
// 使用预定的上下文对象.原函数的方式调用,则内部this就会指向该对象
var res = context[self](...args);
// 删除为该对象添加的方法
delete context[self];
// 返回结果
return res;
};
5 call自实现
call方法传入的第一个参数是一个指定上下文对象,其余是函数的参数,所以不能直接使用arguments[0],arguments[1]
获取
Function.prototype.call1 = function () {
var self = this; // this就是调用binding方法的函数自身
// 第一个参数作为上下文保存
var context = [...arguments].shift();
if (context === undefined || context === null) {
context = "window"; // globalThis
}
// 保存剩余的参数
var args = [...arguments].slice(1);
console.log(args);
context[self] = self;
// 使用预定的上下文对象.原函数的方式调用,则内部this就会指向该对象
var res = context[self](...args);
// 删除为该对象添加的方法
delete context[self];
// 返回结果
return res;
};