如何写JS库,JS库写法
前言:
现在javascript库特别多,其写法各式各样,总结几种我们经常见到的,作为自己知识的积累。而目前版本的 JavaScript 并未提供一种原生的、语言级别的模块化组织模式,而是将模块化的方法交由开发者来实现。因此,出现了很多种 JavaScript 模块化的实现方式,
以 AMD 为例,该规范使用 define 函数来定义模块。
define(factory(){
//模块化
});
模块模式:
模块模式使用了 JavaScript 的一个特性,即闭包(Closures)。现今流行的一些 JS 库中经常见到以下形式的代码:
;(function (参数) {
// 模块代码
// return something;
})(参数);
上面的代码定义了一个匿名函数,并立即调用自己。
也有一些开发者在函数表达式前面加上一个惊叹号(!)或分号(;),而不是用括号包起来。
!function (参数) {
// 代码
// return something
}(参数);
还有些人喜欢用括号将整个 IIFE 围起来,这样就变成了以下的形式:
(function (参数) {
// 代码
// return something
}(参数));
参数输入:
JavaScript 有一个特性叫做隐式全局变量(implied globals),当使用一个变量名时,JavaScript 解释器将反向遍历作用域链来查找变量的声明,如果没有找到,就假定该变量是全局变量。这种特性使得我们可以在闭包里随处引用全局变量,比如 jQuery 或 window。然而,这是一种不好的方式。
考虑模块的独立性和封装,对其它对象的引用应该通过参数来引入。如果模块内需要使用其它全局对象,应该将这些对象作为参数来显式引用它们,而非在模块内直 接引用这些对象的名字。以 jQuery 为例,若在参数中没有输入 jQuery 对象就在模块内直接引用 $ 这个对象,是有出错的可能的。正确的方式大致应该是这样的:
;(function ($, w) {
// $ is jQuery
// w is window
// 局部变量及代码
// 返回
})(jQuery, window);
模块输出(Module Export)
有时我们不只是要使用全局变量,我们也要声明和输出模块中的对象,这可以通过匿名函数的 return 语句来达成,而这也构成了一个完整的模块模式。
var klm= (function () {
var myklm = {},
modeval= 1; function privateMethod() {
// ...
} myklm.moduleProperty = 1;
myklm.moduleMethod = function () {
// ...
}; return myklm ;
}());
这段代码声明了一个变量 MODULE,它带有两个可访问的属性:moduleProperty 和 moduleMethod,其它的代码都封装在闭包中保持着私有状态。参考以前提过的参数输入,我们还可以通过参数引用其它全局变量。
输出简单对象:
使用对象直接量来表达 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;
}());
输出函数:
有时候我们希望返回的并不是一个对象,而是一个函数。有两种需求要求我们返回一个函数,一种情况是我们需要它是一个函数,比如 jQuery,它是一个函数而不是一个简单对象;另一种情况是我们需要的是一个“类”而不是一个直接量,之后我们可以用 "new" 来实例它。目前版本的 JavaScript 并没有专门的“类”定义,但它却可以通过 function 来表达。
var klm= (function () {
// 私有成员及代码 ... return function(name) {
this.name = name;
this.bark = function() { /*...*/ }
};
}()); var com = new klm("蒯灵敏");
com.bark();
前面已经提到一种形式是输出对象直接量(Object Literal Notation),而 Revealing Module Pattern 其实就是这种形式,只是做了一些限定。这种模式要求在私有范围内中定义变量和函数,然后返回一个匿名对象,在该对象中指定要公开的成员。
var klm= (function () {
// 私有变量及函数
var x = 1;
function f1() {}
function f2() {} return {
public_method1: f1,
public_method2: f2
};
}());
扩展:
紧耦合扩展:
有时我们要求在扩展时调用以前已被定义的方法,这也有可能被用于覆盖已有的方法。这时,对模块的定义顺序是有要求的。
var klm = (function (my) {
var old_moduleMethod = my.moduleMethod; my.moduleMethod = function () {
// 方法重载
// 可通过 old_moduleMethod 调用以前的方法...
};
return my;
}(klm));
克隆与继承:
var MODULE_TWO = (function (old) {
var my = {},
key; for (key in old) {
if (old.hasOwnProperty(key)) {
my[key] = old[key];
}
} var super_moduleMethod = old.moduleMethod;
my.moduleMethod = function () {
// override method on the clone, access to super through super_moduleMethod
}; return my;
}(MODULE));
上面代码再精简一下:可以使用 Object.create()
var MODULE_TWO = (function (old) {
var my = Object.create(old); var super_moduleMethod = old.moduleMethod;
my.moduleMethod = function () {
// override method ...
}; return my;
}(MODULE));
与其它模块规范或 JS 库的适配:
模块环境探测:
现今,CommonJS Modules 与 AMD 有着广泛的应用,如果确定 AMD 的 define 是可用的,我们当然可以使用 define 来编写模块化的代码。然而,我们不能假定我们的代码必然运行于 AMD 环境下,有没有办法可以让我们的代码?
其实我们只需要在某个地方加上对 CommonJS Modules 与 AMD 的探测并根据探测结果来“注册”自己就可以了,以上那些模块模式仍然有用。AMD 定义了 define 函数,我们可以使用 typeof 探测该函数是否已定义。若要更严格一点,可以继续判断 define.amd 是否有定义。另外,SeaJS 也使用了 define 函数,但和 AMD 的 define 又不太一样。对于 CommonJS,可以检查 exports 或是 module.exports 是否有定义。
var klm = (function () {
var my = {};
// 代码 ... if (typeof define == 'function') {
define( function(){ return my; } );
}else if (typeof module != 'undefined' && module.exports) {
module.exports = my;
}
return my;
}());
其它一些 JS 库的做法
jquery的检测方式
if ( typeof module === "object" && module && typeof module.exports === "object" ) {
module.exports = jQuery;
} else {
if ( typeof define === "function" && define.amd ) {
define( "jquery", [], function () { return jQuery; } );
}
} if ( typeof window === "object" && typeof window.document === "object" ) {
window.jQuery = window.$ = jQuery;
}
接下来看看多重匿名函数的做法:
(function (root, factory) {
if (typeof exports === "object" && exports) {
factory(exports); // CommonJS
} else {
var mustache = {};
factory(mustache);
if (typeof define === "function" && define.amd) {
define(mustache); // AMD
} else {
root.Mustache = mustache; // <script>
}
}
}(this, function (mustache) {
// 模块主要的代码放在这儿
});
这段代码与前面介绍的方式不太一样,它使用了两个匿名函数。后面那个函数可以看作是模块代码的工厂函数,它是模块的主体部分。前面那个函数对运行环境进行 检测,根据检测的结果对模块的工厂函数进行调用。另外,作为一个通用库,它并没使用 window 对象,而是使用了 this,因为在简单的函数调用中,this 其实就是全局对象。
再看看 doT 的做法
(function() {
"use strict"; var doT = {
version: '1.0.0',
templateSettings: { /*...*/ },
template: undefined, //fn, compile template
compile: undefined //fn, for express
}; if (typeof module !== 'undefined' && module.exports) {
module.exports = doT;
} else if (typeof define === 'function' && define.amd) {
define(function(){return doT;});
} else {
(function(){ return this || (0,eval)('this'); }()).doT = doT;
}
// ...
}());
这段代码里的 (0, eval)('this') 是一个小技巧,这个表达式用来得到 Global 对象,'this' 其实是传递给 eval 的参数,但由于 eval 是经由 (0, eval) 这个表达式间接得到的,因此 eval 将会在全局对象作用域中查找 this,结果得到的是全局对象。若是代码运行于浏览器中,那么得到的其实是 window 对象。
JavaScript 模块化的未来
尚在制定中的 ES 6 会对模块作出语言级别的定义。我们来看一个实例,以下的代码段摘自“ES6:JavaScript中将会有的几个新东西”
复制代码
module Car {
// 内部变量
var licensePlateNo = '556-343';
// 暴露到外部的变量和函数
export function drive(speed, direction) {
console.log('details:', speed, direction);
}
export module engine{
export function check() { }
}
export var miles = 5000;
export var color = 'silver';
};
如何写JS库,JS库写法的更多相关文章
- js页码生成库,一个适合前后端分离的页码生成器
原文:js页码生成库,一个适合前后端分离的页码生成器 前言 上星期写的任务里面有需要进行分页的处理,git搜索了一番,没有觉得合适的,于是自己临时写了个分页的算法. 然后等闲下来的时候,决定把分页进行 ...
- 15 个有趣的 JS 和 CSS 库
开发者们,一起来看看有木有你需要的前端库. 1. DisplayJS DisplayJS 是一个帮助你渲染 DOM 的简易框架.使用它,你可以更容易地将 JS 变量遍历到特定的 HTML 元素中,类似 ...
- 进阶攻略|最全的前端开源JS框架和库
新的 Javascript 库层出不穷,从而Web 社区愈发活跃.多样.在多方面快速发展.详细去描述每一种主流的 Javascript框架和库近乎不可能,所以在这篇文章中主要介绍一些对前端发展最具影响 ...
- 有用的 JS 和 CSS 库
1. Moon Moon 是一个灵感源于 Vue.js 的 JavaScript UI 库,但它却更轻量.简单.它具备优化的虚拟 DOM 引擎,对用户友好的 API,并且在 gzip 压缩后仅有 6K ...
- [转] js画图开发库--mxgraph--[graphlayout-图形布局.html]
[From] http://chwshuang.iteye.com/blog/1797740 布局变化,下方还有动画效果选项: <!Doctype html> <html xmlns ...
- js javascirpt 数学库、 算法库 (转载)
提示:国外官网,谷歌浏览器右键可以翻译成中文. 1.math.js 官网:https://mathjs.org/index.html 其它简介:https://www.jianshu.com/p/4f ...
- 如何优雅的阅读 GitHub 上开源 js 框架和库的源码
如何优雅的阅读 GitHub 上开源 js 框架和库的源码 step 先总后分,即先了解一下啊框架的大体架构,又一个全局的认识,在选择某些和感兴趣的部分,仔细阅读,各个击破: 带着问题阅读,用到了什么 ...
- 百度静态资源(JS)公共库
例如: chosen http://apps.bdimg.com/libs/chosen/1.1.0/chosen.jquery.min.js classlist http://apps ...
- kendo method:destroy 解决有些在kendo.all.js 的js 库里报错问题
首先,不得不承认,kendo UI 是个不错的东西,特别对于一个前端开发到行不足的程序猿来说.而在我们使用过程中貌似还是会遇到各种奇怪的问题.比如我们会经常用到对一些控件进行重赋值. destroy ...
- JavaScript库基本格式写法
/********************************************************************* * JavaScript库基本格式写法 * 说明: * 由 ...
随机推荐
- Upgrade to 17.1 from 17.0 problem:UnicodeEncodeError: 'ascii' codec can't encode character '\xc4' in position 50: ordinal not in range(128)
最近 gentoo 从 17.0 更新到 17.1, 需要手动进行升级配置,使用 unsymlink-lib -p --finish 这一步的时候报错,报错如下: /usr/lib/python-ex ...
- 深度理解js中var let const 区别
首先要理解js中作用域的概念 作用域:指的是一个变量的作用范围 1.全局作用域 直接写在script中的js代码,在js中,万物皆对象,都在全局作用域,全局作用域在页面打开时创建,在全局作用域中有一个 ...
- Day 8:方法上自定义泛型、类上、接口上、泛型的上下限
泛型 泛型是jdk1.5使用的新特性 泛型的好处: 1. 将运行时的异常提前至了编译时 2. 避免了无谓的强制类型转换 泛型在集合中的常见应用: ArrayList<Strin ...
- 2020/2/6 PHP编程学习
今天把后台数据库处理好了,用了框架后真就是搬砖的一天..晚上继续刷题,明天把数据库处理完,这样一个商城框架就有了:
- redis(七)---- SpringBoot和redis整合
SpringBoot和Redis整合非常简单 添加pom依赖 <dependency> <groupId>org.springframework.boot</groupI ...
- CodeForces - 748D Santa Claus and a Palindrome (贪心+构造)
题意:给定k个长度为n的字符串,每个字符串有一个魅力值ai,在k个字符串中选取字符串组成回文串,使得组成的回文串魅力值最大. 分析: 1.若某字符串不是回文串a,但有与之对称的串b,将串a和串b所有的 ...
- javaScript_BOM浏览器对象模型
BOM:浏览器对象模型 Browser Object Model 用来访问和操作浏览器窗口,使JavaScript有能力与浏览器对话 通过使用BOM ,可以移动窗口,更改状态栏.执行其他不与页面内容发 ...
- 计算机网络(1): http原理和uuid
http 的请求报文和响应报文格式 请求报文有哪些方法 一个典型的http报文 状态码有哪几种 以及短语是用来解释状态码的 接口测试中,需要使用到UUID,用来生成唯一ID. 1.什么是UUID UU ...
- Java模板引擎之Freemarker 学习笔记 一
什么是Freemarker Freemarker是模板引擎,不是Web框架,只是视图层的组件,官网是 https://freemarker.apache.org/ Freemarker原理 数据模型+ ...
- Microsoft SQL Server Management Studio连接后报“ viewInfo (Microsoft.SqlServer.Management.SqlStudio.Expl”
解决办法: 在路径:C:\Users\你的用户名\AppData\Local\Temp\”新建文件夹并命名为2,如果已经有 2 则看清楚是否是文件而不是文件夹,删掉文件改为文件夹: 如果是找不到\Us ...