class 的基本使用及轉(zhuǎn)換成 es5 的源代碼

class 是基于原型繼承的語(yǔ)法糖,并不是另外的一套規(guī)則

class 與 構(gòu)造函數(shù)的區(qū)別在于,類聲明沒有提升

聲明

類聲明和類表達(dá)式的主體都執(zhí)行在嚴(yán)格模式下丑念。

// Uncaught ReferenceError: Cannot access 'Person' before initialization
// var p1 = new Person('Tom', 12) 

class Person {
    // constructor 構(gòu)造函數(shù) 一個(gè)類只能有一個(gè)
    constructor(name, age) {
        this.name = name
        this.age = age
    }
}

or

const Person = class {
    constructor(name, age) {
        this.name = name
        this.age = age
    }
}

實(shí)例化

class Person {
    // 公有屬性
    sex = '中性'
    // 私有屬性
    #height = 160

    // constructor 構(gòu)造函數(shù) 一個(gè)類只能有一個(gè)
    // 一個(gè)構(gòu)造函數(shù)可以使用 super 關(guān)鍵字來(lái)調(diào)用一個(gè)父類的構(gòu)造函數(shù)爱只。
    constructor(name, age, height) {
        this.name = name
        this.age = age
        this.#height = height
    }

    // Getter
    get info() {
        console.log('name: ' + this.name + '-- age: ' + this.age)
    }

    // Methods
    log() {
        console.log('name: ' + this.name + '-- age: ' + this.age + '-- sex: ' + this.sex + '-- #height: ' + this.#height)
    }

    // static
    // 靜態(tài)方法 作用于類本身
    // 只有類本身才能調(diào)用
    static getName() {
        console.log(this.name)
    }

    static a = 1
}

const p1 = new Person('Tom', 22, 170)
p1.info
p1.log()
Person.getName()
console.log(Person.a)
console.log(p1.sex)

繼承

// 單繼承
class Man extends Person {
    // 子類中定義了構(gòu)造函數(shù),那么它必須先調(diào)用 super() 才能使用 this 
    constructor(name, age, height, sex) {
        super(name, age, height)
        this.sex = sex
    }

    log() {
        //+ '-- #height: ' + this.#height
        // #height 私有的在這里拿不到
        console.log('name: ' + this.name + '-- age: ' + this.age + '-- sex: ' + this.sex)
    }
}

const man = new Man('Joe', 10, 90, '男')
man.info
man.log()
Man.getName()
console.log(Man.a)
console.log(man.sex)

Mix-ins

實(shí)現(xiàn)多繼承

var calculatorMixin = Base => class extends Base {
  calc() { console.log('calc methods') }
};

var randomizerMixin = Base => class extends Base {
  randomize() { console.log('randomize methods') }
};

class Foo { }
class Bar extends calculatorMixin(randomizerMixin(Foo)) { }

var bar = new Bar()
bar.randomize()  //  randomize methods
bar.calc()  //  calc methods

通過(guò) Babel 轉(zhuǎn)換

轉(zhuǎn)換前

class Person {
    // 公有屬性
    sex = '中性'
    // 私有屬性
    #height = 160

    // constructor 構(gòu)造函數(shù) 一個(gè)類只能有一個(gè)
    // 一個(gè)構(gòu)造函數(shù)可以使用 super 關(guān)鍵字來(lái)調(diào)用一個(gè)父類的構(gòu)造函數(shù)凰兑。
    constructor(name, age, height) {
        this.name = name
        this.age = age
        this.#height = height
    }

    // Getter
    get info() {
        console.log('name: ' + this.name + '-- age: ' + this.age)
    }

    // Methods
    log() {
        console.log('name: ' + this.name + '-- age: ' + this.age + '-- sex: ' + this.sex + '-- #height: ' + this.#height)
    }

    // static
    // 靜態(tài)方法 作用于類本身
    // 只有類本身才能調(diào)用
    static getName() {
        console.log(this.name)
    }

    static a = 1
}

class Man extends Person {
    // 子類中定義了構(gòu)造函數(shù)妥粟,那么它必須先調(diào)用 super() 才能使用 this 
    constructor(name, age, height, sex) {
        super(name, age, height)
        this.sex = sex
    }

