(个人blog迁移文章。)

前言:

下面将探讨javascript面向对象编程的知识。

请不要刻意把javascript想成面向对象编程是理所当然的。

javascript里面,对象思想不可少,但是不一定需要面向对象编程,有时候,我们需要的只是一个实例化了的对象,而不是一个创建对象的类。

偏要这样做的话,也行,请看下文。另外请勿与传统面向对象编程做对比,这没有可比性。

对于javascript来说,所有的变量都可以被称为对象。例如:

var a = 'hello world';
console.log(a.toUpperCase());

这里面,a为字符串对象。有其能直接调用的方法。但是这篇文章不讨论这一类的变量,而是讨论如何自定义对象。

声明一个对象:

var obj1 = {};
var obj2 = new Object(); 

通过上面这两种方式中任意一种,就就声明了一个对象变量,这是一个实例,而且,是一个空对象,不能继承。

何为空对象,就是这个对象里面没有定义任何成员和方法。

设置对象成员和方法:

var Person = {};
Person.name = 'Tom';
Person.gender = 'male';
Person.sayHello = function () {
console.log("Hello "+this.name+".");
}

这是一种最直观最简单的对象定义及成员方法添加的方法,里面定义了person对象,person有name和gender的成员,以及sayHello的方法。这也是一个实例,不能继承。通过person.name/persion.gender能直接引用该对象的成员,person.sayHello()能直接调用该对象的方法。这算基础。

在sayHello方法中,this指向的就是person,和其他的面向对象编程方法相同。

上面声明对象的方法太过于累赘,一般选择下面这种对象声明的方法。

