读书笔记-你不知道的js上-闭包与模块

QH-Jimmy QH-Jimmy     2022-08-23     319

关键词:

闭包定义

 

  当函数可以记住并访问所在的词法作用域时,就产生了闭包,即使函数是在当前词法作用域之外执行。

  看一段最简单的闭包代码:

    function foo() {
        var a = 2;
        //闭包
        function bar() {
            console.log(a);
        }
        return bar;
    }
    //理论上 foo执行完内部数据会被销毁
    //由于闭包的作用 作用域一直保持
    var baz = foo();
    baz(); //2

  bar()函数可以访问foo()的作用域,通过foo()执行后,返回bar()并当成一个值传递给baz。当baz执行时,bar()依然持有对该作用域的引用,而这个引用就叫做闭包。

  在之后这个函数在词法作用域以外的地方被调用,闭包使得函数可以继续访问定义时的词法作用域。

  换一个例子:

    function wait(msg) {
        //这里有一个隐性的赋值语句
        //var msg = msg;
        setTimeout(function() {
            //一秒后 内部函数依然保留对外部msg的引用
            console.log(msg);
        }, 1000);
    }
    wait(‘Hello World‘);

  一个经典的循环闭包例子:

    for (var i = 0; i < 5; i++) {
        //内部引用同一个i
        setTimeout(function() {
            console.log(i); // 5 5 5 5 5
        }, 1000 * i)
    }

  如果加上IIFE:

    for (var i = 0; i < 5; i++) {
        (function() {
            //也没有用 内部没有接受到参数 还是引用外部i
            setTimeout(function() {
                console.log(i); // 5 5 5 5 5
            }, 1000 * i)
        })(i)
    }

  最后,IIFE表达式加上传参,成功!

    for (var i = 0; i < 5; i++) {
        //每次传入一个i
        (function(i) {
            //隐性赋值 var i = i;
            setTimeout(function() {
                console.log(i); // 0 1 2 3 4
            }, 1000 * i)
        })(i)
    }

  当然,用ES6的let简直不要太简单

    for (let i = 0; i < 5; i++) {
        setTimeout(function() {
            console.log(i);
        }, 1000 * i)
    }

  

 

 模块

 

  模块听起来很高大上,但是用一个简单的例子就可以明白什么是模块!

    function module() {
        var a = ‘Hello World‘;
        var b = [1, 2, 3]
            //方法1
        function str() {
            console.log(a);
        }
        //方法2
        function arr() {
            console.log(b);
        }
        //模块暴露
        return {
            showStr: str,
            showArr: arr
        }
    }
    var fn = module();
    fn.showStr(); //Hello World
    fn.showArr(); //[1,2,3]

  这个模式在Javascript中被称为模块。最常见的实现模块模式的方法被称为模块暴露,这里是变体。

  首先,module()作为一个函数,必须通过调用才可以创建一个模块实例。如果不执行外部函数,内部的作用域和闭包都无法被创建。

  其次,函数返回的对象是字面量语法。返回的对象中有对内部函数的引用,内部数据是隐藏且私有的,可以将这个对象类型的返回值看作本质上是模块的公共API。

 

  模块创建需要具备两个必要条件:

  1、 必须有外部的封闭函数,该函数必须被调用一次。(每次调用创建一个新实例)

  2、 封闭函数必须返回至少一个内部函数,这样内部函数才能在私有作用域中形成闭包,并且可以访问或修改私有状态。

  具有函数属性的对象本身并不是真正的模块,真正的模块必须具有闭包函数。

  上例中的模块构造函数,如果变成IIFE,便是一个简单的单例模式。

    var singleModule = (function() {
        //...
        return {
            //...
        }
    })();
    //直接使用singleModule

  

  模块模式另一个简单但强大的变化用法是,命名将要作为公共API返回的对象:

    var singleModule = (function(id1) {
        //get内部数据
        function id() {
            console.log(id1);
        }
        //提供一个方法改变内部数据
        function change(id2) {
            id1 = id2;
        }
        //公共接口
        var public = {
            change: change,
            id: id
        }
        return public;
    })(‘id‘);
    singleModule.id(); //ud
    singleModule.change(‘id2‘);
    singleModule.id(); //id2

  通过在模块实例的内部保留对公共API对象的内部引用,可以从内部对模块实例进行修正,包括添加或删除属性。

 

