再谈javascript面向对象编程


Posted in Javascript onMarch 18, 2012

另外这篇文章是一篇入门文章,我也是才开始学习Javascript,有一点心得,才想写一篇这样文章,文章中难免有错误的地方,还请各位不吝吐槽指正

吐槽Javascript
初次接触Javascript,这门语言的确会让很多正规军感到诸多的不适,这种不适来自于Javascript的语法的简练和不严谨,这种不适也来自Javascript这个悲催的名称,我在想网景公司的Javascript设计者在给他起名称那天一定是脑壳进水了,让Javascript这么多年来受了这么多不白之冤,人们都认为他是Java的附属物,一个WEB玩具语言。因此才会有些人会对Javascript不屑,认为Javascript不是一门真正的语言,但是这此他们真的错了。Javascript不仅是一门语言,是一门真真正正的语言,而且他还是一门里程碑式的语言,他独创多种新的编程模式原型继承,闭包(作者注:闭包不是JS首创,应该Scheme首创,prototypal inheritance 和 dynamic objects 是self语言首创,Javascript的首创并不精彩,谢谢网友的指正。),对后来的动态语言产生了巨大的影响。做为当今最流行的语言(没有之一),看看git上提交的最多的语言类型就能明白。随着HTML5的登场,浏览器将在个人电脑上将大显身手,完全有替换OS的趋势的时候,Javascript做为浏览器上的一门唯一真真的语言,如同C之于 unix/linux,java之于JVM,Cobol之于MainFrame,我们也需要来重新的认真地认识和审视这门语言。另外Javascript的正式名称是:ECMAScript,这个名字明显比Javascript帅太多了!

言归正传,我们切入主题——Javascript的面向对象编程。要谈Javascript的面向对象编程,我们第一步要做的事情就是忘记我们所学的面向对象编程。传统C++或Java的面向对象思维来学习Javascript的面向对象会给你带来不少困惑,让我们先忘记我们所学的,从新开始学习这门特殊的面向对象编程。既然是OO编程,要如何来理解OO编程呢,记得以前学C++,学了很久都不入门,后来有幸读了《Inside The C++ Object Model》这本大作,顿时豁然开朗,因此本文也将以对象模型的方式来探讨的Javascript的OO编程。因为Javascript 对象模型的特殊性,所以使得Javascript的继承和传统的继承非常不一样,同时也因为Javascript里面没有类,这意味着Javascript里面没有extends,implements。那么Javascript到底是如何来实现OO编程的呢?好吧,让我们开始吧,一起在Javascript的OO世界里来一次漫游

首先,我们需要先看看Javascript如何定义一个对象。下面是我们的一个对象定义:

var o = {};

还可以这样定义一个对象

function f() { }

对,你们没有看错,在Javascript里面,函数也是对象。
当然还可以

var array1= [ 1,2,3];

数组也是一个对象。
其他关于对象的基本的概念的描述,还是请各位亲们参见陈皓《Javascript 面向对象编程》文章。
对象都有了,唯一没有的就是class,因为在Javascript里面是没有class关键字的,算好还有function,function的存在让我们可以变通的定义类,在扩展这个主题前,我们还需要了解一个Javascript对象最重要的属性,__proto__成员。

__proto__成员
严格的说这个成员不应该叫这个名字,__proto__是Firefox中的称呼,__proto__只有在Firefox浏览器中才能被访问到。做为一个对象,当你访问其中的一个成员或方法的时候,如果这个对象中没有这个方法或成员,那么Javascript引擎将会访问这个对象的__proto__成员所指向的另外的一个对象,并在那个对象中查找指定的方法或成员,如果不能找到,那就会继续通过那个对象的__proto__成员指向的对象进行递归查找,直到这个链表结束。
好了,让我们举一个例子。
比如上上面定义的数组对象array1。当我们创建出array1这个对象的时候,array1实际在Javascript引擎中的对象模型如下:
再谈javascript面向对象编程
array1对象具有一个length属性值为3,但是我们可以通过如下的方法来为array1增加元素:

array1.push(4);

push这个方法来自于array1的__proto__成员指向对象的一个方法(Array.prototye.push())。正是因为所有的数组对象(通过[]来创建的)都包含有一个指向同一个具有push,reverse等方法对象(Array.prototype)的__proto__成员,才使得这些数组对象可以使用push,reverse等方法。

那么这个__proto__这个属性就相当于面向对象中的”has a”关系,这样的的话,只要我们有一个模板对象比如Array.prototype这个对象,然后把其他的对象__proto__属性指向这个对象的话就完成了一种继承的模式。不错!我们完全可以这么干。但是别高兴的太早,这个属性只在FireFox中有效,其他的浏览器虽然也有属性,但是不能通过__proto__来访问,只能通过getPrototypeOf方法进行访问,而且这个属性是只读的。看来我们要在Javascript实现继承并不是很容易的事情啊。

