ECMAScript函数表达式

——《JavaScript高级程序设计》Chapter7总结

1.匿名函数的做用

(1)动态定义函数

var sayHi;
    var a=1;
    if (a>0) {
        sayHi=function(){
            console.log("Hi");
        }
    }
    else{
        sayHi=function(){
            console.log("Yo");
        }
    }
    
    sayHi();

(2)把函数当成其余函数的返回值来使用

function createComparisonFunction(propertyName) {
    return function(object1,object2){
        var value1=object1[propertyName];
        var value2=object2[propertyName];
        if (value1<value2) {
            return -1;
        }
        else if (value1>value2) {
            return 1;
        }
        else{
            return 0;
        }
    }
}

var data=[{name:"Zachary",age:28},{name:"Nicholas",age:29}];
data.sort(createComparisonFunction("name"));
console.log(data[0].name);//"Nicholas"
data.sort(createComparisonFunction("age"));
console.log(data[0].name);//"Zachary"

2.递归

(1)经过函数声明实现
function factorial(num) {
    if (num<=1) {
        return 1;
    }
    else{
        return num*arguments.callee(num-1);
    }
}

console.log(factorial(5));

arguments.callee是一个指向正在执行的函数的指针javascript

使用arguments.callee而非原函数名factorial能够解决以下报错问题:java

var anotherFactorial=factorial;
factorial=null;
anotherFactorial(5);

问题:不能在严格模式下访问arguments.callee闭包

(2)经过命名函数表达式实现

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

console.log(factorial(5));

3.闭包

(1)简介闭包

闭包指有权访问另外一个函数做用域中变量的函数。函数

闭包的常见形式是在一个函数内部建立另外一个函数。this

(2)闭包的做用域链
通常的做用域链的状况
  • 后台每一个执行环境都有一个表示变量的对象——变量对象。全局环境的变量对象始终存在,函数这样的局部环境的变量对象只在函数执行的过程当中存在。
  • 做用域链本质上是一个指向变量对象的指针列表,它只引用但不实际包含变量对象。
  • 通常来说,当某函数执行完毕后,其局部活动对象就会被销毁,内存中仅保存全局做用域的变量对象。
闭包的状况不同
  • 在另外一个函数内部定义的函数会将外部函数的活动对象添加到它的做用域中。
  • 在外部函数执行完毕后,其活动对象也不会被销毁,由于其内部函数的做用域仍然在引用这个活动对象。例如上述createComparisonFunction()函数被返回后,其执行环境的做用域链会被销毁,但它的活动对象仍然停留在内存中,由于其返回的匿名函数仍在引用它的活动对象;直到内部匿名函数被销毁,createComparisonFunction()的活动对象才会被销毁。

闭包的内存占用问题

由于闭包会携带包含它的函数的做用域,所以会比其余函数占用更多的内存。过分使用闭包可能致使内存占用过多,故建议只在绝对须要闭包的时候再考虑用闭包。prototype

(3)闭包只能访问包含函数中的任何变量的最后一个值

(4)做为闭包(函数内部的函数)的匿名函数的this对象

对比如下代码1和代码2:设计

代码1:指针

var name="The window";

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

console.log(object.getNameFunc()());//"The window"

代码2:code

var name="The window";

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

console.log(object.getNameFunc());//"My Object"

缘由:对象

匿名函数的执行环境具备全局性。为何匿名函数没有取得其包含做用域的this对象呢?由于做用域链的原理,内部匿名函数在搜索this和arguments两个特殊变量是都是从自身的活动对象开始,在搜索外部函数的活动对象,所以不可能直接访问外不函数的这两个变量;而其自身的this是指向全局环境的。

下面那个不是匿名函数,是函数getNameFunc()。能够姑且这么理解吧??

能够把外部做用域的this用其余变量来保存,这样内部闭包能够经过访问这个变量来访问外部做用域的this:

var name="The window";

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

console.log(object.getNameFunc()());//"My Object"

(5)闭包可能引发的内存泄漏

P183,待整理

4.匿名函数模仿块级做用域

(1)JS无块级做用域

function outputNumbers(count) {
    for (var i=0;i<count;i++) {
        console.log(i);
    }
    
    var i;
    console.log(i);
}

outputNumbers(10);//0 1 2 3 4 5 6 8 8 9 10

即便屡次声明一个变量,JS会忽略后续的声明(不过,它会执行后续声明中的变量初始化)。

(2)当即执行的匿名函数

做块级做用域(又叫私有做用域)的匿名函数语法以下:

(IIFE,(Immediately-Invoked Function Expression),当即执行的函数表达式)

