大数据

JavaScript 继承

最近在看 NodeES6,感觉就是 JavaScipt 越来越像 PHP 了。好了,不扯了,直奔主题。这篇文章不是去讲 JS 原型链,而是简单聊聊 ES6 中的 Class,Node 中的 util.inherits(),ES5 中的 Object.create() 方法,及 ES6 中的 Object.setPrototypeOf() 方法。

  • ES6 中引入了 Class 的概念来模拟类。

    JavaScript classes introduced in ECMAScript 6 are syntactical sugar over JavaScript’s existing prototype-based inheritance. The class syntax is not introducing a new object-oriented inheritance model to JavaScript. JavaScript classes provide a much simpler and clearer syntax to create objects and deal with inheritance.

以上概念直接引用自 MDN,简单讲就是 ES6 中的 Class 只不过是基于 JS 原型继承层面上的语法糖。下面是 MDN 中的一个示例:

class  Rectangle { 
  constructor(height, width) { 
    this.height = height;
    this.width = width; 
  } 
  get area() { 
    return this.calcArea(); 
  } 
  calcArea() { 
    return this.height * this.width; 
    }
 }
const newRec = new Rectangle (10, 10);
console.log(newRec.area);

其中 class 里面的 constructor 方法会在该 class 每次实例化的时候自动执行。//和 PHP class 里面的 constructor 一样一样的。
在 class 中定义的 函数 可以省略 function 关键字。//我就稍微科普下 ES6 吧。
下面我用传统的 构造函数 来重写一下这个 Retangle 类:

function Rectangle(height, width){
  this.height = height;
  this.width = width;
}
Rectangle.prototype = {
  get area() {
    return this.calcArea();
  },
  calcArea: function(){
    return this.height * this.width;
  }
}
var newRec = new Rectangle(10,10);
console.log(newRec.area);

所以 ES6 中的 class 只不过是一种特殊的函数,如同 函数的定义有两种方式,函数声明 和 函数表达式,同样 class 的定义也有两种方式,类声明 // class foo{…} 和 类表达式 // var foo = class{…} // 继续科普 ES6,其中 函数声明和类声明的区别是,函数声明会致使声明提升, 而 类声明中则不会导致声明提升。
咦?这篇文章的题目不是 JS 继承吗?文章到现在好想一直TM在科普 ES6 的 class 啊!哈哈哈哈哈
class 之间实现继承的方式是通过使用 extends 关键字。//和 PHP一样一样的,包括 静态方法 static 的用法呀balabala…
e.g.

class Square extends Rectangle {
  constructor(length){
    super();
    this.height = length;
    this.width = length;
  }
}
console.log(Square.area);

Class 之间可以通过 extends 关键字实现继承,这比ES5的通过修改原型链实现继承,要清晰和方便很多。
ECMAScript 6 入门

下面是接着之前用构造函数方法写的 Rectangle 继续写传统构造函数实现的继承:

function Square(length){
  Rectangle.apply(this, [arguments[0], arguments[0]])
}
--------
*Method1//不推荐:
Square = new Rectangle(); 
var square = new Square(10);
console.log(Square.area);
--------
*Method2:
Square.prototype = Object.create(Rectangle.prototype)
var square = new Square(10);
console.log(Square.area);

So,

class Super {}
class Sub extends Super {}
//辣么,我们是不是可以把 ES6 中 class 的 extends 简单理解成?:
function Super(){};
function Sub(){};
Sub.prototype = Object.create(Super.prototype);
//答案是不是的
//因为 extends 实现的 class 继承 中:
console.log(Super.prototype.isPrototypeOf(Sub.prototype)) // true;
console.log(Super.isPrototypeOf(Sub)) //true;
//而 通过 Object.create() 方法实现的继承仅仅是
console.log(Super.prototype.isPrototypeOf(Sub.prototype)) //true
// class 继承的实现是://示例引用自阮老师的 《ECMAScript 6 入门》
class A {}
class B {} 
//B的实例继承A的实例
Object.setPrototypeOf(B.prototype, A.prototype);
//B继承A的静态属性
Object.setPrototypeOf(B, A);
// "Object.setPrototypeOf 方法的作用与 __proto__ 相同,用来设置一个对象的 prototype对象。它是ES6正式推荐的设置原型对象的方法。"
// Object.setPrototypeOf 改方法的等同于:
function (obj, proto) { 
  obj.__proto__ = proto; 
  return obj;
}
//MDN 中的 polyfill:
Object.setPrototypeOf = Object.setPrototypeOf || function(obj, proto) { obj.__proto__ = proto; return obj; }

2016.11.30 补充:最近在看 Babel,Babel 对 ES6 中 Class 的转换是这样的

function _inherits(subClass, superClass) { 
    if (typeof superClass !== "function" && superClass !== null) { 
        throw new TypeError("Super expression must either be null or a function, not " + typeof superClass); 
    } 
    subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, enumerable: false, writable: true, configurable: true } }); 
    if (superClass) Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass; 
}
  • Node 中使用的是 utili 这个模块里面的 inherits() 这个方法。

    util.inherits(constructor, superConstructor)
    Inherit the prototype methods from one constructor into another. The prototype of constructor will be set to a new object created from super Constructor.
    As an additional convenience, superConstructor will be accessible through the constructor.super_ property.

上代码:

"use strict";
const util = require('util');
function Super(){};
function Sub(){};
util.inherits(Sub, Super);
console.log(Super.prototype.isPrototypeOf(Sub.prototype)); // true;
//Buuuuuuuuuuuuut:
console.log(Super.isPrototypeOf(Sub)); // false;

Soooooooooooooo, node 文档中介绍该 util.inherits() 方法的第一句便是:

Note: usage of util.inherits() is discouraged. Please use the ES6 class and extends keywords to get language level inheritance support.


这几个概念到这里就算是草草介绍完了,总体来讲 ES6 中引入的 class ,虽然只是一种语法糖,但是它提供了一种更加简洁、清晰的语法来创建对象以及处理继承关系。