好好理解一下作用域和闭包和预编译

引言

在写业务代码的同时,我觉得还是很需要把基础知识全部理清楚的,闭包和作用域呢我一直觉得我懂了,那么试试看能不能说清楚?当然我也会参考一下别人说法,所以会在参考资料里面写上啦嘻嘻~~

作用域和作用域链

作用域是一个语言无关的概念,当然作用域分为词法作用域动态作用域
作用域:通常来说,一段程序代码中所用的名字并不总是有效/可用的,而限定这个名字的可用性的代码范围就是这个名字的作用域。
词法作用域:也叫做静态作用域,它的作用域是指在词法分析阶段就确定了,是不会改变的。
动态作用域:是在运行的时候根据程序的流程信息来动态确定的,而不是写代码是进行静态确定的。

var a = 2;

function foo() {
  console.log(a); // 会输出2还是3?
}

function bar() {
  var a = 3;
  foo();
}

bar();
如果是词法作用域

foo()函数引用到全局作用域中的a,因此会输出2。因为词法作用域是写代码的时候就静态确定下来的,JavaScript的作用域就是词法作用域(大部分语言也都是基于词法作用域的),所以这段大妈输出应该是2.
在这里插入图片描述

如果是动态作用域

动态作用域不会关心函数和作用域是如何声明以及在何处声明的,只关心它们从何处调用。换句话说,作用域链就是基于调用栈的,而不是代码中的作用域嵌套,因此,如果是JavaScript具有动态作用域,理论上输出结果应该是3.

总结

JavaScript并不具有动态作用域,它只有词法作用域。eval()withthis机制在某种程度上很像动态作用域,使用上要特别注意。
词法作用域关注函数在何处声明,而动态作用域是在运行时确定的(javascript中的this也是)。

闭包

闭包的创造条件

  • 存在内、外两层函数
  • 内层函数对外层函数的局部变量进行了引用

闭包是对作用域的眼神,也是在实际开发中经常使用的一个特性。
一个例子:

    var scope = {};
    if (scope instanceof Object) {
        var j = 1;
        for (var i = 0; i < 10; i++) {
            //console.log(i);
        }
        console.log(i); //输出10
    }
    console.log(j);//输出1

}

无论用过何种手段将内部函数传递到所在的所在的词法作用域以外,它都会持有对原始定义作用域的引用,无论在何处执行这个函数都会使用闭包
一个难一点的例子

function wait(message) {
  setTimeout( function timer() {
    console.log( message );
  }, 1000 );
}
wait( "Hello, closure!" );

这也是闭包。传入的timer函数一定会被执行,知识内部引擎调用执行,深入到引擎的内部,内置的工具函数setTimeout(..)持有对一个参数的引用,引擎会调用这个函数!!!
IIFE(立即执行函数)是闭包吗
它创建了闭包,在内存中创建了一块区域,这块区域保存着作用域链上的作用域引用。等同创建了闭包

// 它需要有自己的变量,用来在每个迭代中储存i 的值:
for (var i=1; i<=5; i++) {
    (function() {
        var j = i;
        setTimeout( function timer() {
            console.log( j );
        }, j*1000 );
    })();
}
// 行了!它能正常工作了!。
// 可以对这段代码进行一些改进:
for (var i=1; i<=5; i++) {
    (function(j) {
        setTimeout( function timer() {
            console.log( j );
        }, j*1000 );
    })( i );
}
//当然你也可以这样写
for (var i=1; i<=5; i++) {
    (function(i) {
        setTimeout( function timer() {
            console.log( i );
        }, i*1000 );
    })( i );
}
//你也可以这样
function timeManage() {
    for (var i = 0; i < 10; i++) {
        setTimeout((function(e) {
            return function() {
                console.log(e);
            }
        })(i), 1000)
    }
}
//timeManager();输出1,2,3,4,5
function createClosure() {
    var result = [];
    for (var i = 0; i < 5; i++) {
        result[i] = function(num) {
            return function() {
                console.log(num);
            }
        }(i);
    }
    return result;
}
//createClosure()[1]()输出1;createClosure()[2]()输出2

由于闭包会额外的附带函数的作用域(内部匿名函数携带外部函数的作用域),因此,闭包会比其它函数多占用些内存空间,过度的使用可能会导致内存占用的增加。

总的来说注意三个问题:

  • 变量
  • this
  • 内存
闭包中的this问题:

闭包中的this之前讲过,是特例,跟动态作用域差不多:

var object = {
    scope:"local",
    getScope:function(){
        return function(){
            return this.scope;
        }
    }
}
//object.getScope()()返回值为global

匿名函数的this永远指向windows

预编译

JavaScript脚本的运行其实是由两个阶段组成的:

  • 预编译阶段:在内存中开辟出一块空间,用来存放变量和函数,为使用varfunction关键字开辟出一片空间,用来存放这二者声明的变量和函数。
    -预编译的时候function的优先级高于var(也说明了函数是一等公民~~~)
    预编译时不会对变量进行赋值(不会进行初始化),赋值是在执行阶段进行的。
  • 执行阶段

结语

嘤嘤嘤终于写完了,今天一天特别不在状态,晕晕乎乎的,所以写出来的东西可能有点乱,我原来的目的是为了让自己方便学习使用,如果大家看到了之后觉得有问题,或者想喷我嘤嘤嘤?评论区是你们的舞台呜呜呜呜呜~~我这么可爱,别喷我,可以留言我很可爱呀~对了,如果觉得我讲的不清楚,都可以看参考资料的,我觉得讲得很到位欸

参考资料

词法作用域 VS 动态作用域
深入javascript——作用域和闭包
你不知道的JS(2)深入了解闭包
JavaScript - 预编译

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章