欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

学习笔记10--函数表达式

程序员文章站 2022-03-18 19:14:45
...

函数表达式的特征

函数表达式和其他表达式一样,在使用前必须先赋值。

递归

    //递归阶乘函数
    function factorial(num) {
        if (num <= 1) {
            return 1;
        } else {
            return num * factorial(num - 1);
        }
    }

    //虽然这个函数表面看来没什么问题,但下面的代码却可能导致它出错。
    var anotherFactorial = factorial;
    factorial = null;
    // alert(anotherFactorial(4));//TypeError

    //可以用命名函数表达式避免
    var factorial = (function f(num) {
        if (num < 1) {
            return 1;
        } else {
            return num * f(num - 1);
        }
    });

    alert(anotherFactorial(4));//24

闭包

闭包是指有权访问另一个函数作用域中变量的函数。
创建闭包的常用方式是在一个函数内部创建另一个函数。

    function createComparisionFunction(propertyName) {

        return function (object1, object2) {
            //内部函数可以访问外部变量propertyName
            var value1 = object1[propertyName];
            var value2 = object2[propertyName];

            if (value1 < value2) {
                return -1;
            } else if (value1 > value2) {
                return 1;
            } else {
                return 0;
            }
        }
    }

    var compare = createComparisionFunction("name1");

    //在createComparisionFunction函数外部调用内部函数compare仍可访问变量propertyName
    compare();

当函数被调用时,会创建一个执行环境和相应的作用域链。

    function compare (value1, value2) {
        if (value1 < value2) {
            return -1;
        } else if (value1 > value2) {
            return 1;
        } else {
            return 0;
        }
    }

    var result = compare(5, 10);

以上代码先定义compare()函数,然后在全局作用域中调用了它。
1. 创建函数时,会创建一个预先包含全局变量对象的作用域链,并被保存到函数内部的[[Scope]]属性中。
2. 调用函数时,从[[Scope]]属性中复制作用域链,此后又有一个活动对象被推到作用域的前端。

compare在被调用时的作用域链
学习笔记10--函数表达式

变量对象存储当前作用域中定义的变量和函数声明,每个执行环境中都有一个变量对象
活动对象在函数被调用时创建,包含形参和arguments
作用域链的前端始终是当前执行代码所在的环境的变量对象,如果这个环境是函数,则将其活动对象作为变量对象。
全局变量对象始终是作用域链中的最后一个对象。

在一个函数内部定义一个函数,内部函数将会把外部函数的活动对象加到其作用域链中。

    /*在匿名函数从createComparisionFunction中返回后,
    匿名函数的作用域链被初始化为包含createComparisionFunction活动对象和全局变量对象,
    这样,匿名函数就可以访问createComparisionFunction中的变量,
    createComparisionFunction()函数执行完毕后,createComparisionFunction()的活动对象不会被销毁,但是作用域链会被销毁。
    */

    //创建函数
    var compareNames = createComparisionFunction("name");

    //调用函数
    var result = compareNames({ name: "Jack" }, { name: "Tom" });

在被调用compareNames时的作用域链
学习笔记10--函数表达式

匿名函数被销毁后,createComparisionFunction的活动对象才会被销毁。

    //解除对匿名函数的引用
    compareNames = null;
闭包与变量

闭包只能取得外部函数中的变量的最终值。

    function createFunctions() {
        var result = new Array();
        for (var i = 0; i < 10; i++) {
            result[i] = function () {
                return i;
            };
        }

        //此时,i = 10
        return result;
    }

    var functions = createFunctions();
    for (var i = 0; i < 10; i++) {
        var temp = functions[i]();
        console.log(temp);
    }

    /**output
    10
    10
    ...
    10
    */

通过一个创建一个匿名立即执行函数,返回一个可以访问匿名函数变量num的闭包。

    function createFunctions() {
        var result = new Array();
        for (var i = 0; i < 10; i++) {
            result[i] = function (num) {
                return function () {
                    return num;
                }
            }(i);
        }
        return result;
    }

    var functions = createFunctions();
    for (var i = 0; i < 10; i++) {
        var temp = functions[i]();
        console.log(temp);
    }

    /**output
    0
    1
    ...
    9
    */
