- 浏览: 338620 次
- 性别:
- 来自: 北京
博客专栏
-
高性能PHP框架Yii分析...
浏览量:84898
文章分类
最新评论
-
huxiaoyunhust:
价值观这种东西,说不好听是口号,其实是公司每天潜移默化的感染, ...
关于企业价值观的一点点感悟 -
silentime:
stef831018 写道你想多了,企业价值观就两个字:利益怎 ...
关于企业价值观的一点点感悟 -
stef831018:
你想多了,企业价值观就两个字:利益
关于企业价值观的一点点感悟 -
guoyuqiangf8:
关于企业价值观的一点点感悟 -
silentime:
mangguo 写道推荐大家去看看我写的python入门,可以 ...
python如何打印变量的类型
Mark一下,等有空了再好好读一下
原文地址:http://msdn.microsoft.com/en-us/scriptjunkie/gg314983.aspx
You've read all the blog posts and books, and have sat through Douglas Crockford's hours of lectures, so you now know all there is to know about JavaScript objects. You're familiar with the various forms of inheritance, how prototypes work, and scoping is a topic you can discuss easily. The remaining question is one of applicability: how do you organize the objects in your application to take advantage of all you know?
Loose Coupling vs. Tight Coupling
Before diving into the objects you'll need to create an application, it's important to take a step back and understand the importance of loose coupling between objects. Loosely-coupled objects are objects that have little or no direction knowledge of each other. Tightly-coupled objects have direction knowledge of each other. The reason why loose coupling is preferred over tight coupling is for maintainability.
When two objects have direct knowledge of each other, an explicit relationship is created wherein changes to either object affect the other one. The following is an example of tightly-coupled code:
- TimelineFilter = {
- changeFilter: function (filter){
- Timeline.applyFilter(filter);
- }
- } ;
- StatusPoster = {
- postStatus: function (status){
- Timeline.post(status);
- }
- } ;
- Timeline = {
- applyFilter: function (filter){
- //implementation
- } ,
- post: function (status){
- //implementation
- }
- } ;
The objects in this example are one possible implementation of a Twitter-like interface. There are two objects, StatusPoster and TimelineFilter, which interact with the main timeline. The problem is that each of those two objects has a direct reference to the Timeline object, creating a tightly-coupled relationship between the objects. This is a major problem when working in a moderately large code base as changes in one part of the code may have unintended consequences in other parts of the code. Here are a few scenarios where you've created maintenance challenges:
- You're refactoring Timeline and want to change the name of post() to postStatus().
- You want to reuse StatusPoster on another page that doesn't have Timeline.
- You want to change the name of Timeline to StatusMessages.
In each of these instances, you're required to change code not just related to one object, but also related to others. Now imagine if there were dozens of objects all doing the same thing. The architecture of your JavaScript becomes incredibly fragile and the potential for introducing bugs grows tremendously.
Compare this to a loosely coupled approach where the knowledge of other objects is limited. The goal is to allow changes to one object without requiring changes to another; this is the foundation of a truly maintainable code base.
Types of Objects
In the quest for a maintainable code base, it's very helpful to identify the types of objects you'll be creating and/or using. You can generally break down all of the objects in a properly designed JavaScript application into the following groups.
Base Library
This is the foundation upon which your application is built. It may be a well-known library such as YUI or jQuery, or it may be your own custom-built library. Either way, the base library is comprised of objects that provide browser normalization and frequently, syntactic sugar, to make common or complex operations easier to execute. One important characteristic of the base library objects is that they allow for easy extension. Generally speaking, only these objects need to know which browser is being used, as their primary goal is to abstract away browser differences. The base library is application-neutral (has no knowledge of the web application as a whole).
Although it's possible to use more than one base library in an application, it's recommended to choose one for simplicity sake. The more base libraries present on the page, the more abstractions you'll need to build into the application architecture to ensure compatibility.
Widgets
These are UI controls that are sometimes built on top of the base library. Widgets are also application-neutral and are responsible for managing visual interfaces to data such as tab controls and menus. Although widgets interact with the DOM to produce certain user experiences, they are more or less a data representation layer that doesn't need to know about the application as a whole. They are just passed the data they need to render and go about their business.
Widgets are often considered to be extensions of the base library in an overall architecture diagram. This is because widgets typically extend or inherit from base functionality in a library in order to function. Widgets frequently use the classical inheritance pattern to centralize around a common widget infrastructure.
Modules
A module is an independent unit of functionality on the page that is comprised of business logic and most often some UI. For this reason, it helps to think of modules as a combination of HTML, CSS, and JavaScript that represents a single part of the page. In the earlier code example, there were three modules: the StatusPoster, TimelineFilter, and Timeline.
Modules are also application-neutral, in that they only know how to do their job and don't really care about what goes on in other parts of the page. The key focus of a module is to ensure that the user can complete a task or series of tasks within the context of the application.
Modules have a lifecycle where they are created and initialized (attaching JavaScript events) and later are destroyed (cleaning up JavaScript). In order to stay loosely coupled, modules don't communicate with other modules directly. Modules also don't touch the DOM outside of their particular area and don't access non-native global objects (hopefully, there are not a lot of these). They may use one or more widgets to represent their UI.
You may choose to create a base module type from which other modules can inherit, or simply implement each module as a separate object literal with a given interface. Either way, you should make sure that your inheritance chain is relatively shallow. These objects should be as light as possible, and burdening them with a deep prototype chain in which to search for functionality is both unnecessary and detrimental to the loose coupling of each module (you want as few dependencies as possible to ensure maximum portability and maintainability).
Sandbox
When a module needs to communicate or interact outside of its particular area, it must request permission to do so. The sandbox object is a module's view into the outside world, and serves to keep the module loosely-coupled. By limiting the module's direct knowledge of other objects to just one, it's easy to remove the module or move it to another page. You can think of the sandbox object as a security guard to prevent the module from doing things it shouldn't. There may be one sandbox per module, or one sandbox that all modules share. It's less important how many of these objects exist and more important that the abstraction it provides is solid.
For a given application (or group of applications), the sandbox should provide an API for the most common activities that a module is likely to do. These include, but are not limited to:
- Communication with other modules
- Making Ajax requests
- Retrieving the module's base DOM node
- Attaching/detaching event handlers
- Requests for extended capabilities via extensions
The sandbox object design is correct when modules don't seek to circumvent it to accomplish a task (whether or not the task is allowable, of course, is up to you).
Perhaps the most important concept to understand about the sandbox is that it doesn't implement any functionality at all. It is simply the module's interface to the application core. All methods on the sandbox should just hand off to the appropriate methods on the core.
Application Core
At the center of the application is a single object called the core. The core's job is to manage the modules on the page, which includes:
- Module lifecycle (starting/stopping)
- Communication between modules
- Error handling
- Extensions
When a module requests something through a sandbox object, the request is marshaled to the application core for completion. The core should be the only part of the architecture that has direct communication with the base library, and likewise, the sandbox is the only part of the architecture that has direct communication of the core. Keeping these layers separate enables you to easily swap out just one layer with minimal impact on the others.
One stylistic note: the application core is usually a global object by necessity (you need someplace to register the modules and extensions). Try to ensure that this is the only global object that is introduced in the application.
Example
Applying the basic architecture design discussed in this article to the earlier code example, you would first choose a base library upon which to build your web application. This example doesn't actually require a library, so it is omitted for simplicity purposes. The second step is to design the interface for the module. Again, for simplicity, this example uses a minimal interface as follows:
- interface Module {
- void init();
- void destroy();
- }
There are only two methods in this Module interface definition: init(), which is called when the module's lifecycle begins, and destroy(), which is called when the lifecycle is over. Only the application core will ever call these methods. The Sandbox interface is also kept simple for this example:
- interface MessageInfo {
- string type;
- variant data;
- }
- interface Sandbox {
- void notify(MessageInfo message);
- void listen(String messageType, Function handler, Object scope);
- void listen(String [] messageType, Function handler, Object scope);
- }
This Sandbox interface primarily deals with intermodule communication (in a full implementation, you'd want more functionality). The notify() method is the one modules call to broadcast information throughout the system to other modules. The listen() method is used to listen for specific types of broadcasted information (similar in design to event handlers in JavaScript).
Each module is created by a creator function in the following format:
- function (sandbox){
- //return object
- }
The creator function receives a sandbox object as its only argument and is expected to return an object representing the module. As defined in the interface, this object must have at least an init() method and a destroy() method. Using a function to create the object allows there to be multiple unique instances of any given module on the same page.
With these interfaces defined, you can build out a Core object to wire things up:
- var Core = function (){
- var moduleData = { } ;
- return {
- register: function (moduleId, creator){
- moduleData[moduleId] = {
- creator: creator,
- instance: null
- } ;
- } ,
- start: function (moduleId){
- moduleData[moduleId].instance =
- moduleData[moduleId].creator(new Sandbox(this ));
- moduleData[moduleId].instance.init();
- } ,
- stop: function (moduleId){
- var data = moduleData[moduleId];
- if (data.instance){
- data.instance.destroy();
- data.instance = null;
- }
- } ,
- startAll: function (){
- for (var moduleId in moduleData){
- if (moduleData.hasOwnProperty(moduleId)){
- this .start(moduleId);
- }
- }
- } ,
- stopAll: function (){
- for (var moduleId in moduleData){
- if (moduleData.hasOwnProperty(moduleId)){
- this .stop(moduleId);
- }
- }
- }
- //other methods not shown
- } ;
- } ();
To start the application, you first register the modules needed and then start them, such as:
- Core.register("module_id" , function (sandbox){
- return {
- init: function (){
- //initialization
- } ,
- destroy: function (){
- //destruction
- }
- } ;
- } );
Going back to the previous example of a Twitter-like interface, you may have code that looks like this:
- //register all modules
- Core.register("timeline-filter" , TimelineFilterCreator);
- Core.register("status-poster" , StatusPosterCreator);
- Core.register("timeline" , TimelineCreator);
- //start all modules
- Core.startAll();
The code for the various module creators is as follows:
- function TimelineFilterCreator(sandbox){
- return {
- //assume init() and destroy()
- changeFilter: function (filter){
- sandbox.notify({ type: "timeline-filter-change" , data: filter } );
- }
- } ;
- }
- function StatusPosterCreator(sandbox){
- return {
- //assume init() and destroy()
- postStatus: function (statusText){
- sandbox.notify({ type: "new-status" , data: statusText } );
- }
- } ;
- }
- function TimelineCreator(sandbox){
- return {
- //assume destroy()
- init: function (){
- sandbox.listen(["timeline-filter-change" , "new-status" ],
- this .handleNotification, this );
- } ,
- handleNotification: function (note){
- switch (note.type){
- case "timeline-filter-change" :
- this .applyFilter(note.data);
- break ;
- case "new-status" :
- this .post(note.data);
- break ;
- }
- } ,
- applyFilter: function (filter){
- //update filter
- } ,
- post: function (status){
- //post status
- }
- }
Note that amongst the module creators, there are no direct references to other modules on the page. Instead, each instance of a module simply notifies the system (via the sandbox.notify() method) that an action has occurred that might affect other modules on the page. Both the TimelineFilter and the StatusPoster take actions that affect the Timeline, so they send out notifications. Since the Timeline knows that it would like to react to those notifications, it listens (via sandbox.listen()) for those notifications and specifies a function to call when one is received. Keep in mind that notifications go back through the Core to be marshaled, meaning that the Core is implementing a mediator pattern.
If the TimelineFilter or StatusPoster are removed from the page, the code for the Timeline doesn't have to change at all. The notifications will never occur, but this won't cause an error in the Timeline. By depending only on the notifications for intermodule communication, you have effectively separated the modules from each other. The other, arguably more powerful part, of this solution is that no module owns any particular notification. You may decide to completely change the page such that TimelineFilter is removed and another control takes it place while performing the same action (filtering the timeline information). That new control can also send a notification of "timeline-filter-change" and the original Timeline object doesn't need to change in order to continue functioning as normal.
Conclusion
Building a loosely coupled JavaScript architecture does require a little extra planning before beginning, but the advantages you receive by doing so are vast. Modules can be added, removed, and reused in multiple contexts, and changes to one module won't cause another to break. This approach really pays off on a team with multiple developers working on various parts of the page at the same time.
The code presented in this article is purposely incomplete, just containing enough information to illustrate the concepts. The reason is that there is likely not one implementation of this architecture that will satisfy everyone's requirements. What's presented here is a jumping off point to get you started on your design rather than a completely formed solution.
If you'd like to learn more about this topic, please see my presentation, Scalable JavaScript Application Architecture .
发表评论
-
【转】npm用法及离线安装方法
2015-06-15 14:44 2700原文转自:https://cnodejs.org/to ... -
IE系列不支持圆角等CSS3属性的解决方案
2013-07-23 23:38 1279IE系列浏览器不支持CSS的圆角(border-radius ... -
【转】javascript中的urlencode
2012-09-20 23:04 1149原文地址:http://www.cnblo ... -
Yii分析13:Yii核心组件之AssetManager
2012-04-10 00:37 4396我们通过使用来讲解CAssetManager的使用和工 ... -
Javascript面向对象之十装饰者模式-《javascript设计模式》笔记
2012-01-18 14:59 1490装饰者模式的含义是将要装饰的对象作为一个成员放在装饰者的内部, ... -
Javascript面向对象之九适配器模式-《javascript设计模式》笔记
2012-01-18 14:58 1304关于适配器模式的定义:适配器模式(有时候也称包装样式或者包装) ... -
Javascript面向对象之八门面模式-《javascript设计模式》笔记
2012-01-17 14:43 1346关于门面模式的定义:门面模式为了系统提供一个 统一的高层接口供 ... -
Javascript面向对象之七组合模式-《javascript设计模式》笔记
2012-01-17 14:42 1376关于组合模式的定义:组合模式(Composite Patter ... -
Javascript面向对象之五工厂模式-《javascript设计模式》笔记
2012-01-13 11:31 1197工厂模式和单例模式(http://blog.sina.com. ... -
Javascript面向对象之四链式调用-《javascript设计模式》笔记
2011-12-30 14:22 13761.链式调用:jquery可能是目前大家最常用到的js框架了, ... -
Javascript面向对象之三单例模式-《javascript设计模式》笔记
2011-12-30 14:21 13091.单例模式概述源自百 ... -
Javascript面向对象之二继承-《javascript设计模式》笔记
2011-12-29 10:49 11631.关于继承:百度百科对继承的解释是:继承是指一个对象直接使用 ... -
Javascript面向对象之一对象成员的定义-《javascript设计模式》笔记
2011-12-29 10:48 1685序: 刚接触javascript的时候,觉得这语言有点儿 ... -
[转]YouTube架构学习体会
2011-11-22 10:15 1217原文地址:http://www.itivy.com/ivy/a ... -
[转]深掘XSS漏洞场景之XSS Rootkit
2011-10-21 12:22 1357转载顿神大作:http://www.80sec.com/%E6 ... -
Yii分析7:runController的执行
2011-10-20 17:28 6007在《Yii分析4:run的执行》一文中,介绍了Yi ... -
[转]JavaScript类型总览(图)
2011-08-12 10:23 896原文地址:http://blog. ... -
[转]交互设计师如何做交互
2011-07-27 11:35 1067原文地址:http://www.ued163.com/?p=1 ... -
[转]10种方式实现跨域资源的共享
2011-07-27 10:50 1062原文地址:http://www.ued16 ... -
JQuery的each函数
2011-07-26 01:07 1486JQuery的each函数是用于遍历一个DOM元素集合的,值得 ...
相关推荐
But, whether you're managing object libraries or just trying to get Ajax to run fast, Crockford's guidance in JavaScript: The Good Parts will help you create truly effective JavaScript code.
The only requirement for this book is a basic understanding of objects and functions in JavaScript. Product Details Paperback: 132 pages Publisher: Packt Publishing (February 19, 2014) Language: ...
20 Managing matrices in memory 21 Matrix operations example Analyzing WebAssembly Applications 22 Analyzing debug information 23 The WebAssembly text format 24 Advanced WebAssembly text format ...
The json module: JavaScript Object Notation The plistlib module: A Property-List Parser ctypes Enhancements Improved SSL Support Deprecations and Removals Build and C API Changes Port-Specific ...
Extending EaselJS DisplayObjects using object-oriented JavaScript JavaScript debugging Wrapping HTML5 games and publishing them to app store Who this book is for Beginning ...
You will also learn how to use this feature for mapping multiple objects and managing them. This book provides an in-depth explanation of native templates, enhanced collection handling, and render ...
Chapter 4: JavaScript Primer 61 Chapter 5: jQuery Basics 93 Chapter 6: Managing the Element Selection 117 Chapter 7: Manipulating the DOM 145 Chapter 8: Manipulating Elements 177 Chapter 9: ...
The use of an Object-Relational Mapping (ORM) tool like MyBatis simplifies the interaction with the database by mapping Java objects directly to database records. Security is another critical aspect...
- Generating the user interface directly from business objects. **Chapter 5: The Role of the Server** - **Description**: While Chapter 4 focuses on the client, this chapter shifts attention to the ...
The Spring framework provides dependency injection and manages the lifecycle of application objects, making it easier to maintain and test the code. SpringMVC, part of the SSM stack, handles ...
It includes classes and objects that represent various SharePoint entities, such as sites, lists, and users. **LINQ to SharePoint** Provides a simplified way to query and manage data in SharePoint ...
#### 十一、管理代码窗口(Managing the Code Window) 对于某些类型的BHO,可能需要直接管理代码窗口,如显示弹出窗口、工具栏等。这通常涉及到使用`IDocHostUIHandler`接口来定制用户界面。 #### 十二、注册BHO...
He has worked on numerous projects using Angular, JavaScript, and other related frameworks. Pablo is known for his problem-solving skills and his ability to create scalable and maintainable ...
Defines the API for the JavaScript Object. jdk.jstatd Defines the jstatd tool for starting a daemon for the jstat tool to monitor JVM statistics remotely. jdk.localedata Provides the locale data for...
Extending JavaScript 9 Events 10 Modularity 12 The Network 13 V8 15 Memory and other limits 16 Harmony 18 The process object 19 The Read-Eval-Print Loop and executing a Node program 21 Summary 23 ...