JavaScript不区分类和实例的概念,而是通过原型(prototype)来实现面向对象编程。

  1. var Student = {
  2. name: 'Robot',
  3. height: 1.2,
  4. run: function () {
  5. console.log(this.name + ' is running...');
  6. }
  7. };
  8. var xiaoming = {
  9. name: '小明'
  10. };
  11. xiaoming.__proto__ = Student;
  12. xiaoming.name; // '小明'
  13. xiaoming.run(); // 小明 is running...

JavaScript的原型链和Java的Class区别就在,它没有“Class”的概念,所有对象都是实例,所谓继承关系不过是把一个对象的原型指向另一个对象而已。
Object.create()方法可以传入一个原型对象,并创建一个基于该原型的新对象,但是新对象什么属性都没有,因此,我们可以编写一个函数来创建xiaoming

  1. // 原型对象:
  2. var Student = {
  3. name: 'Robot',
  4. height: 1.2,
  5. run: function () {
  6. console.log(this.name + ' is running...');
  7. }
  8. };
  9. function createStudent(name) {
  10. // 基于Student原型创建一个新对象:
  11. var s = Object.create(Student);
  12. // 初始化新对象:
  13. s.name = name;
  14. return s;
  15. }
  16. var xiaoming = createStudent('小明');
  17. xiaoming.run(); // 小明 is running...
  18. xiaoming.__proto__ === Student; // true

创建对象

JavaScript对每个创建的对象都会设置一个原型,指向它的原型对象。
当我们用obj.xxx访问一个对象的属性时,JavaScript引擎先在当前对象上查找该属性,如果没有找到,就到其原型对象上找,如果还没有找到,就一直上溯到Object.prototype对象,最后,如果还没有找到,就只能返回undefined

构造函数

除了直接用{ ... }创建一个对象外,JavaScript还可以用一种构造函数的方法来创建对象。它的用法是,先定义一个构造函数:

  1. function Student(name) {
  2. this.name = name;
  3. this.hello = function () {
  4. alert('Hello, ' + this.name + '!');
  5. }
  6. }
  7. //在JavaScript中,可以用关键字new来调用这个函数,并返回一个对象
  8. var xiaoming = new Student('小明');
  9. xiaoming.name; // '小明'
  10. xiaoming.hello(); // Hello, 小明!

new Student()创建的对象还从原型上获得了一个constructor属性,它指向函数Student本身:

  1. xiaoming.constructor === Student.prototype.constructor; // true
  2. Student.prototype.constructor === Student; // true
  3. Object.getPrototypeOf(xiaoming) === Student.prototype; // true
  4. xiaoming instanceof Student; // true

image.png

对new的封装

为了避免忘记写new,我们还可以编写一个createStudent()函数,在内部封装所有的new操作。一个常用的编程模式像这样:

  1. function Student(props) {
  2. this.name = props.name || '匿名'; // 默认值为'匿名'
  3. this.grade = props.grade || 1; // 默认值为1
  4. }
  5. Student.prototype.hello = function () {
  6. alert('Hello, ' + this.name + '!');
  7. };
  8. function createStudent(props) {
  9. return new Student(props || {})
  10. }

这个createStudent()函数有几个巨大的优点:一是不需要new来调用,二是参数非常灵活,可以不传,也可以这么传:

  1. var xiaoming = createStudent({
  2. name: '小明'
  3. });
  4. xiaoming.grade; // 1

原型继承

class继承

新的关键字class从ES6开始正式被引入到JavaScript中。class的目的就是让定义类更简单。
我们先回顾用函数实现Student的方法:

  1. function Student(name) {
  2. this.name = name;
  3. }
  4. Student.prototype.hello = function () {
  5. alert('Hello, ' + this.name + '!');
  6. }

如果用新的class关键字来编写Student,可以这样写:

  1. class Student {
  2. constructor(name) {
  3. this.name = name;
  4. }
  5. hello() {
  6. alert('Hello, ' + this.name + '!');
  7. }
  8. }

最后,创建一个Student对象代码和前面章节完全一样:

  1. var xiaoming = new Student('小明');
  2. xiaoming.hello();

class定义对象的另一个巨大的好处是继承更方便了。现在,原型继承的中间对象,原型对象的构造函数等等都不需要考虑了,直接通过extends来实现:

  1. class PrimaryStudent extends Student {
  2. constructor(name, grade) {
  3. super(name); // 记得用super调用父类的构造方法!
  4. this.grade = grade;
  5. }
  6. myGrade() {
  7. alert('I am at grade ' + this.grade);
  8. }
  9. }