基本上,ES6 的class能夠看做只是一個語法糖,它的絕大部分功能,ES5 均可以作到,新的class寫法只是讓對象原型的寫法更加清晰、更像面向對象編程的語法而已。編程
//類的寫法
class Point{
getName(){
return 'lili';
}
}
//構造函數的寫法
function Point2() {}
Point2.prototype.getName = function () {
return 'lili';
}
複製代碼
其實class也是函數,類裏的方法也是定義在prototype上。類的方法都是不可枚舉的,經過Object.keys不能得到,可是構造函數原型上的方法能夠得到。bash
//類
typeof Point; //'function'
Point === Point.prototype.constructor //true
Object.getOwnPropertyNames(Point.prototype)
//[ 'constructor', 'getName' ]
Object.keys(Point.prototype)
//[]
//構造函數
Object.getOwnPropertyNames(Point2.prototype);//包括自身的,可枚舉和不可枚舉的
//[ 'constructor', 'getName' ]
Object.keys(Point2.prototype);
[ 'getName' ]
複製代碼
類中能夠顯示定義constructor方法,若是沒有定義,則隱式建立一個空的constructor方法。app
class point {}
//等同於
class Point{
constructor(){
}
}
複製代碼
constructor方法,默認返回值是類的實例對象(this)。能夠自定義返回值 ,若是這樣作改變this值了,最好不要這樣作。函數
class Point{
constructor(x, y){
this.x = x;
this.y = y;
return {}; //不推薦用法
}
}
let point = new Point(1, 2); //{}
複製代碼
注意:生成實例,類必須使用new關鍵字定義,不然報錯。構造函數不是必須使用newpost
class Point{}
let point = Point.call({}); //報錯
function Point2(x) {
this.x = x;
return this;
}
let point2 = Point2.call({}, 1);//ok {x: 1}
複製代碼
類的全部實例共享一個原型對象學習
class Point {
y = 10; //定義屬性方式
constructor(x){
this.x = x; //定義屬性方式
}
setZ(z){
this.z = z; //定義屬性
}
getX(){
return this.x;
}
}
let point = new Point(1);
let point2 = new Point(2);
point.hasOwnProperty('x'); //true
point.hasOwnProperty('getX'); //false
point.__proto__ === point2.__proto__ === Point.prototype //true
Object.getOwnPropertyNames(Point.prototype)
//[ 'constructor', 'getX' ]
複製代碼
this是實例對象,定義在this上的屬於實例的自身的屬性,如x,y,z。getX方法是定義在原型對象上的,全部的實例共享的。ui
靜態屬性如今尚未this
靜態方法spa
class Point {
static getX() {
this.getY();
}
static getY(){
console.log('y');
}
getY(){
console.log('yy');
}
}
let point = new Point();
Point.getX();
//'y'
複製代碼
私有屬性和私有方法尚未實現。只能約定下劃線打頭方法爲私有的。或利用Symbol值的惟一性生成prototype
const bar = Symbol('bar');
class Point {
[bar](){
console.log('x')
}
}
let point = new Point()
console.log(Reflect.ownKeys(Point.prototype));
//[ 'constructor', Symbol(bar) ]
複製代碼
通常狀況下外界取不到bar的值,因此成了私有方法。可是也不是絕對不行,Reflect.ownKeys()依然能夠拿到它們。
new是從構造函數生成實例對象的命令。ES6 爲new命令引入了一個new.target屬性,該屬性通常用在構造函數之中,返回new命令做用於的那個構造函數。若是構造函數不是經過new命令或Reflect.construct()調用的,new.target會返回undefined,所以這個屬性能夠用來肯定構造函數是怎麼調用的。
class Point {
constructor() {
console.log(new.target === Point); //true
}
}
let point = new Point();
複製代碼
只能在構造函數constructor中使用new.target。其餘地方使用會報錯
function Point2(name) {
if (new.target === Point2) {
this.name = name;
} else {
throw new Error('必須使用 new 命令生成實例');
}
}
let point3 = new Point2('sha');
let point2 = Point2.call({}, 'li'); //拋出異常
複製代碼
利用new.target,限定構造函數建立實例,只能使用new操做符。
利用這個特色,能夠寫出不能獨立使用、必須繼承後才能使用的類
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); // 正確
複製代碼
繼承就是子類繼承父類的屬性和方法。注意如下幾點:
class Point {
static hello() { //靜態方法會被子類繼承
console.log('hello world');
}
constructor(x, y) {
this.x = x;
this.y = y;
}
getX(){
return this.x;
}
}
class ColorPoint extends Point{
constructor(x, y, color){
this.color = color; //錯誤
super(x, y);
this.color = color; //正確
}
getX(){
console.log(this); //ColorPoint { x: 10, y: 1, color: 'red' }
return this.color + ' ' + super.getX();
}
}
let cp = new ColorPoint(10, 1, 'red');
console.log(cp.getX()); //red 10
ColorPoint.hello(); //hello world
Object.getPrototypeOf(ColorPoint) === Point //true
cp instanceof Point //true
cp instanceof ColorPoint //true
複製代碼
ES5 的繼承,實質是先創造子類的實例對象this,而後再將父類的方法添加到this上面(Parent.apply(this))。ES6 的繼承機制徹底不一樣,實質是先將父類實例對象的屬性和方法,加到this上面(因此必須先調用super方法),而後再用子類的構造函數修改this。若是不調用super方法,子類就得不到this對象。
super這個關鍵字,既能夠看成函數使用,也能夠看成對象使用。
第一種狀況,super做爲函數調用時,表明父類的構造函數。ES6 要求,子類的構造函數必須執行一次super函數。而且super函數只能用在子類的構造函數之中,用在其餘地方就會報錯。
第二種狀況,super做爲對象時,在普通方法中,指向父類的原型對象;在靜態方法中,指向父類。
在子類普通方法中經過super調用父類的方法時,方法內部的this指向當前的子類實例。因爲super指向父類的原型對象,因此定義在父類實例上的方法或屬性,是沒法經過super調用的。
在子類的靜態方法中經過super調用父類的方法時,方法內部的this指向當前的子類,而不是子類的實例。
class A {
constructor() {
console.log(new.target.name);
this.x = 10;
}
getX(){
return this.x;
}
}
class B extends A {
constructor() {
super();
this.x = 20;
}
}
let a = new A() // A
let b = new B() // B
b.getX(); //20
複製代碼
B中super()在這裏至關於A.prototype.constructor.call(this)。new.target指向當前正在執行的函數
class Parent {
static myMethod(msg) {
console.log('static', msg);
}
myMethod(msg) {
console.log('instance', msg);
}
}
class Child extends Parent {
static myMethod(msg) {
super.myMethod(msg);
}
myMethod(msg) {
super.myMethod(msg);
}
}
Child.myMethod(1); // static 1 靜態方法
var child = new Child();
child.myMethod(2); // instance 2 普通方法
複製代碼
子類的__proto__屬性,表示構造函數的繼承,老是指向父類。
子類prototype屬性的__proto__屬性,表示方法的繼承,老是指向父類的prototype屬性。
class A {
}
class B extends A {
}
B.__proto__ === A // true 屬性繼承
B.prototype.__proto__ === A.prototype // true 方法繼承
複製代碼
注:本文是讀阮一峯老師《ECMAScript 6 入門》的學習筆記