https://developer.mozilla.org/zh-CN/docs/JavaScript/Guide/Inheritance_and_the_prototype_chain

本文内容

  • 引入
  • 构造函数创建对象
  • Object.create创建对象
  • 极简主义法
    • 封装
    • 继承
    • 私有属性和私有方法
    • 数据共享

 

引入


近 20 年前,也就是 Javascript 诞生时(1995 年),它只是一种简单的网页脚本语言,像如果你忘记填写用户名,就跳出一个警告,当时的网速只有 28Kbps,这样简单的交互也让 Web 服务器做显然不合适。

如今,Javascript 几乎无所不能,从前端到后端,各种匪夷所思、令人瞠目结舌的用途,程序员用它完成越来越庞大的项目,代码复杂度也在直线飙升。单个网页包含 10000 行 Javascript 代码,早就司空见惯。2010 年,一个工程师透露:Gmail 代码长度是 443000 行!

图 1 Gmail 代码段

编写和维护如此庞大复杂的代码,多年的工程实践表明必须使用模块化策略,而要采用模块化,主流做法是“面向对象编程”,它具有抽象,封装,多态和继承这四个特点。

可 Javascript 是一种基于对象(object-based)的语言,你遇到的所有一切几乎都是对象,它结合(简单的)函数式语言和(简单的)面向对象语言的特点,但其语法不支持“类(class)”,无法直接使用面向对象编程。如果要把“属性”和“方法”,封装成一个对象,甚至要从原型对象生成一个实例对象。因此,程序员们探索如何用 Javascript 模拟“类”?

很多文章在谈到“Javascript 模拟‘类’”,但首先我个人认为这个说法很有误解,即便为“类”加了双引号。面向对象是基于类的语言,构建在两个不同实体的概念之上的:类和实例。而 Javascript 是基于原型的语言,它只有对象。新对象在初始化时以原型对象为模板获得属性。任何对象都可以指定其自身的属性,即可以创建时指定,也可以运行时指定。而且,任何对象都可以关联于并作为另一个对象的原型(prototype),从而允许后者共享前者的属性。要是这样的话,几乎没什么可比性。之所以说“Javascript 模拟‘类’”,完全是为了强调如何用 Javascript 进行像面向对象那样的编程,毕竟面向对象编程是这么多年的工程实践已经证明了的,除此之外,这种说法造成的歧义和误解实在是太大了,尤其是对初学者来说。

Javascript 是有设计缺陷,这多半是因为历史原因。就像其设计师 Brendan Eich 说的:“与其说我爱 Javascript,不如说我恨它。它是 C 语言和 Self 语言一 夜 情的产物。十八世纪英国文学家约翰逊博士说得好:'the part that is good is not original, and the part that is original is not good.(它的优秀之处并非原创,它的原创之处并不优秀)”。

  • 设计过于仓促。Javascript 的设计仅仅用了十天,设计师只是为了向公司交差,本人并不愿意这样设计。而且其设计初衷,只是为了解决一些简单的网页互动,并没有考虑复杂应用的情况。
  • 没有先例借鉴。Javascript 结合了函数式编程和面向对象编程的特点,是世界上唯一使用 Prototype 继承模型的语言。
  • 标准化过早。1995 年 5 月设计计方案定稿;同年 10 月解释器开发成功;同年 12 月推向市场。1996 年 8 月微软强势介入,推出自己的脚本语言 Jscript。因为市场竞争,网景公司申请国际化,于 1998 年标准化。相比之下,C 语言问世将近 20 年之后,国际标准才颁布。

在面向对象编程中,类(class)是对象(object)的模板。Javascript 语言虽然不支持“类”,但可以用一些变通的方法来模拟。本文总结了 Javascript 创建对象的三种方法,讨论每种方法的特点,并着重介绍了我眼中的最佳方法。

能用适当的方式来创建对象是 Javascript 模块化的前提。这样,我们就可以封装属性和方法,并进一步模拟出面向对象编程中已经相当成熟的做法,如继承,接口,甚至是设计模式。

构造函数创建对象


在 Javascript 中,构造方法其实就是一个普通的函数。当使用 new 操作符来作用这个函数时,它就可以被称为构造方法(构造函数,Constructor),使用 this 和 prototype。该方法是经典方法。

代码段一:

function Cat() {

    this.name = "大毛";

}

Cat.prototype.makeSound = function () {

    alert("喵~");

}

 

var cat1 = new Cat();

