再谈javascript面向对象编程

另外这篇文章是一篇入门文章,我也是才开始学习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引擎中的对象模型如下:

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引擎的对象模型是这样的


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

代码如下:

var obj = new Base();

这样代码的结果是什么,我们在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");

这段代码执行后的对象模型又是怎么样的呢?根据之前的推导,应该是如下的对象模型

这样我们的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);

上面函数的执行后的对象模型是:

如何形成这样的对象模型,原理也很简单,只要把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是不是太强大了些?

(0)

相关推荐

  • 探讨javascript是不是面向对象的语言

    1 它拥有对象,可以包含数据和处理数据的方法.对象可以包含其它对象.他没有类(在javascript2.0真正实现之前),但它却有构造器可以做类能做的事,包括扮演变量和方法的容器和角色.他没有基于类的继承,但它有基于原型的继承.两个建立对象系统的方法是通过继承和通过聚合.javaScript两个都有. 2 一些评价说javascript不是真正面向对象的因为它不能提供信息的隐藏.也就是,对象不能有私有变量和私有方法:所有的成员都是公共的.但随后有人证明了javaScript对象可以拥有私有变量和

  • javascript面向对象入门基础详细介绍

    什么是对象 简单点说,编程语言中的对象是对现实中事物的简化.例如,我们一个人就是一个对象,但是编程语言很难完全描述一个这样复杂的对象.所以我们必须做出简化,首先,将人简化成属性和行为的组合,然后仅仅保留对程序有意义的几个属性以及行为.例如,我们做一个统计某学校的人的身高的程序,那么我们在这个程序中就可以把人的行为省略掉,只保留行为,并且只保留身高这一个属性.这样,我们就得到了一个最简单的对象. JavaScript字符串对象 对象的属性 其实我们之前在HTML DOM中已经就是在使用对象了.例如

  • JavaScript中的面向对象介绍

    对象 创建 对象 构造函数 公有.私有.特权.静态成员 this.call和apply 异常处理 继承 原型 对象 在JavaScript,可以说everything is object,那么什么是对象?对象就是包含一组变量和函数的集合.在其它面向对象语言中对象是由类的实例化而来,JavaScript是基于原型的面向对象语言,没有类的概念,对象派生自现有对象的副本.JavaScript中对象可以分为两类:Function和Object. 创建对象 为了提高效率,JavaScript自带内置对象,

  • javaScript面向对象继承方法经典实现

    JavaScript的出现已经将近20多年了,但是对这个预言的褒贬还是众说纷纭.很多人都说JavaScript不能算是面向对象的变成语言.但是JavaScript的类型非常松散,也没有编译器.这样一来给了程序员很大的自由,也带来了一些缺陷. 虽然JavaScript不算是一门面向对象的语言.但是我们可以模仿着其他语言实现面向对象的方式来实现JavaScript的面向编程. 下面是JavaScript教程中非常经典的继承方法. 复制代码 代码如下: //定义一个Pet对象.通过这一个名称和数量的腿

  • 深入领悟JavaScript中的面向对象

    JavaScript 是面向对象的.但是不少人对这一点理解得并不全面. 在 JavaScript 中,对象分为两种.一种可以称为"普通对象",就是我们所普遍理解的那些:数字.日期.用户自定义的对象(如:{})等等. 还有一种,称为"方法对象",就是我们通常定义的 function.你可能觉得奇怪:方法就是方法,怎么成了对象了?但是在 JavaScript 中,方法的确是被当成对象来处理的.下面是一个简单的例子: 复制代码 代码如下: function func()

  • 全面理解面向对象的 JavaScript(来自ibm)

    当今 JavaScript 大行其道,各种应用对其依赖日深.web 程序员已逐渐习惯使用各种优秀的 JavaScript 框架快速开发 Web 应用,从而忽略了对原生 JavaScript 的学习和深入理解.所以,经常出现的情况是,很多做了多年 JS 开发的程序员对闭包.函数式编程.原型总是说不清道不明,即使使用了框架,其代码组织也非常糟糕.这都是对原生 JavaScript 语言特性理解不够的表现.要掌握好 JavaScript,首先一点是必须摒弃一些其他高级语言如 Java.C# 等类式面向

  • 再谈javascript面向对象编程

    另外这篇文章是一篇入门文章,我也是才开始学习Javascript,有一点心得,才想写一篇这样文章,文章中难免有错误的地方,还请各位不吝吐槽指正 吐槽Javascript 初次接触Javascript,这门语言的确会让很多正规军感到诸多的不适,这种不适来自于Javascript的语法的简练和不严谨,这种不适也来自Javascript这个悲催的名称,我在想网景公司的Javascript设计者在给他起名称那天一定是脑壳进水了,让Javascript这么多年来受了这么多不白之冤,人们都认为他是Java的

  • 浅谈javascript 面向对象编程

    感叹是为了缓解严肃的气氛并引出今天要讲的话题,"javascript面向对象编程",接下来,我们围绕面向对象的几大关键字:封装,继承,多态,展开. 封装:javascript中创建对象的模式中,个人认为通过闭包才算的上是真正意义上的封装,所以首先我们先来简单介绍一下闭包,看下面这个例子: 复制代码 代码如下: <script type="text/javascript"> function myInfo(){ var name ="老鱼&quo

  • 老鱼 浅谈javascript面向对象编程

    感叹是为了缓解严肃的气氛并引出今天要讲的话题,"javascript面向对象编程",接下来,我们围绕面向对象的几大关键字:封装,继承,多态,展开. 封装:javascript中创建对象的模式中,个人认为通过闭包才算的上是真正意义上的封装,所以首先我们先来简单介绍一下闭包,看下面这个例子: 复制代码 代码如下: <script type="text/javascript">// <![CDATA[ function myInfo(){ var nam

  • 再谈JavaScript异步编程

    随着前端的发展,异步这个词真是越来越常见了.假设我们现在有这么一个异步任务: 向服务器发起数次请求,每次请求的结果作为下次请求的参数. 来看看我们都有哪些处理方法: Callbacks 最先想到也是最常用的便是回调函数了,我们来进行简单的封装: let makeAjaxCall = (url, cb) => { // do some ajax // callback with result } makeAjaxCall('http://url1', (result) => { result =

  • 浅谈Javascript面向对象编程

    在JS中充分使用面向对象设计思想,可以极大限度的提升代码重用.降低模块间的偶合.更好的逻辑分层与并行开发.下面分几个步骤简单谈下我的理解. 一.数据类型与包装类 包装类 -- 类型名 -- 常见值 -- 分类 Number -- number -- 123.123 -- 基本数据类型 Boolean -- Boolean -- true.false -- 基本数据类型 String -- string -- "hello world!" -- 基本数据类型 Object -- obje

  • Javascript 面向对象编程(一) 封装

    学习Javascript,最难的地方是什么? 我觉得,Object(对象)最难.因为Javascript的Object模型很独特,和其他语言都不一样,初学者不容易掌握. 下面就是我的学习笔记,希望对大家学习这个部分有所帮助.我主要参考了以下两本书籍: <面向对象的Javascript>(Object-Oriented JavaScript) <Javascript高级程序设计(第二版)>(Professional JavaScript for Web Developers, 2nd

  • JavaScript 面向对象编程(2) 定义类

    本文承接上一篇JavaScript面向对象编程(1) 基础. 上篇说过,JavaScript没有类的概念,需要通过函数来实现类的定义.先通过一个例子说明: 复制代码 代码如下: function myClass() { var id = 1; var name = "johnson"; //properties this.ID = id; this.Name = name; //method this.showMessage = function() { alert("ID:

  • 深入剖析JavaScript面向对象编程

    二. Javascript 面向对象编程:构造函数的继承 本节主要介绍,如何生成一个"继承"多个对象的实例. 比如,现在有一个"动物"对象的构造函数, function Animal(){ this.species = "动物"; } 还有一个"猫"对象的构造函数, function Cat(name,color){ this.name = name; this.color = color; } 怎样才能使"猫&qu

  • 再谈Javascript中的基本类型和引用类型(推荐)

    一.基本类型和引用类型概述 js中数据类型的值包括:基本类型值和引用类型值 基本数据类型:undefined;null;boolean;number;string 引用类型值:保存在内存中,js不允许直接访问内存位置,因此时操作引用而不是实际对象 二.如何检测数据类型 1.基本数据类型的检测:使用typeof var s = "AAA"; alert(typeof s); //返回string 2.引用类型(对象类型)检测:使用instanceof alert(person insta

  • 再谈javascript常见错误及解决方法

    初学Javascript,每天总是被很小的问题折磨半天,今晚就有好几个小问题. 第一:全部使用双引号造成匹配错误 <input type="checkbox" onmouseover="document.getElementById("test").style.display="none":"/> 改行一直报错误:unexpected toke "}"  检查半天也没有发现错误,对照发现视频上是

随机推荐