call()函数
call()方法就是使用一个指定的对象替换原来函数中默认的this属性保存的对象。并可以提供相应的函数。
var obj ={
name:'sean',
age: 22
}
function foo(){
console.log('name:'+this.name);
console.log('age:'+this.age);
}
foo.call(obj); // name:sean age:22
- 要点
- foo函数执行了
- 其中this指向了obj
接下来将模拟这个call函数,其中实现为 三个部分
第一部分
- 首先如何实现这个部分,首先把这个方法添加到被执行的对象身上,那么这个对象就有了此函数。
var obj ={
name:'sean',
age: 22,
foo:function(){
console.log('name:'+this.name);
console.log('age:'+this.age);
}
}
obj.foo(); //name:sean age:22
- 这个时候this,就是指向了obj了,可以直接执行。
- 但是又出了一个问题,就是在对象中添加了这个方法,但是本来我这个obj对象是不包含这个方法的,所以在执行结束的时候,我们不要忘了删掉这个我们添加的方法。
- 所以步骤又可以分为:
- 将函数设置为被执行对象的属性方法、
- 执行这个函数
- 删掉这个方法
也就是:
obj.fun =foo; //第一步
obj.fun(); //第二步,执行
delete obj.fun; //第三步,删除
这里的fun是一个属性名,起什么名字无所谓(只要别和原来对象属性冲突就行,一般不会哦,毕竟现在是模拟,真正将开发的时候还是用原生的apply,call函数的)。
根据以上思路,就可以写出call函数的第一版了,起名callBro();
/*
* 将函数设置为被执行对象的属性方法、
* 执行这个函数
* 删掉这个方法
*
* 参数:context,传入this指定的对象,函数执行上下文。
*/
Function.prototype.callBro =function(context){
//可以通过this找到即将需要执行的函数对象
//设置为某个属性
context.fun =this;
//执行这个函数;
context.fun();
//删除
delete context.fun;
};
var obj ={
name:'sean',
age: 22
}
function foo(){
console.log('name:'+this.name);
console.log('age:'+this.age);
}
//执行函数
foo.call(obj); //name:sean age:22
第二部分
- call不仅可以指定this对象,还可以指定参数。
- 下面就是解决参数的问题
var obj ={
name:'sean',
age:22
}
function foo(sex){
console.log('sex:'+sex);
console.log('name:'+this.name);
console.log('age:'+this.age);
}
foo('male'); // sex:male name:sean age:22
- 注意:传入的参数并不确定,这可咋办?
我们可以这样:使用一个arguments伪数组来解决。
取出第二个到最后一个参数,然后放到一个数组里。
比如
// 以上个例子为例,此时的arguments为:
// arguments = {
// 0: obj,
// 1: 'male',
// length: 2
// }
// 因为arguments是类数组对象,所以可以用for循环
//从1开始
var args = [];
for(var i = 1, len = arguments.length; i < len; i++) {
args.push('arguments[' + i + ']');
}
// 执行后 args为 ["arguments[1]", "arguments[2]", "arguments[3]"]
- 不定长的参数问题解决了,但是如何放到执行的函数中
context.fun(argus.join(','))
- 这个方法肯定不可以,应为传入的就是字符串啦,不会解析为变量啦
所以用下面这个eval()
eval('context.fn(' + args +')')
第二版答案:(克服了可变参数的问题)
Function.prototype.callBro =function(context){
//可以通过this找到即将需要执行的函数对象
//设置为某个属性
context.fun =this;
//创建数组,将除了第一个对象保存.arguments类数组可遍历
var args = [];
for(var i = 1, len = arguments.length; i < len; i++) {
args.push('arguments[' + i + ']');
}
//执行这个函数;
//这里 args 会自动调用 Array.toString() 这个方法。
//相当于args.toString()
eval('context.fun(' + args.toString() +')')
//删除
delete context.fun;
};
var obj ={
name:'sean',
age: 22
}
function foo(sex){
console.log('sex:'+sex);
console.log('name:'+this.name);
console.log('age:'+this.age);
}
foo.callBro(obj,'male')// sex:male name:sean age:22
第三部分
今天是疫情期间,第一次吃火锅。自家做的,我赶快去吃啦,回来再更。O(∩_∩)O。
喝了点酒,现在回来了,继续更新。
- 到目前为止,我们已经完成了80%的任务。但还是有俩个问题可以完善
- this 参数可以传 null,当为 null 的时候,视为指向 window
- 函数是可以有返回值的!
因为这个都是很好解决的问题,下满直接放上代码:
Function.prototype.callBro =function(context){
//判断输入的对象是不是null或者空.
context = Object(context) || window
//可以通过this找到即将需要执行的函数对象
//设置为某个属性
context.fun =this;
//创建数组,将除了第一个对象保存.arguments类数组可遍历
var args = [];
for(var i = 1, len = arguments.length; i < len; i++) {
args.push('arguments[' + i + ']');
}
//执行这个函数;
//这里 args 会自动调用 Array.toString() 这个方法。
//相当于args.toString()
var result =eval('context.fun(' + args.toString() +')')
//删除
delete context.fun;
//增加函数返回值
return result;
};
// 测试
var name ='LiuX'
var age =18;
var obj ={
name:'sean',
age: 22
}
function foo(sex){
console.log('sex:'+sex);
console.log('name:'+this.name);
console.log('age:'+this.age);
return '我是一个返回值';
}
foo.callBro(null,'male') // sex:male , name:LiuX , age: 18
console.log(foo.callBro(obj,'male')); // 返回值是 : '我是一个返回值';
applyde 模拟实现基本上和call差不多,就是传入参数的变化。
Function.prototype.applyBro = 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;
}