會js與jq的後端開發如何愉快的轉前端:第二篇-第二節:es6-物件導向-把oop玩出花-原型prototype

wangchunbo發表於2020-12-17
  • 建構函式弊端

  • 建構函式的原型prototype

  • 原型和原型的作用

浪費記憶體

建構函式的弊端:存在浪費記憶體的問題。

原型:每一個建構函式都有一個 prototype 屬性,指向另一個物件。這個 prototype 就是一個物件,這個物件的所有屬性和方法,都會被建構函式所擁有。

原型的作用:資源共享。

為什麼說 浪費記憶體?

寫一個建構函式 用來建立學生物件

屬性:姓名 年齡 身高 體重

行為:學習 吃飯 看片 把妹

function Student (name){

    this . name = name ;

    this . study = function (){

        console . log ( "好好學習 天天向上" ) ;

    }

}

如果建構函式沒有引數,那麼在呼叫的時候小括號可以省略

    var stu = new Student ( "胡一天" ) ;

    stu. study () ;

    var stu1 = new Student ( "沈悅" ) ;

    stu1. study () ;

上面兩個物件的實際情況如下圖:

會js與jq的後端開發如何愉快的轉前端:第二篇-第二節:es6-物件導向-把oop玩出花-原型prototype

每個學生物件的study函式都是同一句程式碼,那同樣的程式碼在記憶體裡面佔兩份就不合適。
那麼傳統的解決方法是讓所有的物件共用一個方法,在建構函式外部定義好該函式,將該函式賦值給建構函式內的方法。

使用這種方式存在的問題:
1就是全域性變數增多造成汙染,
2就是程式碼結構混亂,不易維護

原型定義

原型:每一個建構函式都有一個 prototype 屬性,指向另一個物件。這個 prototype 就是一個物件,這個物件的所有屬性和方法,都會被建構函式所擁有。

作用:資源共享

一般情況下,我們的公共屬性定義到建構函式裡面, 公共的方法我們放到原型物件身上

程式碼:

<body>

    <script>

        // 1. 建構函式的問題. 

        function Star(uname, age) {

            this.uname = uname;

            this.age = age;

            // this.sing = function() {

            //     console.log('我會唱歌');

            // }

        }

        Star.prototype.sing = function() {

            console.log('我會唱歌');

        }

        var dmm = new Star('大冪冪', 18);

        var bo = new Star('波波', 19);

        console.log(dmm.sing === bo.sing); // 如果不是公共方法,這裡就是false

        // console.dir(Star);

        dmm.sing();

        bo.sing();

        // 2. 一般情況下,我們的公共屬性定義到建構函式裡面, 公共的方法我們放到原型物件身上

    </script>

</body>

proto 我自己的物件原型

物件身上系統自己新增一個__proto__ 指向我們建構函式的原型物件 prototype

<body>

    <script>

        function Star(uname, age) {

            this.uname = uname;

            this.age = age;

        }

        Star.prototype.sing = function() {

            console.log('我會唱歌');

        }

        var dmm = new Star('大冪冪', 18);

        var bo = new Star('波波', 19);

        dmm.sing();

        console.log(dmm); // 物件身上系統自己新增一個 __proto__ 指向我們建構函式的原型物件 prototype

        console.log(dmm.__proto__ === Star.prototype);

        // 方法的查詢規則: 首先先看dmm 物件身上是否有 sing 方法,如果有就執行這個物件上的sing

        // 如果沒有sing 這個方法,因為有__proto__ 的存在,就去建構函式原型物件prototype身上去查詢sing這個方法

    </script>

</body>

屬性 constructor 指回 原來的建構函式

很多情況下,我們需要手動的利用 constructor 這個屬性指回 原來的建構函式

為什麼要指回去?

運用場景 :編寫兩個物件原型方法.
思考: 我能不能簡單點優化程式碼?

Star.prototype.sing = function() {
    console.log('我會唱歌');
};
Star.prototype.movie = function() {
    console.log('我會演電影');
}

開始優化,合併 prototype,修改原型

<body>
    <script>
        function Star(uname, age) {
            this.uname = uname;
            this.age = age;
        }
        // 2. 直接定義一個新的prototype
        // constructor 這個屬性指回 原來的建構函式
        Star.prototype = {
            // 如果我們修改了原來的原型物件,給原型物件賦值的是一個物件,則必須手動的利用constructor指回原來的建構函式
            // 如果不指回去,此行註釋,則無法找到原來的建構函式 
            // 列印Star.prototype.constructor dmm.__proto__.constructor 只有此建構函式中的 sing 與 movie
            // 很多情況下,我們需要手動的利用constructor 這個屬性指回 原來的建構函式
            constructor: Star, 
            sing: function() {
                console.log('一生一世');
            },
            movie: function() {
                console.log('php啊');
            }
        }
        var dmm = new Star('大冪冪', 18);
        var bo = new Star('波波', 19);
        console.log(Star.prototype);
        console.log(dmm.__proto__);
        console.log(Star.prototype.constructor);
        console.log(dmm.__proto__.constructor);
    </script>
