learn
learn copied to clipboard
JavaScript深入之call和apply的模拟实现
call 和 apply 的模拟实现
call
call()
在使用一个指定的 this 值和若干个指定的参数值的前提下,调用某个函数或方法。该方法的语法和作用与 apply() 方法类似,只有一个区别,就是 call() 方法接受的是一个参数列表,而 apply() 方法接受的是一个包含多个参数的数组。
使用 call 方法调用函数并且指定上下文的 'this'
var value = 1;
var obj = {
value: 2
};
function foo() {
console.log(this.value);
}
foo(); // 1
foo.call(obj); // 2
使用 call 方法调用父构造函数
function Person(name, age) {
this.name = name;
this.age = age;
}
function Tao(name, age, job) {
Person.call(this, name, age);
this.job = job;
}
var tao = new Tao("yangtao", 27, "Teacher");
所以我们模拟的步骤可以分为:
- 将函数设为对象的属性
- 执行该函数
- 删除该函数
// 类似于:
var foo = {
value: 1,
bar: function() {
return this.value;
}
};
foo.bar(); // 1
// 第一步
foo.fn = bar;
// 第二步
foo.fn();
// 第三步
delete foo.fn;
第一版:绑定 this
Function.prototype.mycall = function(context) {
context.fn = this;
context.fn();
delete context.fn;
};
// 测试一下
var foo = {
value: 1
};
function bar() {
console.log(this.value);
}
bar.mycall(foo); // 1
// 如下所示
var foo = {
value: 1,
bar: function() {
console.log(this.value);
}
};
foo.bar();
第二版:给定参数
Function.prototype.mycall = function(context, name, age) {
context.fn = this;
context.fn();
var args = [];
for (var i = 1, l = arguments.length; i < l; i++) {
args.push("arguments[" + i + "]");
}
eval("context.fn(" + args + ")");
delete context.fn;
};
第三版:传参为 null 和返回结果
Function.prototype.mycall = function(context) {
var context = context || window;
//获取调用call的函数,用this可以获取
context.fn = this;
var args = []; // ["arguments[1]", "arguments[2]"]
for (var i = 1, l = arguments.length; i < l; i++) {
args.push("arguments[" + i + "]");
}
// 把传给call的参数传递给了context.fn函数
// context.fn(args.join(','));
// context.fn(...args)
var result = eval("context.fn(" + args + ")");
delete context.fn;
return result;
};
第四版:考虑 context,以及 context.fn 的可能性
Function.prototype.myCall = function(context) {
// 这一步如果不强制是 object 类型,可以省略
if (typeof context != "object") {
throw new Error("Arguments error");
}
context = context || window;
var args = [];
var result;
if ("fn" in context && context.hasOwnProperty("fn")) {
var fn = context.fn;
var fnFlag = true;
}
context.fn = this;
for (var i = 1, l = arguments.length; i < l; i++) {
args.push("arguments[" + i + "]");
}
result = eval("context.fn(" + args + ")");
if (fnFlag) {
context.fn = fn;
} else {
delete context.fn;
}
return result;
};
apply
apply()
同call()
,只不过将多个参数值,以数组的形式传入而已。
用 apply 将数组添加到另一个数组:
var arr = ["a", "b"];
var arr2 = [1, 2];
arr.push.apply(arr, arr2);
console.log(arr); // ["a", "b", 1, 2]
使用 apply 和内置函数:
var nums = [1, 10, 3, 6, 2];
var max = Math.max.apply(null, nums); // 10
var min = Math.min.apply(null, nums); // 1
// ES6 写法:
var max = Math.max(...nums); // 10
var min = Math.min(...nums); // 1
Function.prototype.myapply = function(context, arr) {
if (typeof context !== "object") {
throw new Error("Arguments error");
}
context = context || window;
context.fn = this;
let result;
if (!arr) {
result = context.fn();
} else {
let args = [];
for (var i = 0, l = arr.length; i < l; i++) {
args.push("arr[" + i + "]");
}
result = eval("context.fn(" + args + ")");
}
delete context.fn;
return result;
};