Javascript是基于原型实现面向对象的,因此并没有类和接口,它的对象也与其他基于类的语言中的对象有所不同。在Javascript中,每个对象都是基于一个引用类型创建的,这个引用类型可以是原生类型,也可以是自定义的类型。在没有类的情况下,可以采用下列模式创建对象。

简单模式

创建对象最简单的方式就是创建一个引用类型的实例,再为其添加属性和方法:

var Person = new Object();

Person.name = "Thom";

Person.action = function(){

};

但是这样使用同一个接口创建很多对象,会产生大量的重复代码,不利于封装

工厂模式

工厂模式使用简单的函数创建对象,为对象添加属性和方法,然后返回对象。这种模式抽象了具体创建对象的过程,用函数封装并用特定接口创建对象细节。如下:

function Person(name, age){

    var obj = new Object();

    obj.name = name;

    obj.age = age;

    obj.action = function(){};

    return obj;

}

var person1 = Person("Thom", 23);

console.log(person1 instanceof Person);//false

工厂模式可以更好的创建多个相似对象,但是却无法识别对象的类型

构造函数模式

通过创建自定义类型,可以想原生类型一样通过new操作符创建实例。因此,可以使用构造函数模式重写上面的例子:

function Person(name, age){

    this.name = name;

    this.age = age;

    this.action = function(){};

}

var person1 = new Person("Thom", 23);

console.log(person1 instanceof Person);//true

注意与工厂模式的区别:

没有显示地创建对象;

直接将属性和方法赋给this对象;

没有return语句;

创建的实例为Person类型的实例。

构造函数这种声明方式是定义在window对象中的,如果不使用new操作符,那么相当于在全局作用域中调用函数,此时this对象指向window对象,因此属性和方法将添加给window对象,可以通过window对象来调用。如下:

Person("Alen", 33);

console.log(window.name);//Alen

然而,构造函数模式和工厂模式都有一个相同的缺点,就是每个函数都要在每个实例上重新创建一遍。由于javascript中函数也是对象,表面上每个实例似乎使用同个Function对象,实际上每个实例都自己实例化了一个Function对象。所以,构造函数的每个成员无法复用,无法在多个对象间共享方法,每个函数都要在每个实例上重新创建一遍。

因此,不同实例间的函数是不相等的:

function Person(name, age){

    this.name = name;

    this.age = age;

    this.action = function(){};

}

var person1 = new Person("Thom", 23);

var person2 = new Person("Alen", 33);

console.log(person1.action == person2.action); //false

console.log(person1.name == person2.name); //false

虽然可以通过将函数定义转移到构造函数外部(即全局作用域)来实现成员共享,但是这样定义全局函数实际上只能在构造函数中被调用。更进一步,如果对象需要定义很多方法,那么就需要定义很多全局函数,严重破坏了封装。

function Person(name, age){

    this.name = name;

    this.age = age;

    this.action = action;

}

function action(){

    return this.name

}

var person1 = new Person("Thom", 23);

var person2 = new Person("Alen", 33);

console.log(person1.action == person2.action); //true

原型模式

原型模式可以说是解决了构造函数成员无法共享的问题,避免创建多个函数实例。但是由于它省略了构造函数传递初始化参数这一个环节,因此所有实例默认情况下都是相同属性值。。虽然共享对于函数来说非常合适,避免实例化多个函数。但是对于包含引用类型的属性,不同实例将共享同一个引用,无法拥有自己的独有属性。

function Person(){

}

Person.prototype.name = new String();

Person.prototype.friends = new Array();

var person1 = new Person();

person1.name = "Kingle";

person1.friends.push("John");

console.log(person1);

var person2 = new Person();

console.log(person2);

用chrome console可以看到:

可以看到 person1.friends.push("John") 其实是将item添加到Person.prototype的array friends上。

但是,person1.name = "Kingle" 却是将String赋值到person1实例的属性上。

上面的用法就是单纯的原型模式了,而之所以会得出那样的结果是因为原型模式的共享特性。由于原型中所有属性被很多实例共享,对于基本类型值的属性倒没什么影响,因为通过在不同实例上添加同名属性,可以隐藏原型中对应属性,像上述代码中的属性name。然而,对于引用类型的属性,不同实例将会共享同一个来自prototype的引用。

但是,实例一般有希望有自己的属性,所以很少单独使用原型模式创建对象。

构造函数模式和原型模式的组合

构造函数模式和原型模式各有各的优缺点,通过组合使用,构造函数模式用于定于非共享的实例属性,而原型模式用于定义方法和共享的属性,取长补短,这也是最常用的创建自定义类型的模式。如下代码:

function Person(name, age){

    this.name = name;

    this.age = age;

    this.friends = ["Evan", "Jack"];

}

Person.prototype = {

    constructor : Person,

    action : function(){

        return this.name;

    }

}

var person1 = new Person("Thom", 23);

person1.friends.push("Alen");

var person2 = new Person("Alen", 33);

console.log(person1.friends);//["Evan", "Jack", "Alen"]

console.log(person2.friends);//["Evan", "Jack"]

console.log(person1.action == person2.action); //true

其他模式

除了上面几种模式,还有一些特殊的的模式,但是基本都是基于上面几种模式的修改。

定义自定义类型对象,再结合javascript的原型链,就可以在javascript中实现继承。

