手写call方法(面试题)

文章详细解释了call方法的作用,即改变函数内部this的指向,并通过实例展示了如何手动实现call功能。在手动实现中,涉及到了ES6的剩余参数和ES5的Function构造函数,以及如何处理参数的动态传递和this的绑定。
摘要由CSDN通过智能技术生成

call方法的作用
运用:函数.call(thisArg,arg1,arg2,……)

改变this指向,将this指向thisArg

现在有一个对象以及一个sum函数

var obj={
	a:1,
	b:2
}
function sum(a,b){
	console.log(this.a,this.b);
}

如果调用了call函数,此时this指向的是obj,所以输出1,2

sum.call(obj,4,5)//输出1,2

手写call方法,需要改变this指向和参数的传递
this指向
context:上下文对象,也就是上面的obj

Function.prototype._call=function(context,...args){
	console.log(this);//此时的this指向的是sum函数,因为是sum函数调用的_call方法
	//改变this指向,需要在对象中新建一个属性来接收这个this
	var key='k';
	context[key]=this;//context中的key属性值就是sum函数,改变this指向完毕
	//参数问题
	//context['key']()(括号需要传入参数,相当于在sum函数中传入参数,但是参数的个数并不确定)
	//先使用es6的剩余参数来解决
	var result=context[key](...args);
	//不能改变对象原来的样子,所以需要删除掉新增的属性
	delete context[key];
	return result;
}
sum._call(obj,4,5)//输出的是1,2,因为this指向的是obj对象

context['key'](...args);相当于

var obj={
	a:1,
	b:2,
	key:function sum(a,b){
	console.log(this.a,this.b);
	}
}

此时问题就解决了,但是如果面试官突然说不能使用es6中的剩余参数,使用es5的方法呢
这时需要动态生成函数了
首先,需要思考我们需要生成什么样的函数呢
动态:参数的个数不确定

 context[key](arg[1],arg[2],arg[3]);//需要生成类似这样的函数

怎么生成一个函数呢?Function

var fn = new Function(arg1,arg2,arg3...,functionBody)

arg1,arg2,arg3是参数,functionBody是函数体代码,参数也可以不写,就相当于是没有传入参数的函数

var sum= new Function("a","b","consloe.log(a+b)")
这个函数就相当于
function anonymous(a,b){
	consloe.log(a+b)
}

所以我需要构造一个函数,然后函数的返回值应该是
return new Function(传入的参数,函数体代码),函数体代码就是return context[key](动态参数)

function createFn(arrLength){
	var code="return context[key](";
	for(var i=0;i<arrLength;i++){
	//第二个就需要加上逗号
	if(i>0){
		code+=",";
	}
	code+="arr["+i+"]";
	}
	code+=")";
	return new Function("context","key","arr",code);
}

在上面的函数中,传入的参数是一个数组的长度,需要将一开始传进来的参数(arguments)装在数组中,然后获取到长度

Function.prototype._call=function(context){
//需要过滤掉第一个参数,所以需要从1开始
var arr=[];
for(var i=1;i<arguments.length;i++){
	arr.push(arguments[i]);
}
var len=arr.length;
//var fn=createFn(len);
//返回的函数是一个函数,Function函数new出来的函数
//function anonymous(context,key,arr){
//	return context[key](arr[0],arr[1]);
//}
//fn(context,key,arr);
var result=context[key](len)(context,key,arr);
delete context[key];
return result;

context参数的情况处理:

  • 传进来是一个null,需要改变this指向,(globalThis在浏览器中指向的是wndow,在nodejs中指的是global),需要context={}
  • context传进来的不是一个对象,而是一个基本数据类型,需要转换为一个对象
  • 如果调用call方法 不是一个函数,而是一个对象,下面说明
var obj = Object.create(Function.prototype);
// obj是一个对象,但他能调用call方法
if(context==null) context=globalThis
if(typeof context!==Object) context=new Object(context)
if(typeof this!=function){
throw new TypeError("Type Error");
}

对象中新增属性key
新增的属性有可能会和原来的相同,需要使用随机函数

//判断这个属性是否在对象身上有的
var key="fn"+Math.random()*new Date().getTime();
while(context hasOwnproperty(key)){
 key="fn"+Math.random()*new Date().getTime();
}

贴上完整代码:

  <script>
        Function.prototype._call=function(context,...args){
            console.log(this);//调用call方法的是sum函数,所以this指向的是sum函数
            //如果传进来的是一个对象,但是这个对象是以Function.prototype为原型,也就是说可以调用函数上的call方法
            if(typeof this!=="function") {
                throw new TypeError("Type Error");
            }
            if(typeof context!==Object)  context=new Object(context);
            //如果传的第一个参数是null,那么内部的this指向的是window,在浏览器中是window,在nodejs中是global
            if(context==null) context=globalThis;//变成一个{},空对象
            console.log(context);
            console.log(globalThis);
            var key='fn_'+Math.random()*new Date().getTime();
            while(context.hasOwnProperty(key)){
                key='fn_'+Math.random()*new Date().getTime();
            }
            //给context添加一个属性,指向调用call方法的函数
            context[key]=this;
            //函数需要有参数传入,有一个值来接收,因为需要删除掉新加的属性
            var result=context[key](...args);
            context[key](...args);
        
        
            delete context[key];
            return result;
        }
        function sum(a,b,c){
            console.log(this.a,this.b,this.c);
            // console.log(a,b,c);
        } 
        var obj={
            a:1,
            b:2
        }
        sum._call(obj,5,6);//输出1,2,undefined
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值