</body>

如果不指向 原型,則列印如圖:(直接把指向註釋掉)

會js與jq的後端開發如何愉快的轉前端:第二篇-第二節:es6-物件導向-把oop玩出花-原型prototype

指向原型:

會js與jq的後端開發如何愉快的轉前端:第二篇-第二節:es6-物件導向-把oop玩出花-原型prototype

原型鏈

  1. 只要是物件就有__proto__ 原型, 指向原型物件
  2. 我們Star原型物件裡面的__proto__原型指向的是 Object.prototype
  3. 我們Object.prototype原型物件裡面的__proto__原型 指向為 null
<body>

    <script>

        function Star(uname, age) {

            this.uname = uname;

            this.age = age;

        }

        Star.prototype.sing = function() {

            console.log('我會唱歌');

        }

        var dmm = new Star('大冪冪', 18);

        // 1. 只要是物件就有__proto__ 原型, 指向原型物件

        console.log(Star.prototype);

        console.log(Star.prototype.__proto__ === Object.prototype);

        // 2.我們Star原型物件裡面的__proto__原型指向的是 Object.prototype

        console.log(Object.prototype.__proto__);

        // 3. 我們Object.prototype原型物件裡面的__proto__原型  指向為 null

    </script>

</body>

原型物件中this指向

在建構函式中,裡面this指向的是物件例項
原型物件函式裡面的this 指向的是 例項物件

<body>
    <script>
        function Star(uname, age) {
            this.uname = uname;
            this.age = age;
        }
        var that;
        Star.prototype.sing = function() {
            console.log('我會唱歌');
            that = this; // 指向的是 例項物件 dmm
        }
        var dmm = new Star('大冪冪', 18);
        // 1. 在建構函式中,裡面this指向的是物件例項 dmm
        dmm.sing();
        console.log(that === dmm);

        // 2.原型物件函式裡面的this 指向的是 例項物件 dmm
    </script>
</body>

結果:

會js與jq的後端開發如何愉快的轉前端:第二篇-第二節:es6-物件導向-把oop玩出花-原型prototype

true

原型物件的應用 擴充套件內建物件方法

可以通過原型物件,對原來的內建物件進行擴充套件自定義的方法。比如給陣列增加自定義求偶數和的功能。

注意: 陣列和字串內建物件不能給原型物件覆蓋操作Array.prototype ={},只能是Array.prototype.xx = function 追加 的方式。

<body>
    <script>
        // 原型物件的應用 擴充套件內建物件方法

        Array.prototype.sum = function() {
            var sum = 0;
            for (var i = 0; i < this.length; i++) {
                sum += this[i];
            }
            return sum;
        };
        // 這一段會直接報錯,js本身存在sum 這個方法我們們還是要的,不可能覆蓋。
        // 我們只可以通告追加的形式來擴充套件內建物件方法
        // Array.prototype = {
        //     sum: function() {
        //         var sum = 0;
        //         for (var i = 0; i < this.length; i++) {
        //             sum += this[i];
        //         }
        //         return sum;
        //     }

        // }
        var arr = [1, 2, 3];
        console.log(arr.sum());
        console.log(Array.prototype);
        var arr1 = new Array(11, 22, 33);
        console.log(arr1.sum());
    </script>
</body>

執行截圖:

會js與jq的後端開發如何愉快的轉前端:第二篇-第二節:es6-物件導向-把oop玩出花-原型prototype

如果使用覆蓋形式進行執行(把上面程式碼 註釋的地方開啟,追加形式擴充套件的sum 註釋):


<body>

    <script>

        // 原型物件的應用 擴充套件內建物件方法

        // Array.prototype.sum = function() {

        //     var sum = 0;

        //     for (var i = 0; i < this.length; i++) {

        //         sum += this[i];

        //     }

        //     return sum;

        // };

        Array.prototype = {

            sum: function() {

                var sum = 0;

                for (var i = 0; i < this.length; i++) {

                    sum += this[i];

                }

                return sum;

            }

        }

        var arr = [1, 2, 3];

        console.log(arr.sum());

        console.log(Array.prototype);

        var arr1 = new Array(11, 22, 33);

        console.log(arr1.sum());

    </script>

</body>

執行結果:

會js與jq的後端開發如何愉快的轉前端:第二篇-第二節:es6-物件導向-把oop玩出花-原型prototype

陣列和字串內建物件不能給原型物件覆蓋操作Array.prototype ={},只能是Array.prototype.xx = function 追加 的方式。

本作品採用《CC 協議》,轉載必須註明作者和本文連結
感謝關注 上海PHP自學中心-免費程式設計視訊教學|

相關文章