class 金沙js娱乐场官方网站就像是特殊的函数,定义并生成新对象

就像你能够定义的函数表达式和函数声明一样,类语法有两个组成部分,ES6提供了更接近传统语言的写法,定义并生成新对象,class 就像是特殊的函数,(class,大概从几个方面来讲解ES6 class类和传统的构造函数的区别,定义并生成新对象,ES6提供了更接近传统语言的写法

生成实例对象的传统方法是通过构造函数

// 先定义一个函数,强行叫它构造函数function Point { this.x = x; this.y = y;}// 构造函数的方法都定义在构造函数的原型上Point.prototype.toString = function () { return '(' + this.x + ', ' + this.y + ')';};// 使用new的方式得到一个实例对象var p = new Point;

类的prototype属性和__proto__属性

大多数浏览器的ES5实现之中,每一个对象都有__proto__属性,指向对应的构造函数的prototype属性。Class作为构造函数的语法糖,同时有prototype属性和__proto__属性,因此同时存在两条继承链。

(1)子类的__proto__属性,表示构造函数的继承,总是指向父类。

(2)子类prototype属性的__proto__属性,表示方法的继承,总是指向父类的prototype属性。

class A {
}

class B extends A {
}

B.__proto__ === A // true
B.prototype.__proto__ === A.prototype // true

上面代码中,子类B__proto__属性指向父类A,子类Bprototype属性的__proto__属性指向父类Aprototype属性。

这样的结果是因为,类的继承是按照下面的模式实现的。

class A {
}

class B {
}

// B的实例继承A的实例
Object.setPrototypeOf(B.prototype, A.prototype);

// B继承A的静态属性
Object.setPrototypeOf(B, A);

导语

class只是语法糖,并没有为js引入一种新的对象继承模式,之前通过原型链一样可以实现class的功能;

//定义类class Point {  constructor(x, y) {    this.x = x;    this.y = y;  }  toString() {    return '(' + this.x + ', ' + this.y + ')';  }}

ES6 class类知识点梳理

大概从几个方面来讲解ES6 class类和传统的构造函数的区别。

  1. 必须要有constructor()方法 ,默认返回this(即构造函数的实例)
  2. 除非this指定,所有的方法都在类的原型链上(Point.prototype),并且所有的方法都是不可枚举的

class Point {
    constructor(a, b) {
        this.a =a;
        this.b = b;
        return Object.create(null)
    }
    toString(){
        console.log(`a: ${this,a} ====== b: ${this.b}  `)
    }
}
var a = new Point() 
a.hasOwnProperty('a') // ture
a.hasOwnProperty('b') // ture
a.hasOwnProperty('constructor') // false
Object.keys(a)  // []
Object.keys(Point.prototype)  // []
Object.getOwnPropertyNames(Point.prototype)  //["constructor", "toString"]
  1. 可以通过改变constructor() 方法的return值,来改变实例的值。

class Point {
    constructor(a, b) {
        this.a =a;
        this.b = b;
        return Object.create(null)
    }
    toString(){
        console.log(`a: ${this,a} ====== b: ${this.b}  `)
    }
}
var a = new Point() 
a // {}
  1. 所有和实例共享同一个原型链,这个和es5一致

let a = new Point();
let b = new Point();
a.__proto__ === b.__proto__
  1. class表达式写法以及name属性(紧跟着class关键值后面的类名)

    和函数一样,class也有表达式的写法,只能用于内部调用。如果用于外部就会报错。

// 这里类名就是myClass,而不是me,me只是给内部调用,如果不需要可以直接省略
const myClass = class me {
  getName() {
    return me.name
  }
}
let a = new myClass() 
a.getName()  // me
  1. 不存在变量提升,可以利用表达式写法,立刻生成实例。
  2. class类对属性值的get和set关键值,拦截属性的获取。所有的get和set都是定义在属性的descriptor中的

class hh {
  constructor() {}
  get prop() {
    return 'getter'
  }
  set prop(val) {
    console.log(`setter ${val}`)
  }
}

let inst = new hh()
inst.prop = 'fs'  //  "setter fs"
inst.prop    //  getter
  1. class的静态方法:
    在方法前面加上static,只能被类本身调用,不可以被实例调用。静态方法可以被子类继承。

  2. new.target属性的使用

    • new targe在函数内部等于构造函数本身,用于类中,指向类本身
    • 可以用于创建只能继承不能实例的类
  3. 用过extends关键字实现继承,通过super返回父类的实例,从而在子类中使用this。

  4. static 静态方法也会被子类继承。

  5. 通过Object.getOwnPrototypeOf(子类)
    获取子类的父类,由于子类的prototype被重写,等于父类的原型。

    class A {
      static hello() {
        console.log('hello world');
      }
    }
    
    class B extends A {
    
    }
    // 通过下面这句,extends继承相当于是  B.__proto__ === A ,所以B的原型链上面可以找到A,实现了继承.正常情况下,一个对象的__proto__会指向构造函数的原型  
    A.__proto__ === Function.prototype  // 函数都是Function构造函数创建
    Object.getPrototypeOf(B)  === A  
    
    let obj = new B()  // 现在实例化B
    // obj.__proto__  => B.prototype  
    // => B.prototype.__proto__ => A.prototype
    // A.prototype.__proto__ => Object.prototype
    
  6. super关键字

    • 作为函数 (只存在于子类的构造函数中)
    • 作为对象 (普通方法中作为 父类的原型) (静态方法中指向
    • 在通过super调用父类的方法时,this指向的是子类
    • super绑定了子类的this
  7. 类的 prototype__proto__属性

    • 子类的__proto__指向父类

    • 子类prototype属性的__proto__指向父类的prototype属性

      B.__proto === A;
      B.prototype.__proto__ === A.prototype;
      
  8. extends关键字多种继承

    • 对象
    • 不存在继承
    • null
  9. 实例__proto__属性

    • 实例的__proto__

类的prototype属性和__proto__属性

大多数浏览器的ES5实现之中,每一个对象都有__proto__属性,指向对应的构造函数的prototype属性。Class作为构造函数的语法糖,同时有prototype属性和__proto__属性,因此同时存在两条继承链。

(1)子类的__proto__属性,表示构造函数的继承,总是指向父类。

(2)子类prototype属性的__proto__属性,表示方法的继承,总是指向父类的prototype属性。

class A {
}

class B extends A {
}

B.__proto__ === A // true
B.prototype.__proto__ === A.prototype // true

上面代码中,子类B__proto__属性指向父类A,子类Bprototype属性的__proto__属性指向父类Aprototype属性。

这样的结果是因为,类的继承是按照下面的模式实现的。

class A {
}

class B {
}

// B的实例继承A的实例
Object.setPrototypeOf(B.prototype, A.prototype);
const b = new B();

// B的实例继承A的静态属性
Object.setPrototypeOf(B, A);
const b = new B();

《对象的扩展》一章给出过Object.setPrototypeOf方法的实现。

Object.setPrototypeOf = function (obj, proto) {
  obj.__proto__ = proto;
  return obj;
}

因此,就得到了上面的结果。

Object.setPrototypeOf(B.prototype, A.prototype);
// 等同于
B.prototype.__proto__ = A.prototype;

Object.setPrototypeOf(B, A);
// 等同于
B.__proto__ = A;

这两条继承链,可以这样理解:作为一个对象,子类(B)的原型(__proto__属性)是父类(A);作为一个构造函数,子类(B)的原型(prototype属性)是父类的实例。

Object.create(A.prototype);
// 等同于
B.prototype.__proto__ = A.prototype;

参考文章

  • class的基本语法-阮一峰
  • class的继承-阮一峰
  • 类-MDN

基本用法

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

class ColorPoint extends Point {}

上面代码定义了一个ColorPoint类,该类通过extends关键字,继承了Point类的所有属性和方法。但是由于没有部署任何代码,所以这两个类完全一样,等于复制了一个Point类。下面,我们在ColorPoint内部加上代码。

class ColorPoint extends Point {
  constructor(x, y, color) {
    super(x, y); // 调用父类的constructor(x, y)
    this.color = color;
  }

  toString() {
    return this.color + ' ' + super.toString(); // 调用父类的toString()
  }
}

上面代码中,constructor方法和toString方法之中,都出现了super关键字,它在这里表示父类的构造函数,用来新建父类的this对象。

子类必须在constructor方法中调用super方法,否则新建实例时会报错。这是因为子类没有自己的this对象,而是继承父类的this对象,然后对其进行加工。如果不调用super方法,子类就得不到this对象。

class Point { /* ... */ }

class ColorPoint extends Point {
  constructor() {
  }
}

let cp = new ColorPoint(); // ReferenceError

上面代码中,ColorPoint继承了父类Point,但是它的构造函数没有调用super方法,导致新建实例时报错

两条继承链

一个继承语句同时存在两条继承链:一条实现属性继承,一条实现方法继承.

class A extends B {}A.__proto__ === B;  //继承属性A.prototype.__proto__ === B.prototype;  //继承方法

ES6的子类的__proto__是父类,子类的原型的__proto__是父类的原型

第二条继承链理解起来没有什么问题,es6 本身就是对es5
混合模式继承的封装,在原型继承上,es6使用的是 

子类.prototype = Object.create (父类.prototype) // 相当于 new 父类

子类的原型是父类的实例(暂时这样理解,其实子类并不能继承父类的属性,只能继承方法),所以子类.prototype(父类的实例)指向父类的prototype。

但是第一个继承链就不好理解了,在ES5中
子类.__proto__是指向Function.prototype的,因为每一个构造函数其实都是Function这个对象构造的。在ES6的继承中,有这样一句话:

 if (superClass) Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass;

es6 的 extends
把子类的__proto__指向父类可以实现属性的继承,在ES5中在没有用借用继承的时候由于父类属性被子类原型继承,所有的子类实例实际上都是同一个属性引用。

可以这么说,在ES5继承和构造实例,ES6构造实例的时候可以理解__proto__指向构造函数的原型的,但是在ES6继承中,__proto__指继承自哪个类或原型。也就是说,两条继承链只存在于两个类之间的关系,实例与构造函数之间的关系,还是es5的模式;

var p1 = new Point(2,3);var p2 = new Point(3,2);p1.__proto__ === p2.__proto__

这也意味着,可以通过实例的__proto__属性为Class添加方法。

var p1 = new Point(2,3);var p2 = new Point(3,2);p1.__proto__.printName = function () { return 'Oops' };p1.printName() // "Oops"p2.printName() // "Oops"var p3 = new Point(4,2);p3.printName() // "Oops"

但是我们不推荐这样做

Class的继承

constructor 方法

  1. constructor方法是类的默认方法,通过new命令生成对象实例时,自动调用该方法。一个类必须有constructor方法,如果没有显式定义,一个空的constructor方法会被默认添加。

class Point {}// 等同于class Point { constructor() {}}
  1. 一个类中只能有一个constructor函数,定义多个会报错。
  2. constructor默认返回该对象实例(即this),也可以指定返回另外一个对象。

class Foo { constructor() { return Object.create; }}new Foo() instanceof Foo// false
  1. 在一个构造方法中可以使用super关键字来调用一个父类的构造方法。

class A {}class B extends A { constructor() { super(); }}

类的实例对象

生成类的实例对象的写法,与ES5完全一样,也是使用new命令。如果忘记加上new,像函数那样调用Class,将会报错。

// 报错
var point = Point(2, 3);

// 正确
var point = new Point(2, 3);

与ES5一样,实例的属性除非显式定义在其本身(即定义在this对象上),否则都是定义在原型上(即定义在class上)。

与ES5一样,类的所有实例共享一个原型对象。

这也意味着,可以通过实例的__proto__属性为Class添加方法。

var p1 = new Point(2,3);
var p2 = new Point(3,2);

p1.__proto__.printName = function () { return 'Oops' };

p1.printName() // "Oops"
p2.printName() // "Oops"

var p3 = new Point(4,2);
p3.printName() // "Oops"

上面代码在p1的原型上添加了一个printName方法,由于p1的原型就是p2的原型,因此p2也可以调用这个方法。而且,此后新建的实例p3也可以调用这个方法。这意味着,使用实例的__proto__属性改写原型,必须相当谨慎,不推荐使用,因为这会改变Class的原始定义,影响到所有实例。

定义class 

class 就像是特殊的函数,和创建函数一样,有类声明(class
declarations),和类表达式( class expressions )两种创建类的方式:

new.target属性

new是从构造函数生成实例的命令。ES6为new命令引入了一个new.target属性,(在构造函数中)返回new命令作用于的那个构造函数。如果构造函数不是通过new命令调用的,new.target会返回undefined,因此这个属性可以用来确定构造函数是怎么调用的。

function Person(name) {
  if (new.target !== undefined) {
    this.name = name;
  } else {
    throw new Error('必须使用new生成实例');
  }
}

// 另一种写法
function Person(name) {
  if (new.target === Person) {
    this.name = name;
  } else {
    throw new Error('必须使用new生成实例');
  }
}

var person = new Person('张三'); // 正确
var notAPerson = Person.call(person, '张三');  // 报错

上面代码确保构造函数只能通过new命令调用。

Class内部调用new.target,返回当前Class。

class Rectangle {
  constructor(length, width) {
    console.log(new.target === Rectangle);
    this.length = length;
    this.width = width;
  }
}

var obj = new Rectangle(3, 4); // 输出 true

需要注意的是,子类继承父类时,new.target会返回子类。

class Rectangle {
  constructor(length, width) {
    console.log(new.target === Rectangle);
    // ...
  }
}

class Square extends Rectangle {
  constructor(length) {
    super(length, length);
  }
}

var obj = new Square(3); // 输出 false

上面代码中,new.target会返回子类。

利用这个特点,可以写出不能独立使用、必须继承后才能使用的类。

class Shape {
  constructor() {
    if (new.target === Shape) {
      throw new Error('本类不能实例化');
    }
  }
}

class Rectangle extends Shape {
  constructor(length, width) {
    super();
    // ...
  }
}

var x = new Shape();  // 报错
var y = new Rectangle(3, 4);  // 正确

上面代码中,Shape类不能被实例化,只能用于继承。

注意,在函数外部,使用new.target会报错。

定义类

ES6
引入了class,让javascript的面向对象编程变得更加容易清晰和容易理解。类只是基于原型的面向对象模式的语法糖。

类实际上是个“特殊的函数”,就像你能够定义的函数表达式和函数声明一样,类语法有两个组成部分:类声明类表达式

  1. 类声明

类声明是定义类的一种方式,使用关键字class,后面跟上类名,然后就是一对大括号。把类需要定义的方法放在大括号中。

//定义类class Point { constructor { // 定义构造方法 // this关键字代表实例对象 this.x = x; this.y = y; } // 定义在类中的方法不需要添加 function toString() { return '(' + this.x + ', ' + this.y + ')'; }}//定义类,可以省略 constructorclass P { toString() { return '(' + this.x + ', ' + this.y + ')'; }}
  1. 类表达式

类表达式是定义类的另一种形式,类似于函数表达式,把一个函数作为值赋给变量。可以把定义的类赋值给一个变量,这时候变量就为类名。class关键字之后的类名可有可无,如果存在,则只能在类内部使用。

  • 定义类 class后面有类名:

const People = class StdInfo { constructor(){ console.log; //可以打印出值,是一个函数 }}new People();new StdInfo(); //报错,StdInfo is not defined;
  • 定义类 class后面没有类名:

const People = class { constructor(){ }}new People();
  • 立即执行的类:

let person = new class { constructor { this.name = name; } sayName() { console.log(this.name); }};person.sayName(); // "张三"

原生构造函数的继承

原生构造函数是指语言内置的构造函数,通常用来生成数据结构。ECMAScript的原生构造函数大致有下面这些。

  • Boolean()
  • Number()
  • String()
  • Array()
  • Date()
  • Function()
  • RegExp()
  • Error()
  • Object()

以前,这些原生构造函数是无法继承的,ES6允许继承原生构造函数定义子类,因为ES6是先新建父类的实例对象this,然后再用子类的构造函数修饰this,使得父类的所有行为都可以继承。下面是一个继承Array的例子。

class MyArray extends Array {
  constructor(...args) {
    super(...args);
  }
}

var arr = new MyArray();
arr[0] = 12;
arr.length // 1

arr.length = 0;
arr[0] // undefined

上面代码定义了一个MyArray类,继承了Array构造函数,因此就可以从MyArray生成数组的实例。这意味着,ES6可以自定义原生数据结构(比如Array、String等)的子类,这是ES5无法做到的。

上面这个例子也说明,extends关键字不仅可以用来继承类,还可以用来继承原生的构造函数。因此可以在原生数据结构的基础上,定义自己的数据结构。

构造器(constructor方法)

一个类只能拥有一个名为constructor的方法(否则会报错),一个类的 constructor
方法只有在实例化的时候被调用。

如果没有显式定义constructor方法,这个方法会被默认添加,即,不管有没有显示定义,任何一个类都有constructor方法。

子类必须在constructor方法中调用super方法,否则新建实例时会报错。因为子类没有自己的this对象,而是继承父类的this对象,然后对其进行加工,如果不调用super方法,子类就得不到this对象。

class Point {}class ColorPoint extends Point {    constructor() {}}let cp = new ColorPoint(); // ReferenceError

上面代码中,ColorPoint继承了父类Point,但是它的构造函数没有调用super方法,导致新建实例时报错。

Class的静态属性和实例属性

静态属性指的是Class本身的属性,即Class.propname,而不是定义在实例对象(this)上的属性。

class Foo {
}

Foo.prop = 1;
Foo.prop // 1

上面的写法为Foo类定义了一个静态属性prop

目前,只有这种写法可行,因为ES6明确规定,Class内部只有静态方法,没有静态属性。

// 以下两种写法都无效
class Foo {
  // 写法一
  prop: 2

  // 写法二
  static prop: 2
}

Foo.prop // undefined

ES7有一个静态属性的提案,目前Babel转码器支持。

这个提案对实例属性和静态属性,都规定了新的写法。

(1)类的实例属性

类的实例属性可以用等式,写入类的定义之中。

class MyClass {
  myProp = 42;

  constructor() {
    console.log(this.myProp); // 42
  }
}

上面代码中,myProp就是MyClass的实例属性。在MyClass的实例上,可以读取这个属性。

以前,我们定义实例属性,只能写在类的constructor方法里面。

class ReactCounter extends React.Component {
  constructor(props) {
    super(props);
    this.state = {
      count: 0
    };
  }
}

上面代码中,构造方法constructor里面,定义了this.state属性。

有了新的写法以后,可以不在constructor方法里面定义。

class ReactCounter extends React.Component {
  state = {
    count: 0
  };
}

这种写法比以前更清晰。

为了可读性的目的,对于那些在constructor里面已经定义的实例属性,新写法允许直接列出。

class ReactCounter extends React.Component {
  constructor(props) {
    super(props);
    this.state = {
      count: 0
    };
  }
  state;
}

(2)类的静态属性

类的静态属性只要在上面的实例属性写法前面,加上static关键字就可以了。

class MyClass {
  static myStaticProp = 42;

  constructor() {
    console.log(MyClass.myProp); // 42
  }
}

同样的,这个新写法大大方便了静态属性的表达。

// 老写法
class Foo {
}
Foo.prop = 1;

// 新写法
class Foo {
  static prop = 1;
}

上面代码中,老写法的静态属性定义在类的外部。整个类生成以后,再生成静态属性。这样让人很容易忽略这个静态属性,也不符合相关代码应该放在一起的代码组织原则。另外,新写法是显式声明(declarative),而不是赋值处理,语义更好。

new.target 属性

  1. new是从构造函数生成实例对象的命令。在构造函数之中,new.target属性返回new命令作用于的那个构造函数。如果构造函数不是通过new命令调用的,new.target会返回undefined,因此这个属性可以用来确定构造函数是怎么调用的。

function Person { if (new.target !== undefined) { this.name = name; } else { throw new Error('必须使用 new 命令生成实例'); }}// 另一种写法function Person { if (new.target === Person) { this.name = name; } else { throw new Error('必须使用 new 命令生成实例'); }}var person = new Person; // 正确var notAPerson = Person.call(person, '张三'); // 报错
  1. Class 内部调用new.target,返回当前 Class

class Rectangle { constructor(length, width) { console.log(new.target === Rectangle); this.length = length; this.width = width; }}var obj = new Rectangle; // 输出 true
  1. 子类继承父类时,new.target会返回子类。

class Rectangle { constructor(length, width) { console.log(new.target === Rectangle); // ... }}class Square extends Rectangle { constructor { super(length, length); }}var obj = new Square; // 输出 false

constructor方法

constructor方法是类的默认方法,通过new命令生成对象实例时,自动调用该方法。一个类必须有constructor方法,如果没有显式定义,一个空的constructor方法会被默认添加。

constructor方法默认返回实例对象(即this),完全可以指定返回另外一个对象。

class Foo {
  constructor() {
    return Object.create(null);
  }
}

new Foo() instanceof Foo
// false

上面代码中,constructor函数返回一个全新的对象,结果导致实例对象不是Foo类的实例。

es5 的继承和 es6 的继承

es5中的原型链继承,就是通过将子类构造函数的原型作为父类构造函数的实例(sub.prototype=new
super),这样就连通了子类-子类原型-父类;

//先来个父类,带些属性  function Super(){      this.flag = true;  }  //为了提高复用性,方法绑定在父类原型属性上  Super.prototype.getFlag = function(){      return this.flag;  }  //来个子类  function Sub(){      this.subFlag = false;  }  //实现继承  Sub.prototype = new Super;  //给子类添加子类特有的方法,注意顺序要在继承之后  Sub.prototype.getSubFlag = function(){      return this.subFlag;  }  //构造实例  var es5 = new Sub;  

但是这样的原型链继承有问题:我们的目标是构造函数的属性私有化,方法复用化,所以我们把属性放在函数内,把方法放到原型上;但是原型链继承显然,父类的属性和方法都放到了子类的原型上;

为了解决上面的做法,我们在es5中混合使用 构造函数call 继承;

function Super(){      this.flag = true;  }  Super.prototype.getFlag = function(){      return this.flag;     //继承方法  }  function Sub(){      this.subFlag = flase      Super.call(this)    //继承属性  }  Sub.prototype = new Super;  var obj = new Sub();  Sub.prototype.constructor = Sub;  Super.prototype.getSubFlag = function(){      return this.flag;  }  

但是还有个小问题是,子类.prototype = new
父类,子类.prototype的constructor 就指向了父类,所以我们要重写一下:

Sub.prototype.constructor = Sub;

ES6的继承实现方法,其内部其实也是ES5组合继承的方式,通过call构造函数,在子类中继承父类的属性,通过原型链来继承父类的方法。

我们将 extend 用babel
进行转码:

function _inherits(subClass, superClass) {     // 确保superClass为function    if (typeof superClass !== "function" && superClass !== null) {         throw new TypeError("Super expression must either be null or a function, not " + typeof superClass);    }     // 把子类.prototype 继承了父类.prototype(new 父类), 同时把子类prototype的constructor进行了重写;    // 给subClass添加constructor这个属性    subClass.prototype = Object.create(superClass && superClass.prototype, {         constructor: {             value: subClass,             enumerable: false,             writable: true,             configurable: true         }     });    // 将父类设为子类的prototype    if (superClass) Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass;}

里面 子类. prototype =
Object.create ( 父类.prototype )这句话,实际上和下面的代码类似:

子类.prototype = new 父类

不同的是, 子类.
prototype = Object.create ( 父类.prototype )不会继承父类的constructor里面的属性,只会继承父类prototype上的方法;

一个关于 Object.create(car.prototype) 用法的代码:

function Car (desc) {    this.desc = desc;    this.color = "red";} Car.prototype = {    getInfo: function() {      return 'A ' + this.color + ' ' + this.desc + '.';    }};//instantiate object using the constructor functionvar car =  Object.create(Car.prototype);car.color = "blue";alert(car.getInfo()); //displays 'A blue undefined.' ??!       // 看见了吧,只会继承方法,不能继承属性

当你只想继承类的原型,而不想继承类的constructor的时候,使用Object.create
是很棒的选择;

如果我们想子类继承父类的prototype
,同时子类也要有自己的属性,请看下面的代码:

var Car2 = Object.create(null); //this is an empty object, like {}Car2.prototype = {  getInfo: function() {    return 'A ' + this.color + ' ' + this.desc + '.';  }}; var car2 = Object.create(Car2.prototype, {  //value properties  color:   { writable: true,  configurable:true, value: 'red' },  //concrete desc value  rawDesc: { writable: false, configurable:true, value: 'Porsche boxter' },  // data properties (assigned using getters and setters)  desc: {     configurable:true,     get: function ()      { return this.rawDesc.toUpperCase();  },    set: function (value) { this.rawDesc = value.toLowerCase(); }    }}); car2.color = 'blue';alert(car2.getInfo()); //displays 'A RED PORSCHE BOXTER.'

每一个属性又是一堆属性的集合,又称descriptor, 分为 data descriptor 和
accessor(访问 ) descriptor

更多的知识:

总之,extends做了两件事情,一个是通过Object.create()把子类的原型赋值为父类的实例,
实现了继承方法,子类.prototype.__proto__也自动指向父类的原型,一个是手动修改了子类的__proto__,
修改为指向父类,(本来在es5 中应该是指向Function.prototype);

在子类中必须执行的super()方法,实际上是用call 方法:

 var _this = _possibleConstructorReturn(this, (b.__proto__ || Object.getPrototypeOf(b)).call(this));

父类被当成普通函数来执行,从而将this绑定到子类上;

同时,extend后面可以跟多种类型的值:

第一种特殊情况,子类继承Object类。

class A extends Object {}A.__proto__ === Object // trueA.prototype.__proto__ === Object.prototype // true

第二种特殊情况,不存在任何继承。

class A {}A.__proto__ === Function.prototype // trueA.prototype.__proto__ === Object.prototype // true

第三种特殊情况,子类继承null。

class C extends null {  constructor() { return Object.create(null); }}

Object.getPrototypeOf()

Object.getPrototypeOf方法可以用来从子类上获取父类。

Object.getPrototypeOf(ColorPoint) === Point
// true

因此,可以使用这个方法判断,一个类是否继承了另一个类。