    log() {
        //+ '-- #height: ' + this.#height
        // #height 私有的在這里拿不到
        console.log('name: ' + this.name + '-- age: ' + this.age + '-- sex: ' + this.sex)
    }
}

const man = new Man('Joe', 10, 90, '男')

轉(zhuǎn)換后

// 全都運(yùn)行在嚴(yán)格模式下
"use strict";

function _typeof(obj) {
    "@babel/helpers - typeof";
    if (typeof Symbol === "function" && typeof Symbol.iterator === "symbol") {
        _typeof = function _typeof(obj) {
            return typeof obj;
        };
    } else {
        _typeof = function _typeof(obj) {
            return obj && typeof Symbol === "function" && obj.constructor === Symbol && obj !== Symbol.prototype ? "symbol" : typeof obj;
        };
    }
    return _typeof(obj);
}

function _inherits(subClass, superClass) {
    // 超類必須是 funciont or null
    if (typeof superClass !== "function" && superClass !== null) {
        throw new TypeError("Super expression must either be null or a function");
    }
    // 繼承
    subClass.prototype = Object.create(superClass && superClass.prototype, {
        constructor: {
            value: subClass,
            writable: true,
            configurable: true
        }
    });
    if (superClass) _setPrototypeOf(subClass, superClass);
}

function _setPrototypeOf(o, p) {
    // 動(dòng)態(tài)這是原型對(duì)象的指向
    _setPrototypeOf = Object.setPrototypeOf || function _setPrototypeOf(o, p) {
        o.__proto__ = p;
        return o;
    };
    return _setPrototypeOf(o, p);
}

function _createSuper(Derived) {
    return function () {
        // 拿到超類
        var Super = _getPrototypeOf(Derived), result;
        if (_isNativeReflectConstruct()) {
            var NewTarget = _getPrototypeOf(this).constructor;
            result = Reflect.construct(Super, arguments, NewTarget);
        } else {
            // 通過(guò) apply 的方式
            // 繼承成員屬性
            result = Super.apply(this, arguments);
        }
        // 返回當(dāng)前子類的實(shí)例
        return _possibleConstructorReturn(this, result);
    };
}

function _possibleConstructorReturn(self, call) {
    if (call && (_typeof(call) === "object" || typeof call === "function")) {
        return call;
    }
    return _assertThisInitialized(self);
}

function _assertThisInitialized(self) {
    // 還沒有被初始化- super()還沒有被調(diào)用
    // void 0 相當(dāng)于 undefined
    if (self === void 0) {
        throw new ReferenceError("this hasn't been initialised - super() hasn't been called");
    }
    return self;
}

//  判斷是否支持 Refect or Proxy
function _isNativeReflectConstruct() {
    // Reflect 是一個(gè)內(nèi)置的對(duì)象,它提供攔截 JavaScript 操作的方法聪黎。這些方法與proxy handlers的方法相同罕容。Reflect不是一個(gè)函數(shù)對(duì)象备恤,因此它是不可構(gòu)造的。
    // https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/Reflect

    // 判斷 Reflect 是否存在
    if (typeof Reflect === "undefined" || !Reflect.construct) return false;
    // sham 屬性暫不知道有啥用
    if (Reflect.construct.sham) return false;
    if (typeof Proxy === "function") return true;
    try {
        Date.prototype.toString.call(Reflect.construct(Date, [], function () {
        }));
        return true;
    } catch (e) {
        return false;
    }
}

function _getPrototypeOf(o) {
    _getPrototypeOf = Object.setPrototypeOf ? Object.getPrototypeOf : function _getPrototypeOf(o) {
        return o.__proto__ || Object.getPrototypeOf(o);
    };
    return _getPrototypeOf(o);
}

// 判斷當(dāng)前 this 是不是 構(gòu)造函數(shù)的實(shí)例
function _instanceof(left, right) {
    if (right != null && typeof Symbol !== "undefined" && right[Symbol.hasInstance]) {
        return !!right[Symbol.hasInstance](left);
    } else {
        return left instanceof right;
    }
}

// 不能已函數(shù)的形式調(diào)用類
function _classCallCheck(instance, Constructor) {
    if (!_instanceof(instance, Constructor)) {
        // 終止程序
        throw new TypeError("Cannot call a class as a function");
    }
}

// 設(shè)置一些描述屬性并綁定值
function _defineProperties(target, props) {
    for (var i = 0; i < props.length; i++) {
        var descriptor = props[i];
        descriptor.enumerable = descriptor.enumerable || false;
        descriptor.configurable = true;
        if ("value" in descriptor) descriptor.writable = true;
        Object.defineProperty(target, descriptor.key, descriptor);
    }
}

function _createClass(Constructor, protoProps, staticProps) {
    // 給原型添加方法
    if (protoProps) _defineProperties(Constructor.prototype, protoProps);
    // 添加靜態(tài)方法
    if (staticProps) _defineProperties(Constructor, staticProps);
    return Constructor;
}

function _defineProperty(obj, key, value) {
    if (key in obj) {
        Object.defineProperty(obj, key, { value: value, enumerable: true, configurable: true, writable: true });
    } else {
        obj[key] = value;
    }
    return obj;
}

function _classPrivateFieldGet(receiver, privateMap) {
    var descriptor = privateMap.get(receiver);
    if (!descriptor) {
        throw new TypeError("attempted to get private field on non-instance");
    }
    if (descriptor.get) {
        return descriptor.get.call(receiver);
    }
    return descriptor.value;
}

function _classPrivateFieldSet(receiver, privateMap, value) {
    var descriptor = privateMap.get(receiver);
    if (!descriptor) {
        throw new TypeError("attempted to set private field on non-instance");
    }
    if (descriptor.set) {
        descriptor.set.call(receiver, value);
    } else {
        // 試圖設(shè)置只讀私有字段
        if (!descriptor.writable) {
            throw new TypeError("attempted to set read only private field");
        }
        descriptor.value = value;
    }
    return value;
}

var Person = /*#__PURE__*/function () {
    // 構(gòu)造函數(shù)
    function Person(name, age, height) {
        // 判斷構(gòu)造函數(shù)的調(diào)用方式
        _classCallCheck(this, Person);

        // 給當(dāng)前實(shí)例添加 sex 屬性
        _defineProperty(this, "sex", '中性');

        // 私有屬性
        _height.set(this, {
            writable: true,
            value: 160
        });

        // 添加成員屬性
        this.name = name;
        this.age = age;

        // 設(shè)置私有屬性
        _classPrivateFieldSet(this, _height, height);
    }


    _createClass(Person, [{ // 原型上的方法
        key: "log",
        value: function log() {
            console.log('name: ' + this.name + '-- age: ' + this.age + '-- sex: ' + this.sex + '-- #height: ' + _classPrivateFieldGet(this, _height));
        }
    }, {
        key: "info",
        get: function get() {
            console.log('name: ' + this.name + '-- age: ' + this.age);
        }
    }], [{ // 靜態(tài)方法
        key: "getName",
        value: function getName() {
            console.log(this.name);
        }
    }]);

    return Person;
}();

// WeakMap 以對(duì)象做于 key ,值為任意類型
// 弱引用 不影響垃圾回收機(jī)制
var _height = new WeakMap();

// 靜態(tài)屬性
_defineProperty(Person, "a", 1);

var Man = /*#__PURE__*/function (_Person) {
    // 實(shí)現(xiàn)繼承 并 動(dòng)態(tài)綁定構(gòu)造函數(shù)的原型對(duì)象
    _inherits(Man, _Person);

    // 創(chuàng)建一個(gè)獲取父類的方法
    // 返回一個(gè)函數(shù)
    var _super = _createSuper(Man);

    function Man(name, age, height, sex) {
        var _this;

        _classCallCheck(this, Man);

        // 調(diào)用父類的構(gòu)造函數(shù)
        _this = _super.call(this, name, age, height);
        _this.sex = sex;
        return _this;
    }

    _createClass(Man, [{
        key: "log",
        value: function log() {
            console.log('name: ' + this.name + '-- age: ' + this.age + '-- sex: ' + this.sex);
        }
    }]);

    return Man;
}(Person);

var man = new Man('Joe', 10, 90, '男');

// 動(dòng)態(tài)的將 Man.__proto__ 指定成 Person
// 默認(rèn)應(yīng)該指向 Function.prototype
console.log(Man.__proto__ === Person) // true
console.log(Man.__proto__ === Function.prototype) // false

【筆記不易锦秒,如對(duì)您有幫助露泊,請(qǐng)點(diǎn)贊,謝謝】

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末旅择,一起剝皮案震驚了整個(gè)濱河市惭笑,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌生真,老刑警劉巖沉噩,帶你破解...
    沈念sama閱讀 211,743評(píng)論 6 492
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異柱蟀,居然都是意外死亡川蒙,警方通過(guò)查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,296評(píng)論 3 385
  • 文/潘曉璐 我一進(jìn)店門长已,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)畜眨,“玉大人,你說(shuō)我怎么就攤上這事术瓮】的簦” “怎么了?”我有些...
    開封第一講書人閱讀 157,285評(píng)論 0 348
  • 文/不壞的土叔 我叫張陵胞四,是天一觀的道長(zhǎng)恬汁。 經(jīng)常有香客問(wèn)我,道長(zhǎng)辜伟,這世上最難降的妖魔是什么氓侧? 我笑而不...
    開封第一講書人閱讀 56,485評(píng)論 1 283
  • 正文 為了忘掉前任,我火速辦了婚禮游昼,結(jié)果婚禮上甘苍,老公的妹妹穿的比我還像新娘。我一直安慰自己烘豌,他們只是感情好载庭,可當(dāng)我...
    茶點(diǎn)故事閱讀 65,581評(píng)論 6 386
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著廊佩,像睡著了一般囚聚。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上标锄,一...
    開封第一講書人閱讀 49,821評(píng)論 1 290
  • 那天顽铸,我揣著相機(jī)與錄音,去河邊找鬼料皇。 笑死谓松,一個(gè)胖子當(dāng)著我的面吹牛星压,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播鬼譬,決...
    沈念sama閱讀 38,960評(píng)論 3 408
  • 文/蒼蘭香墨 我猛地睜開眼娜膘,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來(lái)了优质?” 一聲冷哼從身側(cè)響起竣贪,我...
    開封第一講書人閱讀 37,719評(píng)論 0 266
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎巩螃,沒想到半個(gè)月后演怎,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 44,186評(píng)論 1 303
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡避乏,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,516評(píng)論 2 327
  • 正文 我和宋清朗相戀三年爷耀,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片淑际。...
    茶點(diǎn)故事閱讀 38,650評(píng)論 1 340
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡畏纲,死狀恐怖扇住,靈堂內(nèi)的尸體忽然破棺而出春缕,到底是詐尸還是另有隱情,我是刑警寧澤艘蹋,帶...
    沈念sama閱讀 34,329評(píng)論 4 330
  • 正文 年R本政府宣布锄贼,位于F島的核電站,受9級(jí)特大地震影響女阀,放射性物質(zhì)發(fā)生泄漏宅荤。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,936評(píng)論 3 313
  • 文/蒙蒙 一浸策、第九天 我趴在偏房一處隱蔽的房頂上張望冯键。 院中可真熱鬧,春花似錦庸汗、人聲如沸惫确。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,757評(píng)論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)改化。三九已至,卻和暖如春枉昏,著一層夾襖步出監(jiān)牢的瞬間陈肛,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 31,991評(píng)論 1 266
  • 我被黑心中介騙來(lái)泰國(guó)打工兄裂, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留句旱,地道東北人阳藻。 一個(gè)月前我還...
    沈念sama閱讀 46,370評(píng)論 2 360
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像谈撒,于是被迫代替她去往敵國(guó)和親稚配。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 43,527評(píng)論 2 349

推薦閱讀更多精彩內(nèi)容