现代模块

 

  大多数模块依赖加载器本质上都是将这种模块定义封装进一个友好的API。

    //反正我暂时看不懂
    var module = (function() {
        var modules = {};

        function define(name, deps, impl) {
            for (var i = 0; i < deps.length; i++) {
                deps[i] = modules[deps[i]];
            }
            //核心代码
            //为了模块的定义引入了包装函数
            //将模块API储存在一个根据名字管理的模块列表中
            modules[name] = impl.apply(impl, deps);
        }

        function get(name) {
            return modules[name];
        }
        return {
            define: define,
            get: get
        };
    })();

  没看懂上面的代码到底干嘛的,现在看看定义模块的代码吧!

module.define(‘bar‘, [], function() {
        function hello(who) {
            return ‘let me introduce ‘ + who;
        }
        return {
            hello: hello
        };
    });

    module.define(‘foo‘, [‘bar‘], function() {
        var hungry = ‘hippo‘;

        function awesome() {
            console.log(bar.hello(hungry).toUpperCase());
        }
        return {
            awesome: awesome
        };
    });
    var bar = module.get(‘bar‘);
    var foo = module.get(‘foo‘);
    console.log(bar.hello(‘hippo‘)); //let me introduce hippo
    foo.awesome(); //LET ME INTRODUCE HIPPO

 

js你不知道的javascript笔记——作用域与闭包-编译原理-lhs-rhs-循环与闭包-模块-词法作用域-动态作用域(代码片段)

