建構函式弊端
建構函式的原型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 () ;
上面兩個物件的實際情況如下圖:
每個學生物件的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>
如果不指向 原型,則列印如圖:(直接把指向註釋掉)
指向原型:
原型鏈
- 只要是物件就有
__proto__
原型, 指向原型物件 - 我們
Star
原型物件裡面的__proto__
原型指向的是Object.prototype
- 我們
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>
結果:
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>
執行截圖:
如果使用覆蓋形式進行執行(把上面程式碼 註釋的地方開啟,追加形式擴充套件的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>
執行結果:
陣列和字串內建物件不能給原型物件覆蓋操作Array.prototype ={}
,只能是Array.prototype.xx = function
追加 的方式。
本作品採用《CC 協議》,轉載必須註明作者和本文連結