首页 > 技术文章 > js中bind、apply、call、callee、caller的使用

bijiapo 2016-04-22 11:48 原文

在js中,apply,call,bind三者都是用来改变函数的this对象的指向

相同点

不同点

执行以下代码

console.log("***example start***");

var person1 = {
    name:"张三",
    age:23,
    say:function(){
        console.log("我的名字是:",this.name+";我的年龄是:"+this.age);
    },
    say2:function(school,gender){
        school = school ? school : "社会大学";
        gender = gender ? gender : "男";
        console.log("我的名字是:",this.name+";我的年龄是:"+this.age+";我的学校是:"+school+";我的性别:"+gender);
    }
};
var person2 = {
    name:"李四",
    age:25
};
console.log("***直接调用say start***");
person1.say();
console.log("***直接调用say end***");

console.log("***apply调用say start***");
person1.say.apply(person2);
console.log("***apply调用say end***");

console.log("***call调用say start***");
person1.say.call(person2);
console.log("***call调用say end***");

console.log("***bind调用say start***");
person1.say.bind(person2)();
console.log("***bind调用say end***");

console.log("***带参数直接调用say start***");
person1.say2("哈佛1","男1");
console.log("***带参数直接调用say end***");

console.log("***带参数apply调用say start***");
person1.say2.apply(person2,["哈佛2","男2"]);
console.log("***带参数apply调用say end***");

console.log("***带参数call调用say start***");
person1.say2.call(person2,"哈佛3","男3");
console.log("***带参数call调用say end***");

console.log("***带参数bind调用say start***");
person1.say2.bind(person2,"哈佛4","男4")();
console.log("***带参数bind调用say end***");

console.log("***带参数bind调用say2 start***");
person1.say2.bind(person2)("哈佛5","男5");
console.log("***带参数bind调用say end***");


console.log("***example end***");

输出:
***example start***
***直接调用say start***
我的名字是: 张三;我的年龄是:23
***直接调用say end***
***apply调用say start***
我的名字是: 李四;我的年龄是:25
***apply调用say end***
***call调用say start***
我的名字是: 李四;我的年龄是:25
***call调用say end***
***bind调用say start***
我的名字是: 李四;我的年龄是:25
***bind调用say end***
***带参数直接调用say start***
我的名字是: 张三;我的年龄是:23;我的学校是:哈佛1;我的性别:男1
***带参数直接调用say end***
***带参数apply调用say start***
我的名字是: 李四;我的年龄是:25;我的学校是:哈佛2;我的性别:男2
***带参数apply调用say end***
***带参数call调用say start***
我的名字是: 李四;我的年龄是:25;我的学校是:哈佛3;我的性别:男3
***带参数call调用say end***
***带参数bind调用say start***
我的名字是: 李四;我的年龄是:25;我的学校是:哈佛4;我的性别:男4
***带参数bind调用say end***
***带参数bind调用say2 start***
我的名字是: 李四;我的年龄是:25;我的学校是:哈佛5;我的性别:男5
***带参数bind调用say end***
***example end***

1:以下是apply方法的用法

<input type="text" id="myText" value = "input value">
<script>
console.log("***以下为apply方法的实例  start***");

console.log("***example1:***");
var value = "global value";
function Obj(){
    this.value = "obj value"
}
function GetValue(){
    console.log(this.value);
}
window.GetValue();
GetValue();
GetValue.apply(window);
GetValue.apply(new Obj());
GetValue.apply(document.getElementById("myText"));

console.log("***example2:***");
var first_obj = {
    num:12
}
var second_obj = {
    num:23
}
function multiply(operation,mult){
    var num = ""
    switch (operation){
        case "+":
            num = this.num + mult;
            break;
        case "-":
            num = this.num - mult;
            break;
        case "*":
            num = this.num * mult;
            break;
        case "/":
            num = this.num / mult;
            break;
    }
    console.log(this.num +operation+mult+" = "+num);
}
multiply.apply(first_obj,["+",3])
multiply.apply(second_obj,["*",3])

console.log("***数组之间追加***");
var arr1 = [1,2,3,4];
var arr2 = [5,6,7];
console.log("before arr1:"+arr1)
console.log("before arr2:"+arr2)
Array.prototype.push.apply(arr1,arr2);

console.log("after arr1:"+arr1)
console.log("after arr2:"+arr2)

console.log("***获取数组之间的最大值和最小值***");
var arr1 = [1,2,3,4];
var maxNum = Math.max.apply(Math,arr1)
var minNum = Math.min.apply(Math,arr1)

console.log("最大值:"+maxNum)
console.log("最小值:"+minNum)

console.log("***验证是否是数组***");
function isArray(obj){
    console.log(Object.prototype.toString.apply(obj))
    return Object.prototype.toString.apply(obj) === "[object Array]";
}


console.log("[1,2,3]是数组吗:"+isArray([1,2,3]))
console.log("123是数组吗:"+isArray("123"))
console.log("new Object()是数组吗:"+isArray(new Object()))

console.log("***传入的参数不确定***");
function logArg(arg){
    console.log.apply(console,arguments)
}
logArg(1)
logArg(1,2)

console.log("***传入的参数是字符串,且输出时候拼接字符串***");
function logStr(){
    var args = Array.prototype.slice.apply(arguments);
    args.unshift("zyb:");
    console.log.apply(console,args)
}
logStr("hello"," world");

