javascript高级程序设计学习笔记(二)---函数表达式

函数表达式

递归

在函数中调用身,但是要有调用终止的判断,否则会无限调用,消耗内存空间
使用arguments.callee,指向正在执行的函数的指针,用来实现函数的递归调用

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

闭包

闭包是指有权访问另一个函数作用域中的变量的函数,因此可以通过在一个函数内部创建另一个函数,来创建闭包。

当某个函数第一次被调用时,会创建一个执行环境及相应的作用域链,并把作用域链赋值给一个特殊的内部属性。然后使用this、arguments和其他明明参数的值来初始化函数的活动对象。但在作用域链中,外部函数的活动对象始终处于第二位,外部函数的外部函数的活动对象处于第三位等。

后台每个执行环境都有一个变量对象。全局环境的变量对象始终存在,局部环境的指针函数执行过程中存在。创建函数时会创建一个预先包含全局变量对象的作用域链,被保存在内部的[[Scope]]属性中。调用函数时·,会创建一个执行环境,通过对[[Scope]]属性中的对象进行赋值构建起环境的作用域链。此后,一个活动对象被创建呗推入执行换进作用域链的前端。并且,作用域链本质上是一个指向变量对象的指针列表,只引用但不包含变量对象。

无论什么时候在函数中访问一个变量时,就会从作用域链中搜索具有相应名字的变量,而且一般在函数执行完毕后,局部活动对象就会被销毁,内存中仅保存全局作用域。

但是在闭包中,在另一个函数内部定义的函数将包含函数(外部函数)的活动对象添加到它的作用域链中。所有在内部的匿名函数的作用域链中,实际上将会包含外部函数的活动对象。

当匿名函数被外部函数返回后,它的作用域链别初始化为包含外部函数的活动对象和全局变量对象。所有匿名函数就可以访问外部函数的变量,外部函数再执行完毕后,其活动对象也不会被销毁,因为匿名函数的作用域链仍然在引用同这个活动对象,会被保存在内存中,直到匿名函数被销毁。

作用域链会有一个问题就是,闭包只能取得包含函数中任何变量的最后一个值。因此需要返回每一个变量的值,需要使用一个局部变量对变量进行保存其副本。

在闭包中使用this对象:this对象时在运行时基于函数的执行环境绑定的,而且匿名函数执行环境具有全局性,其this对象通常指向wiindow。每个函数在被调用时,其活动对象都会自动取得两个特殊变量:this和arguments。内部函数在搜索这两个变量时,只会搜索其活动对象为止,因此永远不可能直接访问外部函数中的两个变量。但是可以把外部作用域中的this对象保存在一个闭包能访问的变量里,就可以让闭包访问该对象。因此可以在匿名函数之前,把this保存在一个变量里,再在匿名函数中调用。

内存泄漏:如果闭包的作用域链保存一个HTML元素,如果不对该元素消除处理,该元素将无法被销毁。因为如果匿名函数保存了对外部函数的活动对象引用,如果匿名函数不被消除,对元素的引用数就不会为零,因此占用的内存永远不会被释放。所以在匿名函数调用完之后,应该把该元素设置为null,释放其在被匿名函数引用的包含函数的活动对象占有的内存。

类似块级作用域

js中是没有块级作用域这一概念的。因此在块语句中定义的变量,是在包含函数中而不是语句中创建的。例如在一个函数的for循环中,变量i在js中,是定义在包含函数的活动对象中。而java等语言中,变量i是定义在for循环的语句块中。

JavaScript中对后续声明的变量视而不见。因此可以使用匿名函数来模仿块级作用域解决这个问题,实现一个私有作用域,从而限制向全局作用域添加过多变量和函数。
(function(){
})()
第一个括号表示一个函数表达式,最后一个括号调用这个函数

私有变量

任何函数中定义的变量都可以认为是私有变量。可以通过在这个函数内部创建一个闭包,闭包通过自己的作用域链访问这些变量,从而可以创建访问私有变量的共有方法

因此为了访问私有变量,我们可以创建一个特权方法。

1、在构造函数中创建,对于不想开放的私有变量,可以通过自己规定的方法进行获取和修改

function MyObject(){
    
    
    var privateItem = 'private';
    function privateFun(){
    
    
        return false
    }
// 通过特权方法获得
    this.getPrivate = function(){
    
    
        alert(privateItem)
        return privateFun()
    };
// 通过特权方法修改
    this.setPrivate = function(arg){
    
    
        privateItem+=arg
    }
}

2、使用静态私有变量可以避免使用构造函数创建私有变量带来的缺点:每个实例都会创建同样一组新方法,导致方法的冗余。

(function(){
    
    
    var age = 1;
    Person = function(arg){
    
    
        age = arg
    }
// 获得私有变量
    Person.prototype.getAge = function(){
    
    
        return age
    }
// 修改私有变量
    Person.prototype.setAge = function(arg){
    
    
        age = arg;
    }
})()

共有方法在原型上定义,保证实例都是用同一个函数。而且在定义构造函数没有使用函数声明,只是使用函数表达式,并且没有使用var关键字。因为初始化未经声明的变量,总是会创建一个全局变量,所有这里的Person就变成一个全局变量,但是这种做法只适用于在非严格模式下。

3、模块模式
该模式主要是为单例创建私有变量和特权方法
一般使用一个对象字面量作为函数的值返回,这个对象字面量只包含公开的方法,可以使用公开的方法对私有变量进行访问操作

var modle = function(){
    
    
    // 私有变量
    var age = 1;
    function addAge(arg){
    
    
        age+=arg
    }

    return {
    
    
        getAge: function(){
    
    
            return age
        },

        setAge: function(arg){
    
    
            return addAge(arg)
        }
    }
}

猜你喜欢

转载自blog.csdn.net/ningpeixi679/article/details/112924449