(个人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. 有关Struts2a的ction直接使用response异步问题

    假设我们在项目中使用struts2,正在使用ajax而通信时后端程序.为简单起见,我们经常使用下面的方法:         ActionContext ac = ActionContext.getCo ...

  2. POJ2195 Going Home 【最小费用流】+【最佳匹配图二部】

    Going Home Time Limit: 1000MS   Memory Limit: 65536K Total Submissions: 18169   Accepted: 9268 Descr ...

  3. ipset高大上性能果断将nf-HiPac逼下课

    netfilter.sourceforge,github上有一个凄凉的项目,那就是nf-hipac.这个以前给Linux firewall设计带来希望的项目早在2005年就停止了更新和维护,而我本人则 ...

  4. JSP_include指令和&lt;jsp:include&gt;

    包括三个文件:jsp_include.jsp, static.html, two.jsp 周边环境:tomcat7.0. myeclipse10 1.jsp_include.jsp <%@ pa ...

  5. 使用RESTClient插件数据模拟(GET,POST)提交

    1:在Firefox下载RESTClient插件安装 2:安装界面后, 3:点击设置头文件:(设请求地址有头部文件) 4:设置界面,当然有非常多选择.依据你的须要.一般在输入的时候有智能提示,我这里以 ...

  6. HTML DOM nodeName nodeValue

    在javascript在,我们得title在标签和文本,它们通常要求这样做 var obj =document.getElementsById("id1"); obj.nodeNa ...

  7. IOS获得各种文档文件夹路径的方法

    iphone沙箱模型的有四个目录,各自是什么,永久数据存储一般放在什么位置.得到模拟器的路径的简单方式是什么. documents,tmp.app,Library. (NSHomeDirectory( ...

  8. 大数据量传输时配置WCF的注意事项

    原文:大数据量传输时配置WCF的注意事项 WCF传输数据量的能力受到许多因素的制约,如果程序中出现因需要传输的数据量较大而导致调用WCF服务失败的问题,应注意以下配置: 1.MaxReceivedMe ...

  9. HDU-4628 Pieces 如压力DP

    鉴于他的字符串,每一个都能够删除回文子串.子可以是不连续,因此,像更好的模拟压力.求删除整个字符串需要的步骤的最小数量. 最大长度为16,因此不能逐行枚举状态.首先预处理出来全部的的回文子串,然后从第 ...

  10. 体验安装金蝶K/3 Wise 13.0(图像)

    金蝶13.0它提供windows7支持,而数据库也升级到SQL server 2008,有许多功能上的改善和增强.原本在位置低版本号需要时间来管理此功能,因为有这个模块没有原因一直没能起来,现在,新版 ...