文章出處

  ES6中定義類的方式, 就是ES3和ES5中定義類的語法糖,雖然也有些區別,但是整體定義類的方式更加簡潔,類的繼承更加方便, 如果想對ES6中的繼承更加熟悉, 最好了解ES5中原型繼承的方式, 博客園中說JS繼承的文章很多, 想要深入了解的同學自己去搜;

  定義一個class:

  每一個使用class方式定義的類默認都有一個constructor函數, 這個函數是構造函數的主函數, 該函數體內部的this指向生成的實例, say() {}為原型上的方法, 我們定義一個簡單的類 : 

"use strict";
class Person {
    constructor(name) {
        this.name = name;
    }
    say () {
        console.log("say hi");
    }
};
new Person().say(); //控制臺會輸出say hi

   注意: ES6中聲明的類不存在函數聲明提前的問題, 類必須先聲明再使用,否則會出現異常 , 我們只是把上面Demo中的代碼位置一改, 立馬報錯, (如果用ES5中的思維去理解的話, 聲明的類沒有聲明提前, 有關聲明提前的知識點, 通過class 類名{} 聲明的類,就是var 類名 = function(){});

"use strict";
new Person().say();
class Person {
    constructor(name) {
        this.name = name;
    }
    say () {
        console.log("say hi");
    }
};

  

  定義函數的靜態方法:

  如果定義函數的時候, 大括號內部, 函數名前聲明了static, 那么這個函數就為靜態函數, 就為靜態方法, 和原型沒啥關系:

"use strict";
class Person {
    constructor(name) {
        this.name = name;
    }
    static say () {
        console.log("say hi");
    }
};
Person.say();

  定義原型方法:

  定義原型方法,直接這樣聲明: 函數名 () {} 即可, 小括號內部為參數列表, 大括號內部為代碼塊,  ES5中要定義原型方法是通過: 構造函數.prototype.原型方法名() {} , 這種書寫形式很繁瑣, 使用ES6定義原型的方式有點像java和C#了, 這些都是比較高級語言的特征:

"use strict";
class Person {
    constructor(name) {
        this.name = name;
    }
    say () {
        console.log("say hi");
    }
    sing () {
        console.log("lalalalala");
    }
};
new Person().say(); //輸出 :say hi
new Person().sing(); //輸出 :lalalalala

  靜態屬性和原型屬性:

  只能在類定義完畢以后再定義靜態屬性,有點坑爹, 語言作者實現這種方式可能是為了避免代碼的混亂, 所有的靜態屬性在同一個地方定義, 代碼回更加規范?

"use strict";
class Person {
    constructor(name) {
        this.name = name;
    }
};
Person.hands = 2;
console.log(Person.hands);

  原型上面也不能定義屬性了, 我們只能在原型上定義set和get, 取值設值器, 要注意取值器和設值器是在原型上....:

class Person {
    constructor(_name) {
        this._name = _name;
    }
    get name() {
        return this._name;
    }
    set name(_name) {
        this._name = _name;
    }
}
var p = new Person();
p.name = "heheda";
console.log(p.name); //輸出:heheda
console.log(p._name); //輸出:heheda

  如果要定義原型屬性的話, 直接把屬性定義在constructor內部即可, 如果是繼承的話, 子類也會繼承父類的這個屬性:

class Person {
    constructor() {
        this.name = "default";
    }
}
class Man extends Person{
    constructor() {
        super();
    }
}
console.log( new Man().name );

 

  類的繼承extends:

  ES5已經有繼承, 但是這種繼承經常繞來繞去的, ES6的繼承也只是基于原型繼承的封裝(語法糖), 雖然的確簡潔了不少, 還是java的繼承比較好學啊, 下面Demo的例子中的SMan是超人的意思,別想歪了;

"use strict";
class Person {
    constructor(name) {
        this.name = name;
    }
    say () {
        console.log("say hi");
        return this;
    }
};
class SMan extends Person {
    constructor (name, power) {
        super(name);
        this.superPower = power;
    }
    show () {
        console.log(this.superPower);
        return this;
    }
}
console.log( new SMan("Clark", "pee").show().say().name ); //輸出:  pee  say    hi    Clark

  如果要使用繼承的話, 在子類中必須執行super()調用父類, 否者編譯器會拋錯,  在子類中的super有三種作用, 第一是作為構造函數直接調用,第二種是作為父類實例, 第三種是在子類中的靜態方法中調用父類的靜態方法;

  ES6繼承的和ES5繼承的主要區別, ES5中常用的繼承是把子類的原型設置為父類的實例, 子類自然就有了父類的所有方法和屬性:

var Sup = function() {
    this.sub = true;
};
Sup.prototype.protoSup = {sup:"sup"};

var Sub = function() {
    this.sub = true;
};
Sub.prototype = new Sup(); //繼承原型;
Sub.prototype.constructor = Sub; //修正constructor;

  而在ES6中實現的繼承更加精巧, 不會有受到父類的干擾, 這種繼承是結合了apply繼承和原型繼承實現的組合繼承:

var Sup = function() {
    this.sub = true;
};
var Sub = function() {
    this.sup = true;
    Sup.apply(this); //繼承this的屬性和方法;
};
Sub.__proto__ = Sup; //繼承Sup靜態屬性;
Sub.prototype = Object.create( Sup.prototype, {constructor : { value: Sub, enumerable: false, writable: true, configurable: true }}); //繼承原型屬性,并覆寫constructor;

  用圖片可以比較容易看出兩者區別, 圖示ES5和ES6繼承的區別:http://keenwon.com/1524.html ;

  ES5模擬ES6的繼承:

  因為有了轉碼器babel , 我們能通過ES5的代碼, 去窺探ES6的繼承到底是怎么實現, ES6的繼承:

