es6 Class

一、 概述

在 ES6 中,class (类)作为对象的模板被引入,可以通过 class 关键字定义类。

class 的本质是 function。

它可以看作一个语法糖,让对象原型的写法更加清晰、更像面向对象编程的语法。

二、 写法

1. new 的过程

  1. 创建空对象
  2. 执行构造函数
  3. 改变 this 指向到空对象
  4. 设置空对象的原型和构造函数等属性
function myNew() {
  // 创建一个新对象
  var obj = {};
  // 拿到构造函数,因为构造函数是第一个参数,arguments里面的其余的参数就是构造函数的参数
  var constructorFunction = [].shift.call(arguments);
  // 此时的arguments已经是去掉了第一个参数的arguments
  var params = arguments;
  // 这句是改变constructorFunction的this指向到obj并且执行constructorFunction这个函数
  var res = constructorFunction.apply(obj, params);
  //  res是否 Object 实例的对象的方法
  return res instanceof Object ? res : obj;
}

2. es5 写法

function Dog() {
  this.type = "Dog";
}
Dog.prototype.run = function() {
  console.log("running");
};
let dog = new Dog();
console.log(dog);

3. es6 写法

class Cat {
  //构造函数
  constructor() {
    console.log("在Cat的构造函数中");
    this.type = "cat";
  }
  //Cat.prototype.jump
  jump() {
    console.log("jump");
  }
}
let cat = new Cat();
console.log(cat);

三、 继承

1. es5 写法

function Dog() {
  this.type = "Dog";
}
Dog.prototype.run = function() {
  console.log("running");
};

function Naidog() {
  this.type = "奶狗";
}
//重新赋一个对象作为子类的原型
Naidog.prototype = new Dog();
Naidog.prototype.constructor = Naidog;
console.log(new Naidog());

2. es6 写法

class Cat {
  //构造函数
  constructor() {
    console.log("在Cat的构造函数中");
    this.type = "cat";
  }
  //Cat.prototype.jump
  jump() {
    console.log("jump");
  }
}
//class 子类名 extends 父类名
class Jumao extends Cat {
  constructor() {
    super();
    //作为对象使用
    //super.constructor  === cat
    console.log(super.constructor);
    this.type = "橘猫";
  }
  eat() {}
}
console.log(new Jumao());

3. 详解

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

1️. 子类必须在constructor方法中调用super方法,否则新建实例时会报错。

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

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

这是因为子类自己的this对象,必须通过父类的构造函数完成塑造,得到与父类同样的实例属性和方法,然后对其加工,加上子类自己的实例属性和方法,如果不调用super方法,子类就得不到this对象。

2️. 父类的静态方法,也会被子类继承

class A {
  static hello() {
    console.log('hello world');
  }
}

class B extends A {
}

B.hello()  // hello world

上面代码中,hello()是A类的静态方法,B继承A,业绩承了A的静态方法。

3. Object.propotypeOf()

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

Object.propotypeOf(colorPoint) === Point
//true

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

4. super关键字

super 这个关键字,既可以当作函数使用,也可以当作对象使用。在这两种情况下,它的用法完全不同.

第一种情况, super 作为函数调用时,代表父类的构造函数。ES6 要求,子类的构造函数必须执行一次 super 函数。

第二种情况, super 作为对象时,在普通方法中,指向父类的原型对象;在静态方法中,指向父类。

5. 类的 prototype 属性和 __proto__属性

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

子类的 __proto__ 属性,表示构造函数的继承,总是指向父类。 子类 prototype 属性的 __proto__ 属性,表示方法的继承,总是指向父类的 prototype 属性。

class A {
}

class B extends A {
}

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

四、 传参

在es6中的对象的属性都是写在constructor里面,方法都是在原型身上。在这里面的代码用constructor约定了两个参数,然后用add()方法把参数相加

class Coder{
    name(val){
        console.log(val);
        return val;
    }

    constructor(a,b){
        this.a=a;
        this.b=b;
    }
 
    add(){
        return this.a+this.b;
    }
}
 
let shuang=new Coder(1,2);
console.log(shuang.add());

五、 静态方法

类相当于实例的原型,所有在类中定义的方法,都会被实例继承。如果function前面加上static关键字,就表示该方法不会被实例继承,而是直接通过类来调用,这就称为“静态方法”。

class Cat {
  static mew() {
    return 'hello';
  }
}

Cat.mew() // 'hello'

var buou = new Cat();
buou.mew()
// TypeError: buou.mew is not a function

注意,如果静态方法包含this关键字,这个this指的是类,而不是实例

class Foo {
  static bar() {
    this.baz();
  }
  static baz() {
    console.log('hello');
  }
  baz() {
    console.log('world');
  }
}

Foo.bar() // hello

六、 实例属性的新写法

实例属性除了定义在constructor()方法里面的this上面,也可以定义在类的最顶层。写法对比:

//实例属性this._count定义在constructor()方法里面
class IncreasingCounter {
  constructor() {
    this._count = 0;
  }
  get value() {
    console.log('Getting the current value!');
    return this._count;
  }
  increment() {
    this._count++;
  }
}
//属性定义在类的最顶层,其它不变
class IncreasingCounter {
  _count = 0;
  get value() {
    console.log('Getting the current value!');
    return this._count;
  }
  increment() {
    this._count++;
  }
}

这种新写法的好处是,所有实例对象自身的属性都定义在类的头部,看上去比较整齐,一眼就能看出这个类有哪些实例属性。

七、 静态属性

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

class Foo {
}

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

目前,只有这种写法可行,因为 ES6 明确规定,Class 内部只有静态方法,没有静态属性。现在有一个提案提供了类的静态属性,写法是在实例属性法的前面,加上static关键字。

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

// 新写法(可以使用) class Foo { static prop = 1; }


> 参考链接:https://www.jianshu.com/p/0743e31cd911
Last Updated:
Contributors: zerojs