- 浏览: 147391 次
文章分类
最新评论
-
sadamu900912:
long t1 = System.currentTimeMil ...
ArrayBlockingQueue V.S. LinkedBlockingQueue -
ygmyth:
jag522 写道ArrayBlockingQueue和Lin ...
ArrayBlockingQueue V.S. LinkedBlockingQueue -
iSmile:
虽然一口气没有看完,但是我觉得还是要给楼主一个大大的赞!
深刻理解JavaScript基于原型的面向对象 -
470275283:
var Coder = function (name, lan ...
深刻理解JavaScript基于原型的面向对象 -
470275283:
var Coder = function (name, lan ...
深刻理解JavaScript基于原型的面向对象
一、js是世界上最容易被误解的语言
javascript本质上是基于原型的语言,但是却引入了基于类的语言的new关键字和constructor模式,导致javascript饱受争议。
javascript的作者Brendan Eich 1994年研发这门语言的时候,C++语言是最流行的语言,java1.0即将发布,面向对象编程势不可挡,于是他认为,引入new关键字可以使习惯C++/java程序员更容易接受和使用javascript。
实际上,事实证明引入new是个错误的决定。
C++/java程序员看到new一个 function的时候,会认为js通过function创建对象.他们认为function相当于类,接着他们会尝试在js挖掘类似java/C++面向类的编程特性,结果他们发现function没有extends,反而有个很奇怪的prototype对象,于是他们开始咒骂,js的面向对象太糟糕了。确实,new的引入让他们以为js的面向对象与java/C++类似,实际上并不是,如果不是以原型本质去理解js的面向对象,注定要遭受挫折,new,prototype,__proto__都是javascript实现原型的具体手段。
另一方面,理解原型的程序员,抱怨没有最基本的通过对象创建对象的函数。他们很不高兴,因为居然要使用new function的语法来间接实现原型继承,三行代码才做到最基本的原型继承,下面是实现对象newObject继承对象oldObject的代码,
function F(){}; F.prototype = oldObject; var newObject = new F();
这太繁琐了。基于原型语言理论上应该存在一个函数create(prototypeObject),功能是基于原型对象产生新对象,例如,
var newObject = create(oldObject);
看到这样的代码,人们就会自然很清晰地联想到,newObject是以oldObject模板构造出来的。
js是世界上最容易被误解的语言,原因主要有两个:
1) 作为基于原型的语言中,却连最基本的一个通过原型产生对象的函数create(prototypeObject)也没有,让人不知道js根本上是以对象创建对象。应该添加该函数,现在Chrome和IE9的Object对象就有这个create函数。
2) 使用new func形式创建对象,让人误会js是以类似java类的构造函数创建对象,实际上,构造函数根本上在创建对象上起到次要的作用,甚至不需要,重要的只有函数的属性prototype引用的原型对象,新对象以此为模板生成,生成之后才调用函数做初始化的操作,而初始化操作不是必要的。应该把废弃new 操作符,把new func分解为两步操作,
var newObject = create(func.prototype);
func.call(newObject);
这样程序员才好理解。如果想把这两个步骤合二为一,应该使用new以外的关键字。
到这里,我们务必要牢牢印入脑海的是,js的面向对象是基于原型的面向对象,对象创建的方式根本上只有一种,就是以原型对象为模板创建对象,newObject = create(oldObject)。new function不是通过函数创建对象,只是刻意模仿java的表象。
js在面向对象上遭遇的争议,完全是因为商业因素导致作者失去了自己的立场。就像现在什么产品都加个云一样,如果那时候不加个new关键字来标榜自己面向对象,产生"js其实类似c++/java"的烟幕,可能根本没有人去关注javascript。更令人啼笑皆非的是,原本称作LiveScript的javascript,因为 后期和SUN合作,并且为了沾上当时被SUN炒得火热的Java的光,发布的时候居然改名成Javascript。
二、让我们一起来研发JavaScript语言
既然js遭受那么多批评,那么我们就搞一个大家都满意的JS吧!
假想我们是当时研发javascript的Brendan Eich,我们会怎么设计js的面向对象呢?
现在javascript开发到这样的阶段
1) 拥有基本类型,分支和循环,基本的数学运算,
2) 所有数据都是对象
3) 拥有类似C语言的function
4) 可以用var obj = {}语句生成一个空对象,然后使用obj.xxx或obj[xxx]设置对象属性
5) 没有继承,没有this关键字,没有new
我们任务是,实现javascript的面向对象,最好能达到类似java的创建对象和继承效果。更具体一点,我们要扩充js语言,实现类似下面的java代码。
class Empolyee{ String name; public Employee(String name){ this.name = name; } public getName(){ return this.name; } } class Coder extends Employee { String language; public Coder(name,language){ super(name); this.language = language; } public getLanguage(){ return this.language; } }1 实现创建对象
现有的对象都是基本类型,怎么创建用户自定义的对象呢?
(解释:
var i = 1;
这里的i是解释器帮忙封装的Number对象,虽然看起来跟C的int没区别,但实际上可以i.toString()。
)
java使用构造函数来产生对象,我们尝试把java的Empolyee的构造函数代码拷贝下来,看看可不可以模仿
function Empolyee(name){ this.name = name; }
我们只要生成一个空对象obj,再把函数里面的this换成obj,执行函数,就可以生成自定义对象啦!我们把Employee这样用来创建对象的函数称作构造函数。
1) 首先我们用原生的方式为function添加方法call和apply,实现把把函数里面的this替换成obj。call,apply在Lisp语言中已经有实现,很好参考和实现。
2) 然后实现生成实例
function Empolyee(name){ this.name = name; } var employee = {}; Employee.call(employee,'Jack');
3) 到这里,以类似java方式产生对象基本完成了,但是这个employee对象没有方法
我们的function是第一类对象,可以运行时创建,可以当做变量赋值,所以没有问题。
function Empolyee(name){ this.name = name; this.getName = function(){return this.name}; }
很好,我们团队顺利向前走了一步,今晚大家不用加班了!
2 实现继承
创建对象成功了,接着考虑实现继承。现在我们所有数据都是对象,没有类,有两种方案摆在我们的面前
a.类继承
b.原型继承
2.a 实现类继承
a方案是首选方案,因为跟java相似的话,JS更容易被接受
先粘贴Java构造函数的代码
function Coder extends Employee(name,language){ super(name); this.language = language; }
1) 把extends后面的函数自动记录下来,放到function对象的parentFunc变量
2) 如果第一行是super(),替换成var parent = newInstance(Coder.parentFunc,XXX),这样内部保留一个名为parent父对象;
3) 把this替换为obj,super替换换成parent
4) "."和"[]"重新定义,需要支持在对象内部parent对象查找属性。
这四步都属于比较大的改动,只要认真想一想都觉得不是太容易。
更重要的是,即使把这4步实现了,不但语言变得太复杂了,而且产生的对象根本享受不了继承带来的好处——内存中的代码复用,因为这样产生的每个对象都有"父类(函数)"的代码而不是仅有一份。这时候该注意到java中使用类的意义了,java类的代码在内存只有一份,然后每个对象执行方法都是引用类的代码,所有子类对象调用父类方法的时候,执行的代码都是同一份父类的方法代码。但是JS没有类,属性和方法都是存在对象之中,根本没有办法做到java那样通过类把代码共享给所有对象!
a方案宣告失败
2.b 实现原型继承
看b方案。我们现在的js语言,一切都是对象,显然非常适合使用基于原型的继承方式,就看具体如何实现了。
我们新建一个topObject来代表顶层对象,那么创建employee对象的时候,应该在employee对象内部设置一个属性引用topObject;同理,创建coder对象的时候,应该在coder对象内部设置一个属性引用employee对象,我们把这个引用原型对象的属性命名约定为"__proto__"。更进一步,为了构建一个对象的过程更自然,构建时候应该先在新对象中设置引用原型对象的属性,以表示先用模板制作出一个和模板一致的对象,然后再才执行构造函数初始化这个新对象自身的属性,以添加个性化的东西。具体实现代码如下:
var topObject = { __version__ : 1.0; }; function Empolyee(name){ this.name = name; this.getName = function(){return this.name}; } var employee = {}; employee.__proto__ = topObject; Employee.call(employee,'Jack'); function Coder(name,language){ this.name = name; this.language = this.language; this.getLanguage = function(){return this.language}; } var coder = {}; coder.__proto__ = employee; Coder.call(coder,'Coder Jack','Java');
当然我们还要做的工作就是在javascript解释器中增加对__proto__的支持,当一个对象访问一个自身没有的属性的时候,就通过__proto__属性查找原型链上是否存在该属性。
2.c 优化实现
优化1:函数封装
这一切看起来并不是那么美好,我们创建一个employee对象需要3行代码,我们需要这么一个函数封装这3行代码
function newInstance(prototype,constructor,arg1,arg2,....);
//第一个参数是原型对象,第二个是构造函数,后面的是构造函数的参数
可以这么实现
function sliceArguments(argumentsObj,n){ var args = []; for(var i=0;i<argumentsObj.length;i++){ if(i>=n){ args.push(argumentsObj[i]); } } return args; } function newInstance(prototype,constructor){ var obj = {}; obj.__proto__ = prototype; constructor.apply(obj,sliceArguments(arguments,2)); } var employee = newInstance(topObject,Employee,'Jack'); var coder = newInstance(employee,Coder,'Coder Jack','Java');
优化2:缩减参数
仔细一看,function newInstance的参数可以更少,我们可以把原型对象prototype作为属性放在constructor,那样我们的函数就可以只有一个参数了。属性名就约定为prototype吧。
2.1 我们修改解释器,把topObject写入语言作为原生的顶级对象;再修改function的源代码,让每一个新建的function都默认具有属性prototype = topObject
2.2 优化后的代码如下
function newInstance(constructor){ var obj = {}; obj.__proto__ = constructor.prototype; constructor.apply(obj,sliceArguments(arguments,1)); return obj; } function Employee(name){ this.name = name; this.getName = function(){return this.name}; } var employee = newInstance(Empolyee,'Jack'); var employee2 = newInstance(Empolyee,'Jack2'); var employee3 = newInstance(Empolyee,'Jack3'); function Coder(name,language){ this.name = name; this.language = language; this.getLanguage = function(){return this.language}; } Coder.prototype = newInstance(Empolyee,''); var coder = newInstance(Coder,'Coder Jack','Java'); var coder2 = newInstance(Coder,'Coder Lee','C#'); var coder3 = newInstance(Coder,'Coder Liu','C++'); var coder4 = newInstance(Coder,'Coder Liu','JavaScript');
至此,我们利用已有的设施,简单有效地开发出一个面向对象的javascript版本!Congratulations!
好像有些什么不妥——
突然,我好像明白了什么...
评论
5 楼
lazy_
2012-11-09
iame 写道
能推荐一个比较成熟的的基于原型的无依赖仿OO库吗?
支持extends,parent,static,init等特性。
支持extends,parent,static,init等特性。
我所知道的有prototype,模仿了Class, extends等。应该大多数JS库都会有类似的功能的,YUI,mootools,ext等等。
我更建议的你读完JS权威的这几篇文章后再考虑怎么使用和选择这写封装。目前为止,我只使用过prototype的Class.create。其他情况还是自己搞定,例如利用函数返回一个对象,或者利用原始的new去搞定(参见我博客大技术文f分类下的JAVASCRIPT文章下的后面的章节)。
http://javascript.crockford.com/javascript.html
http://javascript.crockford.com/prototypal.html
http://javascript.crockford.com/inheritance.html
http://www.crockford.com/javascript/private.html
还有。static在面向原型的JS中不存在这个概念。JAVA所谓静态成员就是类共享给所有的对象的成员,每个对象共享同一份静态成员,实际上,JS的原型继承天生就实现了这一点,每个对象都继承父亲对象属性,修改父对象的成员,子对象也马上做出改变,除非子对象重写了该成员。
4 楼
iame
2012-11-08
能推荐一个比较成熟的的基于原型的无依赖仿OO库吗?
支持extends,parent,static,init等特性。
支持extends,parent,static,init等特性。
3 楼
lazy_
2012-10-11
iame 写道
不错,第三篇呢?
不好意思,最近工作比较忙,所以暂时是没有足够的时间和精力去写了。可以关注我。
推荐一些我认为是最优秀的JS文章,让你过把瘾吧。
crockford大师
http://javascript.crockford.com/javascript.html
http://javascript.crockford.com/prototypal.html
http://javascript.crockford.com/inheritance.html
http://www.crockford.com/javascript/private.html
MDN
https://developer.mozilla.org/en-US/docs/JavaScript/Guide/Details_of_the_Object_Model
微软杂志
http://msdn.microsoft.com/zh-cn/magazine/cc163419.aspx#S4
MSDN
http://msdn.microsoft.com/en-us/library/dd282900(v=vs.85).aspx
http://msdn.microsoft.com/en-us/library/dd229916(v=vs.85)
阮一峰
http://www.ruanyifeng.com/blog/2010/05/object-oriented_javascript_encapsulation.html
http://www.ruanyifeng.com/blog/2012/07/three_ways_to_define_a_javascript_class.html?20120830102326#comment-last
http://www.ruanyifeng.com/blog/2010/05/object-oriented_javascript_inheritance.html
http://www.ruanyifeng.com/blog/2010/05/object-oriented_javascript_inheritance_continued.html
others
http://bonsaiden.github.com/JavaScript-Garden/zh/
2 楼
iame
2012-10-10
不错,第三篇呢?
1 楼
fearthenight
2012-09-11
不错哦,解释的很清楚。
发表评论
-
页面优化
2013-04-11 23:48 1020Q: 一万个节点,行为 ... -
使用闭包构造模块(提高篇_实现jQuery)——Object-Oriented Javascript之五
2013-02-19 02:26 9490通过前面两篇博文的 ... -
使用闭包构造模块(提高篇_私有模式实现jQuery)——Object-Oriented Javascript之五
2013-02-04 21:26 0通过前面两篇博文的积累, 使用闭包构造模块(基础篇)—— ... -
使用闭包构造模块(优化篇)——Object-Oriented Javascript之四
2013-02-04 15:41 2716上一篇博客(使用闭包构造模块(基础篇)——Obje ... -
使用闭包构造模块(基础篇)——Object-Oriented Javascript之三
2013-02-04 15:24 3639为什么要模块化js? 如果你问我这个问题,我会这样回答: ... -
使用闭包构造模块(基础篇)——Object-Oriented Javascript之三
2013-02-04 15:20 2为什么模块化js? 如果你问我这个问题,我会这样回答: ... -
使用闭包构造模块——Object-Oriented Javascript之三
2013-02-04 14:32 0为什么模块化js? 如果你问我这个问题,我会这样回答: ... -
笑学原型——Object-Oriented JavaScript(Part 1)
2012-09-10 20:26 2636原型语言 一、什么是原型语言 悟空问观音 ... -
笑学原型——理解JS面向对象系列(Part 1)
2012-09-10 20:19 1原型语言 一、什么是原型语言 悟空问观音 ... -
深刻理解JavaScript基于原型的面向对象
2012-09-10 15:16 9549主题一、原型 ... -
http://dlstu.cn/code/article/JavascriptAndAjax/1799.htm
2012-08-31 09:15 0http://dlstu.cn/code/article/Ja ... -
Jquery事件原理
2012-08-29 10:14 0Jquery事件原理 http://www.cnblogs. ... -
Javascript面向对象封装--原理和实现
2012-08-29 10:13 0Javascript面向对象封装--原理和实现 ...
相关推荐
JavaScript quick start: Familiar with object-oriented programming? This part helps you learn JavaScript quickly and properly. JavaScript in depth: Learn details of ECMAScript 5, from syntax, variables...
Interview: Larry Wall—Part 2: Scripting Languages in General and Perl in Particular 388 History Note 396 History Note 397 History Note 401 9.6 Parameters That Are Subprogram Names 408 History...
Object-Oriented JavaScript Object-Oriented Approaches Using the new Operator Literal Notation Associative Arrays JScript.NET Object Constructors Prototypes Chapter 6. Creating the ...
世界名校 澳大利亚皇家墨尔本理工大学网页编程课程教材,内容覆盖HTML,CSS,JavaScript, PHP等: ...Chapter 2: More CSS; HCI, Usability and GUI design ...Chapter 11: Object-Oriented Programming in PHP
that is well-suited to object-oriented and functional programming styles. JavaScript derives its syntax from Java, its first-class functions from Scheme, and its prototype- based inheritance from Self...
Code using the powerful object-oriented feature in JavaScript Master DOM manipulation, cross-browser strategies, and ES6 Harness the power of patterns for tasks ranging from application building to ...
Extending EaselJS DisplayObjects using object-oriented JavaScript JavaScript debugging Wrapping HTML5 games and publishing them to app store Who this book is for Beginning ...
Written for programmers with a background in C++, Java or other high-level, object-oriented languages, this book applies the Deitel signature live-code approach to teaching programming and explores ...
6 Object-Oriented PHP 7 Error and Exception Handling Part II: Using MySQL 8 Designing Your Web Database 9 Creating Your Web Database 10 Working with Your MySQL Database 11 Accessing Your MySQL ...
9.6 Object-Oriented Techniques in JavaScript 215 9.7 Subclasses 228 9.8 Classes in ECMAScript 5 238 9.9 Modules 246 10. Pattern Matching with Regular Expressions . . . . . . . . . . . . . . . . . . . ...
- **Description**: This appendix is aimed at programmers transitioning from other object-oriented languages to JavaScript. It covers key concepts and patterns for effective JavaScript programming. Key...
For several years, he has taught object-oriented programming part-time at the University of California, Santa Cruz while working as a software developer in Silicon Valley. Kyle received a B.S. in ...
Chapter 6 Object-Oriented PHP Chapter 7 Error and Exception Handling Part II: Using MySQL Chapter 8 Designing Your Web Database Chapter 9 Creating Your Web Database Chapter 10 Working with Your MySQL...
Chapter 20: Object-Oriented Programming with PHP 365 Chapter 21: Advanced Array Functions 409 Chapter 22: String and Regular Expression Functions 421 Chapter 23: Filesystem and System Functions 439 ...