一、初始化时分支

在编写函数库时,我们经常需要进行兼容性操作,例如处理事件监听器的添加和移除。以下是一个示例代码:

```javascript

var utils = {

addListener: function(el, type, fn) {

if (typeof window.addEventListener === 'function') {

el.addEventListener(type, fn, false);

} else if (typeof document.attachEvent === 'function') {

el.attachEvent('on' + type, fn);

} else {

el['on' + type] = fn;

}

},

removeListener: function(el, type, fn) {

//...

}

}

```

这段代码每次运行时都需要判断浏览器类型,效率较低。我们希望有一种方法来解决这个问题。解决方案是在初始化时根据浏览器特征加载相应的代码。为了暴露我们需要的接口,我们使用对象字面量的方式。首先列出我们的接口:

```javascript

var utils = {

addListener: null,

removeListener: null

}

```

由于代码是按顺序执行的,我们需要对其进行接口实现,并在其中进行浏览器特性检查。

这种方式很好的改善了效率低下的问题,而且也相当简单,只需要将重复性的代码从函数实现中分离出来,在外部实现即可。

二.函数属性

有些时候我们不得不应对一些计算量比较大或者耗时比较长的操作,比方说模糊查询,搜索功能等。减少重复性的工作是我们写代码的时候一直注重的一条准则,那么有什么办法能在这些耗时操作中能够尽量提高效率呢?其中一个方法便是缓存, 在javascript中,函数就是对象,是对象那就必然是有属性的,我们可以给任意一个函数添加任意的属性,比如:

```javascript

if (typeof window.addEventListener === 'function') {

utils.addListener = function(el, type, fn) {

el.addEventListener(type, fn, false);

};

utils.removeListener = function(el, type, fn) {

//...

};

} else if (typeof document.attachEvent === 'function') {

utils.addListener = function(el, type, fn) {

el.attachEvent('on' + type, fn);

};

utils.removeListener = function(el, type, fn) {

//...

};

} else {

utils.addListener = function(el, type, fn) {

el['on' + type] = fn;

};

utils.removeListener = function(el, type, fn) {

//...

}

}

```

一、实现缓存功能

我们可以给比较耗时的函数添加一个自身属性,以便缓存计算结果。缓存可以以任意类型的数据形式存在。以下是两种实现方式:

1. 无参形式

```javascript

// 无参形式

var fn = function() {

var self = arguments.callee, result;

if (!self.cache) {

result = {};

// 耗时操作

self.cache = result;

}

return self.cache;

};

fn.cache = null;

```

2. 有参形式

```javascript

// 有参形式

var fn = function() {

var key = JSON.stringify(Array.prototype.slice.call(arguments)), result;

if (!arguments.callee.cache[key]) {

result = {};

// 耗时操作

fn.cache[key] = result;

}

return fn.cache[key];

};

fn.cache = {};

```

二、部分应用(curry化)

在这里我们考虑一种情况,有一个函数,需要传递多个参数去实现,而这个函数会在很多时候用到,很多时候我们会传递一些其中一些相同的参数。例如:

```javascript

// 调用

fn(1, 2, 3, 4);

fn(1, 2, 5, 6);

fn(1, 2, 7, 8);

fn(3, 4, 5, 6);

```

可以看到上面这个函数,我们进行了四次调用,而其有三次调用的前两个参数都是一样的。因此我们可以重新封装一个新的函数,以便减少重复性的参数输入。为了能够自动生成我们需要的函数,我们需要创建工具函数。

以下是重构后的内容:

一.柯里化函数(Curry Function)

柯里化函数接受一个需要进行柯里化的函数作为第一个参数,后面为接受柯里化函数的部分或全部参数。使用示例如下:

```javascript

// 1. 创建柯里化函数

var newFn = curry(fn, 1);

newFn(2, 3, 4);

// 2. 创建另一个柯里化函数

var newFn2 = curry(fn, 2, 3);

newFn2(5, 6);

```

二.命名空间(Namespace)

在项目开发中,我们可能需要对对象进行管理,以避免污染全局空间。为了实现命名空间,我们可以简单地创建一个对象,并在该对象内实现所需的操作。例如:

