(function (global, factory) {
typeof exports === 'object' && typeof module !== 'undefined' ? module.exports = factory() :
typeof define === 'function' && define.amd ? define(factory) :
(global = global || self, global.Vue = factory());
}(this, function () {
//do something
}));
模块模式
闭包与 IIFE (Immediately-Invoked Function Expression) 模块模式使用了 JavaScript 的一个特性,即闭包(Closures)。现今流行的一些 JS 库中经常见到以下形式的代码:
(function (参数) {
// 模块代码
// return something;
})(参数);
上面的代码定义了一个匿名函数,并立即调用自己,这叫做自调用匿名函数(SIAF),更准确一点,称为立即调用的函数表达 (Immediately-Invoked Function Expression, IIFE–读做“iffy”)。
在闭包中,可以定义私有变量和函数,外部无法访问它们,从而做到了私有成员的隐藏和隔离。而通过返回对象或函数,或是将某对象作为参数传入,在函数体内对该对象进行操作,就可以公开我们所希望对外暴露的公开的方法与数据。
这,其实就是模块模式的本质。
参数输入 JavaScript 有一个特性叫做隐式全局变量(implied globals),当使用一个变量名时,JavaScript 解释器将反向遍历作用域链来查找变量的声明,如果没有找到,就假定该变量是全局变量。这种特性使得我们可以在闭包里随处引用全局变量
考虑模块的独立性和封装,对其它对象的引用应该通过参数来引入。如果模块内需要使用其它全局对象,应该将这些对象作为参数来显式引用它们,而非在模块内直接引用这些对象的名字。以 jQuery 为例,若在参数中没有输入 jQuery 对象就在模块内直接引用 $ 这个对象,是有出错的可能的。正确的方式大致应该是这样的:
;(function (q, w) {
// q is jQuery
// w is window
// 局部变量及代码
// 返回
})(jQuery, window);
相比隐式全局变量,将引用的对象作为参数,使它们得以和函数内的其它局部变量区分开来。这样做还有个好处,我们可以给那些全局对象起一个别名
模块输出(Module Export)
有时我们不只是要使用全局变量,我们也要声明和输出模块中的对象,这可以通过匿名函数的 return 语句来达成,而这也构成了一个完整的模块模式。
var MODULE = (function () {
var my = {},
privateVariable = 1;
function privateMethod() {
// ...
}
my.moduleProperty = 1;
my.moduleMethod = function () {
// ...
};
return my;
}());
这段代码声明了一个变量 MODULE,它带有两个可访问的属性:moduleProperty 和 moduleMethod,其它的代码都封装在闭包中保持着私有状态。参考以前提过的参数输入,我们还可以通过参数引用其它全局变量。
输出简单对象 很多时候我们 return 一个对象作为模块的输出,比如上例就是。
另外,使用对象直接量(Object Literal Notation)来表达 JavaScript 对象是很常见的。比如:var x = { p1: 1, p2: “2”, f: function(){ /… / } }
很多时候我们都能见到这样的模块化代码:
var Module1 = (function () {
var private_variable = 1;
function private_method() { /*...*/ }
var my = {
property1: 1,
property2: private_variable,
method1: private_method,
method2: function () {
// ...
}
};
return my;
}());
另外,对于简单的模块化代码,若不涉及私有成员等,其实也可以直接使用对象直接量来表达一个模块:
var Widget1 = {
name: "who am i?",
settings: {
x: 0,
y: 0
},
call_me: function () {
// ...
}
};
不过这只是一种简单的形式,你可以将它看作是模块模式的一种基础的简单表达形式,而把闭包形式看作是对它的一个封装。
输出函数 有时候我们希望返回的并不是一个对象,而是一个函数。有两种需求要求我们返回一个函数,一种情况是我们需要它是一个函数,比如 jQuery,它是一个函数而不是一个简单对象;另一种情况是我们需要的是一个“类”而不是一个直接量,之后我们可以用 “new” 来实例它。
var Cat = (function () {
// 私有成员及代码 ...
return function(name) {
this.name = name;
this.bark = function() { /*...*/ }
};
}());
var tomcat = new Cat("Tom");
tomcat.bark();
为什么不直接定义一个 function 而要把它放在闭包里呢?简单点的情况,确实不需要使用 IIFE 这种形式,但复杂点的情况,在构造我们所需要的函数或是“类”时,若需要定义一些私有的函数,就有必要使用 IIFE 这种形式了。
另外,在 ECMAScript 第五版中,提出了 Object.create() 方法。这时可以将一个对象视作“类”,并使用 Object.create() 进行实例化,不需使用 “new”。
Object.create() Object.create()方法创建一个新对象,使用现有的对象来提供新创建的对象的__proto__。
const person = {
isHuman: false,
printIntroduction: function() {
console.log(`My name is ${this.name}. Am I human? ${this.isHuman}`);
}
};
const me = Object.create(person);
me.name = 'Matthew'; // "name" is a property set on "me", but not on "person"
me.isHuman = true; // inherited properties can be overwritten
me.printIntroduction();
// expected output: "My name is Matthew. Am I human? true"