ES6 學習筆記 (Class)

class 內部屬性/方法

Object.getOwnPropertyNames(class{}); // ["length", "prototype"]
Object.getOwnPropertyNames(class{}); // ["length", "prototype", "name"]
Object.getOwnPropertyNames(class cl{}.prototype); // ["constructor"]複製代碼

基本用法

// 1️⃣ function 與 class 的區別
/*---------------- ES5 寫法 ----------------*/
Object.getOwnPropertyNames(Function); // ["length", "name", "prototype"]
Object.getOwnPropertyNames(Function.prototype); // ["length", "name", "arguments", "caller", "constructor", "apply", "bind", "call", "toString"]
function Point(x, y) {
    this.x = x;
    this.y = y;
}
Point.prototype.toString = function () {
    return '(' + this.x + ',' + this.y + ')';
}
// 實例化時能夠不須要new
const point = new Point(10, 20);
Object.keys(Point.prototype); // ['toString']
Object.getOwnPropertyNames(Object.prototype); // ['constructor', 'toString']
/*---------------- ES6 寫法 ----------------*/
const methodName = 'getArea';
const privateMehod = Symbol('privateMethod');
const privateVariable = Symbol('privateMethod');
// Class 繼承了 Function 的屬性,如 name 等
// Class不像 Function 存在變量提高,必須先定義才能使用
// Class 實例化必須使用 new 調用,不然 TypeError: Class constructor Foo cannot be invoked without 'new'
class Point {
    // 該方法不被繼承,只能 Point.staticMethod() 調用
    static staticMethod() { 
        // 這裏 this 指的是類而不是實例
        this.toString(); 
    }
    // 容許靜態和非靜態方法重名
    static toString() { 
        console.log('');
    }
    // 類的實例屬性
    state = {
        value: '100' 
    };
    // class 內部默認嚴格模式 'use strict'
    // class 內部的全部方法都是公開的,除了Symbol表達式定義的方法名除外
    // 構造函數,至關於上面的 ES5 Point,會默認添加
    constructor(x, y) { 
        console.log('new.target.name: ', new.target.name);
        // new.target 指向當前正在執行的function,即返回當前 class
        // 實例經過 new 構造時輸出true,當在被繼承的子類構造函數中執行時,輸出 false,new.target只能在函數或類內部使用
        console.log(new.target === Point); 
        // x、y屬性定義在其自己上,即 this 上
        // this 默認指向類的實例,經過 super 調用父類的方法時,super會綁定子類的this
        this.x = x; 
        this.y = y;
        // this.publicMethod = this.publicMehtod.bind(this);
        // 與上面等效,運行時綁定this到類內部
        this.publicMethod = (arg) => {
            this.publicMethod(arg);
        }
    }
    /* constructor(...args) { this.args = args; } //*/
    // 容許定義一個 Generator 函數
    * generatorFunc() { 
        for (let arg of this.args) {
            yield arg;
        }
    }
    publicMehtod(parm) {

    }
    // class 的全部方法都定義在 prototype 上
    toString() {
        return '(' + this.x + ',' + this.y + ')';
    }
    // 屬性名能夠用表達式
    [methodName]() { 

    }
    // 內部實際上將 privateMethodName 變成了私有方法
    execPrivateMehod(param) {
        privateMethodName.call(this, param)
    }
    // 私有方法
    [privateMethod](param) {
        return this[privateVariable] = param;
    }
    // 使用set/get
    set prop(value) {
        console.log('setting prop');
    }
    get prop() {
        return 'getter prop';
    }
}
// 類的靜態屬性,不容許直接寫在類內部即 prop: '200' 或 static prop: '200'
Point.prop = '200';
function privateMethodName(name) {
    return this.name = name;
}
const point = new Point(10, 20);
point.prop = 'setting';
point.pop; // getting prop
const descriptor = Object.getOwnPropertyDescriptor(Point.prototype, 'prop');
'set' in descriptor; // true
'get' in descriptor; // true
point.hasOwnProperty('x'); // true
point.hasOwnProperty('toString'); // false
point.__proto__.hasOwnProperty('toString'); // true
point.__proto__.addMethod = function () { // 在原型對象上添加方法
    return 'something';
}
const point1 = new Point(30, 40);
// 共享原型對象
point.__proto__ === point1.__proto__; // true
point.constructor = Point.prototype.constructor; // true
typeof Point; // 'function'
Point === Point.prototype.constructor // true,類自己指向構造函數
Object.keys(Point.prototype); // []
Object.getOwnPropertyNames(Object.prototype); // ['constructor', 'toString']

// 2️⃣ 修改構造函數
class Foo {
    constructor() {
        return Object.create(null); // 返回空對象
    }
}
new Foo() instanceof Foo // false

// 3️⃣ Class 表達式
const ClassDefine = class Cn {
    getClassName() {
        return Cn.name;
    }
};
const classObj = new ClassDefine();
classObj.getClassName(); // Cn;
Cn.name; // ReferenceError: cn is not define

// 4️⃣ 當即執行 Class
const obj = new class {
    constructor(name) {
        this.name = name;
    }
    getName() {
        return this.name;
    }
}('name');
obj.getName(); // 'name'

