您的位置:首页 > Web前端 > JavaScript

Javascript的四种(函数)调用模式

2016-02-01 20:25 706 查看
函数的四种调用模式,分别是函数调用模式方法调用模式构造器调用模式以及Apply/Call调用模式

本文先简单介绍下Javascript的作用域链问题,然后谈下四种调用模式,最后介绍一下闭包以及调用模式在闭包里面的体现

作用域链

四种调用模式

闭包及示例

1.作用域链

在js中整个script是一个全局作用域链,记作0级作用域链。

在全局作用域链中的函数分配一个作用域链,记作1级作用域链。

即:在n级作用域链中的函数,分配n+1级作用域链

每一个函数都会分配一个作用域链,即js没有块级作用域

po一张作用域链图(自行体会函数f1(),f2(),f3())。



访问变量时,先在当前作用域链中找,如果找到就用,找不到就到上一级作用域链中找,直到找到0级作用域链还未找到,报未定义错误。

举个例子(涉及闭包),可去掉对应注释检验

var i="倩倩";
var f1=function(){
//var i="倩宝";
var f2=function(){
//var i="郭子倩";
var f3=function(){
alert(i);
}
return f3;
}
return f2;
}
f1()()();  //倩倩


2.四种调用模式

2.1函数调用模式

当一个函数并非一个对象的属性时,它被当作一个函数来调用,此时this指全局对象,即window。

var name="倩倩";
var f1=function(){
var name="倩宝";
alert(this.name);
}
f1();   //倩倩


但是如果当一个函数内部还有一个函数,则内部的函数用this指的是什么呢?按道理:当内部函数被调用时,this 应该指的是外部函数的 this (就比如我爹生了我,别人问我是谁家的,我一定是说那谁谁家的不是哈哈哈哈哈)但是Javascript的函数就是这么坑爹,只认祖宗(window)。

解决方法:

在函数中定义一个变量并给它赋值为 this,那么内部函数就可以通过那个变量访问到被绑定到外部函数的正确 this。按照约定,我们给那个变量命名为 that。

var name="倩倩";
var f1=function(){
var name="倩宝";
var that=this;
var f2=function(){
alert(that.name);
}
return f2;
}
f1()(); //倩倩


其实这个例子举得不是很恰当,因为在这里f1()的this指的还是window(全局对象),所以赋值给that,that的值仍是全局对象。

that一般用在方法调用模式,用来使用特定对象的变量。

这里仅仅是理解一下that的用法。

2.2方法调用模式

方法调用模式中,this表示当前对象

var name="倩倩";
var person={
name:"倩宝",
sex:"女",
say:function(){
alert(this.name);
alert(this.sex);
}
}
person.say();   //倩宝,女


思考:将上述代码改成下边的样子,会显示什么?如何改进?

var name="倩倩";
var person={
name:"倩宝",
sex:"女",
say:function(){
return function(){
alert(this.name);
alert(this.sex);
}
}
}
person.say()();


结果:倩倩,undefined

改进:that(函数调用模式)

2.3构造器调用模式

JavaScript中如果用 new 方式调用一个函数,

1.如果函数返回一个对象,this就指向这个对象;

var person=function(){
this.name="倩宝";
this.sex="女";
return {
name:"倩倩"
}
}
var p=new person();
alert(p.name);  //倩倩


2.否则,this指向这个新创建出来的对象(类似java中类的实例)

    //return 任意东西。
var person=function(){
this.name="倩宝";
this.sex="女";
return 123;
}
var p=new person();
alert(p.name);  //倩宝


// 没有return
var person=function(){
this.name="倩宝";
this.sex="女";
}
var p=new person();
alert(p.name);  //倩宝


2.4 apply/call调用模式

目前学习当中接触的不多,暂不作讨论,简单记录两个点。

1.语法

函数名.apply(对象,[参数数组]);

函数名.call(对象,参数列表);

当对象为null时,函数进行函数调用模式

当对象不为null时,函数进行方法调用模式

2.this

this指向第一个参数

3.闭包及示例

前面已经了解过了JS的作用域链问题,知道0级作用域链的变量无法访问1级作用域链的变量(即低级作用域链无法访问高级作用域链的变量,“高低”一词根据理解来看了)。而闭包就能改变这种现状。

摘抄某网站里的概念,闭包:是指有权访问另外一个函数作用域中的变量的函数。创建闭包的常见方式就是在一个函数内部创建另外一个函数。

闭包的两个用途:

1.读取函数内部的变量

(前面已经有很多例子)

2.让这些变量的值始终保持在内存中

function f1(){
    var n=999;
    nAdd=function(){n+=1}
    function f2(){
      alert(n);
    }
    return f2;
  }
  var result=f1();
  result(); // 999
  nAdd();
  result(); // 1000


代码解析:

1.result实际上就是f2,通过f1()得到,按道理赋值过后f1应该消亡,但是却没有。(由nAdd()将n从999变为1000可知,n为f1的变量)。这是因为f2赋给了全局变量result,而f2又依赖于f1,因此f1也始终在内存中,不会在调用结束后,被垃圾回收机制(garbage collection)回收。

2.nAdd()前面没有使用var关键字,因此nAdd是一个全局变量,而不是局部变量。其次,nAdd的值是一个匿名函数(anonymous function),而这个匿名函数本身也是一个闭包,所以nAdd相当于是一个setter,可以在函数外部对函数内部的局部变量进行操作。(这句是阮一峰老师的原话,为什么说它是闭包,我大致理解为作为一个全局变量可以访问函数f1中的变量)

附上阮一峰老师博客里面的两道思考题,相信读懂前面的之后,自然就是小case了。

var name = "The Window";
  var object = {
    name : "My Object",
    getNameFunc : function(){
      return function(){
        return this + ", " + this.name;
      };
    }
  };
  alert(object.getNameFunc()());


var name = "The Window";
  var object = {
    name : "My Object",
    getNameFunc : function(){
      var that = this;
      return function(){
        return that + "," + that.name;
      };
    }
  };
  alert(object.getNameFunc()());


本文参考文章:

1.http://blog.saymoon.com/2009/10/difference-from-this-in-invocation-for-function-by-four-ways/

2.http://www.ruanyifeng.com/blog/2009/08/learning_javascript_closures.html
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息