文章目录1.什么是作用域1.1编译原理1.2JS编译原理①引擎怎么查找变量②LHS与RHS的练习③BB几句1.3作用域链1.4RHS与LHS找不到的情况1.5总结2.JS作用域2.1词法作用域2.2函数作用域关于函数声明与函数表达式立即执行函数表达式( ... 查看详情

你不知道的javascript--上卷--读书笔记2

闭包是什么?  答:当函数可以记住并访问所在的词法作用域时,就产生了闭包,即使函数是在当前词法作用域之外执行。通俗地来说:函数可以嵌套在其他函数中定义,这样它们就可以访问它们被定义时所处的作用域中的任... 查看详情

读书笔记-你不知道的js上-对象

好想要对象···   函数的调用位置不同会造成this绑定对象不同。但是对象到底是什么,为什么要绑定他们呢?(可以可以,我也不太懂)  语法  对象声明有两个形式:  1、字面量=>varobj={...};  2、构造形式=&g... 查看详情

读书笔记-你不知道的js中-promise

继续填坑模式  考虑下面的代码:functionfn(x){//dosomethingreturnnewPromise(function(resolve,reject){//调用resolve(..)和reject(...)});}varp=fn(2);  newPromise(..)模式通常称为revealingconstructor。传入函数会立即执行(不会像then(..)中的回调一样异步延 查看详情

你不知道的javascript(上卷)读书笔记之一----作用域

你不知道的Javascript(上卷)这本书在我看来是一本还不错的书籍,这本书用比较简洁的语言来描述Js的那些”坑”,在这里写一些博客记录一下笔记以便消化吸收。1编译原理在此书中,开始便提出:Javascript是一门编译型语言,我... 查看详情

《javascript权威指南》读书笔记

日期:2015-12-04js的原型:;闭包:闭包这是个相当复杂的东西。。。现在初步理解;   http://segmentfault.com/a/1190000000652891  闭包有三个特性:1.函数嵌套函数2.函数内部可以引用外部的参数和变量3.参数和变量不会... 查看详情

读书笔记-js高级程序设计-第七章函数表达式

 闭包有权访问另一个函数作用域中的变量的函数匿名函数函数没有名字少用闭包由于闭包会携带包含它的函数的作用域,因此会比其它函数占用更多的内存。过度使用闭包可能会导致内存占用过多,我们建议读者只在绝对必... 查看详情

你不知道的javascript中,读书笔记

七种内置类型null,undefined,boolean,number,string,object,symboltypeofnull===‘object‘//truenull是typeof是object的唯一的假值typeoffunction会返回‘function‘使用typeofx!==‘undefined‘比直接判断x更加安全,因为不会引发referenceerror 查看详情

《你不知道的js(上卷)》作用域闭包(代码片段)

五、作用域闭包:? 闭包不是神奇的魔法,它只是遵循我们前几章一直介绍的词法作用域书写代码的自然结果。? 闭包是由函数以及声明该函数的词法环境组合而成的。该环境包含了这个闭包创建时作用域内的任何局部变量。一... 查看详情

你不知道的javascript(上卷)读书笔记之二----词法作用域

在前一篇文章中,我们把作用域定义为”管理、维护变量的一套规则”,接下来是时候来深入讨论一下Js的作用域问题了,首先我们要知道作用域一般有两种主要的工作类型,一种是词法作用域,一种是动态作用域,Javascript采用... 查看详情

你不知道的js系列(14)-闭包无处不在(代码片段)

上一节的闭包是为了解释如何使用闭包而人为地在结构上进行修饰,在昨天的闭包基础上,我们可以更加灵活的使用闭包functionwait(message)  setTimeout(functiontimer()    console.log(message)  ,1000)wait(‘hello,consure‘);内部函数timer... 查看详情

javascript中的this—你不知道的javascript上卷读书笔记

this是什么?this是在运行时进行绑定的,并不是在编写时绑定,它的上下文取决于函数调用时的各种条件。this的绑定和函数声明的位置没有任何关系,只取决于函数的调用方式。当一个函数被调用时,会创建一个活动记录(有时... 查看详情

tji读书笔记14-闭包与回调

body,td{font-family:微软雅黑;font-size:10pt;} TJI读书笔记14-闭包与回调闭包与回调为什么要使用内部类?内部类继承自某个类或者实现某个接口,内部类的代码可以操作外嵌类的对象.这不是使用内部类的理由.那么为什么使用内部类呢?... 查看详情

你不知道的javascript--上卷--读书笔记1

作用域是什么?  答:在《你不知道的javascript》书中提到,作用域就是根据名称查找变量的一套规则。古语有“无规矩不成方圆”,但是没有方圆,规矩又给谁用?所以个人理解作用域就是“规矩”+”方圆“。作用域是在创... 查看详情

读书笔记

...,让人心情急躁。再补充点之前读你不知道的JavaScript的读书笔记,和感悟。1、在数组上应用for..in循环有时候会产生出人意料的结果,因为这种枚举不止会包含所有的数值索引还会包括所有可枚举属性,例如:vararr=[];arr[0]=1;arr[1... 查看详情

js随笔(你不知道的js)

    很多开发者不会深入思考程序出现和预期不一样的结果,只会回避并用其他方法来达到目的 一.闭包  无论通过何种方式将函数传递到词法作用域以外,它都会持有对原始定义作用域的引用,无论在何处执行这个... 查看详情

读书笔记《你不知道的javascript(上卷)》——第二部分this和对象原型(代码片段)

文章目录第6章行为委托6.1面向委托的设计6.1.1类理论6.1.2委托理论1.互相委托(禁止)2.调试6.1.3比较思维模型6.2类与对象6.2.1控件“类”ES6的class语法糖6.2.2委托控件对象6.3更简洁的设计反类6.4更好的语法反词法6... 查看详情

你不知道的javascript1(作用域与闭包)(代码片段)

1.编译原理:首先,JavaScript是解释性语言,编译一行,执行一行JavaScript运行三部曲:1.语法分析2.预编译3.解释执行语法分析:js引擎来检查代码是否存在语法错误预编译:简单理解,就是在内存中开辟一些空间来声明存放一些变... 查看详情