1.手写call和apply函数
手写call函数:
步骤:
1.判断第一个参数的类型
2.将当前的函数作为传入对象的方法
3.取出argument类数组的值
4.执行obj.当前函数(newarugument),并保存返回值
5.删除传入对象的方法
6.返回返回值
代码如下:
Function.prototype.newCall = function (obj) {
if (typeof obj != "object" || obj == null) { obj = window }
obj.fn = this
let newArguments = [];
for (let i = 1; i < arguments.length; i++) {
newArguments.push(arguments[i])
}
var result = obj.fn(...newArguments)
delete obj.fn
return result
}
手写apply函数:
apply函数和call函数唯一不同的就是参数不同,apply是有两个参数的。我们可以进行判断,如果没有第二个参数那么我们直接执行函数,如果有参数再去 做参数的处理。
代码如下:
Function.prototype.newApply = function (obj, arr) {
var result;
if (typeof obj != "object" || obj == null) obj = window
obj.fn = this
if (arr == null) {
result = obj.fn()
} else {
let newArgument = []
for (let i = 0; i < arr.length; i++) {
newArgument.push(arr[i])
}
result = obj.fn(...newArgument)
}
delete obj.fn;
return result
}
2.手写bind函数
首先我们需要清楚bind函数的作用以及一些特性。bind函数一般返回一个函数,并且具有以下的特性:
- 具有柯里化的特性
- 返回的函数可以进行new实例化(实例化后bind绑定的obj对象this会丢失)
我们来分析一下,事实上如果是单纯的改变this指向,bind 的内部并不复杂,因为它直接使用了apply函数。重要的是我们如何实现它的两个特性。
首先柯里化的特性我们是这样实现的:我们知道在内部我们会返回一个函数,因为我们就有了两个arguments,这两个arguments分别是我们先后传递的参数,我们只需要将这两个的arguments参数处理并且拼接就可以完成了。
接着就是new实例化的操作,首先我们如果使用了new操作,那么我们内部返回的函数的this就会指向这个构造函数的实例,如果不适用指向window,所以我们当其指向构造函数的实例时我们就将this绑定到apply上,如果不是就绑定在之前的obj上。最后再将我们调用函数的原型指向我们返回函数的原型上,这样一来我们相当于做了一个继承的操作,如果我们进行了实例化操作我们就可以拿到person原型上的数据了。
代码实现:
Function.prototype.newBind = function (obj) {
// 判断传入对象类型
if (typeof obj != "object" || obj == null) obj = window
var that = this
// 外部函数参数
var arr1 = Array.prototype.slice.call(arguments, 1);
var newFun = function () {
console.log(this)
var arr2 = Array.prototype.slice.call(arguments);
//判断时候进行类new实例化操作
if (this instanceof newFun) {
that.apply(this, arr1.concat(arr2))
} else {
that.apply(obj, arr1.concat(arr2))
}
}
newFun.prototype = that.prototype
return newFun;
}