console.log("***以下为apply方法的实例  end***");

</script>

输出:
***apply方法  start***
***example1:***
global value
global value
global value
obj value
input value
***example2:***
12+3 = 15
23*3 = 69
***数组之间追加***
before arr1:1,2,3,4
before arr2:5,6,7
after arr1:1,2,3,4,5,6,7
after arr2:5,6,7
***获取数组之间的最大值和最小值***
最大值:4
 最小值:1
***验证是否是数组***
[object Array]
[1,2,3]是数组吗:true
[object String]
123是数组吗:false
[object Object]
new Object()是数组吗:false
***传入的参数不确定***
1
1 2
***传入的参数是字符串,且输出时候拼接字符串***
zyb: hello  world
***apply方法  end***

2:以下是call方法的用法

<input type="text" id="myText" value = "input value">
<script>
console.log("***以下为bind方法的实例  start***");

console.log("***example1:***");
var value = "global value";
function Obj(){
    this.value = "obj value"
}
function GetValue(){
    console.log(this.value);
}
window.GetValue();
GetValue();
GetValue.bind(window)();
GetValue.bind(new Obj())();
GetValue.bind(document.getElementById("myText"))();

console.log("***example2:***");
var first_obj = {
    num:12
}
var second_obj = {
    num:23
}
function multiply(operation,mult){
    var num = ""
    switch (operation){
        case "+":
            num = this.num + mult;
            break;
        case "-":
            num = this.num - mult;
            break;
        case "*":
            num = this.num * mult;
            break;
        case "/":
            num = this.num / mult;
            break;
    }
    console.log(this.num +operation+mult+" = "+num);
}
multiply.bind(first_obj,"+",3)();
multiply.bind(second_obj,"*",3)();
multiply.bind(first_obj)("-",3);
multiply.bind(second_obj)("/",3);

console.log("***以下为bind方法的实例  end***");

</script>

输出:
***bind方法  start***
***example1:***
global value
global value
global value
obj value
input value
***example2:***
12+3 = 15
23*3 = 69
12-3 = 9
23/3 = 7.666666666666667
***bind方法  end***

3:以下是bind方法的用法

<input type="text" id="myText" value = "input value">
<script>
console.log("***以下为call方法的实例  start***");

console.log("***example1:***");
var value = "global value";
function Obj(){
    this.value = "obj value"
}
function GetValue(){
    console.log(this.value);
}
window.GetValue();
GetValue();
GetValue.call(window);
GetValue.call(new Obj());
GetValue.call(document.getElementById("myText"));

console.log("***example2:***");
var first_obj = {
    num:12
}
var second_obj = {
    num:23
}
function multiply(operation,mult){
    var num = ""
    switch (operation){
        case "+":
            num = this.num + mult;
            break;
        case "-":
            num = this.num - mult;
            break;
        case "*":
            num = this.num * mult;
            break;
        case "/":
            num = this.num / mult;
            break;
    }
    console.log(this.num +operation+mult+" = "+num);
}
multiply.call(first_obj,"+",3)
multiply.call(second_obj,"*",3)

console.log("***以下为call方法的实例  end***");

</script>

输出:

***call方法  start***
***example1:***
global value
global value
global value
obj value
input value
***example2:***
12+3 = 15
23*3 = 69
***call方法  end***

4:callee:返回正在执行的function对象

1):callee可以打印函数本身

function test1(){
    console.log(arguments)
    console.log(arguments.callee)
}
test1();

输出:
Arguments[0]
function test1(){
    console.log(arguments)
    console.log(arguments.callee)
}

2):callee可以验证参数

function test2(arg1,arg2){
    if(arguments.length == arguments.callee.length){
        console.log("形参和实参长度相等")
    }else{
        console.log("形参长度:"+arguments.length)
        console.log("实参长度:"+arguments.callee.length)
    }
}
test2(1,2);
test2(1,2,3);

输出:
形参和实参长度相等
形参长度:3
实参长度:2

3):callee可以进行递归计算

function sum(n){
    if(n <=0){
        return 0;
    }else{
        return n + arguments.callee(n-1)
    }
}
var _sum1 = sum(-1)//1
var _sum2 = sum(0)//1
var _sum3 = sum(1)//2
var _sum4 = sum(2)//4
var _sum5 = sum(3)//7
var _sum6 = sum(4)//11
console.log(_sum1)//1
console.log(_sum2)//1
console.log(_sum3)//2
console.log(_sum4)//4
console.log(_sum5)//7
console.log(_sum6)//11

5:caller:返回一个对函数的引用,即调用了该函数的函数体

对于函数来说,caller属性只有在函数执行时候才有定义,如果有程序顶层调用,caller为null,如果是有其它函数调用,caller为调用函数体

function InnerFun(){
    console.log("caller:"+InnerFun.caller);
    if(InnerFun.caller == null){
        console.log("该函数是由顶层调用,不是由其它函数调用")
    }else{
        console.log("该函数是由其它函数调用,不是由顶层调用")
    }
}
function OuterFun(){
    console.log("OuterFun")
    InnerFun()
}
InnerFun();
OuterFun();

输出:
caller:null
该函数是由顶层调用,不是由其它函数调用
OuterFun
caller:function OuterFun(){
        console.log("OuterFun")
        InnerFun()
    }
该函数是由其它函数调用,不是由顶层调用

推荐阅读