1. 程式人生 > 實用技巧 >【學習打卡】JavaScript原型的自我總結與疑惑(一)

【學習打卡】JavaScript原型的自我總結與疑惑(一)

  初學原型是從老師的視訊入門的,根據老師的講解,我理解到的是,建構函式裡面定義複雜物件或者函式的時候,每次new一個新物件就得複製一遍,浪費空間;為了解決這個問題呢,就有了原型。通過引入原型,使得每個物件擁有一個原型物件,我的理解是原型物件是共享的,物件可以訪問但是不能修改他的原型物件。但是這些都僅僅是我的猜想,那麼針對這些猜想,我做了一些實驗。

  猜想一:new物件的時候,建構函式裡面的變數會複製給新物件,物件改變自身方法或者屬性,並不會影響建構函式;【是的】

  猜想二:原型物件可自身修改自身;【是的】

  猜想三:原型物件是共享的,物件只能訪問但不能修改原型物件;【共享,物件可以修改原型物件】

  【實驗之前的準備:知識儲備】來源:https://developer.mozilla.org/zh-CN/docs/Learn/JavaScript/Objects/Object_prototypes

  定義一個建構函式(例如Father),那麼他自帶一個原型物件(即Father.prototype),new一個Father物件時(例如son),那麼son可以訪問這個原型物件的屬性和方法,每個瀏覽器訪問的方式都不同,文中用的時son.__proto__,這不是一個標準,不推薦用作開發。每個原型物件也會擁有原型,並且從中繼承方法和屬性,一層一層、以此類推。這種關係常被稱為原型鏈 (prototype chain)

,這些屬性和方法定義在Object的構造器函式(constructor functions)之上的prototype屬性上,而非物件例項本身。在 JavaScript 中並不如此複製——而是在物件例項和它的構造器之間建立一個連結(它是__proto__屬性,是從建構函式的prototype屬性派生的),之後通過上溯原型鏈,在構造器中找到這些屬性和方法。

  【開始實驗咯】

  猜想一,實驗如下:

  第一步:Father的gender的值直接固定為“m”,最後console.log(son.gender)的輸出結果為m;

 1 <script>
 2         function Father(first, last, age, gender) {
 3             this.name = {
 4                 'first': first,
 5                 'last': last
 6             };
 7             this.age = age;
 8             this.gender = "m"
 9         }
10         var son = new Father('Mei', 'Lee', 18, 'f');
11         console.log(son.name);
12         console.log(son.age);
13         console.log(son.gender);
14     </script>

  第二步:son.gender重新賦值,比較Father.gender和son.gender的異同。程式碼如下:son.gender的輸出為f,Father的gender值並未改變。

    也就是說,改變物件並不會影響建構函式。即:猜想一成立。

var son = new Father('Mei', 'Lee', 18, 'f');
son.gender = 'f';
console.log(Father);
console.log(son.gender);

  猜想二:原型物件可自身修改自身;實驗如下:

  這個其實可以不用證明的,如果不能修改自身,那它怎麼定義嘛,但是為了後文的發展,簡單驗證一下吧。第一段程式碼中,Father.prototype是沒有speak屬性的,第二段程式碼裡面是有的。那麼猜想二是正確的。

<script>
        function Father(first, last, age, gender) {
            this.name = {
                'first': first,
                'last': last
            };
            this.age = age;
            this.gender = 'm'
        }
        console.log(Father.prototype);
    </script>

  

<script>
        function Father(first, last, age, gender) {
            this.name = {
                'first': first,
                'last': last
            };
            this.age = age;
            this.gender = 'm'
        }
        Father.prototype.speak = 'chinese';
        console.log(Father.prototype);
    </script>

  另外附上一點疑惑,Father.prototype增加speak屬性的時候,增加speak之前和之後分別列印Father.prototype,之前是要展開函式才能看到Chinese,之後是函式最外層就能看到;而且之前訪問speak是訪問不到的,不知道是什麼原因。後來在摯友的幫助下,用IE執行了這段程式碼就不會有這種令人迷惑的問題,可能時chrome的內部執行順序的問題,不知道會不會在專案執行中出啥問題。

<script>
        function Father(first, last, age, gender) {
            this.name = {
                'first': first,
                'last': last
            };
            this.age = age;
            this.gender = 'm'
        }
        console.log(Father.prototype);
        console.log(Father.prototype.speak);
        Father.prototype.speak = 'chinese';
        console.log(Father.prototype);
console.log(Father.prototype.speak);
</script>

  執行結果如圖:

  猜想三:原型物件是共享的,物件只能訪問但不能修改原型物件;共享是正確的,MDN文件中有提到【原型鏈中的方法和屬性沒有被複制到其他物件】,此處做個驗證。但是物件可以修改其原型物件

  分別通過物件son1和son2修改原型物件的speak屬性,會發現屬性真的會變化!也正如文件所說,原型鏈中的方法和屬性沒有被複制到其他物件,其他物件可以修改這些物件方法。

 1 <script>
 2         function Father(first, last, age, gender) {
 3             this.name = {
 4                 'first': first,
 5                 'last': last
 6             };
 7             this.age = age;
 8         }
 9         Father.prototype.speak = 'chinese';
10         var son1 = new Father('Mei', 'Lee', 18);
11         son1.__proto__.speak = 'english';
12         console.log(Father.prototype.speak);
13         console.log(son1.speak)
14         var son2 = new Father('Lei', 'Lee', 18);
15         son2.__proto__.speak = 'japanese';
16         console.log(Father.prototype.speak);
17         console.log(son2.speak)
18     </script>

執行結果:

  針對猜想3的一點延申:如果一個物件真的想用原型物件的一個屬性或者方法,如果要修改,請自己重新定義,不要影響別的物件哦。如下面,如果真的想學外語,可以給自己定義個speak。

    Father.prototype.speak = 'chinese';
        var son1 = new Father('Mei', 'Lee', 18);
        son1.speak = 'english';
        console.log(Father.prototype.speak);
        console.log(son1.speak)
        var son2 = new Father('Lei', 'Lee', 18);
        son2.speak = 'japanese';
        console.log(Father.prototype.speak);
        console.log(son2.speak)
                    

  本文之外的一點疑惑,如果Father在函式體之外定義了一個屬性或者方法,那麼要怎樣才能訪問到這個方法?new的物件是否可以訪問到這個方法,chrome的執行結果如下,菜雞很疑惑。

 1         function Father(first, last, age, gender) {
 2             this.name = {
 3                 'first': first,
 4                 'last': last
 5             };
 6             this.age = age;
 7             this.gender = 'm'
 8         }
 9         console.log(Father);
10         console.log(Father.prototype);
11         var son1 = new Father('Mei', 'Lee', 18, 'f');
12         Father.speak = 'chinese';
13         var son2 = new Father('Lei', 'Lee', 18, 'f');
14         console.log(Father);
15         console.log(Father.speak);
16         console.log(son1);
17         console.log(son1.speak);
18         console.log(son2);
19         console.log(son2.speak);