var Person = {
name:'Tom',
gender:'male',
sayHello:function() {
console.log("Hello "+this.name+".");
}

这样的声明方法比上面的方法都简洁,首推方法。成员方法调用方式一样。

声明一个可继承的类:

情景一:

var Person = function (name) {
this.name = name || 'NoName';
};
Person.sayHello = function () {
console.log("Hello "+this.name+".");
}
var Tom = new Person();

这样子,就等于声明了一个person的类,Tom就是person的一个实例。但是person.sayHello方法就等于是私有属性,不能被继承,所有Tom没有sayHello的方法。

情景二:

var Person = function (name) {
this.name = name||"NoName";
this.sayHello = function () {
console.log("Hello "+this.name+".");
}
};
var Tom = new Person('Tom');
Tom.sayHello(); 

这样也是定义对象的一个方法,person是一个类,其name和sayHello可被实例继承。但是有一个缺陷,如果通过此类创建多个实例,那么这个类就存在多少份的复制,就如上面来说,创建多个实例:

var Tom1 = new Person("Tom1");
var Tom2 = new Person("Tom2");
var Tom3 = new Person("Tom3");

此时,Person就存在三个实例,每个实例有自己的成员和方法,内存中有三个sayHello方法的引用。sayHello作为一个通用的方法,这样定义的话,在新建多个实例时,就会造成内存的浪费。因此,应该把通用的方法使用原型链的方式定义,请看情景三。

情景三:

var Person = function (name) {
this.name = name||"NoName";
};
Person.prototype.sayHello = function () {
console.log("Hello "+this.name+".");
}
var Tom = new Person('Tom');
Tom.sayHello(); 

通过原型链的方式,基于这个类新建的实例,其方法就不会再内存里面存在多个实例。

但是此时,又涉及一个问题,如何知道Tom属于哪个类的呢,通过哪个构造函数来创建的呢?就引申到情景四了。

情景四:

这里,讨论的是实例的构造函数,每一个对象都有一个construcor的成员方法,指向的是创建该对象的那个函数。

例如:

var arr1 = [1,2,3,4,5]; //此时arr1.constructor就是Array。
var func = function() {};//此时func.constructor就是Function。 

因此,情景三中的Tom.constructor 就是 Person,就是

function (name) {
this.name = name||"NoName";
};

这一个函数。通过new运算符创建的实例,该实例成员constructor所指向的就是new后面的变量。

题外话,说说new运算符,MDN上面关于new运算符的定义是这样的:

The new operator creates an instance of a user-defined object type or of one of the built-in object types that has a constructor function.

大意是:new运算符能为那些存在构造函数的用户自定义对象类型或者浏览器内部实现对象类型创建一个实例。

new的语法格式为

new constructor[([arguments])]; 

可以明显看出 实例的成员constructor就是创建该实例的对象类型。

上面的四个情景,都不能说是面向对象编程,因为,还没有实现类的继承,只实现了类的创建实例。下面情景五来探讨如何继承类。

情景五:

不推荐用原生的写法进行面向对象编程,因为确实非常麻烦,推荐使用coffeescript进行面向对象的编程,甚至所有的javascript的编写,能转coffeescript就全部转,你会发现,使用coffeescript写出来的代码非常的优雅,而且,你将全部精力投入的是如何巧妙的设计代码,而不是堆代码的时代。

当然,这里还是会讲讲如何通过原生javascript的方式实现javascript的继承。不过,真的很麻烦。

首先,得要一个extends,把它看成new级别的东西吧。

var __extends = function(child,father) {
for(var property in father) {
child[property] = father[property];
}
}
var Animal = function (name) {
this.name = name;
}
Animal.sayHello = function () {
console.log("Hello "+this.name);
}
var Cat = function (name) {
this.name = name;
}
__extends(Cat,Animal);
Cat.sayHello();

这是一种继承的方式,要完美实现,这还是不足的。代码量好大啊。还是使用coffeescript来写吧。

情景六:

在情景五中,Cat继承了Animal的sayHello的方法。但是,如果改成 Animal.prototype.sayHello = function () {}的话,Cat类就没法继承了,这就是上面那种简单写法的缺陷。

所以需要把prototype的属性也要继承,所以必须把__extends函数重新写,注意prototype对于所有其派生类都是指向同一个内存空间的,修改父类对象的prototype将影响所有的子类。

var __extends = function(child,father) {
for(var property in father) {
child[property] = father[property];
}
function ctor() {
this.constructor = child;
}
ctor.prototype = parent.prototype;
child.prototype = new ctor();
child.__super__ = father.prototype;
}

很麻烦是吧,而且也不知道会不会出错。那您也应该尝试coffeescript的写法了。

情景七:

所以,还是来试试coffeescript的写法吧

class Animal
constructor:(@name) ->
sayHello:->
console.log "Hello #{this.name}."
return
class Cat extends Animal
sayHello:->
console.log "喵喵喵喵喵喵,#{this.name}"
class Dog extends Animal
sayHello:->
console.log "汪汪汪汪汪汪,#{this.name}"
cat1 = new Cat "kitty"
dog1 = new Don "哈士奇"

通过coffeescript写出一个javascript对象继承,代码就是这么简洁。

在这里面,就定义了Animal类,还有派生类Cat和Dog,分别覆盖了父类的sayHello的方法。写完之后,直接使用koala编译一下,马上一段完美的继承代码生成了。

结语:

至此,此片面向对象的文章算是草草结束了,断断续续写了一个星期,就写成了这样子,比较糟糕。大家不妨看看下面的参考文献。

参考文献:

  1. new
  2. JavaScript类和继承:constructor属性
  3. koala
  4. coffeescript

觉得对您有帮助,点个赞。赞赞更健康。

探讨javascript面向对象编程的更多相关文章

  1. 再谈javascript面向对象编程

    前言:虽有陈皓<Javascript 面向对象编程>珠玉在前,但是我还是忍不住再画蛇添足的补上一篇文章,主要是因为javascript这门语言魅力.另外这篇文章是一篇入门文章,我也是才开始 ...

  2. JavaScript面向对象编程学习笔记

    1  Javascript 面向对象编程 所谓"构造函数",其实就是一个普通函数,但是内部使用了this变量.对构造函数使用new运算符,就能生成实例,并且this变量会绑定在实例 ...

  3. 快速学习JavaScript面向对象编程

    到处都是属性.方法,代码极其难懂,天哪,我的程序员,你究竟在做什么?仔细看看这篇指南,让我们一起写出优雅的面向对象的JavaScript代码吧! 作为一个开发者,能否写出优雅的代码对于你的职业生涯至关 ...

  4. 深入理解Javascript面向对象编程

    深入理解Javascript面向对象编程 阅读目录 一:理解构造函数原型(prototype)机制 二:理解原型域链的概念 三:理解原型继承机制 四:理解使用类继承(继承的更好的方案) 五:建议使用封 ...

  5. 【转】Javascript 面向对象编程(一):封装

    原文链接:http://www.ruanyifeng.com/blog/2010/05/object-oriented_javascript_encapsulation.html Javascript ...

  6. Javascript 面向对象编程(一):封装 by 阮一峰

    <Javascript高级程序设计(第二版)>(Professional JavaScript for Web Developers, 2nd Edition) 它们都是非常优秀的Java ...

  7. 转:javascript面向对象编程

    作者: 阮一峰 日期: 2010年5月17日 学习Javascript,最难的地方是什么? 我觉得,Object(对象)最难.因为Javascript的Object模型很独特,和其他语言都不一样,初学 ...

  8. JavaScript面向对象编程(一)原型与继承

    原型(prototype) JavaScript是通过原型(prototype)进行对象之间的继承.当一个对象A继承自另外一个对象B后,A就拥有了B中定义的属性,而B就成为了A的原型.JavaScri ...

  9. JavaScript面向对象编程(二)构造函数和类

    new关键字和构造函数 在文章JavaScript面向对象编程(一)原型与继承中讨论啦JavaScript中原型的概念,并且提到了new关键字和构造函数.利用new关键字构造对象的实例代码如下: // ...

随机推荐

  1. MiniGUI文档参考手册 基于v1.6.10文本

    MiniGUI各种功能都分布在预先定义宏对每个文档标题.特别不方便查找,这是不利于初学者学习. 有一天,我发现doxygen,因此,使用该工具可以生成一个minigui参考文献 .基于v1.6.10文 ...

  2. zoj 3820 Building Fire Stations(树上乱搞)

    做同步赛的时候想偏了,状态总是时好时坏.这状态去区域赛果断得GG了. 题目大意:给一棵树.让求出树上两个点,使得别的点到两个点较近的点的距离最大值最小. 赛后用O(n)的算法搞了搞,事实上这道题不算难 ...

  3. 【Java】【Flume】Flume-NG源代码分析的启动过程(两)

    本节分析配置文件的解析,即PollingPropertiesFileConfigurationProvider.FileWatcherRunnable.run中的eventBus.post(getCo ...

  4. Event Sourcing - ENode(一)

    分布式系统 摩尔定律如果一直能实现,不管是涉及或者实现一个OLTP的系统,我们是不是都会轻松点,用硬件堆就可以了.但是现在硬件已经在求变了,那么我们也得求变,云的概念如此之火,本质就是设施虚拟化,也可 ...

  5. MYSQL C API 记录

    一.环境与条件 MySQL AB 提供了C API,能够提供低等级界面,负责完毕涉及SQLserver交互的大多数常规任务:数据库连接 .查询.结果集处理和错误处置.C API通过两个组件实现: 头文 ...

  6. Java之旅(三)--- JSTL和EL表情

     先给大家看一段JSP的代码.看看有什么感受? <% List<UsEL> usELList = pageModel.getList(); for (ItELator<Us ...

  7. C++ Primer 学习笔记_38_STL实践与分析(12)--集成的应用程序容器:文本查询程序

    STL实践与分析 --容器的综合应用:文本查询程序 引言: 本章中最重点的实例.由于不须要用到multiset与multimap的内容.于是将这一小节提到了前面.通过这个实例程序,大师分析问题的智慧, ...

  8. Oracle数据表被drop后的恢复

    对于被drop的表和索引,都会存放在回收站中(所以对于生产的数据库必须设置好回收站功能) 由于本次生成环境在drop掉已有的表后,又一次创建了很多的表,全部直接还原的话会提示原有对象存在,表名反复.当 ...

  9. Pro Aspnet MVC 4读书笔记(2) - The MVC Pattern

    Listing 3-1. The C# Auction Domain Model using System; using System.Collections.Generic; using Syste ...

  10. js中的open和showModalDialog

    一.window.open()支持环境: JavaScript1.0+/JScript1.0+/Nav2+/IE3+/Opera3+ 二.基本语法:window.open(pageURL,name,p ...