设计和构建你自己的JavaScript代码库:提示与技巧

  // Create and add second product: Blink/20420101

  var engine = new UserAgent.Product('Blink', '20420101');

  userAgent.addProduct(engine);

  // EvilCorpBrowser/1.2 (X11; Linux; en-us) Blink/20420101

  userAgent.toString();

  // Make some more changes to engine product

  engine.setComment('Hello World');

  // EvilCorpBrowser/1.2 (X11; Linux; en-us) Blink/20420101 (Hello World)

  userAgent.toString();

  根据你的代码的复杂度,你可能会在组织结构上花费一些时间。利用设计模式是组织你的代码库的好办法,甚至可以解决一些技术问题。这还能避免加入新特性带来的大面积重构。

  ##灵活性和定制性

  灵活性是让代码库变得强大的要素,但是确定可以定制与不能定制的界限是很困难的。 chart.js 和 D3.js 是很好的例子。这两个代码库都是用来进行数据可视化的。Chart.js可以很简单的创建不同形式的内置图表。但是如果你想对图像进行更多的掌控,D3.js才是你需要的。

  有好几种方法可以把控制权交给用户:配置,暴露公共方法,通过回调和事件。

  配置代码库通常在初始化之前完成。但是一些代码库允许尼在运行时对配置进行修改。配置通常被细小的部分限制,只有为了之后的使用而修改它们的数值才是被允许的。

  // Configure at initialization

  var userAgent = new UserAgent({

  commentSeparator: ';'

  });

  // Run-time configuration using a public method

  userAgent.setOption('commentSeparator', '-');

  // Run-time configuration using a public property

  userAgent.commentSeparator = '-';

  方法通常是暴露给实例使用的,比如说从实例中获取数据,或者设置实例的数据和执行操作。

  var userAgent = new UserAgent;

  // A getter to retrieve comments from all products

  userAgent.getComments();

  // An action to shuffle the order of all products

  userAgent.shuffleProducts();

  回调通常是在公共的方法中被传递的,通常在异步操作后执行用户的代码。

  var userAgent = new UserAgent;

  userAgent.doAsyncThing(function asyncThingDone() {

  // Run code after async thing is done

  });

  事件有很多种可能。有点像回调,除了增加事件句柄是不应该触发操作的。事件通常用于监听,你可能会猜到,这可是事件!更像回调的是,你可以提供更多的信息和返回一个数值给代码库去进行操作。

  var userAgent = new UserAgent;

  // Validate a product on addition

  userAgent.on('product.add', function onProductAdd(e, product) {

  var shouldAddProduct = product.toString().length < 5;

  // Tell the library to add the product or not

  return shouldAddProduct;

  });

  在一些例子中,你可能允许用户对你的代码库进行扩展。因此,你需要暴露一些公共方法或者属性来让用户填充,像Angular的模块 ( angular.module('myModule'))和Jquery的 fn ( jQuery.fn.myPlugin )或者什么都不做,只是简单的让用户获取你的代码库的命名空间:

  // AngryUserAgent module

  // Has access to UserAgent namespace

  (function AngryUserAgent(UserAgent) {

  // Create new method .toAngryString()

  UserAgent.prototype.toAngryString = function() {

  return this.toString().toUpperCase();

  };

  })(UserAgent);

  // Application code

  var userAgent = new UserAgent;

  // ...

  // EVILCORPBROWSER/1.2 (X11; LINUX; EN-US) BLINK/20420101

  userAgent.toAngryString();

  类似的,这允许你重写方法。

  // AngryUserAgent module

  (function AngryUserAgent(UserAgent) {

  // Store old .toString() method for later use

  var _toString = UserAgent.prototype.toString;

  // Overwrite .toString()

  UserAgent.prototype.toString = function() {

  return _toString.call(this).toUpperCase();

  };

  })(UserAgent);

  var userAgent = new UserAgent;

  // ...

  // EVILCORPBROWSER/1.2 (X11; LINUX; EN-US) BLINK/20420101

  userAgent.toString();

  在后面的例子中,允许你的用户获取代码库的命名空间,让你在对扩展和插件的定义方面上的控制变小了。为了让插件遵循一些约定,你可以(或者是应该)写下文档。

  ##测试

  对测试驱动开发 (test-driven development) 来说,写下大纲是良好的开始。简单来说,指的是在你写实际的代码库之前,在你写下测试准则的时候。如果测试检查的是你的代码特性是否跟期待的一样,以及你在写代码库之前写测试,这就是行为驱动开发。不管怎样,如果你的测试覆盖了你的代码库的每一个特性,而且你的代码通过了所有的测试。你可以确定你的代码是可以正常工作的。