JS作用域、立即执行函数、闭包

作用域    

首先先介绍一下作用域等一些基础概念。

 每个JavaScript函数都是一个对象,对象中有些属性我们可以访问,但有些不可以,这些属性仅供JavaScript引擎存取,[[scope]]就是其中一个。

[[scope]] : 指的就是我们所说的作用域,其中存储了执行期上下文的集合

作用域链 : [[scope]] 中所存储的执行期上下文对象的集合,这个集合呈链式链接,我们把这种链接叫做作用域链。

运行期上下文  : 当函数执行时,会创建一个称为执行期上下文的内部对象(AO)。一个执行期上下文定义了一个函数执行的环境,函数每次执行时对应的执行环境都是独一无二的,所以多次调用一个函数会导致创建多个执行上下文,当函数执行完毕,它所产生的执行上下文被销毁。

查找变量  :从作用域链的顶端依次向下查找。

下面举一些例子:

function a(){
    function b(){
        function c(){

        }
        c();
    }
    b();
}
a();


a defined a.[[scope]] ----> 0 : GO          //a定义的时候产生GO对象
a doing   a.[[scope]] ----> 0 : aAO           //a执行的时候新产生AO对象
                            1 : GO

b defined  b.[[scope]] ----> 0 : aAO            //子级b定义会继承父级a运行时产生的对象
                             1 : GO 
b doing    b.[[scope]] ---->  0 : bAO            //子级b新产生AO对象
                              1 : aAO 
                              2 : GO 
                              
c defined  c.[[scope]] ---->  0 : bAO            //c定义时会继承b运行时产生的属性
                              1 : aAO 
                              2 : GO                        
c doing     c.[[scope]] ----> 0 : cAO            //c执行时同时又产生新的AO
                              1 ;bAO 
                              2 : aAO 
                              3 : GO 

立即执行函数

之前学过函数的定义、函数表达式,还有一种函数叫做立即执行函数。

立即执行函数:函数执行过后立即被销毁。

立即执行函数的官方写法:

// 立即执行函数的官方写法
(function() {} ());  W3C建议此种
(function() {})();

针对初始化功能的函数,可以有参数。

var num = function (a,b){
    return a + b;
}(1,2);

(function abc(){
    var a = 123;
    var b = 234;
    console.log(a+b);
}())

只有表达式才能被执行符号执行,能被执行符号执行的表达式,函数名字会被自动忽略。

function test(){
    console.log("a");
}()    会出现语法解析错误,因为括号前面是函数声明

(+ function test( ){
    console.log('a');
}())                    -------->打印出a

下面是一道曾阿里面试题

function test(a, b, c, d){
    console.log(a + b + c + d);
}(1, 2, 3, 4);

// 不报错也没有执行      

下面是几道经典的例题,可以参考一下:

 
 
function test(){
    var arr = [];
    for(var i = 0; i < 10; i ++){
        arr[i] = function (){
            console.log(i);
        }
    }
    return arr;
}
var myArr = test();
for(var j = 0; j < 10; j++){
    myArr[j]();
}  

 
 
// 输出:10个10

那么采用立即执行函数呢?会有怎样的结果呢?

function test(){
    var arr = [];
    for(var i = 0; i < 10; i ++){
        (function(j){
            arr[i] = function (){
            console.log(j + " ");
        }
        }(i))
    }
    return arr;
}
var myArr = test();
for(var j = 0; j < 10; j++){
    myArr[j]();
} 
 
 
// 输出结果  0 1 2 3 4 5 6 7 8 9 

大家可以自行思考一下。

闭包

闭包的现象:当内部函数保存到外部时会产生闭包。


闭包会导致原有的作用域链不释放,造成内存泄漏

(内存泄漏:内存占用(比如:手握沙子,握得越紧手里剩得就越少))


闭包触发的情况:

    两个或多个函数互相嵌套,把里面的函数保存到外部,这样的情况一定会产生闭包。从外面还可以调用里面的函数。


闭包的作用:

            实现公有变量

                    eg:函数累加器

            可以做缓存(存储结构)

                    eg:eater

               可以实现封装,属性私有化

                    eg:person()

                模块化开发,防止污染全局变量



// 函数累加器
function add(){
    var count = 0;
    function demo(){
        count ++;
        console.log(count);
    }
    return demo;
}
var counter = add();
counter();
counter();
counter();
counter();
counter();
counter();


// eater
function test(){
    var food = "apple";
    var obj = {
        eatFood : function (){
            if(food != ""){
                console.log("I am eating  " + food);
                food = "";
            }
            else{
                console.log("There is nothing!");
            }
        },
        pushFood : function (myFood){
            food = myFood;
        }
    }
    return obj;
}
var person = test();
person.eatFood();
person.eatFood();
person.pushFood('banana');
person.eatFood();
 闭包:可以实现封装,属性(变量)私有化
            //属性(变量)私有化:别人访问不出来,只有自己设置方法让显式才能访问比如以下例子的prepareWife

                例如:
                function Deng(name, wife){
                    var prepareWife = "xiaozhang";
                    this.name = name;
                    this.wife = wife;
                    this.divorce = function (){
                        this.wife = prepareWife;
                    }
                    this.changePrepareWife = function (target){
                        prepareWife = target;
                    }
                    this.sayPrepareWife = function(){
                        console.log(prepareWife);
                    }
                }
                var deng = new Deng('deng', 'xiaoliu');   //将方法实例化对象

以上例子中我们用deng.prepareWife是访问不出来的,只能是调用sayPrepareWife方法才能实现。


这就将PrepareWife实现了私有化,实际是存在的,只是访问不出来,只能通过自身设置的方法才能访问,这就防止了污染全局变量。


附加一个逗号操作符:

        先看前面的表达式,再看后面的表达式,把后面表达式的计算结构返回

例题:

var f =(
    function f(){
        return "1";
    },
    function g(){
        return 2;
    }
)();
console.log(typeof(f)); 

// -------number

var x = 1;
if(function f(){}){
    x += typeof f;
}
console.log(x);
// --------> 1undefined

闭包还有一些知识有待补充,等后面学习继续补充。


猜你喜欢

转载自blog.csdn.net/qq_41713692/article/details/80105494
今日推荐