关于作用域、变量提升和闭包的理解

我不得不承认我是一个幸运儿,我遇到的每一个人都是如此的优秀。在一群优秀的人中间逐渐修炼我的性格,完善我的技能 真的是一个不可多得的恩赐

一、 作用域

谈到作用域这一块,很多人其实都不怎么熟悉这一块,我今天趁着闲暇跟大家来扯一扯。

js(es6以前)没有块级作用域(你可以自己闭包或其他方法实现),只有函数级作用域,函数外面的变量函数里面可以找到,函数里面的变量外面找不到。

1、 全局作用域

来看下面一个代码

var a = 99;

function test(){
    console.log("test:",a);
}
test();// test: 99

从上面的代码我们可以清楚的看出:最外层函数定义的变量拥有全局作用域,即对任何内部函数来说,都是可以访问的。

2、局部作用域

function test(){
    var a = 99;
}
console.log("test:",a);
test();// ReferenceError: a is not defined

由上面的代码我们可以清楚的知道:局部作用域一般只在固定的代码片段内可访问到,而对于函数外部是无法访问的。但是有这么种情况:

var a = "hello"

function test(){
    var a;
    console.log("test:",a);
    a=99

}
test();// test: undefined

当外层函数域定义的变量和函数内定义的变量同时存在的时候,会根据最近原则找本域下的变量,找不到才去外层域去寻找。

3、 块级作用域

谈到块级作用域,这我们就得分es5和es6了,在es5及以前的环境中,先看一段代码:

var a = true;

if(a){
    var b = "hello world!!!";
}

console.log("b:",b) // b: hello world!!!

从上面可以清楚的发现,es6以前是没有块级作用域的。但是在es6中,增加了letconst定义方法,就像下面的代码

var a = true;

if(a){
    let b = "hello world!!!";
}

console.log("b:",b) // ReferenceError: b is not defined

4、变量作用域

再看一段代码:

function test(){
    a = "hello"
}
test();// test: hello
console.log("test:",a);

由上面的代码我可以看出:如果没有定义变量(var,let,const等),默认情况下会将变量变成全局变量。

5、作用域链

关于作用域链,我也来写一段代码

function test(){
    a = "hello"
    var b ="klivitam"

    function test1(){
        console.log("test1",b)
    }

    function test2(){
        var b = "1221"
        console.log("test2",b)
    }
    test1() // test1 klivitam
    test2() // test2 1221

}
test();// test: hello
console.log("test:",a);

关于作用域链,我的理解是:函数会按照就近原则,首先在本作用域内寻找 如果没有寻到就去外层作用域去寻找一直找到windows环境下。着一系列操作形成一个链式结构,所以被称为作用域链。

二、 js提升

1、 变量、函数提升

关于变量提升,最近看到好多前端基础题目都会提到这个,废话不多说,直接上代码吧。

console.log("global",global);// undefined

var global = "klivitam";

console.log("global",global); // klivitam


function fn () {
  console.log(a); // undefined
  var a = 'aaa';
  console.log(a); // aaa
}
fn();
console.log(f1); // [Function: f1]
function f1() {} // 函数提升,整个代码块提升到文件的最开始的位置 

由上面的代码可以看出来:js会将变量、函数声明提升到它所在作用域的最开始的位置。

2、 值得注意的是

来看下面一段代码

console.log(f2); // undefined
var f2 = function() {}


console.log("a = "+a);
console.log("b = "+b);

let a = "ni hao!"; // ReferenceError: a is not defined
const b = "nice to meet u"; // ReferenceError: b is not defined
  • 函数字面量是不会进行变量提升的,提升的依然是var
  • const、let也是不会进行变量提升的。

三、 闭包

关于闭包,我的理解是:闭包就是能够读取其他函数内部变量的函数
来看下面一段代码:

function f1(){
    n=999;
    return function(){
        console.log(n)
    }
}
var result = f1();
result(); // 999

我在写文章的时候特意翻了一下js高级程序设计,里面对闭包是这么描述的:闭包是定义在一个外部函数内部,并且能够访问(存取)外部函数中自由变量的函数。

其实在我的理解里面就是:闭包就是内存函数和外层函数的一个桥梁。

四、 闭包的使用

