JavaScript 设计模式 笔记1(一至三章)
第一章:富有表现力的JavaScript
JavaScript是一种弱类型语言(动态语言,java是静态语言) JavaScript有三种原始类型:布尔型,数值型和字符串型,此外还有对象类型、包含可执行代码的函数类型、空类型(null)和未定义类型(undefined)。原始数据类型按值传送,其他数据类型按引用传送。双重非操作可以把字符串或数值转换为布尔值。var bool = !!num;函数是一等对象,可以被存储在变量中,可以作为参数传给其他函数,可以作为返回值从其它函数传出,还可以在运行时进行改造。
(function(){ var foo = 10; var bar = 2; alert(foo * bar); })();//这个函数在定义后便执立即行匿名函数最有趣的用途是来创建闭包。
闭包(closure)是一个受保护的变量空间,由内嵌函数生成。
创建私有类变量的的代码:
var baz; (function(){ var foo = 10; var bar = 2; baz = function(){ return foo * bar; } }) baz();baz可以访问foo和bar,即使他执行在匿名函数之外。 JavaScript中的设计模式
在JavaScript中使用设计模式主要有如下3个原因:
(1)可维护性。有主意降低模块间的耦合程度。
(2)沟通。使程序员更简明的描述自己系统的工作方式。
(3)性能。某些模式能起到优化的作用。
不使用设计模式的理由:
(1)复杂性。获得可维护性会使代码更加复杂、更难被程序设计新手理解。
(2)性能。多数模式对代码的性能都有所拖累。
第二章:接口
接口的利弊:
接口有助于程序员使用类,并促进代码重用,有助于不同类之间通信,有助于测试以及调试
JS中任何实现接口的方法都会对性能造成一定影响,无法强迫程序员遵守定义的接口,在java中如果没有实现接口,编译器会报错,但是在JS中不会,只能通过检查方法间接实现。 在JavaScript中模仿接口
1.用注释描述接口
2.用属性检查模仿接口
3.用鸭式辨型模仿接口(用辅助函数来确保对象具有必须的方法) 代码示例:
// Interfaces.
var Composite = new Interface('Composite', ['add', 'remove', 'getChild']);
var FormItem = new Interface('FormItem', ['save']);
// CompositeForm class
var CompositeForm = function(id, method, action) { // implements Composite, FormItem
...
};
...
function addForm(formInstance) {
Interface.ensureImplements(formInstance, Composite, FormItem);
// This function will throw an error if a required method is not implemented,
// halting execution of the function.
// All code beneath this line will be executed only if the checks pass.
...
}
Interface类(源代码可去http://jsdesignpatterns.com下载)用法: 1.逐一检查代码中所有以对象为参数的方法。
2.为需要用到接口的每个类创建Interface对象。
3.删除所有针对构造器的检查。
4.以Interface.ensureImplements取代原来的构造器检查。 依赖接口的设计模式:
1.工厂模式
2.组合模式
3.装饰者模式
4.命令模式
第三章:封装
封装是面向对象设计的基石,通过将一个方法或属性声明为私用的,可以让对象的实现细节对其他对象保密以降低对象之间的耦合度,可以保持数据的完整性并对其修改方式加以约束。简单来讲封装是为了信息隐藏。 创建对象的基本模式
1.门户大开型:虽然提供了属性赋值方法,但是属性仍然是公开的,可被直接设置,并且无法阻止。
代码示例:
var Book = function(isbn, title) { this.setIsbn(isbn); this.setTitle(title); } Book.prototype = { getIsbn: function() { return this.isbn; }, setIsbn: function(isbn) { this.isbn = isbn; }, getTitle: function() { return this.title; }, setTitle: function(title) { this.title = title; } };
2.命名规范区别私用变量:通过下划线表明一个属性或方法进攻对象内部使用,然仍不能阻止程序员有意使用。
var Book = function(isbn, title) { // implements Publication this.setIsbn(isbn); this.setTitle(title); } Book.prototype = { getIsbn: function() { return this._isbn; }, setIsbn: function(isbn) { this._isbn = isbn; }, getTitle: function() { return this._title; }, setTitle: function(title) { this._title = title; } };
3.作用域、嵌套函数和闭包(实现私有变量):通过使用闭包实现,无法从外部直接访问属性,必须通过取值方法。
\\作用域闭包的例子 function foo() { var a = 10; function bar() { a *= 2; return a; } return bar; } var baz = foo(); // baz is now a reference to function bar. baz(); // returns 20. baz(); // returns 40. baz(); // returns 80. var blat = foo(); // blat is another reference to bar. blat(); // returns 20, because a new copy of a is being used. \\实现私用成员 var Book = function(newIsbn, newTitle) { // Private attributes. var isbn, title; // Privileged methods. this.getIsbn = function() { return isbn; }; this.setIsbn = function(newIsbn) { isbn = newIsbn; }; this.getTitle = function() { return title; }; this.setTitle = function(newTitle) { title = newTitle; }; // Constructor code. this.setIsbn(newIsbn); this.setTitle(newTitle); this.setAuthor(newAuthor); };弊端:每生成一个新对象的实例都将为每个私用方法和特权方法生成一个副本,会耗费更多内存,且不利于派生子类,这种问题叫“继承破坏封装”
静态方法和属性
var Book = (function() { // Private static attributes. var numOfBooks = 0; // Return the constructor. return function(newIsbn, newTitle) { // implements Publication // Private attributes. var isbn, title; // Privileged methods. this.getIsbn = function() { return isbn; }; this.setIsbn = function(newIsbn) { isbn = newIsbn; }; this.getTitle = function() { return title; }; this.setTitle = function(newTitle) { title = newTitle ; }; // Constructor code. numOfBooks++; // Keep track of how many Books have been instantiated // with the private static attribute. if(numOfBooks > 50) throw new Error('Book: Only 50 instances of Book can be ' + 'created.'); this.setIsbn(newIsbn); this.setTitle(newTitle); } })(); // Public static method. Book.convertToTitleCase = function(inputString) { ... }; // Public, non-privileged methods. Book.prototype = { display: function() { ... } };常量
var Class = (function() { // Constants (created as private static attributes). var UPPER_BOUND = 100; // Privileged static method. this.getUPPER_BOUND() { return UPPER_BOUND; } ... // Return the constructor. return function(constructorArgument) { ... } })(); /* Grouping constants together. */ var Class = (function() { // Private static attributes. var constants = { UPPER_BOUND: 100, LOWER_BOUND: -100 } // Privileged static method. this.getConstant(name) { return constants[name]; } ... // Return the constructor. return function(constructorArgument) { ... } })(); /* Usage. */ Class.getConstant('UPPER_BOUND');封装的利弊
保护了内部数据的完整性,减少了其它函数所需的错误检查代码的数量,弱化模块之间的耦合。
私用方法很难进行单元测试,可能损害类的灵活性,JS初学者理解有难度。
P.S.本文所有思想以及代码均源自JavaScript设计模式这本书。