MDN’s new design is in Beta! A sneak peek: https://blog.mozilla.org/opendesign/mdns-new-design-beta/

Object-oriented JavaScript for beginners

翻译正在进行中。

学完基础后, 现在我们集中于面向对象的 JavaScript (OOJS) — 文本首先提出了面向对象编程(OOP) 理论的基本观点,然后探索如何通过构造函数模拟对象类,以及如何创建对象实例.

预备知识: 电脑基础知识, 了解 HTML 和 CSS, 熟悉 JavaScrpit 基础知识 (查看 First stepsBuilding blocks) 和 OOJS 基础 (查看 Introduction to objects).
目标: 掌握面向对象程序的基本理论, 这涉及到 JavaScript  的("万物皆对象"), 以及如何创建构造器和对象实例.

从零开始面向对象的程序设计

首先, 让我们给你定义一个面向对象的程序(OOP) 的极其简单的概念. 我们说的极其简单, 是因为 OOP 很快就变得非常复杂,如果现在给你一个完整的定义可能会让你产生迷惑而非帮助. 最基本的 OOP 思想就是我们想要在我们的程序中使用对象来表示现实世界模型, 并提供一个简单的方式来访问它的功能,否则很难甚至不能实现.

对象可以包含相关的数据和代码,这些代表现实世界模型的一些信息或者功能,或者它特有的一些行为. 对象数据(也经常称为函数) 可以有结构的存储 (官方术语为 封装) 在对象包内 (也可以给一个特殊的名字来表示,有时候也叫做命名空间), 可以使它容易组织和访问; 对象也通常用于存储数据,这样就可以很容易的在网络上传输.

定义一个对象模板

让我们来考虑一个简单的程序,它可以显示一个学校的学生和老师的信息.在这里我们不讨论任何程序语言,我们只讨论 OOP 思想.

首先,我们可以回到上一节拿到定义好属性和方法的Person对象。对于一个人(person)来说,我们能在他们身上获取到很多信息(他们的住址,身高,鞋码,基因图谱,护照信息,显著的性格特征等等),然而,我们仅仅需要他们的名字,年龄,性别,兴趣 这些信息,然后,我们会基于他们的这些信息写一个简短的介绍关于他们自己,在最后我们还需要教会他们打招呼。以上的方式被称为抽象-为了我们编程的目标而利用事物的一些重要特性去把复杂的事物简单化

在一些面向对象的语言中,我们用类(class)的概念去描述一个对象(你在下面就能看到JavaScript使用了一个完全不同的术语)-类并不完全是一个对象,它更像是一个定义对象特质的模板。

创造一个真正的对象

从上面我们创建的class中, 我们能够基于它创建出一些对象实例-一些拥有class中属性及方法的对象。基于我们的Person类,我们可以创建出许许多多的真实的人:

当一个对象实例需要从类中创建出来时,类的构造函数就会运行来创建这个实例。这种创建对象实例的过程我们称之为实例化-实例对象被类实例化。

Specialist classes

在这个例子里,我们不想要泛指的人,我们想要向老师和学生这样类型更为具体的人。在 OOP 里,我们可以创建基于其它类的新类,这些新的子类可以继承它们父类的数据和功能。比起复制来说这样能够使用父对象共有的功能。

