作用域和函数表达式

执行环境和作用域

每个函数都有自己的执行环境,当代码在一个环境中执行时,会创建变量对象的一个作用域链。作用域链的用途,就是保证对执行环境有权访问的所有变量和函数进行访问。

内部环境可以通过作用域链访问所有的外部环境,但是外部环境不能够访问内部环境中的任何变量和函数。

延长作用域链

有些语句可以在作用域链的前端临时增加一个变量对象,该变量对象会在代码执行后被移除。比如try-catch语句的catch块,还有with语句。

查询标识符

查找标识符的过程从作用域链的前端开始,向上逐级查询与给定名字匹配的标示符。如果在局部环境中找到了该标示符,搜索过程停止,变量就绪。如果在局部环境中没有搜索到该变量名,则会继续沿作用域链向上搜索,直到搜到全局环境变量。

在这个搜索过程中,如果存在一个局部的变量定义,那么搜索就会停止,不再进入下一个变量对象。也就是如果局部环境中存在着同名标示符,就不会再去父环境中找了。

var color = 'blue';
function getColor(){
    var color = 'red';
    return color;
}
console.log(color); // red
垃圾收集

JavaScript具有自定垃圾收集机制,执行环境会负责管理代码执行过程中使用的内存。

标记清除:JavaScript最常用的垃圾收集方式就是标记清除,当变量进入环境,就会把这个变量标记为进入环境,当变量离开环境,就标记为离开环境。垃圾收集器在运行的时候会给变量都加上标记,然后它会去掉环境中变量的标记和被环境中变量引用的变量标记,而在此后,还拥有标记的变量就会被视为准备删除的变量,最后,垃圾收集起完成内存清除的工作,销毁那些带标记的值并回收它们所占的内存空间。

引用计数:另一种不太常见的垃圾收集策略叫引用计数,通过跟踪每个值被引用的次数,当声明了一个变量并将一个引用类型的值赋给变量时,该值的引用次数就加一,相反,如果这个变量之后又引用了其他的值来赋值,那么那个值的引用次数剪一。当引用次数为0时,说明这个值已经没有用了,那就可以把它占用的内存空间回收回来。 但是这种方法有一个问题,如果两个变量循环引用,那么引用次数就不会为0,内存也就得不到回收了。

管理内存

为了确保占用最少的内存让页面有更好的性能,一旦数据不再有用,最好通过使用把其值设为null来解除引用。这个做法适用于大多数全局变量和全局对象的属性,局部变量会在它们离开执行环境的时候自动解除引用。

不过,解除一个值的引用并不意味着会立马自动回收该值所占有的内存,而是让值脱离执行环境,等待垃圾收集器下次运行时将其回收。

闭包

之前作用域链中有说到,作用域链的前端可以访问包含它的作用域(外部)中的变量,所以,如果我们在一个函数内部创建一个函数,那么内部的那个函数可以访问外部函数中的变量。闭包就是指有权访问另一个函数作用域中变量的函数。(就是内部创建的那个函数)。

闭包与变量

不过闭包也会有一些问题,因为它能取得父作用域的值,有的时候会有这样的问题。

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

这样的结果是,数组里每个值都是10,这是因为i保留着对creat函数的i的引用,所以i都是10。不过我们可以通过再创建一个匿名函数来达到预期。

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

在全局函数中,this等于window,而当函数被作为某个对象方法调用时,this等于那个对象。  

var name = "window";

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

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

这样相当于直接在全局作用域下去取得this.name,所以如果我们想要取得object的作用域,可以把object的外部作用域保存在一个闭包能访问到的变量里,这样闭包就能访问到该对象了。

var name = "window";

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

console.log(object.getNameFunc()); //object
模仿块级作用域
(function(){
    //这里是块级作用域
})();

通过一层封装创造一个单独的作用域,这样用在全局中,其他的全局函数和变量就访问不到我们块级作用域中的变量了,通过创建私有作用域,每个开发者既可以拥有自己的变量,又不比担心污染全局作用域。而且也可以减少闭包占用的内存问题,因为没有指向匿名函数的引用,只要函数执行完毕,就可以立即销毁其作用域链了。

模块模式

道格拉斯所说的模块模式则是为单例创建瓷釉变量和特权方法,所谓单例,指的是只有一个实例的对象。

var single = function(){
    //私有
    var privateV = 10;
    var privateF = function(){
        return flase;
    };
    //特权 公有方法和属性
    return{
    publicv : 20,
    publicF : function(){
        return privateV;
    }
    };
}();
增强的模块模式

有人进一步改进了模块模式,这种增强的模块模式适合那些单例必须是某种类型的实例。同时还必须添加某些属性和方法对其加以增强的情况。

var single = function(){
    //私有
    var privateV = 10;
    var privateF = function(){
        return flase;
    };
    //特权 公有方法和属性
    var object = new Object();
    object.publicv = 20;
    object.publicF = function(){
        return privateV;
    };
    return object;
}();

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值