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

In this case we don't want generic people — we want teachers and students, which are both more specific types of people. In OOP, we can create new classes based on other classes — these new child classes can be made to inherit the data and code features of their parent class, so you can reuse functionality common to all the object types rather than having to duplicate it.  Where functionality differs between classes, you can define specialized features directly on them as needed.

This is really useful — teachers and students share many common features such as name, gender, and age, so it is convenient to only have to define those features once. You can also define the same feature separately in different classes, as each definition of that feature will be in a different namespace. For example, a student's greeting might be of the form "Yo, I'm [firstName]" (e.g Yo, I'm Sam), whereas a teacher might use something more formal, such as "Hello, my name is [Prefix] [lastName]" (e.g 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.

You can now create object instances from your child classes. For example:

In the rest of the article we'll start to look at how OOP theory can be put into practice in JavaScript.

Constructors and object instances

Some people argue that JavaScript is not a true object-oriented language — for example it doesn't have a class statement for creating classes like many OO languages. JavaScript instead uses special functions called constructor functions to define objects and their features. They are useful because you'll often come across situations in which you don't know how many objects you will be creating; constructors provide the means to create as many objects as you need in an effective way, attaching data and functions to them as required.

When a new object instance is created from a constructor function, the functionality is not all copied over to the new object like "classic" OO languages — instead the functionality is linked to via a reference chain called a prototype chain (see Object prototypes). So this is not true instantiation, strictly speaking — JavaScript uses a different mechanism to share functionality between objects.

Note: Not being "classic OOP" is not necessarily a bad thing; as mentioned above, OOP can get very complex very quickly, and JavaScript has some nice ways to take advantage of OO features without having to get too deep into it.

Let's explore creating classes via constructors and creating object instances from them in JavaScript. First of all, we'd like you to make a new local copy of the oojs.html file we saw in our first Objects article.

A simple example

  1. Let's start by looking at how you could define a person with a normal function. Add this function below the existing code:
    function createNewPerson(name) {
      var obj = {};
      obj.name = name;
      obj.greeting = function () {
        alert('Hi! I\'m ' + this.name + '.');
      }
      return obj;
    }
  2. You can now create a new person by calling this function — try the following lines in your browser's JavaScript console:
    var salva = createNewPerson('salva');
    salva.name;
    salva.greeting();
    This works well enough, but it is a bit longwinded; if we know we want to create an object, why do we need to explicitly create a new empty object and return it? Fortunately JavaScript provides us with a handy shortcut, in the form of constructor functions — let's make one now!
  3. Replace your previous function with the following:
    function Person(name) {
      this.name = name;
      this.greeting = function() {
        alert('Hi! I\'m ' + this.name + '.');
      };
    }

The constructor function is JavaScript's version of a class. You'll notice that it has all the features you'd expect in a function, although it doesn't return anything or explicitly create an object — it basically just defines properties and methods. You'll see the this keyword being used here as well — it is basically saying that whenever one of the these object instances is created, the object's name property will be equal to the name value passed to the constructor call, and the greeting() method will use the name value passed to the constructor call too.

Note: A constructor function name usually starts with a capital letter — this convention is used to make constructor functions easier to recognize in code.

So how do we call a constructor to create some objects?

  1. Add the following lines below your previous code addition:
    var person1 = new Person('Bob');
    var person2 = new Person('Sarah');
  2. Save your code and reload it in the browser, and try entering the following lines into your text input:
    person1.name
    person1.greeting()
    person2.name
    person2.greeting()

Cool! You'll now see that we have two new objects on the page, each of which is stored under a different namespace — when you access their properties and methods, you have to start calls with person1 or person2; they are neatly packaged away so they won't clash with other functionality. They do however have the same name property and greeting() method available. Note that they are using their own name value that was assigned to them when they were created; this is one reason why it is very important to use this, so they will use their own values, and not some other value.

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.

文档标签和贡献者

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