"use strict";
class Person {
    constructor(name) {
        this.name = name;
    }
    say () {
        console.log("say hi");
        return this;
    }
};
class SMan extends Person {
    constructor (name, power) {
        super(name);
        this.superPower = power;
    }
    show () {
        console.log(this.superPower);
        return this;
    }
}
console.log( new SMan("Clark", "pee").show().say().name );
View Code

  使用babel轉化為ES5以后, 代碼變成這樣了, 我自己加了一點注釋, 原諒我放蕩不羈愛自由..:

 var _createClass = function () {
        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);
            }
        }

        return function (Constructor, protoProps, staticProps) {
            //復制原型
            if (protoProps) defineProperties(Constructor.prototype, protoProps);
            //復制屬性
            if (staticProps) defineProperties(Constructor, staticProps);
            return Constructor;
        };
    }();

    function _classCallCheck(instance, Constructor) {
        if (!(instance instanceof Constructor)) {
            throw new TypeError("Cannot call a class as a function");
        }
    }

    function _possibleConstructorReturn(self, call) {
        if (!self) {
            throw new ReferenceError("this hasn't been initialised - super() hasn't been called");
        }
        return call && (typeof call === "object" || typeof call === "function") ? call : self;
    }

    //下面是ES6繼承使用ES5表達出來的代碼,_inherits實現的是原型的繼承和父類狀態屬性的繼承:
    function _inherits(subClass, superClass) {
        if (typeof superClass !== "function" && superClass !== null) {
            throw new TypeError("Super expression must either be null or a function, not " + typeof superClass);
        }
        //繼承父類的原型,并修正constructor為子類;
        subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, enumerable: false, writable: true, configurable: true } });
        //又給子類這個對象定義__proto__ 為父類, 這樣能夠實現靜態屬性繼承;
        if (superClass) Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass;
        //最后的如果開發者:new 子類, 實際的狀態為: 對象{__proto__:父類,constuctor:子類}
    };
    /*
    var Sup = function() {};
    var Sub = function() {};
    _inherits(Sub, Sup);
    //這個繼承實現的意思; 作為對象的子類繼承父類, 作為構造函數的話,子類繼承
    Sub.prototype.__proto__ === Sup.prototype //true
    Sub.prototype.constructor === Sub;//true
    Sub.__proto__ === Sup;//true
    */


    var Person = function () {
        function Person(name) {
            _classCallCheck(this, Person);

            this.name = name;
        }

        _createClass(Person, [{
            key: "say",
            value: function say() {
                console.log("say hi");
                return this;
            }
        }]);

        return Person;
    }();

    ;

    var SMan = function (_Person) {
        _inherits(SMan, _Person);

        function SMan(name, power) {
            //此時的this.__proto__已經指向 構造函數的prototyp了
            _classCallCheck(this, SMan);

            //這句話相當于是ES6中的super(), 把父類的屬性通過call, 執行繼承;
            var _this = _possibleConstructorReturn(this, Object.getPrototypeOf(SMan).call(this, name));

            _this.superPower = power;
            //動態返回_this;
            return _this;
        }

        _createClass(SMan, [{
            key: "show",
            value: function show() {
                console.log(this.superPower);
                return this;
            }
        }]);

        return SMan;
    }(Person);

    console.log(new SMan("Clark", "pee").show().say().name);
View Code 

 

  多重繼承:

    使用mix-in, 實現多重繼承,  書寫方式為:class Sub extends mix(obj0, obj1, obj2)  , mix只是一個方法 ,這個方法我們要自己去定義:

<html>
<head>
    <meta charset="utf-8">
</head>
<body>

<script>

    "use strict";

    function mix(...mixins) {
        class Mix {}

        for (let mixin of mixins) {
            copyProperties(Mix, mixin);
            copyProperties(Mix.prototype, mixin.prototype);
        }

        return Mix;
    }

    function copyProperties(target, source) {
        for (let key of Reflect.ownKeys(source)) {
            if ( key !== "constructor"
                    && key !== "prototype"
                    && key !== "name"
                    ) {
                let desc = Object.getOwnPropertyDescriptor(source, key);
                Object.defineProperty(target, key, desc);
            }
        }
    }

    class Man{
        work () {
            console.log("working");
        }
    }
    class Woman{
        say () {
            console.log("saying");
        }
    }
    class SuperMan extends mix(Man, Woman) {
        constructor () {
            super();
        }
    }

    var sm = new SuperMan();
    sm.work();
    sm.say();
    //實際上它們不存在繼承關系, 只是把屬性復制到子類上;
    console.log(sm instanceof Man);
    console.log(sm instanceof Woman);
</script>
</body>
</html>

 

  參考:

  圖示ES5和ES6繼承的區別:http://keenwon.com/1524.html

  MDN:https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Classes

  caibaojian:http://caibaojian.com/es6.html

作者: NONO
出處:http://www.cnblogs.com/diligenceday/
QQ:287101329
微信:18101055830 


文章列表


不含病毒。www.avast.com
arrow
arrow
    全站熱搜
    創作者介紹
    創作者 大師兄 的頭像
    大師兄

    IT工程師數位筆記本

    大師兄 發表在 痞客邦 留言(0) 人氣()