闭包的作用主要有两个
1、可以读取函数内部的变量
2、让这些变量的值始终保持在内存中。

首先来看一段代码

var count = 99;

function outer(){
    add = function inside(){
        return ++count;
    }

    function console1(){
        console.log(count);
    }
    return console1

}
var o = new outer();

o(); // 99
add();
o(); // 100

上面的代码就能解释前面所说的两个定义。我们发现第一外层函数能够通过闭包访问到内存函数,并且再次执行的时候,count会进行累加。这说明count值一直处于内存中 没有被回收。如果想要进行回收操作,请在使用完闭包之后:

0 = null;

那关于闭包的使用呢?因为我最近在狂补充基础知识,也多多少少看了一些面试指南什么的,我看到了几个面试题是这样的:

1、 定义五个a标签,然后添加绑定事件,要求点击哪个标签的时候就打印哪个?

function timeCount() {
    for (var i = 1; i < 5; i++) {
        // 用settimeout模拟 onclick,感觉类似
        setTimeout(function(){ 
            console.log(i)
        },2000)
    }
}

timeCount();    //5 5 5 5 5

通常的小白都会这么来写,但是结果却发现不能如我们所愿。因为在js中,js会将所有的异步操作加入到一个异步队列中。会等所有的操作结束之后,在进行处理异步操作。所以打印出来的就全部都是5了。为了解决这个问题, 此时就需要用到闭包了。
修改的代码如下:

function timeCount() {
    for (var i = 1; i < 5; i++) {
        (function(i){
            setTimeout(function(){
                console.log(i)
            },1000)
        })(i)
    }
}

timeCount();    //1 2 3 4 5

2、 单例模式

这个是我在一篇文章里面看到的,我之后也在项目里面尝试用了这种方式,反正我觉得还不错。因为之前接触Android的时候,经常会用到单例模式。所以在这里格外的关注一哈。单例的意思是希望产生一个类的唯一实例,。代码如下:

var singleton = function( fn ){
    var result;
    
    return function(){
        return result || ( result = fn .apply( this, arguments ) );
    }
}
var createMask = singleton(
    function(){
        return document.body.appendChild( document.createElement('div') );
    }
)

3、 用闭包模拟私有方法

有时候,我们为了隐藏数据和封装功能经常会使用闭包。比如,写一个时间的累加器:

var makeCounter = function() {
  var privateCounter = 0;
  function changeBy(val) {
    privateCounter += val;
  }
  return {
    increment: function() {
      changeBy(1);
    },
    decrement: function() {
      changeBy(-1);
    },
    value: function() {
      return privateCounter;
    }
  }  
};

var counter1 = makeCounter();
var counter2 = makeCounter();
console.log(counter1.value()); /* Alerts 0 */
counter1.increment();
counter1.increment();
console.log(counter1.value()); /* Alerts 2 */
counter1.decrement();
console.log(counter1.value()); /* Alerts 1 */
console.log(counter2.value()); /* Alerts 0 */

五、 闭包的注意点

  • 由于闭包会使得函数中的变量都被保存在内存中,内存消耗很大,所以不能滥用闭包,否则会造成网页的性能问题,在IE中可能导致内存泄露。解决方法是,在退出函数之前,将不使用的局部变量全部删除。

  • 闭包会在父函数外部,改变父函数内部变量的值。所以,如果你把父函数当作对象(object)使用,把闭包当作它的公用方法(Public Method),把内部变量当作它的私有属性(private value),这时一定要小心,不要随便改变父函数内部变量的值。

说在最后

最近一直失眠搞的我整个人都不怎么好,外加上一些私事儿搞得我整个人都有点炸。当然也有自己期待的东西,希望能够成功的,哈哈。总之,我希望我能愉快的渡过这一个阶段,并朝着下一个阶段走去。🤣🤣🤣 洗澡去了。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 215,923评论 6 498
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 92,154评论 3 392
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 161,775评论 0 351
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 57,960评论 1 290
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 66,976评论 6 388
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,972评论 1 295
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,893评论 3 416
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,709评论 0 271
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,159评论 1 308
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,400评论 2 331
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,552评论 1 346
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,265评论 5 341
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,876评论 3 325
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,528评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,701评论 1 268
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,552评论 2 368
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,451评论 2 352

推荐阅读更多精彩内容