javascript高級程序設(shè)計讀書筆記(五)

面向?qū)ο蟮某绦蛟O(shè)計

NO.1 對象

OO語言都有一個標志,就是都有類的概念,通過類可以創(chuàng)建多個具有相同屬性的方法的對象。但JS里沒有類的概念,所以它的對象也有所不同。JS把對象定義為無序?qū)傩缘募稀?/p>

JS的每個對象都是基于一個引用類型創(chuàng)建的。

JS中有兩種屬性,數(shù)據(jù)屬性和訪問器屬性;
1.數(shù)據(jù)屬性:
數(shù)據(jù)屬性包含一個數(shù)據(jù)值的位置。在這個位置可以讀取和寫入值。數(shù)據(jù)屬性有四個描述其行為的特性:
Configurable:表示能否通過delete刪除屬性從而重新定義屬性。
Enumerable: 表示能否通過for-in循環(huán)返回屬性。
Writable: 表示能否修改屬性的值。
Value: 包含這個屬性的數(shù)據(jù)值。

這四個是對象的默認屬性。要修改默認是屬性的特性,可以用Object.defindProperty()方法.例如:把屬性定義為只讀,把屬性定義為不能刪除等。

var book = {
_year: 2004,
edtion: 1
}
_year是一種常見的記號,表示中能通過對象方法訪問的屬性。

創(chuàng)建對象:
雖然Object構(gòu)造函數(shù)和字面的方法都可以用來創(chuàng)建單個對象,但是這些方法都有明顯的缺點,就是當一個接口創(chuàng)建很多對象時,會有很多重復(fù)代碼。為了解決這個問題,人們使用工廠模式的一種變體。

工廠模式抽象了創(chuàng)建對象的具體過程,例如:

function createPerson(name, age, job){
    var o = new Object();
    o.name = name;
    p.age = age;
    o.job = job;
    o.sayname = function(){
        alert(this.name);
    };
    return o;
}

工廠模式雖然解決了對個類似對象的問題,但是沒有解決對象識別問題(怎么樣知道一個對象的類型)。于是有了構(gòu)造函數(shù)模式:

function Person(name,age,job){
    this.name = name;
    this.age = age;
    this.job = job;
    this.sayName = function(){
        alert(this.name);
    };
}

var person1 = new Person("yzq", 25, "software engineer");

構(gòu)造函數(shù):
構(gòu)造函數(shù)和其他的函數(shù)的唯一區(qū)別是調(diào)用的方式不同,構(gòu)造函數(shù)都是通過new來調(diào)用的。

構(gòu)造函數(shù)的問題:
構(gòu)造函數(shù)雖然好用,但是在用構(gòu)造函數(shù)創(chuàng)建對象時,當對象有方法時,就需要在每個構(gòu)造函數(shù)里創(chuàng)建一個方法。為了解決這一個問題。將函數(shù)定義轉(zhuǎn)移到構(gòu)造函數(shù)的外面:

function Person(name,age,job){
    this.name = name;
    this.age = age;
    this.job = job;
    this.sayName = sayName
}
function sayName(){
        alert(this.name);
};

var person1 = new Person("yzq", 25, "software engineer");
        

原型模式:
我們創(chuàng)建的每個函數(shù)都有一個prototype(原型)屬性,這個屬性是一個指針,指向一個對象,這個對象包含由特定類型所有實例共享的屬性和方法。
例子:
function Person(){};
Person.prototyoe.name = "yzq";
Person.prototyoe.age = 25;
Person.prototyoe.job = "cc"
Person.prototyoe.sayName = function(){
alert(this.name);
};

var person1 = new Person();
person1.sayName(); //"yzq"

var person2 = new Person();
person2.sayName(); //"yzq"
通過這種方式,將屬性和方法共享。

理解原型對象:
1.只要一個新函數(shù)被創(chuàng)建時,就會根據(jù)一組的特定的規(guī)則為該函數(shù)創(chuàng)建一個prototype屬性。
2.這個prototype屬性是一個指針,指向函數(shù)的原型對象。
3.然后在默認情況下,原型對象都會自動獲得一個構(gòu)造函數(shù)屬性。這個屬性是指向prototype屬性所在函數(shù)的指針。例如:Person.prototype.constructor指向Person
通過構(gòu)造函數(shù),可以繼續(xù)為原型對象添加其他屬性和方法。
4.在創(chuàng)建了自定義的構(gòu)造函數(shù)之后,其原型對象默認只會取得constructor。其他方法,都是從Object繼承而來
5.當用構(gòu)造函數(shù)創(chuàng)建了一個新實例后,實例內(nèi)部將包含一個指針為[[Prototype]]