alert(cat1.name);

alert(cat1. makeSound());

该方法的缺点是,比较复杂,用到了 this 和 prototype,不便于编写和阅读,而且浪费内存。

Object.create 创建对象


为了解决“构造函数法”的缺点,更方便地生成对象,ECMAScript 5(目前通行第三版),提出了一个新的方法 Object.create(),调用这个方法来创建一个新对象。新对象的原型就是调用 create方法时传入的第一个参数。

代码段二:定义 Cat 对象,用 Object.create 创建,不用 new

var Cat = {

    name: "大毛",

    makeSound: function () { alert("喵~"); }

};

 

var cat1 = Object.create(Cat);

alert(cat1.name);

cat1.makeSound();

目前,各大浏览器的最新版本(包括 IE9)都部署了这个方法。如果遇到老式浏览器,可以用下面的代码自行部署。

代码段三:

  if (!Object.create) {

    Object.create = function (o) {

       function F() {}

      F.prototype = o;

      return new F();

    };

  }

这种方法比“构造函数”简单,但是不能实现私有属性和私有方法,实例对象之间也不能共享数据,对面向对象的模拟不够全面。

极简主义法


荷兰程序员 Gabor de Mooij 提出了一种比 Object.create 更好的方法,他称这种方法为“极简主义法(minimalist approach)”,也是我推荐的方法。

封装

这种方法不使用 this 和 prototype,代码部署起来非常简单。

它也是先用一个对象模拟“类”。在这个类里面,定义一个构造函数 createNew,用来生成对象实例。

代码段四:

var Cat = {

    createNew: function () {

        // some code here

    }

};

然后,在 createNew 函数,定义一个对象实例,把这个对象作为返回值。

代码段五:

var Cat = {

    createNew: function () {

        // some code here

        var cat = {};

        cat.name = "大毛";

        cat.makeSound = function () { alert("喵~"); };

        return cat;

    }

};

使用时,调用 createNew 方法,就可以得到对象实例。

代码段六:

var cat1 = Cat.createNew();

cat1.makeSound();

这种方法的好处在于,容易理解,结构清晰优雅,符合传统的"面向对象编程"的构造方式,因此可以方便地部署下面的特性。

继承

让一个类继承另一个类,实现起来很方便。只要在前者的createNew()方法中,调用后者的 createNew 方法即可。

先定义一个 Animal 类。

代码段七:

var Animal = {

    createNew: function () {

        var animal = {};

        animal.sleep = function () { alert("睡懒觉"); };

        return animal;

    }

};

然后,在 Cat 的 createNew 方法中,调用 Animal 的 createNew 方法。

代码段八:

var Cat = {

    createNew: function () {

        var cat = Animal.createNew();

        cat.name = "大毛";

        cat.makeSound = function () { alert("喵~"); };

        return cat;

    }

};

这样,Cat 对象就会继承 Animal。

代码段九:

var cat1 = Cat.createNew();

cat1.sleep();

私有属性和私有方法

只要不是定义在 cat 对象上的方法和属性,都是私有的。

代码段十:

var Cat = {

    createNew: function () {

        var cat = {};

        var sound = "喵~";

        cat.makeSound = function () { alert(sound); };

        return cat;

    }

};

上面,sound 是内部私有变量,外部无法访问,只有通过 cat 的公有方法 makeSound 来读取。

代码段十一:

  var cat1 = Cat.createNew();

  alert(cat1.sound); // undefined

数据共享

有时候,我们需要所有实例对象,能够读写同一项内部数据。这个时候,只要把这个内部数据,封装在类对象的里面、createNew()方法的外面即可。

代码段十二:

var Cat = {

    sound: "喵~",

    createNew: function () {

        var cat = {};

        cat.makeSound = function () { alert(Cat.sound); };

        cat.changeSound = function (x) { Cat.sound = x; };

        return cat;

    }

};

然后,生成两个实例对象:

var cat1 = Cat.createNew();

 

var cat2 = Cat.createNew();

 

cat1.makeSound(); // 喵~

这时,如果有一个实例对象,修改了共享的数据,另一个实例对象也会受到影响。

  cat2.changeSound("啦~");

  cat1.makeSound(); // 啦~

