js进阶之call(),apply,bind()的模拟实现。

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
  • 要点
  1. foo函数执行了
  2. 其中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;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值