- 浏览: 311431 次
- 性别:
- 来自: 上海
文章分类
- 全部博客 (243)
- Core Java (13)
- Java (12)
- Android (2)
- Lucene (3)
- J2EE (3)
- Hibernate (2)
- Jsp & Servlet (3)
- Struts2 (3)
- Spring (5)
- JSF (6)
- RichFaces (2)
- HTML & JS & DOM & CSS (87)
- browser (1)
- Ajax & A4J (2)
- Workflow (1)
- Maven (3)
- Linux (2)
- VM & VBOX (1)
- Tomcat (1)
- Cache (3)
- Others (36)
- design (1)
- PHP (1)
- Try.js (1)
- HTML5 && CSS3 && ECMAScript5 (26)
- 疯言疯语 (5)
- mongodb (2)
- Hardware Common Sence (1)
- RESTful (3)
- Nginx (2)
- web安全 (8)
- Page Design (1)
- web performance (1)
- nodejs (4)
- python (1)
最新评论
-
New_Mao_Er:
求最新的版本破解啊!!!
Omondo eclipseUML插件破解 -
konglx:
讲得仔细,谢了
全面分析 Spring 的编程式事务管理及声明式事务管理 -
cilendeng:
对所有post有效只能使用过滤器
说说Tomcat的Server.xml的URIEncoding=‘UTF-8’配置 -
jiulingchen:
mark了,灰常感谢!
JAVA中最方便的Unicode转换方法 -
anlaetion:
这算法可以有
js 字符串搜索算法
Classical Inheritance in JavaScript
Douglas
Crockford
www.crockford.com
And you think you're so clever and classless and free
— John Lennon
JavaScript is a class-free , object-oriented language, and as such, it uses prototypal inheritance instead of classical inheritance. This can be puzzling to programmers trained in conventional object-oriented languages like C++ and Java. JavaScript's prototypal inheritance has more expressive power than classical inheritance, as we will see presently.
Strongly-typed | Loosely-typed |
Static | Dynamic |
Classical | Prototypal |
Classes | Functions |
Constructors | Functions |
Methods | Functions |
But first, why do we care about inheritance at all? There are primarily two reasons. The first is type convenience. We want the language system to automatically cast references of similar classes. Little type-safety is obtained from a type system which requires the routine explicit casting of object references. This is of critical importance in strongly-typed languages, but it is irrelevant in loosely-typed languages like JavaScript, where object references never need casting.
The second reason is code reuse. It is very common to have a quantity of objects all implementing exactly the same methods. Classes make it possible to create them all from a single set of definitions. It is also common to have objects that are similar to some other objects, but differing only in the addition or modification of a small number of methods. Classical inheritance is useful for this but prototypal inheritance is even more useful.
To demonstrate this, we will introduce a little sugar which will let us write in a style that resembles a conventional classical language. We will then show useful patterns which are not available in classical languages. Then finally, we will explain the sugar .
Classical Inheritance
First, we will make a Parenizor class that will have set and get methods for its value , and a toString method that will wrap the value in parens.
function Parenizor(value) { this.setValue(value); } Parenizor.method('setValue', function (value) { this.value = value; return this; }); Parenizor.method('getValue', function () { return this.value; }); Parenizor.method('toString', function () { return '(' + this.getValue() + ')'; });
The syntax is a little unusual, but it is easy to recognize the classical pattern in it. The method method takes a method name and a function, adding them to the class as a public method.
So now we can write
myParenizor = new Parenizor(0); myString = myParenizor.toString();
As you would expect, myString is "(0)" .
Now we will make another class which will inherit from Parenizor , which is the same except that its toString method will produce "-0-" if the value is zero or empty.
function ZParenizor(value) { this.setValue(value); } ZParenizor.inherits(Parenizor); ZParenizor.method('toString', function () { if (this.getValue()) { return this.uber('toString'); } return "-0-"; });
The inherits method is similar to Java's extends . The uber method is similar to Java's super . It lets a method call a method of the parent class. (The names have been changed to avoid reserved word restrictions.)
So now we can write
myZParenizor = new ZParenizor(0); myString = myZParenizor.toString();
This time, myString is "-0-" .
JavaScript does not have classes, but we can program as though it does.
Multiple Inheritance
By manipulating a function's prototype object, we can implement multiple inheritance, allowing us to make a class built from the methods of multiple classes. Promiscuous multiple inheritance can be difficult to implement and can potentially suffer from method name collisions. We could implement promiscuous multiple inheritance in JavaScript, but for this example we will use a more disciplined form called Swiss Inheritance .
Suppose there is a NumberValue class that has a setValue method that checks that the value is a number in a certain range, throwing an exception if necessary. We only want its setValue and setRange methods for our ZParenizor . We certainly don't want its toString method. So, we write
ZParenizor.swiss(NumberValue, 'setValue', 'setRange');
This adds only the requested methods to our class.
Parasitic Inheritance
There is another way to write ZParenizor . Instead of inheriting from Parenizor , we write a constructor that calls the Parenizor constructor, passing off the result as its own. And instead of adding public methods, the constructor adds privileged methods .
function ZParenizor2(value) { var that = new Parenizor(value); that.toString = function () { if (this.getValue()) { return this.uber('toString'); } return "-0-" }; return that; }
Classical inheritance is about the is-a relationship, and parasitic inheritance is about the was-a-but-now's-a relationship. The constructor has a larger role in the construction of the object. Notice that the uber née super method is still available to the privileged methods.
Class Augmentation
JavaScript's dynamism allows us to add or replace methods of an existing class. We can call the method method at any time, and all present and future instances of the class will have that method. We can literally extend a class at any time. Inheritance works retroactively. We call this Class Augmentation to avoid confusion with Java's extends , which means something else.
Object Augmentation
In the static object-oriented languages, if you want an object which is slightly different than another object, you need to define a new class. In JavaScript, you can add methods to individual objects without the need for additional classes. This has enormous power because you can write far fewer classes and the classes you do write can be much simpler. Recall that JavaScript objects are like hashtables. You can add new values at any time. If the value is a function, then it becomes a method.
So in the example above, I didn't need a ZParenizor class at all. I could have simply modified my instance.
myParenizor = new Parenizor(0); myParenizor.toString = function () { if (this.getValue()) { return this.uber('toString'); } return "-0-"; }; myString = myParenizor.toString();
We added a toString method to our myParenizor instance without using any form of inheritance. We can evolve individual instances because the language is class-free.
Sugar
To make the examples above work, I wrote four sugar methods. First, the method method, which adds an instance method to a class.
Function.prototype.method = function (name, func) { this.prototype[name] = func; return this; };
This adds a public method to the Function.prototype , so all functions get it by Class Augmentation. It takes a name and a function, and adds them to a function's prototype object.
It returns this . When I write a method that doesn't need to return a value, I usually have it return this . It allows for a cascade-style of programming.
Next comes the inherits method, which indicates that one class inherits from another. It should be called after both classes are defined, but before the inheriting class's methods are added.
Function.method('inherits', function (parent) { var d = {}, p = (this.prototype = new parent()); this.method('uber', function uber(name) { if (!(name in d)) { d[name] = 0; } var f, r, t = d[name], v = parent.prototype; if (t) { while (t) { v = v.constructor.prototype; t -= 1; } f = v[name]; } else { f = p[name]; if (f == this[name]) { f = v[name]; } } d[name] += 1; r = f.apply(this, Array.prototype.slice.apply(arguments, [1])); d[name] -= 1; return r; }); return this; });
Again, we augment Function . We make an instance of the parent class and use it as the new prototype . We also correct the constructor field, and we add the uber method to the prototype as well.
The uber method looks for the named method in its own prototype . This is the function to invoke in the case of Parasitic Inheritance or Object Augmentation. If we are doing Classical Inheritance, then we need to find the function in the parent 's prototype . The return statement uses the function's apply method to invoke the function, explicitly setting this and passing an array of parameters. The parameters (if any) are obtained from the arguments array. Unfortunately, the arguments array is not a true array, so we have to use apply again to invoke the array slice method.
Finally, the swiss method.
Function.method('swiss', function (parent) { for (var i = 1; i < arguments.length; i += 1) { var name = arguments[i]; this.prototype[name] = parent.prototype[name]; } return this; });
The swiss method loops through the arguments . For each name , it copies a member from the parent 's prototype to the new class's prototype .
Conclusion
JavaScript can be used like a classical language, but it also has a level of expressiveness which is quite unique. We have looked at Classical Inheritance, Swiss Inheritance, Parasitic Inheritance, Class Augmentation, and Object Augmentation. This large set of code reuse patterns comes from a language which is considered smaller and simpler than Java.
Classical objects are hard. The only way to add a new member to a hard object is to create a new class. In JavaScript, objects are soft. A new member can be added to a soft object by simple assignment.
Because objects in JavaScript are so flexible, you will want to think differently about class hierarchies. Deep hierarchies are inappropriate. Shallow hierarchies are efficient and expressive.
I have been writing JavaScript
for 8 years now, and I have never once found need to use an uber
function. The super
idea is fairly important in the classical
pattern, but it appears to be unnecessary in the prototypal and functional
patterns. I now see my early attempts to support the classical model in
JavaScript as a mistake.
[转自 :http://javascript.crockford.com/inheritance.html ]
发表评论
-
循环数组的逻辑怎么写
2015-03-23 10:24 643应用场景这样的: var imgUrls = [ ... -
发布`代码生成器`
2014-11-17 00:45 580闲话不说,直接上地址 npm: https://www. ... -
MutationObserver
2014-10-27 15:29 1090MutationObserver MutationObse ... -
a simple mvvm library - bird
2014-10-09 18:26 726see here:https://github.com/i ... -
遍历dom tree是一件相当耗时的事情
2014-09-23 01:15 752遍历dom tree是一件相当耗时的事情,尤其是在遍历的同时 ... -
今天再讲下js里的继承
2014-09-18 00:27 693js的继承说简单也很简单,请看: function ... -
Text 类型
2014-09-05 18:52 841文本节点由Text类型表 ... -
JavaScript插入动态脚本
2014-09-05 18:47 635动态脚本指的是在页面加载时不存在,但将来的某一时刻通过修改该 ... -
innerHTML插入<style>元素
2014-09-05 18:37 1157通过innerHTML写入<style>元素没 ... -
CSS实现相对浏览器窗口定位彻底研究
2014-09-05 18:33 3718Web Developer / Designer 经常需要将 ... -
JavaScript插入动态样式
2014-09-05 18:07 604能够把CSS样式包含到HTML页面中的元素有两个。其中,& ... -
再理解jQuery;delete原型属性
2014-05-13 22:05 1855以前用jQuery的时候曾粗略看了它的源码,但却不求甚解。 ... -
javascript &&和||
2012-07-23 00:38 700一直以为 && 和 || ... -
undefined 和 void 0 的区别
2012-07-20 11:15 701在读backbone源码时看见这么一段代码: if ( ... -
Fiddler - 前端开发值得拥有
2012-07-16 14:41 825最近换了新工作,搬了新家,换了新室友,一切都在重新开始。 ... -
说说我的web前端之路,分享些前端的好书
2012-07-16 14:38 791WEB前端研发工程师,在国内算是一个朝阳职业,这个领域没 ... -
JavaScript实现 页面滚动图片加载
2012-07-16 14:29 685又到了这个月的博客时间了,原计划是打算在这个月做一个的功 ... -
JavaScript 操作 Cookie
2012-07-16 11:18 682从事web开发也有些日 ... -
Javascript定义类(class)的三种方法
2012-07-12 12:35 584程序员们做了很多探索,研究如何用Javascript模拟”类” ... -
服务端解决跨源共享
2012-06-21 10:18 4514跨源资源共享是web开发领域中一个非常有趣的话题,互联网 ...
相关推荐
Classical Inheritance in JavaScript。 Crockford是JavaScript开发社区最知名的权威,是JSON、JSLint、JSMin和ADSafe之父,是《[removed] The Good Parts》的作者。 现在是Yahoo的资深JavaScript架构师,参与YUI的...
This brief book explains the advantages of the object model, inheritance, both classical and prototypical, and shows how these concepts can be implemented in JavaScript. It also shows how object ...
classical inheritance pattern allows inheritance by creating a blueprintlike form that objects follow during inheritance. However, the prototypal inheritance pattern means copying the objects and ...
Because JavaScript follows the prototypal inheritance pattern, unlike Java and C++ (which follow the inheritance pattern), there are some changes in writing data structures in JavaScript. The ...
If you want to write beautiful, structured, and maintainable JavaScript code, this guide shows you how to apply both classical and modern design patterns to the language. The patterns in this book ...
3. 使用IE运行Inheritance for Javascript.html文档之后,查看源代码,该代码说明了怎样在JavaScript实现类的继承:使用prototype inheritance(原型继承方式)和classical inheritance(古典继承方式)。...
##Augment 简介The world's smallest and fastest classical JavaScript inheritance pattern, augment, is a seven line function which allows you to write CoffeeScript style classes with a flair of ...
### 三、组合继承(Classical Inheritance) 在JavaScript中,通常结合构造函数继承和原型继承来实现更完善的继承机制,如上例所示。这样,不仅可以继承构造函数的属性,还可以继承原型的属性和方法。 ### 四、...