call函数的使用方式如下:
var obj = {
value: 1
};
function foo(name, age) {
console.log(this.value);
console.log(name);
console.log(age);
}
foo.call(obj, 'aaa', 12); // 1 aaa 12
首先我们要知道call函数都有哪些功能:
1. 改变了this的指向,让它指向obj
2. 函数foo执行
3. 允许传入参数,且参数个数不确定
4. 允许第一个参数context为null
5. 执行后的返回值为函数执行后的返回值
如何实现this指向obj呢,参考对象内部函数定义,如果fn是obj的方法属性,那么就this就指向了obj,然后执行函数,之后再删除函数,这就实现了前两步。
Function.prototype.call2 = function (context){
context.fn = this;
context.fn();
delete context.fn;
}
如何允许传入参数,用到arguments,要将参数一个个传入context.fn,只能动态拼接执行,考虑用eval,eval能将string字符串作为js执行的函数。
Function.prototype.call2 = function (context){
context.fn = this;
var args = [];
for(var i = 1, len = arguments.length; i < len; i++) {
args.push('arguments[' + i + ']');
}
eval( 'context.fn(' + args + ')' );
delete context.fn;
}
如何允许第一个参数可以为null,验证context是否存在,context默认为window对象。即 context = context || window
如何允许有返回值,将函数的执行结果用result变量存储,返回result即可。最终代码如下:
Function.prototype.call2 = function (context){
context = context || window;
context.fn = this;
var args = [];
for(var i = 1, len = arguments.length; i < len; i++) {
args.push('arguments[' + i + ']');
}
const result = eval('context.fn('+ args + ')');
delete context.fn
return result
}
const fn = function(name, age) {
console.log(this.value);
return {
name: name,
age: age
};
}
const obj = {
value: 'obj'
}
var value = 'window';
fn.call(null); //'window'
const result = fn.call2(obj, '小明', 12); // 'obj'
console.log(result.age); //12
apply函数的功能和call基本一致,区别在于只有两个参数,第一个参数是context,第二个是函数参数数组。实现和call一致,实现代码如下:
Function.prototype.apply = function(context, arr) {
var ctx = context || window;
ctx.fn = this;
var result;
if (!arr) {
result = ctx.fn();
} else {
var args = [];
for (var i = 0, len = arr.length; i < len; i++) {
args.push('arr[' + i + ']');
}
result = eval('ctx.fn(' + args + ')');
}
delete ctx.fn;
return result;
};