prototype對象就是讓你有能力向對象添加屬性和方法。__proto__相當於繼承了prototype。
js創建對象的五種方式:
- 工廠模式
- 構造函數模式
- 原型模式
- 混合模式(原型+構造函數)
- 動態原型模式
首先工廠模式:
1 var fn=function(){ 2 return "啊打"; 3 }; 4 function Parent(){ 5 var Child = new Object(); 6 Child.name="李小龍"; 7 Child.age="30"; 8 Child.fn=fn; 9 return Child; 10 }; 11 12 var x = Parent();//實例化 13 alert(x.name);//調用 14 alert(x.fn());
var x = {name:'張安',age:22,fn:function(){return "啊打"}};他跟這種寫法一樣
工廠模式就是在定義一個函數,在函數內部創建Object對象,然后創建屬性和方法,最后將對象作為返回值返回給調用者。
在定義方法時最好在外部定義,這樣可以避免重復創建該方法。
這種方式引用對象時不推薦使用new關鍵字,因為使用后可能會出現一些問題,所以這里不建議使用。
其次構造函數模式:
function person(name,age){ this.name = name; this.age = age; this.family = ["爸爸","媽媽","哥哥"]; this.sayName = function(){ console.log(this.name); }; this.myFamily = function(){ var size = this.family.length; var str = "家庭成員:"; for(var i = 0;i < size;i++){ str += this.family[i]+","; } console.log(str); }; } var p = new person("張三",22); p.sayName();//張三 p.family.push("弟弟"); p.myFamily();//家庭成員:爸爸,媽媽,哥哥,弟弟 var p1 = new person("李四",22); p1.sayName();//李四 p1.myFamily();//家庭成員:爸爸,媽媽,哥哥
構造函數模式在實例化時必須使用關鍵字new,不然無法作為構造函數調用而是普通的函數調用,所以不能把里面的屬性賦給調用者。
同時內部this的屬性還會映射到外部作用域,影響到window,不推薦使用。了解就可以
如果把方法定義到外部.
var fn = function(value){ return value; }; var fnList = function(value){ var size = value.length; var str = ""; for(var i = 0;i < size;i++){ str += value[i] + ","; } return str; }; function Parent(name,age){ this.name = name; this.age = age; this.list = ["爸爸","媽媽","哥哥"]; this.sayName = fn(this.name); this.fnList = fnList(this.list); } var x = new Parent("張三",22); console.log(x); alert(x.sayName);//張三 alert(x.fnList);//爸爸,媽媽,哥哥 var y = new Parent("張三",24); y.name = "李四"; y.list.push("弟弟"); alert(y.sayName);//張三 alert(y.fnList);//爸爸,媽媽,哥哥
這樣的寫法在Y重新賦值后無法內部沒有受影響,不明白為什么。
原型模式:
function createObject(){} createObject.prototype.name="張三"; createObject.prototype.age="22"; createObject.prototype.family=["爸爸","媽媽","兒子"]; createObject.prototype.sayName=function(){ alert(this.name); }; createObject.prototype.fnFamily=function(){ var size = this.family.length; var str = "家庭成員:"; for(var i = 0;i < size;i++){ str += this.family[i] + ","; } alert(str); }; var x = new createObject(); console.log(x); x.name="李四"; x.family.push("閨女"); x.sayName(); //李四 x.fnFamily();//家庭成員:爸爸,媽媽,兒子,閨女 var y = new createObject(); console.log(y); y.sayName(); //張三 y.fnFamily();//家庭成員:爸爸,媽媽,兒子,閨女
原型模式,就是先創建一個函數,然后用函數的prototype屬性給函數添加屬性和方法。prototype特點是共享。
在實例化時,new createObject()生成一個createObject.__proto__,接着x和y的__proto__繼承了createObject.__proto__,所以x,y都擁有了相同的屬性。
然而x在自己的作用域把那么改成李四,所以輸出的是自己的name屬性而不是父類的name屬性,但是family用的是push是改變的父類的值。而y沒有在自己的作用域改變那么值,所以調用的是父類的name值所以是張三。
這里如果變成x.fanily = [,,,],那么就是在x自己的作用域重新賦值family,這樣y的fanily就不會受影響。
混合模式(原型模式+構造函數模式)
function Person(name, age){ this.name = name; this.age = age; this.family = ["爸爸","媽媽","兒子"]; } Person.prototype.sayName = function(){ alert(this.name); }; Person.prototype.fnFamily = function(){ var size = this.family.length; var str = "家庭成員:"; for(var i = 0;i < size;i++){ str += this.family[i]+","; } alert(str); }; var a = new Person("張三",22); a.sayName();//張三 a.family=["閨女"]; a.fnFamily();//閨女 var b = new Person("李四",24); b.sayName();//李四 b.fnFamily();//爸爸,媽媽,兒子
這種模式就是現在比較常用的,並且這種模式跟原型模式是結果一樣的。這里我們看a的屬性,a.family = ["閨女"],跟上面說的一樣,這個是在自己的作用域重新賦值,沒有影響到父類,所有b輸出的還是爸爸,媽媽,兒子。
最后動態原型模式:
function Person(name,age){ this.name = name; this.age = age; this.family = ["爸爸","媽媽","兒子"]; if(typeof this.fnFamily != "function"){ Person.prototype.fnFamily = function(){ var size = this.family.length; var str = "家庭成員:"; for(var i = 0;i < size;i++){ str += this.family[i]+","; } alert(str); } } if(typeof this.sayName != "function"){ Person.prototype.sayName = function(){ alert(this.name); } } } var x = new Person(); x.name= "小明"; x.family.push("閨女"); x.sayName();//小明 x.fnFamily();//家庭成員:爸爸,媽媽,兒子,閨女 var y = new Person(); y.sayName();//undefined y.fnFamily();//家庭成員:爸爸,媽媽,兒子
這種模式將所有的信息都封裝在了構造函數里,在實例化時,通過給里面賦值來實現動態操控屬性的值。
中間的if判斷是為了避免重復創建函數。
總結js原型鏈:
function xxx(){}...
var a = new xxx();
a.__proto__繼承xxx.__proto__繼承object.__proto__繼承null