call
call和apply是什么?
call()和apply方法在使用一个指定的 this 值和若干个指定的参数值的前提下调用某个函数或方法。简单来说就是改变函数this的方法
举个栗子:
var foo = {
value: 1
};
function test(name, age) {
console.log(name)
console.log(age)
console.log(this.value);
}
test.call(foo, 'kevin', 18);
// kevin
// 18
// 1
对上call方法的定义:
1.call改变了this指向,指向了foo,传入指定的['kevin', 18]参数
2.test函数执行
实现call第一步
试想当调用 call 的时候,把test函数成为foo对象的一个属性,然后调用完再用delete删除不就可以了?
所以我们模拟的步骤可以分为:
1.将函数设置为对象的一个属性
foo.fn = test
2.执行函数
foo.fn()
3.删除函数
delete foo.fn
根据这个思路,我们模拟写下callTest函数第一个版本
//第一版
Function.prototype.callTest = function (obj) {
obj.fn = this
obj.fn()
delete obj.fn;
}
//测试
var foo = {
value:1
}
function test() {
console.log(this.value)
}
test.callTest(foo) //1
实现call第二步(传递参数)
要实现参数传递,主要有两个问题:
1.传入的参数并不确定
2.把参数数组放到要执行的函数的参数里面去并执行
传入的参数不确定,我们可以用arguments来解决,参数数组是从arguments的第二项到最后一项。将参数数组放进执行函数参数中并执行可以用eval方法
Function.prototype.callTest = function (obj) {
obj.fn = this;
var args = []
var len = arguments.length;
for (var i = 1; i < len; i++){
args.push('arguments[' + i + ']');
}
eval('obj.fn(' + args + ')')
delete obj.fn
}
实现call最后一步
最后还有两个情况:
1.参数为null的时候指向window
2.函数可以有返回值
Function.prototype.callTest = function (context) {
var obj = context || window;
obj.fn = this
var args = []
var len = arguments.length
for (var i = 1; i < len; i++){
args.push('arguments[' + i +']');
}
var result = eval('obj.fn(' + args + ')');
delete obj.fn
return result
}
apply 实现和call差不多一样,只是将arguments传参变成数组
Function.prototype.apply = function (context, arr) {
var context = Object(context) || window;
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;
}