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 行!

Javascript 创建对象的三种方法及比较【转载+整理】-LMLPHP

图 1 Gmail 代码段

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

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

在面向对象编程中,类(class)是对象(object)的模板。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());

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(); // 啦~

04-16 21:30
查看更多