探讨javascript面向对象编程

时间:2022-09-20 08:13:38

(个人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. vim&plus;vundle配置

    Linux环境下写代码虽然没有IDE,但通过给vim配置几个插件也足够好用.一般常用的插件主要包括几类,查找文件,查找符号的定义或者声明(函数,变量等)以及自动补全功能.一般流程都是下载需要的工具,然 ...

  2. swfupload 例子

    upload.html <!DOCTYPE html> <html lang="en"> <head> <script type='tex ...

  3. Qt之重写QLabel类

    在mylabel.h 文件中#ifndef MYLABEL_H#define MYLABEL_H #include <QLabel>/*重新实现QLabel类,使其支持点击事件*/clas ...

  4. Dynamo涉及的算法和协议——p2p架构,一致性hash容错&plus;gossip协议获取集群状态&plus;向量时钟同步数据

    转自:http://www.letiantian.me/2014-06-16-dynamo-algorithm-protocol/ Dynamo是Amazon的一个分布式的键值系统,P2P架构,没有主 ...

  5. php curl下载图片 URL地址

    <?php $url = 'http://mf1905.com/upload/video_img/df3074c98ec5124ad47c52ff59f74e04_middle.jpeg'; f ...

  6. string与char&ast; 互相转换以及周边问题

    先插一个小知识点 string str = "abc" str += 'd'; cout<<str<<endl;  //"abcd"   ...

  7. hover带有动画效果的导航

    html,body{overflow-x:hidden;} ul,li{list-style: none;} .nav{width:100%; height: 26px; overflow: hidd ...

  8. Git添加远程库和从远程库中获取

    一. Git添加远程库 1. 在本地新建一个文件夹,在该文件夹使用Git工具,运行$ git init,将该文件夹变为本地Git仓库,同时会生成一个隐藏的.git文件夹. 2. 在该文件夹中用Note ...

  9. 笔记:XML-解析文档-流机制解析器(SAX、StAX)

    DOM 解析器完整的读入XML文档,然后将其转换成一个树型的数据结构,对于大多数应用,DOM 都运行很好,但是,如果文档很大,并且处理算法又非常简单,可以在运行时解析节点,而不必看到完整的树形结构,那 ...

  10. django的配置文件字符串是怎么导入的?

    写在开头: 每个APP都会有配置文件,像下代码Django等等这种的settings里面的配置导入都是字符串的,他们是怎么做的呢? MIDDLEWARE = [ 'django.middleware. ...