关于this对象

this是基于函数的执行环境绑定的,当函数被作为某个对象的方法调用时,this对象指向那个对象。

    var name = "the window"

    var object = {
        name: "my object",
        getNameFunc: function () {//此函数被object对象的方法调用,所以this指向object 
            console.log("getNameFunc  " + this.name);
            return function () {//此匿名函数没有绑定到任何一个对象上,所以this指向window
                return this.name;
            }
        }
    }

    console.log(object.getNameFunc()());

    /*output
    getNameFunc  my object
    the window
    */

在外部函数中定义一个that变量保存外部函数(getNameFunc)的this对象,通过闭包访问that变量来访问外部函数的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
模仿块级作用域
    //可以通过这种方式,创建一个私有作用域,匿名函数中定义的任何变量都在执行后被销毁
    //经常子在全局作用域中使用,从而限制向全局作用域中添加过多的变量和函数
    (function () {
        //do something
    })();
私有变量

所有对象的属性都是公有的
任何在函数中定义的变量都可以被看做私有变量。

在函数内部创建的闭包可以通过作用域链访问函数的私有变量。私有变量和私有函数的公有方法称为特权方法

    //在构造函数中定义特权方法
    function MyObject() {

        //私有变量和私有函数
        var privateVariable = 10;

        function privateFunction() {
            return false;
        }

        //特权方法
        this.publicMethod = function (){
            privateVariable++;
            return privateFunction();
        }
    }

静态私有变量

    (function() {
        var name = '';

        //没有使用var声明,是全局变量
        Person = function(value) {
            name = value;
        }

        Person.prototype.getName = function() {
            return name;
        };

        Person.prototype.setName = function(value) {
            name = value;
        };
    })();

    var person1 = new Person('Jack');
    alert(person1.getName()); //Jack
    person1.setName('Tom');
    alert(person1.getName()); //Tom

    var person2 = new Person('Jerry');

    //因为setName(), getName()在原型上定义的,被所有实例共享,在一个实例上调用会影响所有实例
    //所以私有变量可以看做是静态的。
    alert(person1.getName()); //Jerry
    alert(person2.getName()) //Jerry

模块模式

    //返回对象的匿名函数
    var singleton = function() {
        var privateVariable = 10;

        function privateFunction() {
            return false;
        }
        // 特权 / 公有方法和属性
        //返回包含可以访问私有变量和函数的对象字面量
        return {
            publicProperty: true,

            publicMethhod: function() {
                privateVariable++;
                privateFunction();
            }
        }
    }

如果必须创建一个对象并以某些数据对其进行初始化,同时还要公开一些能够访问这些私有数据的方法,那么就可以使用模块模式。以这种模式创建的每个单例都是Object的实例,因为最终要通过一个对象字面量来表示它。

   /**
    * 在创建这个对象的过程中,首先声明了一个私有的components数组,并向数组中添加了一个BseComponent的新实例。
    * 而返回对象的getComponentCount()和registerComponent()方法,都是有权访问数组components的特权方法。
    */
   var application = function() {
        //私有变量和函数
        var components = new Array();
        //初始化
        components.push(new BaseComponent());
        //公共
        return {
            getComponentCount: function() {
                return components.length;
            },
            registerComponent: function(component) {
                if (typeof component == "object") {
                    components.push(component);
                }
            }
        };
    }();

增强的模块模式

增强的模块模式适合那些单例必须是某种类型的实例,同时还必须添加某些属性和(或)方法对其加以增强的情况。例子:

如果需要application对象必须是BaseComponent的实例

 var application = function() {
        //私有变量和函数
        var components = new Array();
        //初始化
        components.push(new BaseComponent());
        //创建application的一个局部副本
        var app = new BaseComponent();
        //公共接口
        app.getComponentCount = function() {
            return components.length;
        };
        app.registerComponent = function(component) {
            if (typeof component == "object") {
                components.push(component);
            }
        };
        //返回这个副本
        return app;
    }();