```javascript

// 1. 定义全局命名空间

var Main = Main || {};

// 2. 在Main的命名空间内添加模块

Main.utils = Main.utils || {};

Main.utils.http = Main.utils.http || {};

```

当我们希望创建一个名为`Main.utils.http`的模块时,我们需要进行三次检查来判断`Main`、`utils`和`http`这几个模块是否存在。这样做显然不太方便。因此,我们可以使用一个工具函数来帮助我们创建新模块:

有了命名空间辅助工具函数,我们能够轻易地构建自己的常用工具库:

1. Ken.namespace(‘utils.http’);

2. Ken.utils.http = (function() {

3. var privatePropoty = ‘ken’;

4. function ajax(option) {

5. //todo

6. }

7. function jsonp(option) {

8. //todo

9. }

10. //...

11. return {

12. ajax: ajax,

13. jsonp: jsonp

14. }

15. })();

当然,在项目开发中,我们总是希望能够让代码尽量模块化。因此,我们可能会经常这样操作。

```javascript// 封装模块

var model = (function() {

var fn = function() {};

fn.prototype = {};

var a = 1;

function fn2() {}

return {

fn: fn,

fn2: fn2,

a: a

};

})();

// 使用模块

require([

'jquery',

'ux/jquery.common'

], function($, common) {

var a = 1, b = 2;

//todo...

});

```

```javascript// 创建一个名为Sandbox的对象和其属性

var Sandbox = {};

Sandbox.modules = {};

// 为Sandbox对象添加dom模块函数

Sandbox.modules.dom = function (box) {

box.getElement = function () {}; // 空函数作为占位符

box.foo = "bar"; // 一个字符串值作为示例属性

};

// 为Sandbox对象添加event模块函数

Sandbox.modules.event = function (box) {

box.attachEvent = function () {}; // 空函数作为占位符

box.dettachEvent = function () {}; // 空函数作为占位符

};

// 定义Sandbox构造函数,接收参数并执行回调函数

function Sandbox() {

var args = Array.prototype.slice.call(arguments), // 将传入的参数转换为数组形式

callback = args.pop(), // 取出最后一个参数作为回调函数

modules = (args[0] && typeof args[0] === "string") ? args : args[0], // 如果第一个参数是字符串或存在且为字符串,则将其赋值给modules;否则将第一个参数本身赋值给modules

i;

if (!(this instanceof Sandbox)) { // 如果当前上下文不是Sandbox实例,则返回一个新的实例

return new Sandbox(modules, callback);

}

if (!modules || modules === "*") { // 如果modules不存在或等于“*”,则初始化modules数组,并将所有模块名称推入该数组

modules = [];

for (i in Sandbox.modules) { // 从Sandbox对象中枚举所有模块名称

if (Sandbox.modules.hasOwnProperty(i)) { // 确保该属性为自身属性而不是原型链中的属性

modules.push(i);

}

}

}

for (i = 0; i < modules.length; i += 1) { // 对于每个模块名称,调用相应的模块函数并传入当前上下文(即Sandbox实例)

Sandbox.modules[modules[i]](this);

}

callback(this); // 在执行完所有操作后,调用传入的回调函数并传入当前上下文(即Sandbox实例)

}

// 为Sandbox原型对象添加name和version属性以及getName方法(用于获取name属性值)

Sandbox.prototype = {

name: "ken", // name属性值为"ken"字符串

version: "1.0", // version属性值为"1.0"字符串

getName: function() { // 该方法返回name属性值(即"ken"字符串)

return this.name; // 直接访问name属性值并返回结果

},

};

```

JavaScript设计模式是编程实践中的一种重要思想,它提供了一套经过时间考验的最佳实践,用来解决常见的编程问题和提高代码的可维护性、可扩展性和可复用性。JavaScript设计模式包括创建型模式、结构型模式和行为型模式,其中创建型模式有单例模式、工厂模式等等,结构型模式有装饰器模式、组合模式等等,行为型模式有观察者模式、策略模式等等。