- 浏览: 45772 次
- 性别:
- 来自: 湖南
最新评论
-
laj12347:
javascript 闭包真是牛逼呀。。看了好久,终于有点 ...
JavaScript: The Good Parts 读书笔记(二) -
mu0001:
赞一个,很不错。以现在的知识来看,先看了第一部分。
JavaScript 数据访问(翻译自High Performance Javascript 第二章) -
e421083458:
得买本书好好看看了!
JavaScript 数据访问(翻译自High Performance Javascript 第二章) -
adamed:
这本书已经有中文版出版了。。到处都可以买到。高性能 javas ...
JavaScript 数据访问(翻译自High Performance Javascript 第二章) -
jiyanliang:
elvishehai 写道来源于那里的呀,写的很不错,学习了. ...
JavaScript 数据访问(翻译自High Performance Javascript 第二章)
6.Design Pattern
- Singleton
The idea of the singleton pattern is to have only one instance of a specific class. This means that the second time you use the same class to create a new object, you should get the same object that was created the first time.
And how does this apply to JavaScript? In JavaScript there are no classes, just objects.When you create a new object, there’s actually no other object like it, and the new object is already a singleton. Creating a simple object using the object literal is also an example of a singleton:var obj = { myprop: 'my value' };
In JavaScript, objects are never equal unless they are the same object, so even if you create an identical object with the exact same members, it won’t be the same as the first one:var obj2 = { myprop: 'my value' }; obj === obj2; // false obj == obj2; // false
So you can say that every time you create an object using the object literal, you’re actually creating a singleton, and there’s no special syntax involved.
Using new
JavaScript doesn’t have classes, so the verbatim definition for singleton doesn’t technically make sense. But JavaScript has the new syntax for creating objects using constructor functions, and sometimes you might want a singleton implementation using this syntax. The idea is that when you use new to create several objects using the same constructor, you should get only new pointers to the exact same object. The following snippet shows the expected behavior (assuming that you dismiss the idea of the Multiverse and accept that there’s only one Universe out there):var uni = new Universe(); var uni2 = new Universe(); uni === uni2; // true
In this example, uni is created only the first time the constructor is called. The second time (and the third, fourth, and so on) the same uni object is returned. This is why uni === uni2—because they are essentially two references pointing to the exact same object. And how to achieve this in JavaScript?
You need your Universe constructor to cache the object instance this when it’s created and then return it the second time the constructor is called. You have several options to achieve this:
• You can use a global variable to store the instance. This is not recommended because of the general principle that globals are bad. Plus, anyone can overwrite this global variable, even by accident. So let’s not discuss this option any further.
• You can cache in a static property of the constructor. Functions in JavaScript are objects, so they can have properties. You can have something like Universe.instance and cache the object there. This is a nice, clean solution with the only drawback that the instance property is publicly accessible, and code outside of yours might change it, so you lose the instance.
• You can wrap the instance in a closure. This keeps the instance private and not available for modifications outside of your constructor at the expense of an extra closure.
Let’s take a look at an example implementation of the second and third options.
Instance in a Static Property
Here’s an example of caching the singular instance in a static property of the Universe constructor:function Universe() { // do we have an existing instance? if (typeof Universe.instance === "object") { return Universe.instance; } // proceed as normal this.start_time = 0; this.bang = "Big"; // cache Universe.instance = this; // implicit return: // return this; } // testing var uni = new Universe(); var uni2 = new Universe(); uni === uni2; // true
As you see, this is a straightforward solution with the only drawback that instance is public. It’s unlikely that other code will change it by mistake (much less likely than if instance was a global) but still possible.
Instance in a Closure
Another way to do the class-like singleton is to use a closure to protect the single instance. You can implement this by using the private static member pattern discussed in Chapter 5. The secret sauce here is to rewrite the constructor:function Universe() { // the cached instance var instance = this; // proceed as normal this.start_time = 0; this.bang = "Big"; // rewrite the constructor Universe = function () { return instance; }; } // testing var uni = new Universe(); var uni2 = new Universe(); uni === uni2; // true
The original constructor is called the first time and it returns this as usual. Then the second, third time, and so on the rewritten constrictor is executed. The rewritten constructor has access to the private instance variable via the closure and simply returns it. This implementation is actually another example of the self-defining function pattern from Chapter 4. The drawback, as we discussed there, is that the rewritten function (in this case the constructor Universe()) will lose any properties added to it between the moment of initial definition and the redefinition. In our specific case anything you add to the prototype of Universe() will not have a live link to the instance created with the original implementation.
Here’s how you can see the problem with some testing:// adding to the prototype Universe.prototype.nothing = true; var uni = new Universe(); // again adding to the prototype // after the initial object is created Universe.prototype.everything = true; var uni2 = new Universe(); // only the original prototype was // linked to the objects uni.nothing; // true uni2.nothing; // true uni.everything; // undefined uni2.everything; // undefined // that sounds right: uni.constructor.name; // "Universe" // but that's odd: uni.constructor === Universe; // false
The reason that uni.constructor is no longer the same as the Universe() constructor is because uni.constructor still points to the original constructor, not the redefined one.If getting the prototype and the constructor pointer working as expected is a requirement, it’s possible to achieve this with a few tweaks:function Universe() { // the cached instance var instance; // rewrite the constructor Universe = function Universe() { return instance; }; // carry over the prototype properties Universe.prototype = this; // the instance instance = new Universe(); // reset the constructor pointer instance.constructor = Universe; // all the functionality instance.start_time = 0; instance.bang = "Big"; return instance; }
An alternative solution would also be to wrap the constructor and the instance in an immediate function. The first time the constructor is invoked, it creates an object and also points the private instance to it. From the second invocation on, the constructor simply returns the private variable. All the tests from the previous snippet will work as expected, too, with this new implementation:var Universe; (function () { var instance; Universe = function Universe() { if (instance) { return instance; } instance = this; // all the functionality this.start_time = 0; this.bang = "Big"; }; }());
- Factory
The purpose of the factory is to create objects. It’s usually implemented in a class or a static method of a class, which has the following purposes:
• Performs repeating operations when setting up similar objects
• Offers a way for the customers of the factory to create objects without knowing
the specific type (class) at compile time The second point is more important in static class languages in which it may be nontrivial to create instances of classes, which are not known in advance (in compile time).
In JavaScript, this part of the implementation is quite easy.The objects created by the factory method (or class) are by design inheriting from the same parent object; they are specific subclasses implementing specialized functionality. Sometimes the common parent is the same class that contains the factory method.
Let’s see an example implementation where we have:
• A common parent CarMaker constructor.
• A static method of the CarMaker called factory(), which creates car objects.
• Specialized constructors CarMaker.Compact, CarMaker.SUV, and CarMaker.Convertible that inherit from CarMaker. All of them will be defined as static properties of the parent so that we keep the global namespace clean, and so we also know where to find them when we need them.
Let’s first see how the finished implementation will be used:var corolla = CarMaker.factory('Compact'); var solstice = CarMaker.factory('Convertible'); var cherokee = CarMaker.factory('SUV'); corolla.drive(); // "Vroom, I have 4 doors" solstice.drive(); // "Vroom, I have 2 doors" cherokee.drive(); // "Vroom, I have 17 doors" var corolla = CarMaker.factory('Compact');
You have a method that accepts a type given as a string at runtime and then creates and returns objects of the requested type. There are no constructors used with new or any object literals in sight, just a function that creates objects based on a type identified by a string. Here’s an example implementation of the factory pattern that would make the code in preceding snippet work:// parent constructor function CarMaker() {} // a method of the parent CarMaker.prototype.drive = function () { return "Vroom, I have " + this.doors + " doors"; }; // the static factory method CarMaker.factory = function (type) { var constr = type, newcar; // error if the constructor doesn't exist if (typeof CarMaker[constr] !== "function") { throw { name: "Error", message: constr + " doesn't exist" }; } // at this point the constructor is known to exist // let's have it inherit the parent but only once if (typeof CarMaker[constr].prototype.drive !== "function") { CarMaker[constr].prototype = new CarMaker(); } // create a new instance newcar = new CarMaker[constr](); // optionally call some methods and then return... return newcar; }; // define specific car makers CarMaker.Compact = function () { this.doors = 4; }; CarMaker.Convertible = function () { this.doors = 2; }; CarMaker.SUV = function () { this.doors = 24; };
There’s nothing particularly difficult about the implementation of the factory pattern. All you need to do is look for the constructor function that creates an object of the required type. In this case a simple naming convention was used to map object types to the constructors that create them. The inheritance part was just an example of a common repeating piece of code that could be put into the factory method instead of repeated for every constructor type.
Built-in Object Factory
And for an example of “factory in the wild,” consider the built-in global Object() constructor.It also behaves as a factory, because it creates different objects, depending on the input. If you pass it a primitive number, it can create an object with the Number() constructor behind the scenes. The same is true for the string and boolean values. Any other values, including no input values, will create a normal object.
Here are some examples and tests of the behavior. Note that Object can be called with or without new:var o = new Object(), n = new Object(1), s = Object('1'), b = Object(true); // test o.constructor === Object; // true n.constructor === Number; // true s.constructor === String; // true b.constructor === Boolean; // true
The fact that Object() is also a factory is of little practical use, just something worth mentioning as an example that the factory pattern is all around us.
- Decorator
In the decorator pattern, additional functionality can be added to an object dynamically,at runtime. When dealing with static classes, this could be a challenge. In JavaScript, objects are mutable, so the process of adding functionality to an object is not a problem in itself.
A convenient feature of the decorator pattern is the customization and configuration of the expected behavior. You start with your plain object, which has some basic functionality. Then you pick and choose from an available pool of decorators which ones you want to use to enhance your plain object and in which order, if the order is important.
Let’s take a look into an example usage of the pattern. Say you’re working on a web application that sells something. Every new sale is a new sale object. The sale “knows” about the price of the item and can return it by calling the sale.getPrice() method.
Depending on the circumstances, you can start decorating this object with extra functionality.
Imagine a scenario where the sale for a customer is in the Canadian province of Québec. In this case the buyer needs to pay a federal tax and a provincial Québec tax. Following the decorator pattern, you’ll say that you “decorate” the object with a federal tax decorator and a Québec tax decorator. You can then also decorate the object with price formatting functionality. This scenario could look like the following:var sale = new Sale(100); // the price is 100 dollars sale = sale.decorate('fedtax'); // add federal tax sale = sale.decorate('quebec'); // add provincial tax sale = sale.decorate('money'); // format like money sale.getPrice(); // "$112.88"
In another scenario the buyer could be in a province that doesn’t have a provincial tax, and you might also want to format the price using Canadian dollars, so you can do:var sale = new Sale(100); // the price is 100 dollars sale = sale.decorate('fedtax'); // add federal tax sale = sale.decorate('cdn'); // format using CDN sale.getPrice(); // "CDN$ 105.00"
One way to implement the decorator pattern is to have each decorator be an object containing the methods that should be overwritten. Each decorator actually inherits the object enhanced so far after the previous decorator. Each decorated method calls the same method on the uber (the inherited object) and gets the value and proceeds with doing something in addition.
The end effect is that when you do sale.getPrice() in the first usage example, you’re calling the method of the money decorator. But because each decorated method first calls the parent’s method, money’s getPrice() first calls quebec’s getPrice(), which in turn calls fedtax’s getPrice() and so on. The chain goes all the way up to the original undecorated getPrice() implemented by the Sale() constructor. The implementation starts with a constructor and a prototype method:function Sale(price) { this.price = price || 100; } Sale.prototype.getPrice = function () { return this.price; };
The decorator objects will all be implemented as properties of a constructor property:Sale.decorators = {};
Let’s see one example decorator. It’s an object that implements the customized getPrice() method. Note that the method first gets the value from the parent method and then modifies that value:Sale.decorators.fedtax = { getPrice: function () { var price = this.uber.getPrice(); price += price * 5 / 100; return price; } };
Similarly we can implement other decorators, as many as needed. They can be extensions to the core Sale() functionality, implemented like plugins. They can even “live” in additional files and be developed and shared by third-party developers:Sale.decorators.quebec = { getPrice: function () { var price = this.uber.getPrice(); price += price * 7.5 / 100; return price; } }; Sale.decorators.money = { getPrice: function () { return "$" + this.uber.getPrice().toFixed(2); } }; Sale.decorators.cdn = { getPrice: function () { return "CDN$ " + this.uber.getPrice().toFixed(2); } };
Finally let’s see the “magic” method called decorate() that ties all the pieces together. Remember it will be called like:sale = sale.decorate('fedtax');
The 'fedtax' string will correspond to an object that’s implemented in Sale.decorators.fedtax. The newly decorated object newobj will inherit the object we have so far (either the original, or the one after the last decorator has been added), which is the object this. To do the inheritance part, let’s use the temporary constructor pattern from the previous chapter. We also set the uber property of newobj so the children have access to the parent. Then we copy all the extra properties from the decorator to the newly decorated object newobj. At the end, newobj is returned and, in our concrete usage example, it becomes the new updated sale object:Sale.prototype.decorate = function (decorator) { var F = function () {}, overrides = this.constructor.decorators[decorator], i, newobj; F.prototype = this; newobj = new F(); newobj.uber = F.prototype; for (i in overrides) { if (overrides.hasOwnProperty(i)) { newobj[i] = overrides[i]; } } return newobj; };
- Strategy
The strategy pattern enables you to select algorithms at runtime. The clients of your code can work with the same interface but pick from a number of available algorithms to handle their specific task depending on the context of what they are trying to do. An example of using the strategy pattern would be solving the problem of form validation.You can create one validator object with a validate() method. This is the method that will be called regardless of the concrete type of form and will always return the same result—a list of data that didn’t validate and any error messages.
But depending on the concrete form and the data to be validated, the clients of your validator may choose different types of checks. Your validator picks the best strategy to handle the task and delegates the concrete data checks to the appropriate algorithm.
- Façade
The façade is a simple pattern; it provides only an alternative interface to an object. It’s a good design practice to keep your methods short and not have them handle too much work. Following this practice you’ll end up with a greater number of methods than if you have uber methods with lots of parameters. Sometimes two or more methods may commonly be called together. In such cases it makes sense to create another method that wraps the repeating method calls. For example, when handling browser events, you have the following methods:
• stopPropagation()
Traps the event and doesn’t let it bubble up to the parent nodes
• preventDefault()
Doesn’t let the browser do the default action (for example, following a link or
submitting a form)
These are two separate methods with different purposes, and they should be kept separate, but at the same time, they are often called together. So instead of duplicating the two method calls all over the application, you can create a façade method that calls both of them:var myevent = { // ... stop: function (e) { e.preventDefault(); e.stopPropagation(); } // ... };
The façade pattern is also suitable for browser scripting where the differences between the browsers can be hidden behind a façade. Continuing from the previous example, you can add the code that handles the differences in IE’s event API:var myevent = { // ... stop: function (e) { // others if (typeof e.preventDefault === "function") { e.preventDefault(); } if (typeof e.stopPropagation === "function") { e.stopPropagation(); } // IE if (typeof e.returnValue === "boolean") { e.returnValue = false; } if (typeof e.cancelBubble === "boolean") { e.cancelBubble = true; } } // ... };
- Proxy
In the proxy design pattern, one object acts as an interface to another object. It’s different from the façade pattern, where all you have is convenience methods that combine several other method calls. The proxy sits between the client of an object and the object itself and protects the access to that object.This pattern may look like overhead but it’s useful for performance purposes. The proxy serves as a guardian of the object (also called a “real subject”) and tries to have the real subject do as little work as possible.
One example use would be something we can call lazy initialization. Imagine that initializing the real subject is expensive, and it happens that the client initializes it but never actually uses it. In this case the proxy can help by being the interface to the real subject. The proxy receives the initialization request but never passes it on until it’s clear that the real subject is actually used.
- Mediator
Applications—large and small—are made up of separate objects. All these objects need a way to communicate among themselves in a manner that doesn’t hurt maintenance and your ability to safely change a part of the application without breaking the rest of it. As the application grows, you add more and more objects. Then, during refactoring, objects are removed and rearranged. When objects know too much about each other and communicate directly (call each other’s methods and change properties) this leads to undesirable tight coupling. When objects are closely coupled, it’s not easy to change one object without affecting many others. Then even the simplest change in an application is no longer trivial, and it’s virtually impossible to estimate the time a change might take.
The mediator pattern alleviates this situation promoting loose coupling and helping improve maintainability. In this pattern the independent objects (colleagues) do not communicate directly, but through a mediator object. When one of the colleagues changes state, it notifies the mediator, and the mediator communicates the change to any other colleagues that should know about it.
- Observer
The observer pattern is widely used in client-side JavaScript programming. All the browser events (mouseover, keypress, and so on) are examples of the pattern. Another name for it is also custom events, meaning events that you create programmatically, as opposed to the ones that the browser fires. Yet another name is subscriber/publisher pattern.
The main motivation behind this pattern is to promote loose coupling. Instead of one object calling another object’s method, an object subscribes to another object’s specific activity and gets notified. The subscriber is also called observer, while the object being observed is called publisher or subject. The publisher notifies (calls) all the subscribers when an important event occurs and may often pass a message in the form of an event object.
发表评论
-
High Performance JavaScript 读书笔记(五)
2011-02-27 21:24 2276五.Strings and Regular Expressio ... -
High Performance JavaScript 读书笔记(六)
2011-02-27 20:49 2662六.Responsive Interfaces There ... -
High Performance JavaScript 读书笔记(四)
2011-02-27 19:50 1216四.Algorithms and Flow Control ... -
High Performance JavaScript 读书笔记(三)
2011-02-27 14:43 1728三.DOM Scripting DOM scripting ... -
High Performance JavaScript 读书笔记(一)
2011-02-27 13:44 1613一.Loading and Execution JavaS ... -
JavaScript 数据访问(翻译自High Performance Javascript 第二章)
2011-02-27 13:21 1933计算机科学中一 ... -
JavaScript Patterns 读书笔记(七)
2011-02-15 20:32 14447.Client Pattern DOM Access ... -
JavaScript Patterns 读书笔记(五)
2011-02-15 19:08 12345.Inheritance Pattern Classic ... -
JavaScript Patterns 读书笔记(四)
2011-02-15 18:19 11894.Function Pattern Namespace ... -
JavaScript Patterns 读书笔记(二)
2011-02-14 21:20 1166二.Object Object Constructor C ... -
JavaScript Patterns 读书笔记(三)
2011-02-14 21:19 1465三.Function Background The ... -
JavaScript Patterns 读书笔记(一)
2011-02-14 20:09 1664一.Global 全局域中的this = window.m ... -
JavaScript: The Good Parts 读书笔记(五)
2011-01-27 12:56 2093五.Javascript 总结 语 ... -
JavaScript: The Good Parts 读书笔记(四)
2011-01-27 11:37 1003四.数组与正则表达式 ... -
JavaScript: The Good Parts 读书笔记(三)
2011-01-26 23:38 1410三.继承 概述 Javascr ... -
JavaScript: The Good Parts 读书笔记(二)
2011-01-26 23:01 1784二.函数 JS 中函数亦是对象 使用字面变量表面 ... -
JavaScript: The Good Parts 读书笔记(一)
2011-01-26 21:44 1291一.基础特性 在浏览器中,每个<script> ...
相关推荐
其次,笔记可能会深入讲解设计模式(Design Patterns),这是软件设计中的重要概念,如工厂模式、单例模式、观察者模式等。这些模式为解决常见的软件设计问题提供了标准化的解决方案,是提高代码复用性和灵活性的...
这篇博客涵盖了前端开发的多个重要知识点,包括心法、LeetCode算法练习、框架原理、计算机网络、设计模式、自造轮子以及读书笔记等。以下是对这些内容的详细阐述: **心法**: 在前端开发中,心法指的是开发者应该...
它包含一些经典的GoF设计模式的集合,这些模式使用JavaScript和ES6类以及Node.js API在JavaScript中实现。 感谢并非常欢迎您做出贡献,调整现有模式的README.md,或改进示例或添加新模式。 只需做出一个叉子,进行...
本笔记主要依据曾探的著作《JavaScript设计模式与开发实践》进行整理,涵盖了设计模式的基本知识,以及创建、结构、行为、状态和策略等模式的应用。 **基础知识** 在深入设计模式之前,我们需要理解一些基本概念,...
2. `languages/` - 不同编程语言的学习笔记,如Python、Java、JavaScript等。 3. `frameworks/` - 使用的各种框架的笔记,如Django、React、Angular等。 4. `algorithms/` - 数据结构和算法的学习笔记,包括实现和...
2. **GWT_speakernoted.pdf**:这可能是关于Google Web Toolkit (GWT)的演讲笔记,GWT是一个开源的Java开发工具,它允许开发者使用Java编写客户端Web应用,然后编译成优化的JavaScript代码。GWT支持Ajax特性,并且...
8. **Design_Patterns**:讨论软件设计模式,如单例模式、工厂模式等在前端开发中的应用。 9. **Interview_Tips**:提供面试技巧,如何回答行为问题,如何展示自己的项目经验等。 10. **Projects**:可能包含一些...
都是参考众多文章归纳整理学习而写的,文章包括了HTML基础、CSS基础、JavaScript基础与拓展、Browser浏览器相关、Vue使用与分析、React使用与分析、Plugin插件相关、Patterns设计模式、Linux命令、LeetCode题解等...
都是参考众多文章归纳整理学习而写的,文章包括了HTML基础、CSS基础、JavaScript基础与拓展、Browser浏览器相关、Vue使用与分析、React使用与分析、Plugin插件相关、Patterns设计模式、Linux命令、LeetCode题解等...
除了每日一题的学习笔记之外还有一些做项目时的记录以及遇到的坑等,对于技术相关的文章基本都是参考众多文章归纳整理学习而写的,文章包括了HTML基础、CSS基础、JavaScript基础与拓展、Browser浏览器相关、Vue使用...
项目介绍小路描述CSS CSS世界练习代码UIAndAnamited 画布,SVG,三等练习代码算法算法练习成分组件封装练习源代码原始解析笔记数据结构斑点结构DesignPatterns |设计模式演示自己实现的一些好玩的小演示
女性笔记 :clapping_hands:欢迎star,也不容易搞丢链接哦! :clapping_hands: 欢迎加入明星,失去联系并不容易!它是什么?我是在参加2020年秋招的时候,实际上那时候往前2个月我才开始系统接触前沿领域。为了快速...
单页图书项目使用项目的IT笔记(Java,Linux,密码学,云,体系结构等)的超级备忘单。克隆并执行下一行以启动本地Web服务器。 $ ./httpServer.py仅需要服务器,以避免浏览器禁用从文件系统路径加载脚本的麻烦。 ...