JS之call,apply, bind,caller,callee的区别

346 阅读3分钟

为什么要用call()和apply()方法?apply、call、bind 这三个函数的存在意义是什么?答案是改变函数执行时的上下文,再具体一点就是改变函数运行时的this指向。现用以下例子说明

function cat(){}
cat.prototype = {
	food:'fish',
	say: function(){
		console.log('I love '+this.food);
	}
}
var blackCat = new cat();
blackCat.say();

call或者apply用法: 现在有一个对象whiteDog = {food:'bone'},我们不想对它重新定义say方法,那么我们可以通过call或apply用blackCat的say方法:blackCat.say.call(whiteDog);也可这种写法cat.prototype.say.call(whiteDog);

let whiteDog = {food:'bone'};
blackCat.say.call(whiteDog); //输出结果I love bone
//or
cat.prototype.say.call(whiteDog); //输出结果I love bone

bind用法: 现在有一个对象rabbit = {food:'carrot'},可这样写

let rabbit = {food:'carrot'};
let bind = cat.prototype.say.bind(rabbit); //返回一个函数
bind();//输出结果I love carrot

apply、call区别 仅在定义参数方式有所区别

xxx.say.call(test,"1","2");
xxx.say.apply(test,["1","2"]);

apply、call这两个方法在调用的时候,如果我们传入数字或者字符串,这两个方法会把传入的参数转成对象类型。

var number = 1;
function getThisType () {
    var number = 3;
    console.log('this指向内容',this); //this指向内容 Number {1}
    console.log(Object.prototype.toString.call(this)) //[object Number]
}
getThisType.call(number);

call和apply改变了函数的this上下文后便执行该函数,而bind则是返回改变了上下文后的一个函数。

关于call、apply的题集

  • 1 怎么利用call、apply来求一个数组中最大或者最小值,Demo如下所示:
let arr = [1, 2, 19, 6];
console.log(Math.max.call(null, 1,2,19,6)); // 输出结果:19
console.log(Math.max.apply(null, arr)); //  输出结果:19
  • 2 将伪数组转化为数组,伪数组是指具有length属性,且可以通过0、1、2…下标来访问其中的元素,但是没有Array中的push、pop等方法,就可以利用call,apply来转化成真正的数组,Demo如下所示:
let obj = {
	0: 'candy',
	1: 'thomas',
	2: 'peter',
	length: 3 // 一定要有length属性
};
console.log(Array.prototype.slice.call(obj)); 
//输出结果:['candy','thomas','peter']
  • 3 利用call和apply做继承,Demo如下所示:
function Animal(name){      
    this.name = name;      
    this.showName = function(){      
        console.log(this.name);      
    }      
}      
function Cat(name){    
    Animal.call(this, name);    
}      
// Cat对象就能够直接调用Animal的方法以及属性了
var cat = new Cat("Candy");     
cat.showName();
//输出结果:Candy

caller functionName.caller 返回一个对函数的引用,该函数调用了当前函数。对于函数来说,caller 属性只有在执行时才有定义。如果函数是由 Javascript 程序的顶层调用的,那么 caller 包含的就是 null 。

function callerDemo() {
     if (callerDemo.caller) {
         var a= callerDemo.caller.toString();
         console.log(a);
     } else {
         console.log("this is a top function");
     }
}
function handleCaller() {
     callerDemo();
}
handleCaller();
//输出结果:"function handleCaller() { callerDemo();}"

callee 返回正被执行的 Function 对象,也就是所指定的 Function 对象的正文。 callee 属性是 arguments 对象的一个成员,它表示对函数对象本身的引用,这有利于匿名函数的递归或者保证函数的封装性. ES5 提示: 在严格模式下,arguments.callee 会报错 TypeError,因为它已经被废除了。 arguments.length是实参长度,arguments.callee.length是形参长度

//callee可以打印其本身
function calleeDemo() {
   console.log(arguments.callee);
}  
calleeDemo();
//输出结果:function calleeDemo() { console.log(arguments.callee);}
//callee用于验证参数
function calleeLengthDemo(arg1, arg2) {
    if (arguments.length == arguments.callee.length) {
        window.alert("验证形参和实参长度正确!");
        return;
    } else {
        alert("实参长度:" + arguments.length);
        alert("形参长度: " + arguments.callee.length);
    }
}
//callee递归计算
var sum = function (n) {
    if (n < = 0)
        return 1;
    else
        return n +arguments.callee(n - 1)
}