1.关于call和apply的补充介绍
关于JavaScript这两个函数,
call和apply都是定义在函数上的方法,都会以指定的this值来调用函数,即会设置调用函数时函数
体内this对象的值。apply()方法接收两个参数:函数内this的值和一个参数数组。第二个参数可以是Array的实例,但是也可以是argument对象。
我们来看一个例子:
function sum(num1,num2){
return num1+num2;
}
function callsum(num1,num2){
return sum.call(this,num1,num2);
}
console.log(callsum(10,10));//20
这里的callsum()函数必须逐个地把参数传到call()方法中去。
apply和call真正强大的地方不在于给函数传参,而在于控制函数调用上下文即函数体内this值的能力。考虑下面的例子:
var name="林俊杰";
let o={
name:'周杰伦'
};
function sayname(){
console.log(this.name)
};
sayname();//林俊杰
sayname.call(window);//林俊杰
sayname.call(this)//林俊杰
sayname.call(o);//周杰伦
你看,sayname()是一个全局函数。如果在全局作用域中调用它,那么就会显示“林俊杰”。因为这里的this.name指向的是window。如果在全局作用域中调用this和window,就会同样的打印林俊杰,然而在使用call的时候,把函数执行的上下文即this切换为对象,这样结果就显示周杰伦了
。
2.手把手教大家写call函数
我们下面来看一个例子哈:
var name="haha";
var person={
getName:function(){
console.log(this)
return this.name;
}
};
var person1={
name:"kobe"
}
console.log(person.getName())//undefined
console.log(person.getName.call(this))//这里是haha
大家看,首先第一个是undefined的原因是什么呢,因为此时this值不会指向window,而是person,除非我们显式调用this,也就是第二个打印的haha
下面我们
console.log(person.getName.call(person1))//kobe
这样的话打印的就是kobe。
ok 下面开始自己实现自己的call函数
首先:call是定义在function上的,其他函数要想调用,那么就必须在Function.prototype上面使用:,下面我们尝试书写一下!大家一定要跟着我的思路写!
Function.prototype.mycall=function(data) { //这里我们把mycall函数里面的传入的叫啥,data也行,其他也行。
if(typeof this!="function"){
throw new Error('error');//我们传入的肯定要必须是一个方法(函数)上文中的call里面的this就是getName这个方法
}
data=data||window //如果没有传入值,比如上文中的call(),那么this就指向window了
var args=[...arguments].slice(1)//因为call不仅能接受this,还可以接收其他的参数,这里调用ES6中的语法
//...arguments 这个能够起到解构的作用,比如var sad=[1,2,3]
// console.log(...sad)输出的是1 2 3
var result=this(...args);//这里的this就是getName
return result;
}
console.log(person.getName.mycall(person1,1,2,3))
结果出来是多少呢,你会发现
还是haha! 出问题了啊啊啊啊,那么此时你肯定会很疑惑,到底是哪里出问题了啊,其实这个
当你打印出来的时候你会发现,调用最后一行代码的时候,这里打印出来了一个
window对象,只要你那个getName()里面有console.log(this)这行代码,你就会发现这里的this还是window,根本不是person1,所有我们最后肯定得不到kobe这个值
那么怎么把这里的person1传过去呢?
记住 person.getName.call(this)这里this是window是因为person.getName()本质也是window的一个属性,所有这里我们!
Function.prototype.mycall=function(data) { //这里我们把mycall函数里面的传入的叫啥,data也行,其他也行。
if(typeof this!="function"){
throw new Error('error');//我们传入的肯定要必须是一个方法(函数)上文中的call里面的this就是getName这个方法
}
data=data||window //如果没有传入值,比如上文中的call(),那么this就指向window了
var args=[...arguments].slice(1)//因为call不仅能接受this,还可以接收其他的参数,这里调用ES6中的语法
//...arguments 这个能够起到解构的作用,比如var sad=[1,2,3]
// console.log(...sad)输出的是1 2 3
//错误var result=this(...args);//这里的this就是getName,并且将数组args解构
data.shuxing=this;//这行代码最关键!!!!!我们将person.getName()变成data的一个属性,那么调用data.shuxing就会指向data,而不是window了!!!
return data.shuxing(...args);
}
console.log(person.getName.mycall(person1,1,2,3))
最关键的那行代码你看到了吗,这里随便让this成为data的一个属性,比如data.shuxing,比如data.p,随便你取,这样就打印出来了kobe!!!
手写apply函数
Function.prototype.myapply=function(data) { //这里我们把mycall函数里面的传入的叫啥,data也行,其他也行。
if(typeof this!="function"){
throw new Error('error');//我们传入的肯定要必须是一个方法(函数)上文中的call里面的this就是getName这个方法
}
data=data||window //如果没有传入值,比如上文中的call(),那么this就指向window了
data.shuxing=this;
var args=arguments[1];//只有这里有一点差别,因为apply是数组传入
let result;
if(args){
result=data.shuxing(...arguments[1]);//将数组解构
}
else {
result=data.shuxing();
}
return result;
}
console.log(person.getName.myapply(person1,[1,2,3]))
这里的apply函数就实现了!!!是不是很清晰呢。希望你可以通过此文对apply和call理解更为清晰!