这五种方法经常用到,也经常看到,但是一直没有深入的理解它们直接的区别,为了不埋坑,重新学习总结一下。

这五种方法大体可以分为两类,一类是callee,caller,另一类是call,apply,bind

callee

返回正被执行的 Function 对象,也就是所指定的 Function 对象的正文。

首先看一个斐波那契数列

function factorial(num){
  if(num<=1){
    return 1
  }else{
    return num*factorial(num-1)
  }
}

这么看很完美,但是有一个问题就是递归的时候函数的执行与函数名紧紧的耦合在一起了,例如:

var testFactorial = factorial
factorial = function(){
  return 0
}
testFactorial(5) //0

之所以是0的原因是factorial这个变量指向了return 0的这个匿名函数。

接下来我们用callee来对这个斐波那契数列进行解耦,目的很简单让函数里return指向自身

function factorial(num){
  if(num<=1){
    return 1;
  }else{
    return num*arguments.callee(num-1)
  }
}

var testFactorial = factorial;
factorial = function(){
  return 0;
}
console.log(testFactorial(5)); //120

这样以来就解决了刚才的问题

更多用法

// callee可以打印其本身
function calleeDemo() {
  alert(arguments.callee);
}

// 用于验证参数
function calleeLengthDemo(arg1, arg2) {
  if (arguments.length == arguments.callee.length) {
    window.alert("验证形参和实参长度正确!");
    return;
  }else{
    alert("实参长度:" + arguments.length);
    alert("形参长度: " + arguments.callee.length);
  }
}

// 递归计算
var sum = function(n){
  if (n <= 0) {
    return 1;
  }else{
    return n + arguments.callee(n - 1);
  }
}

caller

返回一个对函数的引用,即调用了当前函数的函数体。

functionName.caller :functionName 对象是所执行函数的名称。

说明

对于函数来说,caller 属性只有在函数执行时才有定义。 如果函数是由JavaScript 程序的顶层调用的,那么 caller 包含的就是 null 。如果在字符串上下文中使用 caller 属性,那么结果和 functionName.toString 一样,也就是说,显示的是函数的反编译文本。

function outer(){
  inner();
}
function inner(){
  console.log(inner.caller);
}
outer(); //显示outer的源代码

因为outer()调用了inner(),所以inner.caller就指向outer()。为了实现松散的耦合也可以把inner.caller换成arguments.callee.caller。

call和apply

call和apply作用完全是一样的,区别也仅在于接受参数的方式不同。

apply第二个参数可以是Array的实例,也是一个argument对象,而call方法接收的第二个参数必须逐个列出来。

call和apply是改变函数执行的作用域的,说的通俗点就是改变函数体内this的指向

window.color = 'red'
var obj = {
  color:'blue'
}
function sayColor(color){
  console.log(this.color+' param:'+color)
}
sayColor.call(window,'black'); //red param:black
sayColor.apply(window,['black']);//red param:black

sayColor.call(obj,'black') //blue param:black
sayColor.apply(obj,['black') //blue param:black

这里面color存在在两个环境里,分别全局环境中和对象obj中。使用了call或apply方法后,接收的第一个参数就是改变this的指向,将this指向参数传入的作用域中去,因此输出了不同环境下的color值。

使用apply和call的最大好处就是,对象不需要与方法有任何耦合关系。

bind

bind方法是ECMAscript5定义的一个方法。这个方法会创建一个函数的实例,其this值会被绑定到传给bind()函数的值。

window.color = 'red'
var obj = {
  color:'blue'
}
function sayColor(color){
  console.log(this.color)
}
var objSayColor = sayColor.bind(obj);
objSayColor(); //blue

bind方法会创建一个函数实例,因此需要有变量指向这个函数实例。使用bind的好处,除了能够解耦对象和方法外,在全局作用域中调用这个函数,也能够将this指向所对应的环境。