目录
1 call和apply的区别
call()方法接受的是参数列表
apply()方法接受的是一个参数数组。
它们的共同之处:都可以用来代替另一个对象调用一个方法,将一个函数的对象上下文从初始的上下文改变为由this指定的新对象。
call()方法使用一个指定的 this 值和单独给出的一个或多个参数来调用一个函数。
fun.call(thisArg, arg1, arg2, ...)
thisArg 表示在 fun 函数运行时指定的 this 值。
arg1, arg2, ... 表示指定的参数列表。
apply()方法调用一个具有给定this值的函数,
以及作为一个数组(或类似数组对象)提供的参数。
func.apply(thisArg, [argsArray])
thisArg 在 func 函数运行时使用的 this 值
argsArray 一个数组或者类数组对象,其中的数组元素将作为单独的参数传给func 函数
实际上,apply和call的功能是一样的,只是传入的参数列表形式不同。
2 call 方法的实现
call() 方法在使用一个指定的 this 值和若干个指定的参数值的前提下调用某个函数或方法。
2.1 设计思路
2.1.1 第一步改变this指向
var foo = {
value: 1
};
function bar() {
console.log(this.value);
}
bar.call(foo); // 1
上述代码实现了 call改变this指向 和 执行bar函数
调用call函数需要改变this指向,如下代码this指向foo的value属性,但是给foo添加了一个value属性。
var foo = {
value: 1,
bar: function() {
console.log(this.value)
}
};
foo.bar(); // 1
给对象添加的属性用完及时删除就可以了
所以模拟步骤:(fn为对象的属性名,可以随便起)
- 将函数设置为对象属性 foo.fn = bar
- 执行该函数 foo.fn()
- 删除该函数 delete foo.fn
所以我们可以实现代码,改变this指向
Function.prototype.newcall = function (context) {
// 首先要获取调用call的函数,使用this获取
context.fn = this;
context.fn();
delete context.fn;
}
var foo ={
value:1
};
function bar() {
console.log(this.value);
}
bar.newcall(foo);
2.1.2 第二步参数个数不确定
var foo = {
value: 1
};
function bar(name, age) {
console.log(name)
console.log(age)
console.log(this.value);
}
bar.call(foo, 'aaaa', 18);
// aaaa
// 18
// 1
// 我们可以从arguments对象中提取值为:
// arguments = {
// 0: foo,
// 1: 'kevin',
// 2: 18,
// length: 3
// }
// 因为arguments是类数组对象,所以可以用for循环
var args = [];
for(var i = 1, len = arguments.length; i < len; i++) {
args.push('arguments[' + i + ']');
}
// 执行后 args为
//["arguments[1]", "arguments[2]", "arguments[3]"]
//不定长的参数问题解决了,我们接着要把这个参数数组
//放到要执行的函数的参数里面去。
eval('context.fn(' + args +')')
以下代码解决参数个数不确定的问题
Function.prototype.newcall = 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; // 删除该函数
}
var foo ={
value:1
};
function bar(name,age) {
console.log(name)
console.log(age)
console.log(this.value);
}
bar.newcall(foo,'aaaa',18);
// aaaa
// 18
// 1
2.1.3 传入参数为空或有返回值
if (typeof context === 'object' || typeof context === 'function') {
context = context || window
} else {
context = Object.create(null)
}
2.2 最终代码
2.2.1 传入参数只有context 时
Function.prototype.call1 = function (context) {
console.log(context)
if (typeof context === 'object' || typeof context === 'function') {
context = context || window
} else {
context = Object.create(null)
}
context.fn = this;
// console.log(this) // [Function: ClassA]
var args = [];
for(var i = 1, len = arguments.length; i < len; i++) {
args.push('arguments[' + i + ']');
// console.log(arguments[i]+'---------------------')
// string---------------------
// function(){
// var fnStr = 'this is a string in function';
// console.log(fnStr)
// }---------------------
// [object Object]---------------------
// 1,2,3---------------------
}
console.log(args) // [ 'arguments[1]', 'arguments[2]', 'arguments[3]', 'arguments[4]' ]
var result = eval('context.fn(' + args +')');
console.log(result)
delete context.fn
return result;
}
function ClassA(str, fn, obj, arr) {
console.log(this.name);
console.log(str);
fn();
console.log(obj);
console.log(arr);
}
var obj = {
name: 'aaaaaaaaaa'
};
ClassA.call1(obj, 'string', function(){
var fnStr = 'this is a string in function';
console.log(fnStr)
}, {
color: 'red'
}, [1, 2, 3]);
// name
// string
// this is a string in function
// { color: 'red' }
// [ 1, 2, 3 ]
2.2.2 参数为2的时候
Function.prototype.newCall = function(context, ...arr) {
if (typeof context === 'object' || typeof context === 'function') {
context = context || window
} else {
context = Object.create(null)
}
context.fn = this
const res =context.fn(...arr)
delete context.fn;
return res
}
let person = {
name: 'Abiel'
}
function sayHi(age,sex) {
console.log(this.name, age, sex);
}
sayHi.newCall (person, 25, '男'); // Abiel 25 男
3 apply 的实现
apply的实现跟call类似,只是处理参数的方式不同。
Function.prototype.apply = function (context, arr) {
var context = Object(context) || window;
// console.log(context) // { name: 'Abiel' }
context.fn = this;
// console.log(context.fn) // [Function: sayHi]
var result;
if (!arr) {
result = context.fn();
} else {
var args = [];
for (var i = 0, len = arr.length; i < len; i++) {
// console.log(arr[i])
args.push('arr[' + i + ']');
}
result = eval('context.fn(' + args + ')')
// console.log(args) // [ 'arr[0]', 'arr[1]' ]
}
delete context.fn
return result;
}
let person = {
name: "Abiel"
};
function sayHi(age, sex) {
console.log(this.name, age, sex);
}
sayHi.apply (person,[ 25, '男']) //Abiel 25 男
// sayHi.apply(person,[]) // Abiel undefined undefined