这是非常有用的,老师和学生具有一些相同的特征比如姓名、性别、年龄,因此只需要定义这些特征一次就可以了。你可以在不同的类里分开定义这些相同的特征,这样该特征会有一个不同的命名空间。比如,一个学生的 greeting 可以是 "Yo, I'm [firstName]" (例子 Yo, I'm Sam),老师的可能会正式一些,比如"Hello, my name is [Prefix] [lastName]" (例子 Hello, My name is Mr Griffiths)。

Note: The fancy word for the ability of multiple object types to implement the same functionality is polymorphism. Just in case you were wondering.

现在可以从子类从创建对象实例。如:

下面我们来看看 OOP 理论如何应用到 JavaScript 实践中去的。

构建函数和对象实例

有些人认为 JavaScript 不是真正的面向对象的语言,比如它没有像许多面向对象的语言一样有用于创建class类的声明。JavaScript 用一种称为构建函数的特殊函数来定义对象和它们的特征。构建函数非常有用,因为很多情况下你不知道实际需要多少个对象(实例)。构建函数提供了创建你所需对象(实例)的有效方法,将对象的数据和特征函数按需联结至相应对象。

不像“经典”的面向对象的语言,从构建函数创建的新实例的特征并非全盘复制,而是通过一个叫做原形链的参考链链接过去的。(see Object prototypes),所以这并非真正的实例,严格的讲, Javascript 在对象间使用和其它语言不同的机制共享特征。

Note: “经典”的面向对象的语言并非坏事,就像上面提到的,OOP 可能很快就变得非常复杂,JavaScript 找到了在不变的特别复杂的情况下利用面向对象的优点的方法。

让我们来看看 JavaScript 如何通过构建函数对象实例来创建类。首先,请先复制一个新的前文提到的oojs.html

一个简单的例子

  1. 让我们看看如何通过一个普通的函数定义一个”人“。在你的文件中添加以下代码:
    function createNewPerson(name) {
      var obj = {};
      obj.name = name;
      obj.greeting = function () {
        alert('Hi! I\'m ' + this.name + '.');
      }
      return obj;
    }
  2. 你现在可以通过调用这个函数创建一个新的叫 salva 的人,在你浏览器的JavaScript console 试试 :
    var salva = createNewPerson('salva');
    salva.name;
    salva.greeting();
    上述代码运行良好,但是有点冗长;如果我们知道如何创建一个对象,就没有必要创建一个新的空对象并且返回它。幸好 JavaScript 通过构建函数提供了一个便捷的方法,方法如下:
  3. 将之前的代码用如下代码代替:
    function Person(name) {
      this.name = name;
      this.greeting = function() {
        alert('Hi! I\'m ' + this.name + '.');
      };
    }

这个构建函数是 JavaScript 版本的类。你会发现,它只定义了对象的属性和方法,除了没有明确创建一个对象和返回任何值和之外,它有了你期待的函数所拥有的全部功能。这里使用了this关键词,即无论是该对象的哪个实例被这个构建函数创建,它的 name 属性就是传递到构建函数形参 name 的值,它的 greeting() 方法中也将使用相同的传递到构建函数形参  name 的值。

Note: 一个构建函数通常是大写字母开头,这样便于区分构建函数和普通函数。

那如何调用构建函数创建新的实例呢?

  1. 将下面的代码加在你之前的代码下面:
    var person1 = new Person('Bob');
    var person2 = new Person('Sarah');
  2. 保存并刷新浏览器,在 console 里输入如下代码:
    person1.name
    person1.greeting()
    person2.name
    person2.greeting()

酷!你现在看到页面上有两个对象,每一个保存在不同的命名空间里,当你访问它们的属性和方法时,你需要使用 person1或者 person2 来调用它们。尽管它们有着相同的 name 属性和 greeting() method 它们是各自独立的,所以相互的功能不会冲突。注意它们使用的是自己的 name 值,这也是使用 this 关键字的原因,它们使用的从实参传入形参的自己的值,而不是其它的什么值。

Let's look at the constructor calls again:

var person1 = new Person('Bob');
var person2 = new Person('Sarah');

In each case, the new keyword is used to tell the browser we want to create a new object instance, followed by the function name with its required parameters contained in parentheses, and the result is stored in a variable — very similar to how a standard function is called. Each instance is created according to this definition:

function Person(name) {
  this.name = name;
  this.greeting = function() {
    alert('Hi! I\'m ' + this.name + '.');
  };
}

After the new objects have been created, the person1 and person2 variables effectively contain the following objects:

{
  name : 'Bob',
  greeting : function() {
    alert('Hi! I\'m ' + this.name + '.');
  }
}

{
  name : 'Sarah',
  greeting : function() {
    alert('Hi! I\'m ' + this.name + '.');
  }
}

We say effectively because in actual fact the functionality is still defined in the class, rather than in the object instances, in contrast to the object literal we looked at earlier.

Creating our finished constructor

The example we looked at above was only a simple example to get us started. Let's now get on and create our final Person() constructor function.

  1. Remove the code you inserted so far, and add in this replacement constructor — this is exactly the same as the simple example in principle, with just a bit more complexity:
    function Person(first, last, age, gender, interests) {
      this.name = {
        first,
        last
      };
      this.age = age;
      this.gender = gender;
      this.interests = interests;
      this.bio = function() {
        alert(this.name.first + ' ' + this.name.last + ' is ' + this.age + ' years old. He likes ' + this.interests[0] + ' and ' + this.interests[1] + '.');
      };
      this.greeting = function() {
        alert('Hi! I\'m ' + this.name.first + '.');
      };
    };
  2. Now add in the following line below it, to create an object instance from it:
    var person1 = new Person('Bob', 'Smith', 32, 'male', ['music', 'skiing']);

You'll now see that you can access the properties and methods just like we did with the first object we defined:

person1['age']
person1.interests[1]
person1.bio()
// etc.

Note: If you are having trouble getting this to work, try comparing your code against our version — see oojs-class-finished.html (also see it running live).

Further exercises

To start with, try adding a couple more object creation lines of your own, and try getting and setting the members of the resulting object instances.

In addition, there are a couple of problems with our bio() method — the output always includes the pronoun "He", even if your person is female, or some other preferred gender classification. And the bio will only include two interests, even if more are listed in the interests array. Can you work out how to fix this in the class definition (constructor)? You can put any code you like inside a constructor (you'll probably need a few conditionals and a loop). Think about how the sentences should be structured differently depending on gender, and depending on whether the number of listed interests is 1, 2, or more than 2.

Note: If you get stuck, we have provided an answer inside our GitHub repo (see it live) — try writing it yourself first though!

Other ways to create object instances

So far we've seen two different ways to create an object instance — declaring an object literal, and using a constructor function (see above).

These make sense, but there are other ways — we want to make you familiar with these in case you come across them in your travels around the Web.

The Object() constructor

First of all, you can use the Object() constructor to create a new object. Yes, even generic objects have a constructor, which generates an empty object.

  1. Try entering this into your browser's JavaScript console:
    var person1 = new Object();
  2. This stores an empty object in the person1 variable. You can then add properties and methods to this object using dot or bracket notation as desired; try these examples:
    person1.name = 'Chris';
    person1['age'] = 38;
    person1.greeting = function() {
      alert('Hi! I\'m ' + this.name + '.');
    }
  3. You can also pass an object literal to the Object() constructor as a parameter, to prefill it with properties/methods. Try this:
    var person1 = new Object({
      name : 'Chris',
      age : 38,
      greeting : function() {
        alert('Hi! I\'m ' + this.name + '.');
      }
    });

Using the create() method

JavaScript has a built-in method called create(), which allows you to create a new object instance based on an existing object.

  1. Try this in your JavaScript console:
    var person2 = Object.create(person1);
  2. Now try these:
    person2.name
    person2.greeting()

You'll see that person2 has been created based on person1 — it has the same properties and method available to it. This is pretty useful, as it allows you to create new object instances without needing to define a constructor. The downside is that create() is not supported by browsers as far back as constructors (IE9, as opposed to IE8 or even before), plus some think constructors give your code more order — you can create your constructors in one place, then create instances as needed, and it is clear where they came from.

However, if you are not too worried about supporting really old browsers, and you only need a couple of copies of an object, creating a constructor could be overkill for your code. It is up to you what you prefer. Some people simply find create() easier to understand and use.

We'll explore the effects of create() in more detail later on.

Summary

This article has provided a simplified view of object-oriented theory — this isn't the whole story, but it gives you an idea of what we are dealing with here. In addition, we have started to look at how JavaScript relates and how it differs from "classic OOP", how to implement classes in JavaScript using constructor functions, and different ways of generating object instances.

In the next article we'll explore JavaScript object prototypes.

文档标签和贡献者

 此页面的贡献者: Viki-Zhang, Leivy, breakair, zhangyangcisco, cylmemory, keyulin
 最后编辑者: Viki-Zhang,