Day 28 28.3 JS-Function对象之call和apply方法

发布时间 2023-04-27 16:14:28作者: Chimengmeng

JS-Function对象之call和apply方法

  • call,apply都属于Function.prototype的一个方法,它是JavaScript引擎内在实现的,
    • 因为属于Function.prototype,所以每个Function对象实例(就是每个方法)都有call,apply属性。
    • 既然作为方法的属性,那它们的使用就当然是针对方法的了,
    • 这两个方法是容易混淆的,因为它们的作用一样,只是使用方式不同。
foo.call(this, arg1,arg2,arg3) == foo.apply(this, arguments) == this.foo(arg1, arg2, arg3);

【一】定义

  • apply

    • 方法能劫持另外一个对象的方法,继承另外一个对象的属性.
  • Function.apply(obj,args)

    • 方法能接收两个参数
      • obj:这个对象将代替Function类里this对象
      • args:这个是数组,它将作为参数传给Function(args–>arguments)
  • call

    • 和apply的意思一样,只不过是参数列表不一样.
  • Function.call(obj,[param1[,param2[,…[,paramN]]]])

    • obj:这个对象将代替Function类里this对象
    • params:这个是一个参数列表
  • 注释

    • 一般来说,this总是指向调用某个方法的对象,
      • 但是使用call()和apply()方法时,就会改变this的指向。
function add(c,d){
 	return this.a + this.b + c + d;
 }
 
 var s = {a:1, b:2 };
 console.log(add.call(s,3,4)); // 1+2+3+4 = 10
 console.log(add.apply(s,[5,6])); // 1+2+5+6 = 14 

【二】案例展示

【2.1】call()方法使用示例

<script>

	// 例 1
    window.color = 'red';
    document.color = 'yellow';

    var s1 = {color: 'blue' };
    function changeColor(){
        console.log(this.color);
    }

    changeColor.call();         //red (默认传递参数)
    changeColor.call(window);   //red
    changeColor.call(document); //yellow
    changeColor.call(this);     //red
    changeColor.call(s1);       //blue

	// 例 2
	var Pet = {
	   words : '...',
	   speak : function (say) {
	       console.log(say + ''+ this.words)
	   }
	}
	Pet.speak('Speak'); // 结果:Speak...
	var Dog = {
	   words:'Wang'
	}
	// 将this的指向改变成了Dog
	Pet.speak.call(Dog, 'Speak'); // 结果: SpeakWang
	
</script>

【2.2】apply()方法使用示例

<script type="text/javascript">  
    /*定义一个人类*/  
    function Person(name,age)  
    {  
        this.name=name;  
        this.age=age;  
    }  
    /*定义一个学生类*/  
    function Student(name,age,grade)  
    {  
        Person.apply(this,arguments);  
        this.grade=grade;  
    }  
    //创建一个学生类  
    var student=new Student("zhangsan",21,"一年级");  
    //测试  
    alert("name:"+student.name+"\n"+"age:"+student.age+"\n"+"grade:"+student.grade);  
    //大家可以看到测试结果name:zhangsan age:21  grade:一年级  
    //学生类里面我没有给name和age属性赋值啊,为什么又存在这两个属性的值呢,这个就是apply的神奇之处.  
</script>  

【2.3】分析

(1)Person.apply(this,arguments);

  • this
    • 在创建对象在这个时候代表的是student
  • arguments
    • 是一个数组,也就是[“zhangsan”,”21”,”一年级”];
  • 也就是通俗一点讲就是
    • 用student去执行Person这个类里面的内容,在Person这个类里面存在this.name等之类的语句,这样就将属性创建到了student对象里面

(2)call示例

  • 在Studen函数里面可以将apply中修改成如下:
    • Person.call(this,name,age);
  • 这样就ok了

【三】使用情况

【3.1】在给对象参数的情况下

  • 比如apply示例里面传递了参数arguments
    • 这个参数是数组类型
    • 并且在调用Person的时候参数的列表是对应一致的(也就是Person和Student的参数列表前两位是一致的)
    • 就可以采用 apply ,
  • 如果我的Person的参数列表是这样的(age,name)
    • 而Student的参数列表是(name,age,grade)
    • 这样就可以用call来实现了,也就是直接指定参数列表对应值的位置(Person.call(this,age,name,grade));

【3.2】apply的一些其他巧妙用法

  • 细心的人可能已经察觉到
    • 在我调用apply方法的时候
      • 第一个参数是对象(this)
      • 第二个参数是一个数组集合
    • 在调用Person的时候
      • 他需要的不是一个数组
      • 但是为什么他给我一个数组我仍然可以将数组解析为一个一个的参数
  • 这个就是apply的一个巧妙的用处
    • 可以将一个数组默认的转换为一个参数列表([param1,param2,param3] 转换为 param1,param2,param3)
    • 这个如果让我们用程序来实现将数组的每一个项,来装换为参数的列表,可能都得费一会功夫
    • 借助apply的这点特性,所以就有了以下高效率的方法:

(1)Math.max 可以实现得到数组中最大的一项

 var max=Math.max.apply(null,array);
  • 因为Math.max 参数里面不支持Math.max([param1,param2]) 也就是数组
  • 但是它支持Math.max(param1,param2,param3…)
  • 所以可以根据刚才apply的那个特点来解决 var max=Math.max.apply(null,array)
  • 这样轻易的可以得到一个数组中最大的一项(apply会将一个数组装换为一个参数接一个参数的传递给方法)

(2)Math.min 可以实现得到数组中最小的一项

 var min=Math.min.apply(null,array);

同样和 max是一个思想 var min=Math.min.apply(null,array);

(3)Array.prototype.push 可以实现两个数组合并

  • 同样push方法没有提供push一个数组
  • 但是它提供了push(param1,param,…paramN)
  • 所以同样也可以通过apply来装换一下这个数组
var arr1=new Array("1","2","3");  
  
var arr2=new Array("4","5","6");  
  
Array.prototype.push.apply(arr1,arr2);
// 或者
 arr1.push.apply(arr1,arr2)

console.log(arr1)

// 结果: [1,2,3,4,5,6]
  • 也可以这样理解
    • arr1调用了push方法
    • 参数是通过apply将数组装换为参数列表的集合.

【3.3】特殊情况

  • 一般在目标函数只需要n个参数列表
    • 而不接收一个数组的形式([param1[,param2[,…[,paramN]]]])
  • 可以使用apply类似Math.min等之类的特殊用法解决这个问题

【四】案例简解

案例1:

function Person(name, age) {
    this.name = name
    this.age = age
}

Person.prototype.eat = function () {
    console.log(this.name + "正在吃东西")
}

p = new Person("dream", 22)

p.eat()

案例2:

function Person(name, age) {
    this.name = name
    this.age = age
}
p = new Person("dream", 22)

function eat () {
    console.log(this.name + "正在吃东西")
}
eat.call(p)

案例3:

function Person(name, age) {
    this.name = name
    this.age = age
}
p = new Person("dream", 22)

function eat () {
    console.log(this.name + "正在吃东西")
}
eat.call(p)
eat.apply(p)

如果call和apply的第一个参数写的是null,那么this指向的是window对象

案例4:

function Person(name, age) {
    this.name = name
    this.age = age
}

p = new Person("dream", 22)

function eat(a, b, c) {
    console.log(this.name + "正在吃东西")
    console.log(a, b, c)
}

eat.call(p, "1", "2", "3")
eat.apply(p, ["1", "2", "3"])
// apply传递多个参数的时候第二个参数需要传递一个数组