call和apply的作用
call() 方法使用一个指定的 this 值和单独给出的一个或多个参数来调用一个函数,该方法的语法和作用与 apply() 方法类似,只有一个区别,就是 call() 方法接受的是一个参数列表,而 apply() 方法接受的是一个包含多个参数的数组
在说实现自定义call、apply之前,我们首先看下一段代码
var age = 6;
var person = {
age: 3,
fn: function() {
console.log(this.age);
}
}
var fn = person.fn;
fn();
person.fn();
结果是
6
3
为什么呢?记住一句话:this的最终指向的是那个调用它的对象var fn = person.fn
,将person
的fn
方法赋值给fn
,但未调用,而fn()
调用的时候实际等于window.fn()
,所以fn中的this指向window
,所以fn()
的结果是window.age = 6
而person.fn()
是person
这个对象调用的,所以此时fn
函数中this
的指向是person
这个对象。所以this.age = 3
我们自己实现call方法,思路就是基于此
Function.prototype.myCall = function(_context) {
// 当_context未传值或者为null或者undefined,context指向window
var context = _context || window;
// 给对象添加一个方法,这个this就是使用call使用的函数
context.fn = this;
// 拼接执行eval时候的参数
var args = [];
for(var i = 1; i < arguments.length; i++) {
args.push('arguments['+ i +']');
}
// 字符串拼接数组,数组隐式转换成args.toString()即'context.fn('+ args +')', => 'context.fn(arguments[1])'
var ret = eval('context.fn('+ args +')');
delete context.fn;
return ret;
}
Function.prototype.myCallEs6 = function(context = window) {
// 给对象添加一个方法,这个this就是使用call使用的函数
context.fn = this;
// 获取除了第一个以外参数
const args = [...arguments].slice(1);
// 执行方法,此时执行方法的this指向的是context
const ret = context.fn(...args);
delete context.fn;
// 返回执行结果
return ret;
}
const person = {
age: 3
};
function fn(name) {
this.name = name;
console.log(this);
}
fn.myCall(person, 'xzf');
fn.call(person, 'xzf');
call和apply作用相同,仅仅只是参数不同,apply的参数以数组的形式传入,所以我们只要做一下修改
Function.prototype.myApply = function(_context) {
// 当_context未传值或者为null或者undefined,context指向window
var context = _context || window;
// 给对象添加一个方法,这个this就是使用call使用的函数
context.fn = this;
var ret;
var args = arguments[1];
if(args) {
// 假设args为['123','abc'],['123','abc'].toString()会转换成'123,abc'
// 执行eval的时候,会变成一个数字,一个变量,此时是不存在变量abc的
// 需要通过上面mycall类似的方法进行转换
var argsArr = [];
for(var i = 0; i < arguments.length; i++) {
argsArr.push('args['+ i +']');
}
ret = eval('context.fn('+ argsArr +')');
} else {
ret = context.fn();
}
delete context.fn;
return ret;
}
// es6
Function.prototype.myApplyEs6 = function(context = window) {
// 当_context未传值或者为null或者undefined,context指向window
// 给对象添加一个方法,这个this就是使用call使用的函数
context.fn = this;
let ret;
// 如果有参数,展开数组参数传入
if(arguments[1]) {
ret = context.fn(...arguments[1]);
} else {
ret = context.fn();
}
delete context.fn;
return ret;
}
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。