函数对象prototype成员
首先我们先来看一段函数prototype成员的定义,

When a function object is created, it is given a prototype member which is an object containing a constructor member which is a reference to the function object
当一个函数对象被创建时,这个函数对象就具有一个prototype成员,这个成员是一个对象,这个对象包含了一个构造子成员,这个构造子成员会指向这个函数对象。

例如:

function Base() { 
this.id = "base" 
}

Base这个函数对象就具有一个prototype成员,关于构造子其实Base函数对象自身,为什么我们将这类函数称为构造子呢?原因是因为这类函数设计来和new 操作符一起使用的。为了和一般的函数对象有所区别,这类函数的首字母一般都大写。构造子的主要作用就是来创建一类相似的对象。

上面这段代码在Javascript引擎的对象模型是这样的

再谈javascript面向对象编程
new 操作符
在有上面的基础概念的介绍之后,在加上new操作符,我们就能完成传统面向对象的class + new的方式创建对象,在Javascript中,我们将这类方式成为Pseudoclassical。
基于上面的例子,我们执行如下代码

var obj = new Base();

这样代码的结果是什么,我们在Javascript引擎中看到的对象模型是:
再谈javascript面向对象编程
new操作符具体干了什么呢?其实很简单,就干了三件事情。

var obj = {}; 
obj.__proto__ = Base.prototype; 
Base.call(obj);

第一行,我们创建了一个空对象obj
第二行,我们将这个空对象的__proto__成员指向了Base函数对象prototype成员对象
第三行,我们将Base函数对象的this指针替换成obj,然后再调用Base函数,于是我们就给obj对象赋值了一个id成员变量,这个成员变量的值是”base”,关于call函数的用法,请参看陈皓《Javascript 面向对象编程》文章
如果我们给Base.prototype的对象添加一些函数会有什么效果呢?
例如代码如下:
Base.prototype.toString = function() { 
return this.id; 
}

那么当我们使用new创建一个新对象的时候,根据__proto__的特性,toString这个方法也可以做新对象的方法被访问到。于是我们看到了:
构造子中,我们来设置‘类'的成员变量(例如:例子中的id),构造子对象prototype中我们来设置‘类'的公共方法。于是通过函数对象和Javascript特有的__proto__与prototype成员及new操作符,模拟出类和类实例化的效果。

Pseudoclassical 继承
我们模拟类,那么继承又该怎么做呢?其实很简单,我们只要将构造子的prototype指向父类即可。例如我们设计一个Derive 类。如下

function Derive(id) { 
this.id = id; 
} 
Derive.prototype = new Base(); 
Derive.prototype.test = function(id){ 
return this.id === id; 
} 
var newObj = new Derive("derive");

这段代码执行后的对象模型又是怎么样的呢?根据之前的推导,应该是如下的对象模型
再谈javascript面向对象编程
这样我们的newObj也继承了基类Base的toString方法,并且具有自身的成员id。关于这个对象模型是如何被推导出来的就留给各位同学了,参照前面的描述,推导这个对象模型应该不难。
Pseudoclassical继承会让学过C++/Java的同学略微的感受到一点舒服,特别是new关键字,看到都特亲切,不过两者虽然相似,但是机理完全不同。当然不关什么样继承都是不能离不开__proto__成员的。

Prototypal继承
这是Javascript的另外一种继承方式,这个继承也就是之前陈皓文章《Javascript 面向对象编程》中create函数,非常可惜的是这个是ECMAScript V5的标准,支持V5的浏览器目前看来也就是IE9,Chrome最新版本和Firefox。虽然看着多,但是做为IE6的重灾区的中国,我建议各位还是避免使用create函数。好在没有create函数之前,Javascript的使用者已经设计出了等同于这个函数的。例如:我们看看Douglas Crockford的object函数。

function object(old) { 
function F() {}; 
F.prototype = old; 
return new F(); 
} 
var newObj = object(oldObject);

例如如下代码段
var base ={ 
id:"base", 
toString:function(){ 
return this.id; 
} 
}; 
var derive = object(base);

上面函数的执行后的对象模型是:
再谈javascript面向对象编程
如何形成这样的对象模型,原理也很简单,只要把object这个函数扩展一下,就能画出这个模型,怎么画留给读者自己去画吧。
这样的继承方式被称为原型继承。相对来说要比Pseudoclassical继承来的简单方便。ECMAScript V5正是因为这原因也才增加create函数,让开发者可以快速的实现原型继承。
上述两种继承方式是Javascript中最常用的继承方式。通过本文的讲解,你应该对Javascript的OO编程有了一些‘原理'级的了解了吧

