Function.prototype.call()
// call函数的签名
fun.call(thisArg, arg1, arg2, ...)
call函数可以这样理解
- 允许为不同的对象分配和调用属于一个对象的函数/方法
- 提供新的 this 值给当前调用的函数/方法。
参数:
- thisArg: 在 fun 函数运行时指定的 this 值。
- arg1..:指定的参数列表。
call函数把某个方法的this改成了第一个参数。给该函数传入多个参数,并执行当前函数。
call函数有这么几个要点,需要理解:
- this可以传null,可以传空,这时候this指向window
- 函数是可以有返回值的!有返回值返回返回值,没有返回值返回undefined
/*
把函数当成对象,然后删除对应的属性值
*/
Function.prototype.call2 = function (context) {
// 判断当前的this对象是否为null,为空指向window
var context = context || window;
// 首先要获取调用call的函数,用this可以获取
context.fn = this;
var args = [];
// 获取call的参数
for(var i = 1, len = arguments.length; i < len; i++) {
args.push('arguments[' + i + ']');
}
// eval执行该函数
var result = eval('context.fn(' + args +')');
// 删掉调用对象的这个属性
delete context.fn
return result;
}
Function.prototype.apply()
// apply的函数签名
func.apply(thisArg, [argsArray])
apply函数和call函数实现的是相同的功能
- 允许为不同的对象分配和调用属于一个对象的函数/方法
- 提供新的 this 值给当前调用的函数/方法。
apply函数的参数:
- thisArg: 在 fun 函数运行时指定的 this 值。
- argsArray:指定的参数数组。
实现:
Function.prototype.apply = function (context, arr) {
// 判断当前的this对象是否为null,为空指向window
var context = context || window;
// 首先要获取调用call的函数,用this可以获取
context.fn = this;
var result;
if (!arr) {
result = context.fn();
}
else {
var args = [];
for (var i = 0, len = arr.length; i < len; i++) {
args.push('arr[' + i + ']');
}
result = eval('context.fn(' + args + ')')
}
delete context.fn
return result;
}
Function.prototype.bind()
// bind函数的函数签名
function.bind(thisArg[, arg1[, arg2[, ...]]])
bind()方法创建一个新的函数,在bind()被调用时,这个新函数的this被bind的第一个参数指定,其余的参数将作为新函数的参数供调用时使用。
参数:
- thisArg 在 fun 函数运行时指定的 this 值。
- arg1..:指定的参数列表。
bind() 函数生成的新函数,不能自己执行,需要手动执行一下。但是这个新函数的this永远都是bind的第一个参数。
因为 bind 还有一个特点,就是
一个绑定函数也能使用new操作符创建对象:这种行为就像把原函数当成构造器。提供的 this 值被忽略,同时调用时的参数被提供给模拟函数。
也就是说当 bind 返回的函数作为构造函数的时候,bind 时指定的 this 值会失效,但传入的参数依然生效
实现
Function.prototype.bind2 = function (context) {
// 调用bind的是否是函数做一个判断
if (typeof this !== "function") {
throw new Error("Function.prototype.bind - what is trying to be bound is not callable");
}
// 把调用者和要传给函数的参数保存一下。
var self = this;
// 获取bind2函数从第二个参数到最后一个参数
var args = Array.prototype.slice.call(arguments, 1);
// 创建一个空函数来中转
var fNOP = function () {};
var fBound = function () {
var bindArgs = Array.prototype.slice.call(arguments);
// 当作为构造函数时,this 指向实例,此时结果为 true,将绑定函数的 this 指向该实例,可以让实例获得来自绑定函数的值
// 以上面的是 demo 为例,如果改成 `this instanceof fBound ? null : context`,实例只是一个空对象,将 null 改成 this ,实例会具有 habit 属性
// 当作为普通函数时,this 指向 window,此时结果为 false,将绑定函数的 this 指向 context
return self.apply(this instanceof fNOP ? this : context, args.concat(bindArgs));
}
// 修改返回函数的 prototype 为绑定函数的 prototype,实例就可以继承绑定函数的原型中的值
fNOP.prototype = this.prototype;
fBound.prototype = new fNOP();
return fBound;
}