javascript面向对象--自定义类型的更多相关文章

  1. JavaScript之面向对象学习七(动态原型模式、寄生构造函数模式、稳妥构造函数模式创建自定义类型)

    一.动态原型模式 在面向对象学习六中的随笔中,了解到组合构造函数模式和原型模式创建的自定义类型可能最完善的!但是人无完人,代码亦是如此! 有其他oo语言经验的开发人员在看到独立的构造函数和原型时,很可 ...

  2. javascript面向对象系列第三篇——实现继承的3种形式

    × 目录 [1]原型继承 [2]伪类继承 [3]组合继承 前面的话 学习如何创建对象是理解面向对象编程的第一步,第二步是理解继承.本文是javascript面向对象系列第三篇——实现继承的3种形式 [ ...

  3. javascript面向对象系列第一篇——构造函数和原型对象

    × 目录 [1]构造函数 [2]原型对象 [3]总结 前面的话 一般地,javascript使用构造函数和原型对象来进行面向对象编程,它们的表现与其他面向对象编程语言中的类相似又不同.本文将详细介绍如 ...

  4. 《javascript面向对象精要》读书笔记

    <javascript面向对象精要> 买这本书的原因主要是因为作者,Nicholas C. Zakas 牛X闪闪的js专家,读过js高程的应该都知道他,而这本书是他的最新力作,感觉也是js ...

  5. 【转】javascript面向对象编程

    摘要:本文本来是想自己写的,奈何花了好长时间写好之后忘记保存,还按了刷新键,一键回到解放前,索性不写了,所以本文是转载的. 面向对象编程是用抽象方式创建基于现实世界模型的一种编程模式,主要包括模块化. ...

  6. JavaScript 面向对象继承详解

    题记 由于js不像java那样是完全面向对象的语言,js是基于对象的,它没有类的概念.所以,要想实现继承,一般都是基于原型链的方式: 一.继承初探 大多数JavaScript的实现用 __proto_ ...

  7. JavaScript面向对象简介

    JavaScript面向对象简介 @(编程) [TOC] 1. 命名空间 命名空间是一个容器,它允许开发人员在一个独特的,特定于应用程序的名称下捆绑所有的功能. 在JavaScript中,命名空间只是 ...

  8. javascript面向对象程序设计系列(一)---创建对象

    javascript是一种基于对象的语言,但它没有类的概念,所以又和实际面向对象的语言有区别,面向对象是javascript中的难点之一.现在就我所理解的总结一下,便于以后复习: 一.创建对象 1.创 ...

  9. 深入解读JavaScript面向对象编程实践

    面向对象编程是用抽象方式创建基于现实世界模型的一种编程模式,主要包括模块化.多态.和封装几种技术.对JavaScript而言,其核心是支持面向对象的,同时它也提供了强大灵活的基于原型的面向对象编程能力 ...

随机推荐

  1. AllJoyn Bundled Daemon 使用方式研究

    关于AllJoyn不多做介绍,请看官网:www.alljoyn.org/ 0. 问题来源: 应用程序要使用AllJoyn库,就必须启动deamon. 目前有两种方式: 使用standalone形式,单 ...

  2. 修改FFMpeg源码—捕获丢包

    概述 最近我们项目有一个需求就是解决客户端播放RTSP视频流花屏的问题,一般来说丢包就会引起花屏,导致客户端花屏的因素又有很多,比如说: 相机到服务器丢包 服务器到客户端丢包 等等... 其中服务器到 ...

  3. Border绘制虚线框(转)

    <Grid> <Border HorizontalAlignment="Center" VerticalAlignment="Center" ...

  4. 【Android 界面效果25】android中include标签的使用

    在一个项目中我们可能会需要用到相同的布局设计,如果都写在一个xml文件中,代码显得很冗余,并且可读性也很差,所以我们可以把相同布局的代码单独写成一个模块,然后用到的时候可以通过<include ...

  5. [转]poll技术

    poll()函数:这个函数是某些Unix系统提供的用于执行与select()函数同等功能的函数,下面是这个函数的声明: #include <poll.h> int poll(struct ...

  6. (三)u-boot2013.01.01 for TQ210:《mkconfig分析》

    /* 和分析makefile一样,分析mkconfig同样注重句法分析 */ ############################################################# ...

  7. 重构7-Rename(method,class,parameter)

    这是我最常用也是最有用的重构之一.我们对方法/类/参数的命名往往不那么合适,以至于误导阅读者对于方法/类/参数功能的理解.这会造成阅读者的主观臆断,甚至引入bug.这个重构看起来简单,但却十分重要. ...

  8. linux_jvm_jmap_dump内存分析

    jmap命令   jmap命令 jmap命令可以获得运行中的jvm的堆的快照,从而可以离线分析堆,以检查内存泄漏,检查一些严重影响性能的大对象的创建,检查系统中什么对象最多,各种对象所占内存的大小等等 ...

  9. DML 数据操纵语言

    1.INSERT(插入)语言结构 INSERT INTO table(表名)(要插入的列名) VALUES(要插入的具体值): table:要插入数据的表的表名 column[,column]:表中要 ...

  10. Java跳出循环-break和continue语句

    在实际编程中,有时需要在条件语句匹配的时候跳出循环.在Java里,由break和continue语句控制. “break”语句 “break”语句用来结束循环,即不再执行后边的所有循环. 示例:计算1 ...