archerU / notes

博客笔记 预计写七个系列:JavaScript深入系列、JavaScript专题系列、数据结构和算法系列、设计模式系列、React系列、Webpack 系列、前端开发日常优化方案

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

JavaScript深入系列之闭包

archerU opened this issue · comments

闭包

函数在定义时的词法作用域以外的地方被调用,并且函数可以继续访问定义时的词法作用域。

模块

模块模式必要条件:
1.必须有外部的封闭函数,该函数必须至少被调用一次(每次调用都会创建一个新的模块实例)。
2.封闭函数必须返回至少一个内部函数,这样内部函数才能在私有作用域中形成闭包,并且可以访问或者修改私有的状态。

function CoolModule() {
    var something = "cool";
    var another = [1,2,3];
    
    function doSomething() {
        console.log(something);
    }
    function doAnother() {
        console.log(another.join(" ! "));
    }
    
    return {
        doSomething: doSomething,
        doAnother: doAnother    
    };
}
var foo = CoolModule();

foo.doSomething(); // cool
foo.doAnother(); // 1 ! 2 ! 3

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

AMD的模块机制(requirejs)

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

var myModules = (function Manager(){
    var modules = [];
    
    function define(name,deps,impl) {
        for(var i=0; i<deps.length; i++) {
            deps[i] = modules[deps[i]];
        }
        modules[name] = impl.apply(impl,deps);
    }
    
    function get(name) {
        return modules[name];
    }
    
    return {
        define: define,
        get: get
    };
})();

这段代码的核心是 modules[name] = impl.apply(impl,deps) 为了模块的定义引入了包装函数(可以传入任何依赖),并且将返回值,也就是模块的API,储存在一个根据名字来管理的模块列表中。

使用以上函数来定义模块

MyModules.define("bar",[],function(){
    function hello(who) {
        return "Let me introduce:" + who;
    }
    return {
        hello: hello
    };
});
MyModules.define("foo",["bar"],function(){
    var hungry = "hippo";
    
    function awesome() {
        console.log(bar.hello(hungry).toUpperCase());
    }
    return {
        awesome: awesome
    };
});

var bar = MyModules.get("bar");
var foo = MyModules.get("foo");

console.log(
    bar.hello("hippo");
); // Let me introduce: hippo
foo.awesome(); // LET ME INTRODUCE: HIPPO

CMD 的模块机制(seajs)

CMD 依赖就近,延迟执行。

// CMD
define(function(require, exports, module) {
  var a = require('./a');
  a.doSomething();
  var b = require('./b');
  b.doSomething();
})

UMD 的模块机制

异步加载,异步执行,兼容服务器、浏览器端

CommonJS 的模块机制

  • 一个单独的文件就是一个模块
  • 加载模块采用同步方式,加载完成后才能执行后面的操作
  • 加载模块使用require方法,该方法读取一个文件并执行,最后返回内部的exports对象

ES6的模块机制

ES6将文件当作独立的模块处理。

bar.js

function hello(who) {
    return "Let me introduce:" + who;
}

export hello;

foo.js

// 仅从"bar"模块导入hello()
import hello from "bar";

var hungry = "hippo";

function awesome() {
    console.log(
      hello(hungry).toUpperCase()  
    );
} 
export awesome;

baz.js

module foo from "foo";
module bar from "bar";

console.log(
    bar.hello("rhino");  
); // Let me introduce: rhino
foo.awesome(); // LET ME INTRODUCE: HIPPO

模块文件中的内容会被当作好像包含在作用域闭包中一样来处理,就和前面介绍的函数闭包模块一样。