ES6 类(class)
JS语言的传统方法是通过构造函数,定义并生成新对象,是一种基于原型的面向对象系统。在ES6中新增加了类的概念,可以使用 class 关键字声明一个类,之后以这个类来实例化对象。
构造函数示例
const Demo = function (a, b) { this.a = a; this.b = b; return this; }; Demo.prototype = { constructor: Demo, print: function () { console.log(this.a + ' ' + this.b); } }; const demo = new Demo('hello', 'world').print();
class示例
class Demo { constructor(a, b) { this.a = a; this.b = b; return this; } print() { console.log(this.a + ' ' + this.b); } }; const demo = new Demo('hello', 'world').print(); console.log(typeof demo);
1 Demo中的constructor方法是构造方法,this关键字则代表实例对象。也就是说,ES5的构造函数Demo,对应ES6的Demo这个类的构造方法。
2 Demo这个类除了构造方法,还定义了一个print方法。注意,定义"类"的方法的时候,前面不需要加上function这个关键字,直接把函数定义放进去了就可以了。另外,方法之间不需要逗号分隔,加了会报错。
3 构造函数的prototype属性,在ES6的“类”上面继续存在。而且类的所有方法都定义在类的prototype属性上面。
class Point { string(){ return 'aaa' } } class ColorPoint extends Point { constructor(color) { super(); // 调用父类的constructor() this.color = color; } toString() { return (this.string() ) } } console.log(new ColorPoint('#000')); //console.log(new ColorPoint(1,2,'#000')) console.log(new ColorPoint('#000').toString()); //aaa console.log(new ColorPoint('#000').__proto__); //Point {constructor: function, toString: function} console.log(ColorPoint.__proto__); // class Point{...} console.log(ColorPoint.prototype); // Point {constructor: function, toString: function} console.log(ColorPoint.__proto__.prototype); // Object {constructor: function, string: function}
作为一个对象,子类(B)的原型(__proto__属性)是父类(A);作为一个构造函数,子类(B)的原型(prototype属性)是父类的实例。
Object.setPrototypeOf(B.prototype, A.prototype);
等同于
B.prototype.__proto__ = A.prototype;
Object.setPrototypeOf(B, A);
等同于
B.__proto__ = A;
super
1、super作为函数调用时,代表父类的构造函数。ES6 要求,子类的构造函数必须执行一次super函数。
2、super作为对象时,在普通方法中,指向父类的原型对象;在静态方法中,指向父类
super代表了父类A的构造函数,但是返回的是子类B的实例,即super内部的this指的是B,因此super()在这里相当于A.prototype.constructor.call(this)。
class A { constructor() { this.x = 1; } print() { console.log(this.x); } } class B extends A { constructor() { super(); this.x = 2; } m() { super.print(); } } let b = new B(); b.m() // 2
class A { constructor() { this.x = 1; } } class B extends A { constructor() { super(); this.x = 2; super.x = 3; console.log(super.x); // undefined console.log(this.x); // 3 } } let b = new B();
类相当于实例的原型,所有在类中定义的方法,都会被实例继承。如果在一个方法前,加上static关键字,就表示该方法不会被实例继承,而是直接通过类来调用,这就称为“静态方法”。
new.target
new是从构造函数生成实例的命令。ES6为new命令引入了一个new.target属性,(在构造函数中)返回new命令作用于的那个构造函数。如果构造函数不是通过new命令调用的,new.target会返回undefined,因此这个属性可以用来确定构造函数是怎么调用的。
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
ps:子类继承父类时,new.target会返回子类。
class Shape { constructor() { if (new.target === Shape) { throw new Error('本类不能实例化'); } } } class Rectangle extends Shape { constructor(length, width) { super(); console.log(length+' '+width) // ... } } var y = new Rectangle(3, 4); // 正确 var x = new Shape(); // 报错
静态方法
函数命前声明static,就为静态方法。该方法不会被实例继承,而是直接通过类来调用。
class Person { constructor(name) { this.name = name; } static say () { console.log("say hi"); } }; Person.say();
静态属性
静态属性指的是Class本身的属性,即Class.propname
,而不是定义在实例对象(this
)上的属性。
class Foo { } Foo.prop = 1; Foo.prop // 1
新写法
class MyClass { static myStaticProp = 42; constructor() { console.log(MyClass.myStaticProp); // 42 } }
实例属性
class ReactCounter extends React.Component { constructor(props) { super(props); this.state = { count: 0 }; } }
新写法
class ReactCounter extends React.Component { state = { count: 0 }; }