一、函數(建構函式)、原型、執行個體對象之間的關係
A、函數與原型的關係
1、只要建立了一個函數,那麼它就會根據一組特定的規則為該函數建立一個prototype屬性(原型對象)
如:
function fun(){
}
console.log(fun.prototype); //fun {},說明prototype是一個對象
注意:原型中定義的屬性、方法是所有調用建構函式執行個體化對象所共用的
2、prototype屬性都會自動獲得一個constructor(建構函式)屬性,constructor屬性包含了prototype屬性所在函數(fun)的指標(很重要),說明在建構函式(fun)中可以訪問原型中定義的屬性和方法,
如果建構函式中定義了原型中的同名屬性方法時,那麼執行個體就會調用重新定義的屬性與方法了;當一個函數使用new操作符執行個體化一個對象時,它用來標識執行個體化對象的類型(並沒有太大的實際意義)
如:
function fun(name){
console.log(fun.prototype.name == this.name);//true(yjh)
this.name = "yjh1";
console.log(this.name);//yjh1
console.log(fun.prototype.name == this.name);//false(yjh,yjh1)
}
fun.prototype = {
constructor: fun,
name: "yjh"
}
var fun1 = new fun();
console.log(fun.prototype.constructor == fun); //true
console.log(fun1.constructor == fun); //true
B、執行個體化對象與原型的關係
1、當函數使用new操作符執行個體化一個對象時,那麼對象就包含了一個內在的指向原型的__proto__屬性,它只存在執行個體對象與原型對象之間
如:
function fun(name,age){
this.name = name;
this.age = age;
this.sayName = function(){
alert(this.name);
}
}
fun.prototype = {
constructor: fun,
age: 22,
sayName: function(){
alert(this.age);
}
}
var fun1 = new fun("yjh","23");
console.log(fun1.__proto__) //fun { age="22", sayAge=function()}
console.log(fun1.__proto__ == fun.prototype); //true
C、執行個體對象與函數(建構函式)的關係
1、當函數使用new操作符執行個體化一個對象時,執行個體對象通過內部屬性__proto__指向原型,共用原型(prototype)中定義的屬性和方法,
由於原型(prototype)中的constructor屬性指向建構函式,因此執行個體對象也擁有建構函式中定義的屬性和方法
如:
function fun(name,age){
this.name = name;
this.age = age;
this.sayName = function(){
alert(this.name);
}
}
var fun1 = new fun("yjh","23");
fun1.sayName(); //yjh
D、函數(建構函式)與執行個體對象、原型三者之間的關係
1、在調用執行個體化對象屬性,方法時,那麼首先會搜尋執行個體對象自身定義的屬性和方法,如果沒有的話,會繼續搜尋原型
如:
function fun(name,age){
this.name = name;
this.age = age;
this.sayName = function(){
alert(this.name);
}
}
fun.prototype.age = "22";
fun.prototype.sayAge = function(){
alert(this.age);
}
var fun1 = new fun("yjh","23");
fun1.age = 24;
fun1.sayAge(); //24 ,調用了原型中的方法;
首先搜尋sayAge方法,執行個體對象中沒有,搜尋原型,在原型中找到sayName方法,繼續搜尋age屬性,發現在fun1執行個體對象中已有定義age值,因此直接使用,不再繼續搜尋了;如果沒有直接在fun1執行個體對象定義age屬性的話,那麼結果就是23了,原因是因為建構函式重新定義了執行個體對象的age屬性
________________________________________
二、物件導向模式:
對函數的初步理解:
a、JavaScript中的任何一個函數都是一個Function類型的一個執行個體,也是一個Object類型的執行個體,定義了一個function函數,那麼它就是一個執行個體對象,執行個體對象的內部屬性__proto__指向了Object建構函式的原型prototype屬性,因此執行個體繼承了Object對象的預設屬性和方法;
b、普通函數預設返回undefined,建構函式返回一個執行個體對象。
1、建立對象,使用一個特定介面new Object()
缺點:使用同一個介面建立很多個物件,會產生大量重複的代碼
2、使用原廠模式,用函數來封裝,以特定介面建立對象
如:
function createObj(name,age){
var o = new Object();
o.name = name;
o.age = age;
return o;
}
var o1 = createObj("yjh",23)
優點:解決了使用一個介面建立多個相似對象產生大量重複代碼的問題
缺點:沒有解決對象識別的問題,即o1是怎麼樣的一個物件類型
3、建構函式模式,JavaScript沒有類概念
如:
function CreateObj(name,age){
this.name = name;
this.age = age;
this.sayName = function(){
alert("hi" + this.name);
}
}
var obj1 = new CreateObj("yjh1",23);
var obj2 = new CreateObj("yjh2",23);
優點:解決了執行個體物件類型識別的問題,obj1,obj2對象為CreateObj類型www.2cto.com
缺點:建構函式定義的屬性和方法不是所有執行個體所共用的,各執行個體調用的方法是兩個不同Function類型的執行個體(obj1.sayName != obj2.sayName)
4、原型模式
如:
function CreateObj(){
}
CreateObj.prototype = {
constructor: CreateObj,
name: "yjh",
age: 23,
colors: ["a","b"],
sayName: function(){
alert(this.name);
}
}
var obj1 = new CreateObj();
var obj2 = new CreateObj();
alert(obj1.sayName == obj2.sayName);//true
obj1.colors.push("c");
alert(obj2.colors);//a,b,c
說明:調用obj1,obj2執行個體的屬性和方法,首先會搜尋執行個體自身定義的屬性和方法,如果沒有,由於執行個體的__proto__內部屬性指向原型,
因此會繼續搜尋原型中定義的屬性和方法
優點:原型中定義的屬性和方法是所有執行個體對象所共用的,解決了多個函數實現同一功能的問題
缺點:如果在原型中定義的屬性包含的是參考型別的值,那麼通過修改一個執行個體屬性值會影響到另一個執行個體屬性值,這正是由於原型的共用本質所導致的
5、組合模式(建構函式模式與原型模式)
如:
function CreateObj(name,age){
console.log(this.name);//yjhyjh
this.name = name;
this.age = age;
this.colors = ["a","b"];
}
CreateObj.prototype = {
constructor: CreateObj,
name: "yjhyjh",
sayName: function(){
return this.name;
}
}
var obj1 = new CreateObj("yjh1",23);
var obj2 = new CreateObj("yjh2",23);
alert(obj1.sayName == obj2.sayName);//true
alert(obj1.sayName());//yjh1
alert(obj2.sayName());//yjh2
obj1.colors.push("c");
alert(obj2.colors);//a,b
說明:把所有執行個體不需要共用的屬性定義在建構函式中,把需要共用的屬性,方法定義在原型中,互補建構函式模式和原型模式的優缺點,
原型是所有執行個體化對象的原型對象,執行個體與原型之間是通過執行個體內部屬性__proto__串連到原型,所有執行個體共用原型中的屬性和方法,如果建構函式中重新定義了原型中的同名屬性、方法,
那麼執行個體對象將會調用建構函式中定義的屬性與方法。
6、繼承(實現繼承,原型鏈)
就是把一個建構函式的原型作為另一個建構函式的執行個體化對象,那麼這個執行個體化原型對象就會繼承另一個建構函式的原型屬性和方法,這就是所謂的原型鏈
如:
function Fun1(){
this.name = ["yjh1","yjh2"];
}
Fun1.prototype = {
constructor: Fun1,
sayName: function(){
alert(this.name)
}
}
function Fun2(){}
Fun2.prototype = new Fun1();
var fun2 = new Fun2();
fun2.sayName();//yjh1,yjh2
fun2.name.push("yjh3"); //fun2.name = ["yjh1","yjh2","yjh3"];
var fun3 = new Fun2();
alert(fun3.name);//yjh1,yjh2,yjh3
缺點:來自包含參考型別值的原型,原型中定義的屬性,方法是所有執行個體所共用的,Fun2.prototype原型對象是Fun1類型的一個執行個體,
因此原型對象中有包含參考型別值的name屬性,當執行個體化多個Fun2類型對象時,所有的執行個體對象都共用這個原型name屬性,通過修改執行個體中的name屬性值,
會直接修改原型中定義的name屬性值
7、組合繼承(繼承與借用建構函式)
如:
function Fun1(){
this.name = ["yjh1","yjh2"];
}
Fun1.prototype = {
constructor: Fun1,
sayName: function(){
alert(this.name)
}
}
function Fun2(){
Fun1.call(this);
}
Fun2.prototype = new Fun1();
var fun2 = new Fun2();
fun2.sayName();//yjh1,yjh2
fun2.name.push("yjh3"); //fun2.name = ["yjh1","yjh2","yjh3"];
var fun3 = new Fun2();
alert(fun2.name);//yjh1,yjh2,yjh3
alert(fun3.name);//yjh1,yjh2
說明:由於建構函式中定義的屬性,方法不是所有執行個體共用的,而且會覆蓋原型中定義的屬性與方法,所以它不會由於執行個體化原型對象(Fun2.prototype)中包含了參考型別值的屬性而
存在的問題
摘自 華子yjh