// 5️⃣ 繼承(extends)
class ColorPoint extends Point {
    // 如子類沒顯式定義constructor函數,自動添加 constructor(...args) { super(...args) }
    constructor() {
        // 只有先調用super以後纔可使用this關鍵字,不然報 ReferenceError
        // 至關於 Point.prototype.constructor.call(this);
        // super() 函數只能用在構造函數中
        // super 當作對象使用時至關於 Point.prototype,注意:只能調取父類的prototype屬性或方法(即父類顯式定義的),而不能調用其實例方法或屬性
        super(); // 調用父類的構造函數,返回父類實例
        super.staticMethod(); // 能夠直接調用父類的靜態方法
    }
}
ColorPoint.staticMethod(); // 能夠直接調用父類的靜態方法
const cp = new ColorPoint(); // 輸出new.target.name: ColorPoint
// cp 同時是子類和父類的實例
cp instanceof Point; // true
cp instanceof ColorPoint; // true
Object.getPrototypeOf(ColorPoint) === Point; // true

// 6️⃣ 使用 Proxy綁定this
function selfish (target) {
    const cache = new WeakMap();
    const handler = {
        get (target, key) {
            const value = Relect.get(tartget, key);
            if (typeof value !== 'function') {
                return value;
            }
            if (!cache.has(value)) {
                cache.set(value, value.bind(target));
            }
            return cache.get('value');
        }
    };
    const proxy = new Proxy(target, handler);
    reutn proxy;
}複製代碼

繼承

ES5 的繼承,實質是先創造子類的實例對象 this,而後再將父類的方法添加到 this 上面(Parent.call(this)
ES6 的繼承,是先創造父類的實例對象 this(super()), 而後再用子類的構造函數修改 thisjavascript

// 1️⃣ 基本用法和注意事項
class Point {
  static hello() {
    console.log('hello, world');
  }
  p() {
    return 2;
  }
}
// 繼承同時會繼承父類的靜態方法
class ColorPoint extends Point {
  // 若是顯式寫了 constructor,內部必需要有 super(),不然實例化時報: Uncaught ReferenceError: Must call super constructor in derived class before accessing 'this' or returning from derived constructor
  constructor() {
    // 子類沒有本身的 this 對象,而是繼承 父類的 this 對象,若是不調用 super 方法,子類就得不到 this 對象,在constructor 中使用 this 會報 Uncaught ReferenceError: Must call super constructor in derived class before accessing 'this' or returning from derived constructor
    // super() 這種函數用法只能用在子類 constructor 中
    // 至關於調用父類構造函數,可是返回的是子類的實例,見 3️⃣
    super();
    // 等效於
    // Point.prototype.constructor.call(this);

    // super 做爲對象使用,能夠在 constructor 和子類的其它方法中使用,至關於調用 Point.prototype.p,見4️⃣
    console.log(super.p()); // 2
  }
  // 若是沒有顯式定義 constructor,則會隱式添加 constructor(...args){super(...args)}
}
let cp = new ColorPoint();
// ES5 和 ES6 這兩種行爲一致
cp instanceof ColorPoint; // true
cp instanceof Point; // true

// 2️⃣ 判斷繼承關係
Object.getPrototypeOf(ColorPoint) === Point; // true

// 3️⃣ 子類的super()中this指向子類
class A {
  constructor() {
    console.log(new.target.name);
  }
}
class B extends B {
  constructor() {
    super();
  }
}
new A; // A
new B; // B

// 4️⃣ supr 當作對象使用時指向父類prototype
class A {
  constructor() {
    this.p = 2;
  }
}
A.prototype.x = 2;
class B extends A {
  get m() {
    // super 指向父類原型對象
    return super.p;
  }
  get x() {
    return super.x;
  }
}
let b = new P();
b.m; // undefined
b.x; // 2

// 5️⃣ 經過suer調用父類的方法時,方法內部的this指向子類
class A {
  constructor() {
    this.x = 1;
  }
  print() {
    // 此處 this 指向子類B
    console.log(this.x);
  }
}
class B extends A {
  constructor() {
    super();
    this.x = 2;
    // 若是經過 super 對屬性賦值,這時 super 就是 this
    super.x = 3;
    // 此處super仍是指向 A.prototype,因此打印 undefined
    console.log(super.x); // undefined
    console.log(this.x); // 3
  }
  m() {
    // 至關於 A.prototype.print()
    super.print();
  }
}
let b = new B();
b.m() // 2

// 6️⃣ 在靜態方法中使用super,指向父類,在普通方法中super指向父類的prototype
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

// 7️⃣ super不能單獨使用
class A2 {}
class B2 extends A2 {
  constructor() {
    super();
    // super.valueOf() 返回 B 的實例
    console.log(super.valueof() instanceof B); // true
    // 編譯階段就會報錯
    console.log(super); // Uncaught SyntaxError: 'super' keyword unexpected here
  }
}
let b = new B();

// 8️⃣ 對象老是繼承其它對象,因此能夠用super
var obj = {
  toString() {
    return 'MyObject: ' + super.toString();
  }
}
obj.toString(); // "MyObject: [object Object]"複製代碼

類的 prototype 屬性和 proto 屬性

原生構造函數的繼承

  • Boolean()
  • Number()
  • String()
  • Array()
  • RegExp()
  • Date()
  • Function()
  • Error()
  • Object()
    ES5 沒法繼承原生構造函數
    ES6 容許繼承原生構造哈數定義子類

Mixin 模式的實現

相關文章
相關標籤/搜索