结合从网上搜集的资料,现将Pro Javascript Design Patterns一书几处有错误嫌疑的地方整理出来。
1. P.28: Chapter 3: Encapsulation and Information Hiding > Fully Exposed Object 错误类型:印刷错误+计算方法错误
原代码为:
Book.prototype = {
checkIsbn: function(isbn) {
if(isbn == undefined || typeof isbn != 'string') {
return false;
}
isbn = isbn.replace(/-/. ''); // Remove dashes.
if(isbn.length != 10 && isbn.length != 13) {
return false;
}
var sum = 0;
if(isbn.length === 10) { // 10 digit ISBN.
If(!isbn.match(\^\d{9}\)) { // Ensure characters 1 through 9 are digits.
return false;
} var sum = 0; if (isbn.length === 10) { console.log("checking for 10..."); if (!isbn.match(/^\d{9}/)) { return false; } for ( var i = 0; i < 9; i++) { sum += isbn.charAt(i) * (10 - i); } var checksum = sum % 11; checksum = (checksum === 10) ? "X" : checksum; if (isbn.charAt(9) != checksum) { return false; } } else { if (!isbn.match(/^\d{12}/)) { return false; } for ( var i = 0; i < 12; i++) { sum += isbn.charAt(i) * ((i % 2 === 0) ? 1 : 3); } var checksum = sum % 10; if (isbn.charAt(12) != checksum) { return false; } } ...
};
应更正为:
Book.prototype = {
checkIsbn: function(isbn) {
if(isbn == undefined || typeof isbn != 'string') {
return false;
}
isbn = isbn.replace(/-/g, ''); // Remove dashes.
if(isbn.length != 10 && isbn.length != 13) {
return false;
}
var sum = 0;
if(isbn.length === 10) { // 10 digit ISBN.
if(!isbn.match(\^\d{9}\)) { // Ensure characters 1 through 9 are digits.
return false;
}
var sum = 0;
if(isbn.length === 10) { // 10 digit ISBN.
If(!isbn.match(\^\d{9}\)) { // Ensure characters 1 through 9 are digits.
return false;
} var sum = 0; if (isbn.length === 10) { console.log("checking for 10..."); if (!isbn.match(/^\d{9}/)) { return false; } for ( var i = 0; i < 9; i++) { sum += isbn.charAt(i) * (10 - i); } var checksum = (11 - sum % 11) % 11; checksum = (checksum === 10) ? "X" : checksum; if (isbn.charAt(9) != checksum) { return false; } } else { if (!isbn.match(/^\d{12}/)) { return false; } for ( var i = 0; i < 12; i++) { sum += isbn.charAt(i) * ((i % 2 === 0) ? 1 : 3); } var checksum = (10 - sum % 10) % 10; if (isbn.charAt(12) != checksum) { return false; } }
...
};
关于验证ISBN号的正确计算方法,我参考了维基百科上的International Standard Book Number。这一点作者太疏忽大意了。
2. P.38: Chapter 3: Encapsulation and Information Hiding > Constants 错误类型:印刷错误+结构错误
原代码为:
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');
应更正为:
var Class = (function() { // Constants (created as private static attributes). var UPPER_BOUND = 100; // Privileged static method. this.getUPPER_BOUND=function() { 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=function(name) { return constants[name]; } ... // Return the constructor. return function(constructorArgument) { ... } })(); /* Usage. */ Class.getConstant('UPPER_BOUND');
关于此处代码的运行,有人提出并不能达到作者所提出的需求,因为即使照上面的方法更正后,由于this.getUPPER_BOUND
和this.getConstant
都是在自执行函数的内部运行,这里的this
实际上将指向全局变量,即window
。在StacksOverflow上,有人提出此讨论,众人相继给出解决办法,敬请参阅原文。
3. P.89: Chapter 6: Chaining > Using Callbacks to Retrieve Data from Chained Methods 错误类型:印刷错误+结构错误
原代码为:
// Accessor without function callbacks: returning requested data in accessors. window.API = window.API || {}; API.prototype = function() { var name = 'Hello world'; // Privileged mutator method. setName: function(newName) { name = newName; return this; }, // Privileged accessor method. getName: function() { return name; } }(); // Implementation code. var o = new API; console.log(o.getName()); // Displays 'Hello world'. console.log(o.setName('Meow').getName()); // Displays 'Meow'. // Accessor with function callbacks. window.API2 = window.API2 || {}; API2.prototype = function() { var name = 'Hello world'; // Privileged mutator method. setName: function(newName) { name = newName; return this; }, // Privileged accessor method. getName: function(callback) { callback.call(this, name); return this; } }(); // Implementation code. var o2 = new API2; o2.getName(console.log).setName('Meow').getName(console.log); // Displays 'Hello world' and then 'Meow'.
应更正为:
// Accessor without function callbacks: returning requested data in accessors. window.API = window.API || function(){}; API.prototype = function() { var name = 'Hello world';
return {
// Privileged mutator method. setName: function(newName) { name = newName; return this; }, // Privileged accessor method. getName: function() { return name; }
}
}(); // Implementation code. var o = new API; console.log(o.getName()); // Displays 'Hello world'. console.log(o.setName('Meow').getName()); // Displays 'Meow'. // Accessor with function callbacks. window.API2 = window.API2 || function(){}; API2.prototype = function() { var name = 'Hello world'; return {// Privileged mutator method. setName: function(newName) { name = newName; return this; }, // Privileged accessor method. getName: function(callback) { callback.call(this, name); return this; }
} }(); // Implementation code. var o2 = new API2; o2.getName(console.log).setName('Meow').getName(console.log); // Displays 'Hello world' and then 'Meow'.
这个问题是http://www.apress.com/9781590599082/上有人提出的。
4. P.100: Chapter 7: The Factory Pattern > XHR Factory 错误类型:印刷错误?结构错误?
原代码为:
/* SimpleHandler class. */
var SimpleHandler = function() {}; // implements AjaxHandler
SimpleHandler.prototype = {
request: function(method, url, callback, postVars) {
...
},
createXhrObject: function() { // Factory method.
var methods = [
function() { return new XMLHttpRequest(); },
function() { return new ActiveXObject('Msxml2.XMLHTTP'); },
function() { return new ActiveXObject('Microsoft.XMLHTTP'); }
];
for(var i = 0, len = methods.length; i < len; i++) {
try {
methods[i]();
}
catch(e) {
continue;
}
// If we reach this point, method[i] worked.
this.createXhrObject = methods[i]; // Memoize the method.
return methods[i];
}
// If we reach this point, none of the methods worked.
throw new Error('SimpleHandler: Could not create an XHR object.');
}
};
应更正为:
/* SimpleHandler class. */
var SimpleHandler = function() {}; // implements AjaxHandler
SimpleHandler.prototype = {
request: function(method, url, callback, postVars) {
...
},
createXhrObject: function() { // Factory method.
var methods = [
function() { return new XMLHttpRequest(); },
function() { return new ActiveXObject('Msxml2.XMLHTTP'); },
function() { return new ActiveXObject('Microsoft.XMLHTTP'); }
];
for(var i = 0, len = methods.length; i < len; i++) {
try {
methods[i]();
}
catch(e) {
continue;
}
// If we reach this point, method[i] worked.
this.createXhrObject = methods[i]; // Memoize the method.
return methods[i]();
}
// If we reach this point, none of the methods worked.
throw new Error('SimpleHandler: Could not create an XHR object.');
}
};
这一错误很可能是作者没有测试代码,照书中所述,createXhrObject
要在第一次运行之后记住当前的运行环境中能够使用的HttpRequest对象,methods
数组中的对象为函数,因此要用一个括号执行后才能得到真正的对象。
5. P.130: Chapter 9: The Composite Pattern > Form Validation 错误类型:印刷错误
原代码为:
/* Field class, abstract. */
var Field = function(id) { // implements Composite, FormItem
this.id = id;
this.element;
};
...
Field.prototype.save = function() {
setCookie(this.id, this.getValue);
};
应更正为:
/* Field class, abstract. */
var Field = function(id) { // implements Composite, FormItem
this.id = id;
this.element;
};
...
Field.prototype.save = function() {
setCookie(this.id, this.getValue());
};
6. P.155: Chapter 11: The Adapter Pattern > Adapting an Email API 错误类型:印刷错误
原代码为:
// dedMail application interface. var dedMail = (function() { function request(id, type, callback) { ... } return { ... formatMessage: function(e) { var e = e || window.event; try { e.preventDefault(); } catch(ex) { e.returnValue = false; } var targetEl = e.target || e.srcElement; var id = targetEl.id.toString().split('-')[1]; dedMail.getMail(id, function(msgObject) { var resp = eval('('+msgObject+')'); ... messagePane.innerHTML = DED.util.substitute(details, resp); } }; })(); // Set up mail implementation. addEvent(window, 'load', function() { var threads = getElementsByClass('thread', 'a'); var messagePane = $('message-pane'); for (var i=0, len=threads.length; formatMessage ); } });
应更正为:
var dedMail = (function() { function request(id, type, callback) { ... } return { ... formatMessage: function(e) { var e = e || window.event; try { e.preventDefault(); } catch(ex) { e.returnValue = false; } var targetEl = e.target || e.srcElement; var id = targetEl.id.toString().split('-')[1]; dedMail.getMail(id, function(msgObject) { var resp = eval('('+msgObject+')'); ... messagePane.innerHTML = DED.util.substitute(details, resp); }) } }; })(); // Set up mail implementation. addEvent(window, 'load', function() { var threads = getElementsByClass('thread', 'a'); var messagePane = $('message-pane'); for (var i=0, len=threads.length; dedMail.formatMessage ); } });
7. P.161,162,165,166: Chapter 12: The Decorator Pattern > The Structure of the Decorator 错误类型:结构错误
在多个页码处均为同一错误类型,仅举其中一处为例。P.161:
原代码为:
/* HeadlightDecorator class. */
var HeadlightDecorator = function(bicycle) { // implements Bicycle
this.superclass.constructor(bicycle); // Call the superclass's constructor.
}
extend(HeadlightDecorator, BicycleDecorator); // Extend the superclass.
HeadlightDecorator.prototype.assemble = function() {
return this.bicycle.assemble() + ' Attach headlight to handlebars.';
};
HeadlightDecorator.prototype.getPrice = function() {
return this.bicycle.getPrice() + 15.00;
};
应更正为:
/* HeadlightDecorator class. */
var HeadlightDecorator = function(bicycle) { // implements Bicycle
HeadlightDecorator.superclass.constructor.call(this, bicycle); // Call the superclass's constructor.
}
extend(HeadlightDecorator, BicycleDecorator); // Extend the superclass.
HeadlightDecorator.prototype.assemble = function() {
return this.bicycle.assemble() + ' Attach headlight to handlebars.';
};
HeadlightDecorator.prototype.getPrice = function() {
return this.bicycle.getPrice() + 15.00;
};
8. P.191: Chapter 13: The Flyweight Pattern > Sotring Instances for Later Reuse 错误类型:结构错误
原代码为:
var DialogBoxManager = (function() {
var created = [];
return { displayDialogBox : function(id, header, body, footer) { var inUse = this.numberInUse(); if (inUse > created.length) { created.push(this.createDialogBox(id)); } created[i].show(header, body, footer); }, createDialogBox : function(id) { return new DialogBox(id); },
numberInUse : function() {
var inUse = 0;
for ( var i = 0; i < created.length; ++i) {
console.log(created[i].state());
if (created[i].state() == "visible") {
inUse++;
}
}
return inUse;
}
}
})();
应更正为:
var DialogBoxManager = (function() { var created = []; return { displayDialogBox : function(header, body, footer) { (this.getFirstHiddenDialogBox() || this.createDialogBox()).show(header, body, footer); }, createDialogBox : function() { var db = new DialogBox(this.numberInUse()); created.push(db); return db; }, getFirstHiddenDialogBox : function() { for ( var i = 0; i < created.length; ++i) { if (created[i].state() != "visible") { return created[i]; } continue; } return null; }, numberInUse : function() { var inUse = 0; for ( var i = 0; i < created.length; ++i) { if (created[i].state() == "visible") { inUse++; } } return inUse; } } })();
原代码中inUse > created.length是显然行不通的,inUse永远也不可能大于created.length,其判断也就失去了意义。改进后的代码将能在调用自动displayDialogBox时判断是否有隐藏的已经建立的DialogBox,如果有即用之,如果没有即调用createDialogBox创建一个新的,numberInUse在createDialogBox函数中使用。
9. P.198, P.248, P.252: Chapter 14: The Proxy Pattern > The Structure of the Proxy; Chapter 17: The Chain of Responsibility > The Structure of the Chain of Responsibility 错误类型:印刷错误
两处文字中均少了book,导致程序运行出现错误。
原代码为:
PublicLibrary.prototype = {
findBooks : function(searchString) {
var results = [];
for ( var isbn in this.catalog) {
if (!this.catalog.hasOwnProperty(isbn)) {
continue;
}
if (this.catalog[isbn].getTitle().match(searchString) || this.catalog[isbn].getAuthor().match(searchString)) { results.push(this.catalog[isbn]); }
}
return results;
}
...
addBook : function(newBook) {
this.catalog[newBook.getIsbn()] = {
book : newBook,
available : true
};
}
}
应更正为:
PublicLibrary.prototype = {
findBooks : function(searchString) {
var results = [];
for ( var isbn in this.catalog) {
if (!this.catalog.hasOwnProperty(isbn)) {
continue;
}
if (this.catalog[isbn].book.getTitle().match(searchString) || this.catalog[isbn].book.getAuthor().match(searchString)) { results.push(this.catalog[isbn].book); }
}
return results;
}
...
addBook : function(newBook) {
this.catalog[newBook.getIsbn()] = {
book : newBook,
available : true
};
}
}
10. P.250, P.254: Chapter 17: The Chain of Responsibility > The Structure of the Chain of Responsibility 错误类型:结构错误
原代码为:
var SciFiCatalog = function() {};// Implements Catalog
extend(SciFiCatalog , GenreCatalog);
应更正为:
var SciFiCatalog = function() {// Implements Catalog SciFiCatalog.superclass.contructor.apply(this,arguments); };
extend(SciFiCatalog , GenreCatalog);
Why?因为作者在本书中一直在用的extend方法是不能继承“父类”的constructor中的属性的,所以必须在“子类”的constructor中调用一遍该函数,以便“子类”继承“父类”的这些属性。
11. P.253: Chapter 17: The Chain of Responsibility > The Structure of the Chain of Responsibility 错误类型:印刷错误
原代码为:
if (found) { outerloop: for ( var i = 0; i != this.catalog.length; ++i) { var book = this.catalog[i]; if (book.getTitle().match(searchString) || book.getAuthor().match(searchString)) { for ( var j = 0; j != request.results.length; ++j) { if (request.results[j].getIsbn() === book.getIsbn()) { continue outerloop; } } request.results.push(book); } } }
应更正为:
if (found) { outerloop: for ( var i = 0; i != this.catalog.length; ++i) { var book = this.catalog[i]; if (book.getTitle().match( request.searchString) || book.getAuthor().match( request.searchString)) { for ( var j = 0; j != request.results.length; ++j) { if (request.results[j].getIsbn() === book.getIsbn()) { continue outerloop; } } request.results.push(book); } } }
这里的searchString前面显然是少了request,不然就是一个未定义的变量。
12. P.260: Chapter 17: The Chain of Responsibility > Adding Tags to Photos 错误类型:结构错误
原代码为:
DynamicGallery.prototype = { ... getAllLeaves: function() { var leaves = []; for(var node, i = 0; node = this.getChild(i); i++) { leaves.concat(node.getAllLeaves()); } return leaves; }, ... for(var results = [], node, i = 0; node = this.getChild(i); i++) { results.concat(node.getPhotosWithTag(tag)); } return results; }, ... };
应更正为:
DynamicGallery.prototype = { ... getAllLeaves: function() { var leaves = []; for(var node, i = 0; node = this.getChild(i); i++) { leaves=leaves.concat(node.getAllLeaves()); } return leaves; }, ... for(var results = [], node, i = 0; node = this.getChild(i); i++) { results=results.concat(node.getPhotosWithTag(tag)); } return results; }, ... };
Javascript中的Array.concat是在不改变原数组的情况下返回一个新数组。
本文来自pinocchioatbeijing(专注前端技术 追求至美生活 以文会友)在博客园的博客,文章URL:http://www.cnblogs.com/pinocchioatbeijing/archive/2012/02/01/2334126.html,转载请注明,并欢迎大家不吝赐教。