function outputNumbers(count) {
    (function(){
        for (var i=0;i<count;i++) {
            console.log(i);
        }
    })();
    
    console.log(i);//(1)
}

outputNumbers(10);//0 1 2 3 4 5 6 7 8 9 报错(语句(1)错误)
  • 这样在内部匿名函数中定义的任何变量,在执行结束时都会被销毁。故(1)不能访问i。
  • 该匿名函数是闭包,故能够访问外部函数的count。
  • 这种技术继承在全局做用域中被用在函数外部,从而限制向全局做用域添加过多的变量和函数。咱们都一个尽可能少向全局做用域添加变量和函数。由于在有多个开发人员参与的应用程序中,过多全局变量和函数容易致使命名冲突。而经过建立私有做用域,每一个开发人员可使用本身的变量,又不用担忧搞乱全局做用域)

5.私有变量

1)私有变量概念

任何在函数中定义的变量,均可以认为是私有变量。由于不能在函数外访问。

私有变量包括函数的参数局部变量函数内部定义的其余函数

2)特权方法概念

有权访问私有变量私有函数的方法叫作特权方法

建立这样的公有方法的思想是:在函数内部建立一个闭包,利用闭包能够经过本身的做用域链访问这些变量。

3)特权方法建立方式

(1)构造函数模式

构造函数中定义特权方法。
例一:

function MyObject() {
    var privateVariable=20;//私有变量
    function privateFunction() {//私有函数
        return false;
    }
    
    this.publicMethod=function(){//特权方法
        privateVariable++;
        return privateFunction();
    };
}

var object=new MyObject()
console.log(object.publicMethod());//false

例二:

function Person(name) {//参数为私有变量
    this.getName=function(){//特权方法
        return name;
    }
    this.setName=function(newName){//特权方法
        name=newName;
    }
}

var aperson=new Person("Tom");

console.log(aperson.getName());//Tom
aperson.setName("Ben");
console.log(aperson.getName());//Ben
(2)原型模式

经过在私有做用域中定义私有变量和函数,也能够建立特权方法。

(function () {
    var privateVariable=20;//私有变量
    function privateFunction() {//私有函数
        return false;
    }
    
    MyObject=function(){//构造函数,没有var声明就是全局变量
    };
    
    MyObject.prototype.publicMethod=function(){//公有/特权方法
         privateVariable++;
        return privateFunction();
    }
})();


var object=new MyObject();
console.log(object.publicMethod());//false
  • 该模式在定义构造函数时没有使用函数声明,由于函数声明只能建立局部函数,而不带var的函数表达式能够建立全局函数
  • 该模式私有变量和函数是由实例共享的,因为原型方法是在原型上定义的,故全部实例都引用同一个函数(以下例)。而特权方法做为一个闭包保存着对包含做用域的引用。

    (function(){
          var name="";
    
          Person=function(value){
              name=value;
          };
    
          Person.prototype.getName=function(){
              return name;
          };
    
           Person.prototype.setName=function(value){
              name=value;
           };
      })();
    
      var person1=new Person("Nicholas");
      console.log(person1.getName());//"Nicholas"
      person1.setName("Greg");
      console.log(person1.getName());//"Greg"
    
      var person2=new Person("Michael");
      console.log(person1.getName());//"Michael"
      console.log(person2.getName());//"Michael"
(3)模块模式

做用:是为单例建立私有变量和特权方法。

单例:只有一个实例的对象。

方式:在匿名函数内部,首先定义私有变量和函数,而后将一个对象字面量做为匿名函数的返回值。返回的对象字面量里面只包括能够公开的属性和方法。该对象是在匿名函数内部定义的,故它的公有方法有权访问私有变量和函数。

var singleton=function(){
    var privateVariable=10;//私有变量
    function privateFunction() {//私有函数
        return false;
    }
    
    return {
        publicProperty:true,//特权/公有属性
        publicMethod:function(){//特权/公有方法
            privateVariable++;
            return privateFunction();
        }
    };
}();

适用:若是必须建立一个对象并以一些数据对其进行初始化,同时还要公开一些可以访问这些私有数据的方法,就可使用模块模式。

(4)加强的模块模式

对模块模式的改进,即在返回对象以前加入对其加强的代码。

适用于:单例必须是某种类型的实例,同时还必须添加某些属性和方法对其加强的状况。

var singleton=function(){
    var privateVariable=10;//私有变量
    function privateFunction() {//私有函数
        return false;
    }
    
    var object=new CustomType();//建立对象
    
    object.publicProperty=true;
    
    object.publicMethod=function(){
        privateVariable++;
        return privateFunction();
    }
    
    return object;
}();
相关文章
相关标签/搜索