javascript学习笔记--理解apply()、call()、bind() 以及caller、callee属性

本文目录如下:
- 前言
- apply()、all()函数、bind()函数
- caller属性与callee属性
- 总结


前言

在公司做一些全栈开发,实际上也不算,啥都会一点儿,其中就包括前端的javascript脚本语言。之前有过一些代码经验(代码算不上精致,用面向对象语言,写面向过程的代码逻辑),最近,突然想,无论什么语言,多沉淀一下总是好的,于是有了这个博客专栏,希望用笔头记下自己学习的点点滴滴,欢迎大家拍砖。

apply()函数与call()函数

  无论是小白还是老司机,相比对这两个大名鼎鼎的函数不陌生。先来看看这两个函数的基本描述:

  • 对于javascript脚本语言,所有的函数都包含这两个函数,不需要任何的继承
  • 他们的用途都是一样的,都是为了扩充函数的功能,有的说法也叫作用域,比较难以理解
  • 接收参数方面不同,apply()接收两个参数,一个是函数运行的作用域(this),另一个是参数数组。
    call()方法第一个参数与apply()方法相同,但传递给函数的参数必须列举出来。
      
      对于前面两点,实际上还是比较好理解的,想必对于第三点,尤其是对于我这种小白来说,还是比较蒙逼的,什么是函数运行作用域?下面举个例子,然后看起运行的行为,然后我们再来重新进行定义,让第三点读起来通俗易懂。
//声明一个Person对象
function Person(name){
    this._name=name
    this.showName=function(){//打印自己名字的方法showName()
        return this._name;
    }
}
//声明一个Chinese对象,无showName()
function Chines(name){
    this._name = name;
    }
}
//声明一个American对象,showName()
function American(name){
    this._name = name;
    }
}
//从声明来看,这三个函数没有依赖关系;
//那么现在Chinese和American都想有一个这样的方法,有什么解决思路呢?
/*
    1.继承 已经超出本文的研究范围,不做深入研究
    2.在这两个对象中加上showName()方法,那如果是100个对象呢,是不是要写100遍这个函数,这个简直就是灾难
    3.利用apply()和call方法
*/
//ok,讨论完方案之后我们来改造下代码,当然我们直接摒弃了方法2,因为方法2太low了,我们来说下方法3
function Chinese(name){
    Persion.apply(this);
    this._name=name
}
//声明一个American对象,showName()
function American(name){
    Persion.apply(this);
    this._name=name
}
//wait 这不就是第二种方法吗,实际上还是要在每个对象上加代码,不过我们的代码看起来优雅了不少,下面来跑下测试结果看看
var chinese = new Chinese("chenglong");
var american = new American("john");

console.log(chinese.showName()); //输出chenglong
console.log(american.showName()); //输出john
//通过apply,两对象就有了Person对象的方法了


//当然用call方法也是一样的,不过call方法需要指定具体的参数,看起来像这个样子
function Chinese(name){
    Person.call(this,name);
    this._name=name
}
//声明一个American对象,showName()
function American(name){
    Person.call(this,name);
    this._name=name
}
var chinese = new Chinese("chenglong");
var american = new American("john");

console.log(chinese.showName()); //输出chenglong
console.log(american.showName()); //输出john

//你也可以不用继承的方式 这就退化成了普通的调用了,这种方式不必改造任何的代码
//声明一个Chinese对象,无showName()
function Chinese(name){
    this._name = name;
}
//声明一个American对象,showName()
function American(name){
    this._name = name;

}
var person = new Persion("person");
var chinese = new Chinese("chenglong");
var american = new American("john");
console.log(person.showName.apply(chinese)); //输出chenglong
console.log(person.showName.call(chinese,"chenglong")); //输出chenglong
console.log(person.showName.apply(american)); //输出john
console.log(person.showName.call(american,"john")); //输出john
//看起来这种情况下,apply更优雅,但是没指定参数,总是发慌,用call限定了参数;而且多了个person对象,真让人不爽,因此把apply和call运用到类中,一劳永逸

  现在,我们来总结一下,基本描述的第三点,在继承中我们有一句这样的代码Persion.call(this,name)和 Persion.apply(this),当有了这个代码之后,我们的Chineses从此就有了showName的方法,apply中文是啥意思?应用的意思,我们似乎可以这么拆解:将Person的所有的方法(或者某个方法)应用在apply()/call()传进来的第一个参数上,我们的函数对象(有一个对象A,有一些基础的方法,比如打印名字什么的,而有个对象B,不想重载这些方法,那这个时候就用apply()/call()来把A的方法扩展下吧,为我所用)这就达到了,将Chinese对象的方法进行扩展的目的,不得不佩服javascript语言发明者的绝妙思维。
 

bind()函数

 实际上理解了call函数和apply函数之后,再来理解bind函数就没那么难了,bind函数从某种意义上来说,用法和上述两个函数一模一样。最大的区别是,bind函数具有懒加载,可以用着回调,该函数实际上返回的是一个更换了this指针的函数引用,你可以不立即执行它,将它保存在变量中,而apply和call就不行,你调用了就立即执行。举个例子:
 

var obj = {
    x: 100,
};

var foo = {
    getX: function() {
        return this.x;
    }
}

console.log(foo.getX.bind(obj)());  //100
console.log(foo.getX.call(obj));    //100
console.log(foo.getX.apply(obj));   //100
//虽然结果都是一样的,但是你可以发现bind函数在后面多了一对小括号,这就是区别

caller和callee属性

  caller:调用者的意思,返回的是谁是这个函数的调用者的函数引用。举个例子:
  

function callerDemo()
{
    if(arguments.caller){//调用者不是null,则返回调用者的函数引用
        console.log(callerDemo.caller.toString());
    }else
    {
        console.log("This function's caller is windows");
    }
}
function handleCaller(){
    callerDemo();
}
handleCaller();//输出的是handleCaller函数的引用
callerDemo();//This function's caller is windows

callee:callee 属性是 arguments 对象的一个成员,它表示对函数对象本身的引用。这个特性在递归函数中非常的有用。

function calleeDemo() {
    alert(arguments.callee);
}
callerDemo();//打印函数对象本身的引用

总结

最后,再总结一下:

  • apply 、 call 、bind 三者都是用来改变函数的this对象的指向的,第一个参数都是传入的对象的this指针;
  • bind函数返回的是函数,具备回调性质,apply、call立即调用
  • caller 返回的是调用该函数的函数的引用;
  • calee是argument的一个属性方法,返回的是这个函数的本身的引用
  • apply、call、bind的通用理解方式是:xxx对象的xx方法运用到xxx对象上(obj.method.apply,call,bind),使得目标对象的方法得到了扩展
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章