区别
call() 和 apply()的区别在于,call()方法接受的是若干个参数的列表,而 apply()方法接受的是一个包含多个参数的数组
var func = function(arg1, arg2) {};
func.call(this, arg1, arg2); // 使用 call,参数列表
func.apply(this, [arg1, arg2]); // 使用 apply,参数数组
复制代码
使用场景
数组中最大值
var numbers = [5, 458, 120, -215];
Math.max.apply(Math, numbers); //458
Math.max.call(Math, 5, 458, 120, -215); //458
// ES6
Math.max.call(Math, ...numbers); // 458
复制代码
验证是否是数组
function isArray(obj){
return Object.prototype.toString.call(obj) === '[object Array]';
}
isArray([1, 2, 3]);
// true
// 直接使用 toString()
[1, 2, 3].toString(); // "1,2,3"
"123".toString(); // "123"
123.toString(); // SyntaxError: Invalid or unexpected token
Number(123).toString(); // "123"
Object(123).toString(); // "123"
复制代码
可以通过 toString() 来获取每个对象的类型,但是不同对象的 toString()有不同的实现,所以通过 Object.prototype.toString() 来检测,需要以 call() / apply() 的形式来调用,传递要检查的对象作为第一个参数。
call实现
/**
context 是改变this的目标对象,...args扩展符兼容多个参数
*/
Function.prototype.myCall = function(context, ...args) {
// 此处的 this,指向了 say 方法,打印的结果就是 [Function: say]
let __ = (context[this.name] = this);
__(...args);
};
复制代码
看上去是不是挺简单的,下面来试下这个方法
let Person = {
name: "zhangsan",
say(age, className) {
console.log(`your name ${this.name}, age ${age}, className ${className}`);
}
};
let Person1 = {
name: "lisi"
};
// your name lisi, age 12, className class1
Person.say.myCall(Person1, 12, "class1");
复制代码
apply实现
有了call
的实现,apply
就简单多了,就是参数的不同。
Function.prototype.myApply = function(context, args) {
let __ = (context[this.name] = this);
__(args);
};
复制代码
试下看看:
let Person = {
name: "zhangsan",
say(age, className) {
console.log(
`say your name ${this.name}, age ${age}, className ${className}`
);
},
speak([age, className]) {
console.log(
`speak your name ${this.name}, age ${age}, className ${className}`
);
}
};
let Person1 = {
name: "lisi"
};
// speak your name lisi, age 20, className class2
Person.speak.myApply(Person1, [20, "class2"]);
复制代码
当然,这是一个简单的处理,仅仅是为了实现,提供了一个思路,为了健壮性还缺少对参数进行校验等。