個人理解:
Person.prototype指向原型對象,原型對象的構(gòu)造函數(shù)Person.prototype.constructor又指回了Person。原型對象中除了包含構(gòu)造函數(shù)屬性之外,還有其他的屬性。
而真正的每個實例都包含一個內(nèi)部屬性,數(shù)值只指向Person.prototype。實例和構(gòu)造函數(shù)是沒有任何直接聯(lián)系的。

當代碼讀取某個對象的某個屬性時,都會先從對象實例本身開始,如果在實例本身中找到了給定的名字的屬性,則返回屬性的值,如果找不到,則繼續(xù)搜索指針指向的原型對象。例如:在調(diào)用person1.sayName()的時候,在person1中沒有找到sayName屬性,但是在person1的原型中有sayName屬性。

另外,當對象實例可以訪問原型中的屬性,但是不能重寫原型中的屬性。所以當對象實例中有原型中的同名屬性時,實例中的該同名屬性會屏蔽原型里的屬性。要想重新訪問原型里的屬性,可以用DELETE刪除實例中的屬性。

NO.2 原型和in操作符

有兩種方式使用in操作符,單獨使用和在for-in循環(huán)中使用。
單獨使用時,in操作符會通過對象能夠訪問給定屬性時,返回true。無論該屬性存在實例還是原型中。

alert("name in person1")一直都是true,當屬性存在時。不管在實例中還是原型對象里。

要想確定屬性在對象中還是存在原型中,可以同時使用hasOwnProperty()和in操作符。方法如下:
function hasPrototypeProperty(object, name)[
return !object.hasOwnproperty(name) && (name in object);
}
hasOwnproperty只有在實例中才返回true。所以以上那個函數(shù)為true時,可以判斷屬性是在原型中。

要獲取對象上所有可枚舉的實例屬性,可以使用Object.keys()方法。這個方法接受一個對象作為參數(shù),返回一個所有屬性的字符串數(shù)組

var keys = Object.keys(Person.prototype);
alert(keys); //"name,age,job,sayName"

var p1 = new Person()
p1.name = "yzq"
p1.age = 25;
var p1keys = Object.keys(p1);
alert(p1keys); // "name,age"

更簡單的原型語法:

function Person(){
}
Person.prototype = {
    name : "yzq",
    age : 25,
    sayName : function(){
        alert(this.name);
    }
}

注意:使用上面語法會導致constructor的屬性等于Object而不等于Person了。
如果constructor真的很重要,可以自己定義。

function Person(){
}
Person.prototype = {
    constructor : Person,
    name : "yzq",
    age : 25,
    sayName : function(){
        alert(this.name);
    }
}

NO.3原型的動態(tài)性

先創(chuàng)建實例,在修改原型,指在原型里添加屬性和方法。這樣是OK的。

var friend = new Preson();
Person.prototype.sayHi = function(){
 alert("hi");
}
friend.sayHi(); //"hi"

但是先創(chuàng)建實例,在重新寫整個原型。這樣就會報錯。

function Person(){
}
var friend = new Preson();
Person.prototype = {
    constructor : Person,
    name :"yzq",
    age :25,
    sayName: function(){
        alert(this.name);
    }
}

friend.sayName(); //error

NO.4原型模式的缺點

因為原型模式中的屬性和方法都是共用的,所以,當一個實例修改原型模型中的屬性時,其他實例中的屬性也會改變。

所以原型模式和構(gòu)造函數(shù)模式一般是組合使用:


NO.5 動態(tài)原型模式

為了解決同時寫構(gòu)造函數(shù)和原型這個問題,于是出現(xiàn)了動態(tài)原型模式。

function person(name,age,job){
    //屬性
    this.name = name;
    this.age = age;
    this.job = job
    //方法
    if (typeof this.sayName != "function"){
        Person.prototype.sayName = function(){
            alert(this.name);
        };
    }
}

typeof this.sayName != "function"指的是當sayName方法不存在的時候,將方法放入原型中。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容