一步步學習javascript基礎篇(4):面向對象設計之創建對象(工廠、原型和構造函數等模式)


前面我們介紹了可以通過Object構造函數或對象字面量都可以用來創建單個對象,但是如果需要創建多個對象的話,顯然很多冗余代碼。

接下來介紹幾種模式來創建對象。不過在此之前,我們還是先來了解下 typeof和instanceof 。

typeof和instanceof 

//typeof主要用了檢查值類型數據,如:
alert(typeof (1) + " " + typeof ("1") + " " + typeof (false) + " " + typeof (undefined));
//instanceof主要用了檢查對象,如:
var arr = new Array();
alert((arr instanceof Object) + " " + (arr instanceof Array) + " " + (arr instanceof Number));//既是Object也是array,但不是Number

例:

下面我們接着介紹數種創建對象的方式。

一、 工廠模式:

function createPerson(name) {
    var o = new Object();
    o.name = name;
    o.sayName = function () {
        alert(this.name);
    };
    return o;
}
var obj = createPerson("張三");
var obj2 = createPerson("李四");
alert(obj instanceof Object);
alert(obj instanceof createPerson)

由上可知,工廠模式簡單、思路清晰、容易理解,也可以創建對象,不過有個缺點不能確定對象類型。因為它總是一個object類型,而不能判定是createPerson類型。

例1:

二、構造函數模式 

var obj = { name: "李四" };
function Person(name) {
    this.name = name;
    this.sayName = function () {
        alert(this.name);
    };
}
var per = new Person("張三");
per.sayName();//張三
var per2 = new Person("李四");
alert(per.sayName==per2.sayName);//false

 

其實,構造函數模式我們在上篇博文就簡單介紹過了。同樣,構造函數模式也不完美,因為每個實例化出來的對象所擁有的方法都是獨立的,而一個對象類型的方法完全是可以同享引用來節省內存空間。

例2:

由例2可以看出,第二次彈出消息為false,證明了每個實例對象中的方法都是獨立的。 第三次彈出消息為true,與上面的工廠模式不同,這里通過構造函數創建的方法,可以直接判斷對象類型了。

我們說實例對象的方法應該是共享的,那么我們可以用接下來的模式實現。

三、原型模式

1.0在使用原型模式之前,我們首先需要了解什么是原型。我的理解就是,原始對象類型的模型。每個對象都有一個屬性(prototype)指向對象的原型。

function Person() {
    this.sayHi1 = function () { }
}
Person.prototype.sayHi2 = function () { };

var per1 = new Person();
var per2 = new Person();
alert(per1.sayHi1 === per2.sayHi1);//每個實例化出來的對象所獨有的,所以為false
alert(per1.sayHi2 === per2.sayHi2);//因為是同一個引用,所以為true
//我們再次證明了構造函數中的屬性(或是方法、對象)是實例化對象獨有的,原型中的屬性(或是方法、對象)是共享的。

第一個比較是對象的屬性,所以每個實例對象擁有獨立的方法,而第二個比較是原型方法,就算是實例對象,它們直接也是引用共享的。

例3:

我們看到了 per1.sayHi2 === per2.sayHi2  比較是true。(===是全等的意思,不僅比較值,還比較類型。)

 

我們看到了 __proto__ 指向的就是我們所謂的原型(只有Firefox、 Safari 和 Chrome瀏覽器有此屬性)。還有一個 constructor 指向我們的構造函數。

1.1 __proto__ 和原型 prototype 的關系(其實__proto__並不是一個js語言中規定的對象屬性,只是某些瀏覽器實現了

function Person() {
    this.name1 = "張三",
    this.sayHi1 = function () { }
}
Person.prototype.sayHi2 = function () { };

var per1 = new Person();
var per2 = new Person();
alert(per1.constructor);//constructor指向了構造函數
alert(per1.constructor.prototype);//constructor.prototype 指向了構造函數的原型
alert(per1.constructor.prototype === per1.__proto__);//true 由此看出__proto__和原型的關系。(指向了構造函數的原型)

例:

1.2如果原型中的屬性和構造函數中的屬性重名,會優先訪問構造函數中的屬性 

function Person(name) {
    this.name1 = name;
};
Person.prototype.name1 = "test1";
Person.prototype.name2 = "test2";

var per1 = new Person("name1");
alert(per1.name1);//訪問到的是實例對象中的name1屬性“name1”
delete per1.name1;//刪除實例對象中的name1屬性
alert(per1.name1);//訪問類型原型中的name1屬性“test1”

alert(per1.name2);//訪問原型屬性“name2”
per1.name2 = "name2";//這里並不是修改了原型屬性“name2”的值,而是為實例對象動態添加了一個“name2”的屬性,並賦值。
alert(per1.name2);//訪問實例屬性“name2”
delete per1.name2;
alert(per1.name2);//訪問原型屬性“name2”

例:

如圖:

 

1.3使用字面量表示法為原型統一添加方法和屬性

我們上面看到定義原型的屬性和方法都是一個個定義的,看上去明顯感覺雜亂。其實我們也可以通過字面量的方式為原型添加屬性或方法,如:

function Person() { }
Person.prototype = {
    name1: "張三",
    age: 23,
    sayHi: function () {
        alert(this.name1);
    }
}
var per1 = new Person();
per1.name1 = "李四"
per1.sayHi()

 

缺點:這樣的定義,相當與重寫了對象類型的prototype屬性,也就是我們再也訪問不到 constructor 屬性了。

當然,我也可以手動設置,如:

function Person() { }
Person.prototype = {
    constructor: Person,//手動設置constructor賦值Person
    name1: "張三",
    age: 23,
    sayHi: function () {
        alert(this.name1);
    }
}
var per1 = new Person();
per1.name1 = "李四"
per1.sayHi()

 

我們通過在構造函數中定義屬性,在原型中通過字面量表示法定義方法已經可以很好的創建對象了。唯一的缺點就是分為兩個步驟,那么我們下面試着全部封裝到構造函數中。如: 

function Person(str1, str2, str3) {
    this.name1 = str1;
    this.name2 = str2;
    this.age = str3;
    //方法
    if (typeof this.sayName != "function") {//只用判斷一個就可以了,第一次構造的時候是不能有sayName方法的。
        //在構造函數里面貌似不能通過字面量來為prototype統一賦值
        Person.prototype.sayName = function () {
            alert(this.name1);
        };
        Person.prototype.sayHi = function () {
            alert(this.name2);
        };
    }
}

var per1 = new Person("張三","李四","12");
per1.sayName();

 

我們平時常用的一些創建對象的方式就介紹到這里了。

下一篇繼續分析對象的繼承,歡迎大家繼續關注。

這是學習記錄,不是教程。文中錯誤難免,您可以指出錯誤,但請不要言辭刻薄。

原文鏈接:http://haojima.net/zhaopei/516.html

本文已同步至目錄索引:一步步學習javascript

歡迎上海“程序猿/媛”、"攻城獅"入群:【滬猿】229082941 入群須知

歡迎對個人博客感興趣的道友加入群:【嗨-博客】469075305 入群須知

如果您覺得文章對您有那么一點點幫助,那么麻煩您輕輕的點個贊,以資鼓勵。

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM