【學習打卡】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)
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);