2025-05-24

一、函數(構造函數)、原型、實例對象之間的關系

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.aiwalls.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

 

發佈留言

發佈留言必須填寫的電子郵件地址不會公開。 必填欄位標示為 *