Javascript 创建对象的三种方法及比较【转载+整理】的更多相关文章

  1. JavaScript创建对象的三种方法

    在 JavaScript 中我们知道无法通过类来创建对象,那么如何创建对象呢? (1)通过“字面量”方式创建对象 将你的信息写到{ }中,并赋值给一个变量,此时这个变量就是一个对象,例: var ga ...

  2. C++创建对象的三种方法

    我自己以前经常弄混 A a(1); 栈内存中分配 A b = A(1); 栈内存中分配,和第一种无本质区别 A c = new A(1); 堆内存中分配 前两种在函数体执行完毕之后会被释放,第三种需要 ...

  3. JavaScript创建对象的4种方法

    我们有很多种方式去构造一个对象.可以构造一个对象字面量,也可以和new前缀连用去调用一个构造器函数,或者可以使用Object.create方法去构造一个已经存在的对象的新实例,还可以调用任意一个会返回 ...

  4. JavaScript | 创建对象的9种方法详解

    ————————————————————————————————————————————————————————— 创建对象 标准对象模式 "use strict"; // *** ...

  5. js创建对象的三种方法:文本标识法和构造器函数法和返回对象的函数

    文本标识法和定义变量差不多,像这样 var obj = {name:'HanMM','2':'Dali'}; 函数构造器法  先创建一个对象函数 function Obj() { this.addre ...

  6. sqlserver2012——SqlCommand创建对象的三种方法

    1.使用不带参构造函数 SqlCommand cmd = new SqlCommand(); cmd.Connection = SqlConnnection对象: cmd.CommandText=Sq ...

  7. JavaScript创建对象的七种方法

    一. 工厂模式 创建: function createPerson(name,behavior){ var p=new Object(); p.name=name; p.behavior=behavi ...

  8. js创建对象的三种方法

    1.使用对象初始化器:{} var person = {....} 2 var person=new object() function person(参数) { this.参数=... } var ...

  9. Javascript 创建对象的三种方式

    function createPerson(name, qq) //工厂方式 { //在工厂里创建个对象 var obj=new Object(); obj.name=name; obj.qq=qq; ...

随机推荐

  1. Delphi模拟最小化恢复关闭按纽

    https://yq.aliyun.com/wenji/96083 本文讲的是Delphi模拟最小化恢复关闭按纽, 我们做多文档应用程序开发时,如果在主From中指定mainMenu时,在主菜单上右角 ...

  2. delphi 启动停止windows服务 转

    http://blog.csdn.net/haiou327/article/details/6106233 不用cmd用delphi如何实现启动停止windows服务建议参考一下Delphi的Sckt ...

  3. SVN的管理方式和git的管理方式

    SVN是集中式的管理方式.大致流程如下: 1.从服务器上将整个项目代码检出到本地电脑硬盘中(一般来说,是从主分支上下载的代码).2.然后在svn服务器中建立新的开发分支,将硬盘中的代码提交到该开发分支 ...

  4. delete_failed_internal_error安装失败

    一.错误提示 二.最简单的解决方式 使用Android Studio (IDEA 2018.3.5)进行开发时,无论是模拟器还是,USB连接手机,都是无法安装和启动APP.后来发现是IDEA中,设置的 ...

  5. iPhone开发中从一个视图跳到另一个视图有三种方法:

    iPhone开发中从一个视图跳到另一个视图有三种方法:   1.self.view addSubView:view .self.window addSubView,需要注意的是,这个方法只是把页面加在 ...

  6. 学习 HTML+CSS 的书籍推荐

    1.<CSS那些事儿> 本书专注于CSS技巧实例的讲解,由浅入深地分析了CSS样式在布局时所需要理解的原理.绕开随处可见的基础知识.网络中能随意搜索到的hack技巧,侧重原理分析,拓展读者 ...

  7. 技术人生:Knowing when or where it’s appropriate to use a technique or tool is just as important as knowing how to use it.

    Knowing when or where it’s appropriate to use a technique or tool is just as important as knowing ho ...

  8. jquery插件--在input下输入密码时提示大写锁定键

    //密码大写输入提示 function capitalTip(id){ $('#' + id).after('<div class="capslock" id="c ...

  9. linux下生成https的crt和key证书

    今天在配置kibana权限设置时,kibana要求使用https链接. 于是总结了一下linux下openssl生成 签名的步骤: x509证书一般会用到三类文,key,csr,crt   Key 是 ...

  10. 监听Listview的滚动状态,是否滚动到了顶部或底部

    /** * @author:Jack Tony * @description : 监听listview的滑动状态,如果到了顶部就刷新数据 * @date :2015年2月9日 */ private c ...