参考:
《Prototypes and Inheritance in JavaScript Prototypes and Inheritance in JavaScript》
Advance Javascript (Douglas Crockford 大神的视频,一定要看啊)题外话:
web2.0后,web应用可谓飞速发展,如今在HTML5发布之际,浏览器的功能被大大强化,我感觉Browser远远在不是一个Browser那么简单了。记得C++之父曾经这样说过JAVA,JAVA不是跨平台,JAVA本身就是一个平台。如今的Browser也本身就是一个平台了,好在这个平台是基于标准的。如果Browser是平台,由于Browser安全沙箱的限制,个人电脑的资源被使用的很少,感觉Browser就是一个NC(Network Computer)?我们居然又回到了Sun最初提出的构想,Sun是不是太强大了些?

Javascript 相关文章推荐
JQuery入门——事件切换之toggle()方法应用介绍
Feb 05 Javascript
解析Javascript中中括号“[]”的多义性
Dec 03 Javascript
jQuery版AJAX简易封装代码
Sep 14 Javascript
利用fecha进行JS日期处理
Nov 21 Javascript
vue实现添加标签demo示例代码
Jan 21 Javascript
EasyUI的DataGrid每行数据添加操作按钮的实现代码
Aug 22 Javascript
详解JSONObject和JSONArray区别及基本用法
Oct 25 Javascript
微信小程序上传图片实例
May 28 Javascript
详解Vue.js在页面加载时执行某个方法
Nov 20 Javascript
layui实现checkbox的目录树tree的例子
Sep 12 Javascript
微信小程序实现吸顶效果
Jan 08 Javascript
解决idea开发遇到javascript动态添加html元素时中文乱码的问题
Sep 29 Javascript
一个简单的Ext.XTemplate的实例代码
Mar 18 #Javascript
Javascript 面向对象编程(coolshell)
Mar 18 #Javascript
Javascript面向对象编程
Mar 18 #Javascript
javascript 常用功能总结
Mar 18 #Javascript
Jquery实现简单的动画效果代码
Mar 18 #Javascript
文本框获得焦点和失去焦点的判断代码
Mar 18 #Javascript
jQuery控制图片的hover效果(smartRollover.js)
Mar 18 #Javascript
You might like
PHP 批量删除 sql语句
2009/06/05 PHP
PHP中的output_buffering详细介绍
2014/09/27 PHP
PHP中的traits简单使用实例
2015/05/13 PHP
PHP图像裁剪缩略裁切类源码及使用方法
2016/01/07 PHP
解决PHPstudy Apache无法启动的问题【亲测有效】
2020/10/30 PHP
页面加载完成后再执行JS的jquery写法以及区别说明
2014/02/22 Javascript
AngularJS基础 ng-show 指令简单示例
2016/08/03 Javascript
JavaScript面试题大全(推荐)
2016/09/22 Javascript
如何正确理解javascript的模块化
2017/03/02 Javascript
Vue组件中的data必须是一个function的原因浅析
2018/09/03 Javascript
Node.js中读取TXT文件内容fs.readFile()用法
2018/10/10 Javascript
微信小程序车牌号码模拟键盘输入功能的实现代码
2018/11/11 Javascript
7个好用的JavaScript技巧分享(译)
2019/05/07 Javascript
微信小程序实现拖拽功能
2019/09/26 Javascript
JavaScript ECMA-262-3 深入解析(二):变量对象实例详解
2020/04/25 Javascript
[01:16:16]DOTA2-DPC中国联赛定级赛 RNG vs Phoenix BO3第二场 1月8日
2021/03/11 DOTA
Python面向对象编程中关于类和方法的学习笔记
2016/06/30 Python
零基础使用Python读写处理Excel表格的方法
2019/05/02 Python
Python程序包的构建和发布过程示例详解
2019/06/09 Python
python实现大文件分割与合并
2019/07/22 Python
python jenkins 打包构建代码的示例代码
2019/11/29 Python
在pycharm中创建django项目的示例代码
2020/05/28 Python
卸载tensorflow-cpu重装tensorflow-gpu操作
2020/06/23 Python
Django ModelForm组件原理及用法详解
2020/10/12 Python
Python类class参数self原理解析
2020/11/19 Python
python中time tzset()函数实例用法
2021/02/18 Python
HTML5 WebSocket实现点对点聊天的示例代码
2018/01/31 HTML / CSS
Timberland德国官网:靴子、鞋子、衣服、夹克及配件
2019/12/10 全球购物
mysql的最长数据库名,表名,字段名可以是多长
2014/04/21 面试题
Ajax主要包含了哪些技术
2014/06/12 面试题
单位授权委托书范本
2014/09/26 职场文书
介绍信怎么写
2015/01/30 职场文书
品质保证书格式
2015/02/28 职场文书
不服劳动仲裁起诉书
2015/05/20 职场文书
python 使用Tensorflow训练BP神经网络实现鸢尾花分类
2021/05/12 Python
使用Nginx的访问日志统计PV与UV
2022/05/06 Servers