成人国产在线小视频_日韩寡妇人妻调教在线播放_色成人www永久在线观看_2018国产精品久久_亚洲欧美高清在线30p_亚洲少妇综合一区_黄色在线播放国产_亚洲另类技巧小说校园_国产主播xx日韩_a级毛片在线免费

資訊專(zhuān)欄INFORMATION COLUMN

JS面向?qū)ο蟮某绦蛟O(shè)計(jì)之創(chuàng)建對(duì)象_工廠(chǎng)模式,構(gòu)造函數(shù)模式,原型模式-1

incredible / 997人閱讀

前言:最近在細(xì)讀Javascript高級(jí)程序設(shè)計(jì),對(duì)于我而言,中文版,書(shū)中很多地方翻譯的差強(qiáng)人意,所以用自己所理解的,嘗試解讀下。如有紕漏或錯(cuò)誤,會(huì)非常感謝您的指出。文中絕大部分內(nèi)容引用自《JavaScript高級(jí)程序設(shè)計(jì)第三版》。 2. 原型對(duì)象與in操作符

有兩種方式使用in操作符: 多帶帶使用和在for-in循環(huán)中使用。

多帶帶使用時(shí),in操作符會(huì)通過(guò)對(duì)象能夠訪(fǎng)問(wèn)給定屬性時(shí)返回true。

==無(wú)論該屬性存在實(shí)例中還是原型中。==

請(qǐng)看下下面的例子

function Person(){
}
Person.prototype.name = "Shaw";
Person.prototype.age = 18;
Person.prototype.sayName = function(){
    console.log(this.name);
}
var person1 = new Person();
var person2 = new Person();

console.log(person1.hasOwnProperty("name")); // false , 實(shí)例對(duì)象person1沒(méi)有name屬性
console.log("name" in person1); // true, "name"屬性存在于構(gòu)造函數(shù)的原型對(duì)象中,所以返回true

person1.name = "Roc";
console.log(person1.name); // "Roc"
console.log(person1.hasOwnProperty("name")); // true
console.log("name" in person1); // true

console.log(person2.name); // "Shaw"
console.log(person2.hasOwnProperty("name")); // false;
console.log("name" in person2); // true

delete person1.name
console.log(person1.hasOwnProperty("name")); //false
console.log(person1.name); // "Shaw"
console.log("name" in person1); //true

在以上代碼執(zhí)行的整個(gè)過(guò)程中, name屬性要么是直接在實(shí)例對(duì)象上訪(fǎng)問(wèn)到的,要么是通過(guò)構(gòu)造函數(shù)的原型對(duì)象訪(fǎng)問(wèn)到的。

因此, 調(diào)用“name” in person1 始終返回true,無(wú)論該屬性存在于實(shí)例對(duì)象中還是存在于構(gòu)造函數(shù)的原型對(duì)象中。

同時(shí)使用hasOwnProperty()方法和in操作符,就可以確定該屬性到底是存在于對(duì)象中,還是存在于原型對(duì)象中。

//確定屬性是否只存在于原型對(duì)象中。

function isPrototypeProperty(object, property) {
    return !object.hasOwnProperty(property) && (property in object);
}

由于in操作符只要通過(guò)對(duì)象能夠訪(fǎng)問(wèn)到屬性就返回true,因此只要in操作符返回true而實(shí)例對(duì)象的hasOwnProperty()方法返回false,就可以確定屬性是原型中的屬性。

function Person(){   
}
Person.prototype.name = "Shaw";
Person.prototype.age = 19;
Person.prototype.job = "Designer";
Person.prototype.sayName = function(){
    console.log(this.name);
}

var person1 = new Person();
console.log(isPrototypeProperty(person1, "name")); // true;

person1.name = "Roc";
console.log(isPrototypeProperty("person1", "name")); false;

在這里,name屬性存在于Person.prototype中,因此isPrototypeProperty()方法返回true。

當(dāng)在實(shí)例中重寫(xiě)name屬性后,該屬性就存在于實(shí)例中,因此isPrototypeProperty()返回false。

在使用for-in循環(huán)時(shí),返回的是所有能夠通過(guò)對(duì)象訪(fǎng)問(wèn) 且 可枚舉的(enumerated) 屬性,其中既包括存在于實(shí)例中對(duì)象的屬性,也包括存在于原型中的屬性。

==回想下,in操作符的定義, 只要屬性存在于對(duì)象中,就會(huì)返回true。==

屬性的枚舉特性(即將屬性的特性[[Enumerable]]的值標(biāo)記為false)標(biāo)記為false的話(huà),就不可被枚舉了!!

要取得對(duì)象上所有可枚舉的屬性,可以使用ECMAScript 5的Object.keys()方法。 這個(gè)方法接收一個(gè)對(duì)象作為參數(shù), 返回一個(gè)包含所有可枚舉屬性的字符竄數(shù)組。

function Person(){
}
Person.prototype.name = "Shaw";
Person.prototype.age = 19;
Person.prototype.job = "Soft Engineer";
Person.prototype.sayName = function() {
    console.log(this.name);
}

console.log(Object.keys(Person.prototype)); //?["name", "age", "job", "sayName"]

Object.defineProperty(Person.prototype, "name", {
    enumerable: false
})

Object.getOwnPropertyDescriptor(Person.prototype, "name").enumerable; //false

console.log(Object.keys(Person.prototype));  //name屬性的枚舉特性變?yōu)閒alse了,所以?[ "age", "job", "sayName"];

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

for(var prop in Person.prototype) {
    console.log(prop); // "age", "job", "sayName"
}

這里,變量keys將保存一個(gè)數(shù)組,數(shù)組中是字符竄“age”,“job” 和“sayName”。 這個(gè)順序也是他們?cè)趂or-in循環(huán)出現(xiàn)的順序。

如果是通過(guò)Person的實(shí)例對(duì)象調(diào)用,則返回的實(shí)例對(duì)象屬性。

如果想要得到所有實(shí)例屬性, 無(wú)論它是否枚舉,可以使用方法Object.getOwnPropertyNames()。

Object.getOwnPropertyNames(Person.prototype);  //["constructor", "name", "age", "job", "sayName"]

注意結(jié)果中包含了不可枚舉的constructor和name屬性。

Object.keys()和Object.getOwnPropertyNames()都可以用來(lái)代替for-in循環(huán)。

區(qū)別在于Object.keys(), 只會(huì)返回參數(shù)(對(duì)象)的可枚舉屬性,而Object.getOwnPropertyNames()返回所有傳入對(duì)象參數(shù)的所有屬性。

3. 更簡(jiǎn)單的原型語(yǔ)法

可以注意到,前面例子中每添加一個(gè)屬性和方法就要敲一遍 Person.prototype。

為減少不必要的輸入,也為了從視覺(jué)上更好地封裝原型的功能, 更常見(jiàn)的做法是用一個(gè)包含屬性和方法的對(duì)象字面量來(lái)重寫(xiě)整個(gè)原型對(duì)象。

function Person(){
}
Person.prototype = {
    name: "Shaw",
    age: 25,
    job: "Designer",
    sayName : function(){
        console.log(this.name);
    }
}

在上面的代碼中,我們將Person.prototype賦值為一個(gè)以對(duì)象字面量形式創(chuàng)建的新對(duì)象。

最終結(jié)果一樣,==但有一個(gè)例外: constructor屬性不再指向Person了。==

==前面提到過(guò),每創(chuàng)建一個(gè)函數(shù),就會(huì)創(chuàng)建它的prototye屬性(該屬性是一個(gè)對(duì)象),這個(gè)對(duì)象也會(huì)自動(dòng)獲得一個(gè)constructor屬性指向函數(shù)本身。==

==而我們?cè)谶@里使用的語(yǔ)法(對(duì)象字面量形式),本質(zhì)上完全重寫(xiě)了默認(rèn)的prototype對(duì)象,==

==變成一個(gè)全新的對(duì)象,因此constructor屬性也就變成了新對(duì)象的constructor屬性(指向Object構(gòu)造函數(shù)), 不再指向Person函數(shù)了。==

==此時(shí),盡管instanceof操作符還能返回正確的結(jié)果,但通過(guò)constructor已經(jīng)無(wú)法確定對(duì)象的類(lèi)型了。==

function Person(){
}

//字面量賦值給prototype屬性的偽代碼過(guò)程
//Person.prototype = new Object(); 改寫(xiě)了prototype
//Person.prototype.__proto__.constructor = function Object(){}
//Person.prototype.name
//....
//
//所以prototype此時(shí)的constructor指向了Object;
//不再指向Person了。

Person.prototype = {
    name: "Shaw",
    age: 19,
    job: "Designer",
    sayName: function(){
        console.log(this.name);
    }
}

var person1 = new Person();

console.log(person1 instanceof Person); // true
console.log(person1 instanceof Object); // true

console.log(person1.constructor == Person); // false
console.log(person1.constructor == Object); // true

在此,用instanceof操作符,測(cè)試Object和Person仍然返回true,但constructor屬性則等于Object而不等于Person了。

如果constructor的值真的很重要,可以像下面這樣特意將它設(shè)置回適當(dāng)?shù)闹怠?/p>

function Person(){
}

// Person.prototype還是被改寫(xiě)了!
Person.prototype = {
    constructor: Person,
    name: "Shaw",
    age: 25,
    job: "Designer",
    sayName: function(){
        console.log(this.name);
    }
}

以上代碼特意包含了一個(gè)constructor屬性,并將它的值設(shè)置為Person,從而確保了通過(guò)概述性能夠訪(fǎng)問(wèn)到適當(dāng)?shù)闹怠?/p>

==注意, 以這種方式重設(shè)constructor屬性會(huì)導(dǎo)致它的[[Enumerable]]特性被設(shè)置為true。默認(rèn)情況下,原生的constructor屬性是不可枚舉的。因此,如果你使用兼容ECMAScript 5的JavaScrpt引擎, 可以試一試Object.defineProperty()。

function Person(){
}

Person.prototype = {
    name: "Shaw",
    age: 28,
    job: "Designer",
    sayName: function () {
        console.log(this.name);
    }
}; 

Object.defineProperty(Person.prototype, "constructor", {
    enumerable: false,
    value: Person
})
4. 原型的動(dòng)態(tài)性

由于在函數(shù)的原型對(duì)象中查找值的過(guò)程是一次搜索,因此我們對(duì)原型對(duì)象所做的任何修改都能夠立即從實(shí)例上反應(yīng)出來(lái)- 即使是先創(chuàng)建了實(shí)例后修改原型對(duì)象也照樣如此。

function Person(){
}

var friend1 = new Person();

Person.prototype.name = "Shaw";
Person.prototype.sayName = function(){
    console.log(this.name);
}

friend1.sayName(); // "Shaw", no problem.

以上代碼創(chuàng)建了Person的一個(gè)實(shí)例對(duì)象, 并將其保存在變量friend1中,下一條語(yǔ)句在Person的prototype中添加一個(gè)方法sayName()。

即使friend1實(shí)例對(duì)象是在添加新方法之前創(chuàng)建的,但它仍然可以訪(fǎng)問(wèn)這個(gè)新方法。

其原因可以歸結(jié)為實(shí)例與原型之間松散連接關(guān)系。 當(dāng)我們調(diào)用friend1.sayName()時(shí),首先會(huì)在實(shí)例對(duì)象中搜索名為sayHi的屬性,沒(méi)有找到的話(huà),會(huì)計(jì)算搜索原型對(duì)象。

==因?yàn)閷?shí)例對(duì)象與原型之間的連接只不過(guò)是一個(gè)指針,而非一個(gè)副本。==

因此可以在原型對(duì)象中找到新的sayName屬性并返回保存在原型中的函數(shù)。

盡管可以隨時(shí)為原型添加屬性和方法,并且修改能夠立即在所有實(shí)例中反映出來(lái),但如果是重寫(xiě)整個(gè)原型對(duì)象,那么情況就不一樣了。

==調(diào)用構(gòu)造函數(shù)時(shí)會(huì)為實(shí)例對(duì)象添加一個(gè)指針[[prototype](__proto__), 這個(gè)指針指向構(gòu)造函數(shù)最初的原型對(duì)象。而把原型修改為另外一個(gè)對(duì)象,實(shí)例對(duì)象的指針理所當(dāng)然的指向了新對(duì)象。==

==一定要記住: 實(shí)例對(duì)象的[[prototype]](__proto__)指向的是構(gòu)造函數(shù)的原型對(duì)象。==

function Person(){
}

var friend = new Person(); // friend.__proto__ => Person.prototype 指向沒(méi)改寫(xiě)之前的原型對(duì)象

console.log(Person.prototype); //{constructor: ?}

Person.prototype = {
    constructor: "Person",
    name: "Shaw",
    age: "19",
    job: "Designer",
    sayName: function(){
        console.log(this,name);
    }
}

console.log(Person.prototype); //{constructor: "Person", name: "Shaw", age: "19", job: "Designer", sayName: ?}
// Person.prototype => new Object(); 改寫(xiě)了, 指向一個(gè)新對(duì)象。


friend.sayName(); // 還是指向沒(méi)改成之前的原型對(duì)象,里面沒(méi)有sayName屬性,Uncaught TypeError: friend.sayName is not a function

重寫(xiě)原型對(duì)象切斷了與任何之前已經(jīng)存在的實(shí)例對(duì)象之間的聯(lián)系。它們?nèi)匀灰玫氖亲畛醯脑汀?/p> 5. Javascript原生對(duì)象的原型

原型模式的重要性不僅體現(xiàn)在創(chuàng)建自定義類(lèi)型方面,就連所有原生的原生引用類(lèi)型,都是采用這種模式創(chuàng)建的。

所有原生引用類(lèi)型(object、Array、String等等)都在其構(gòu)造函數(shù)的原型上定義了方法。

例如, 在Array.prototype中可以找到sort()方法,而在String.prototype中可以找到substring()方法。

// 偽代碼

function Array(arguments) {
}
Array.prototype.sort = function(){};

var arr = new Array(1,3,2);

arr.sort();



function String(arguments) {
}
String.prototype.substring = function(){};

var str = new String("abc");

str.substring();


console.log(typeof Array.prototype.sort); // function
console.log(typeof String.prototype.substring); // function

通過(guò)原生引用對(duì)象的原型,不僅可以取得所有默認(rèn)方法的引用,而且也可以定義新方法。

可以像修改自定義對(duì)象的原型一樣修改原生對(duì)象的原型,因此可以隨時(shí)添加方法。 下面的代碼就給基本包裝類(lèi)型String添加一個(gè)名為startWith()的方法。

String.prototype.startsWith = function(text) {
    return this.indexOf(text) == 0;
}

var str = "Hello World";
console.log(str.startsWith("Hello")); // true

這里新定義的startsWith()會(huì)在傳入的文本位于一個(gè)字符竄開(kāi)始時(shí),返回true。既然方法被添加給了String.prototype, 那么當(dāng)前環(huán)境中的所有字符竄都可以調(diào)用它。 由于str是字符竄,而且后臺(tái)會(huì)調(diào)用String基本包裝函數(shù)創(chuàng)建這個(gè)字符竄,因此通過(guò)str是可以調(diào)用startsWith()放的。

== 盡管可以這樣做,但是不推薦這種方法(修改原生對(duì)象的原型)==

如果實(shí)現(xiàn)中缺少某個(gè)方法,就在原生對(duì)象的原型上添加這個(gè)方法,那么當(dāng)在另一個(gè)支持該方法的實(shí)現(xiàn)中運(yùn)行代碼時(shí),就可能會(huì)導(dǎo)致命名沖突。 而且這樣做也可能會(huì)意外的重寫(xiě)原生方法。

6. 原型模式的問(wèn)題

任何模式都有優(yōu)缺點(diǎn), 那么原型模式的缺點(diǎn)呢?

首先,它省略了為構(gòu)造函數(shù)傳遞初始化參數(shù)這一環(huán)節(jié),結(jié)果所有實(shí)例在默認(rèn)情況下都將獲得相同的屬性值。 雖然這會(huì)在某種程度上帶來(lái)一些不方便,但還不是原型模式的最大問(wèn)題,原型模式的最大問(wèn)題是由其共享的本性導(dǎo)致的。

原型中所有屬性是被很多實(shí)例共享的,這種共享對(duì)函數(shù)非常適合。

對(duì)于那些包含基本值的屬性也說(shuō)得過(guò)去,畢竟通過(guò)在實(shí)例上添加一個(gè)同名屬性,可以隱藏原型中的對(duì)應(yīng)屬性。

==然而對(duì)于包含引用類(lèi)型值的屬性來(lái)說(shuō), 問(wèn)題就比較突出了。==

function Person() {
}

Person.prototype = {
    constructor: Person,
    name: "Shaw",
    job: "Designer",
    friends: ["Roc", "Van"],
    sayName: function(){
        console.log(this.name);
    }
}

var person1 = new Person();
var person2 = new Person();
person1.friends.push("Avich");

console.log(person1.friends); //["Roc", "Van", "Avich"]
console.log(person2.friends); //["Roc", "Van", "Avich"]

console.log(person1.friends == person2.friends); // true

在此,Person.prototype對(duì)象有一個(gè)friends屬性,該屬性的值為一個(gè)字符竄數(shù)組。
然后創(chuàng)建了兩個(gè)Person的實(shí)例對(duì)象。 接著,修改person1.friends引用的數(shù)組,向數(shù)組添加了一個(gè)字符竄。
由于friends數(shù)組存在于Person.prototype而非person1當(dāng)中,所以剛剛提到的修改也會(huì)通過(guò)person2.friends(與person1.friends指向同一個(gè)數(shù)組)反映出來(lái)。

假如我們的初衷是在所有實(shí)例中共享一個(gè)數(shù)組,那么是適用的。

但是一般情況下,實(shí)例對(duì)象都是要有屬于自己的全部屬性的。 而這個(gè)問(wèn)題,正是我們很少看到有人多帶帶使用原型模式的原因所在。

文章版權(quán)歸作者所有,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。

轉(zhuǎn)載請(qǐng)注明本文地址:http://systransis.cn/yun/98299.html

相關(guān)文章

  • JS面向對(duì)象編程封裝

    摘要:在基于原型的面向?qū)ο蠓绞街?,?duì)象則是依靠構(gòu)造函數(shù)和原型構(gòu)造出來(lái)的。來(lái)看下面的例子優(yōu)點(diǎn)與單純使用構(gòu)造函數(shù)不一樣,原型對(duì)象中的方法不會(huì)在實(shí)例中重新創(chuàng)建一次,節(jié)約內(nèi)存。 我們所熟知的面向?qū)ο笳Z(yǔ)言如 C++、Java 都有類(lèi)的的概念,類(lèi)是實(shí)例的類(lèi)型模板,比如Student表示學(xué)生這種類(lèi)型,而不表示任何具體的某個(gè)學(xué)生,而實(shí)例就是根據(jù)這個(gè)類(lèi)型創(chuàng)建的一個(gè)具體的對(duì)象,比如zhangsan、lisi,由...

    YFan 評(píng)論0 收藏0
  • JavaScript面向對(duì)象程序設(shè)計(jì)

    摘要:目錄導(dǎo)語(yǔ)理解對(duì)象和面向?qū)ο蟮某绦蛟O(shè)計(jì)創(chuàng)建對(duì)象的方式的繼承機(jī)制原型對(duì)象原型鏈與原型對(duì)象相關(guān)的方法小結(jié)導(dǎo)語(yǔ)前面的系列文章,基本把的核心知識(shí)點(diǎn)的基本語(yǔ)法標(biāo)準(zhǔn)庫(kù)等章節(jié)講解完本章開(kāi)始進(jìn)入核心知識(shí)點(diǎn)的高級(jí)部分面向?qū)ο蟮某绦蛟O(shè)計(jì),這一部分的內(nèi)容將會(huì)對(duì)對(duì)象 目錄 導(dǎo)語(yǔ) 1.理解對(duì)象和面向?qū)ο蟮某绦蛟O(shè)計(jì) 2.創(chuàng)建對(duì)象的方式 3.JavaScript的繼承機(jī)制 3.1 原型對(duì)象 3.2 原型鏈 3.3 與...

    gitmilk 評(píng)論0 收藏0
  • JS面向對(duì)象程序設(shè)計(jì)_創(chuàng)建對(duì)象組合使用模式-2

    摘要:組合使用構(gòu)造函數(shù)模式和原型模式創(chuàng)建自定義類(lèi)型的最常見(jiàn)方式,就是組合使用構(gòu)造函數(shù)模式與原型模式。也就是說(shuō),寄生構(gòu)造函數(shù)模式下,構(gòu)造函數(shù)創(chuàng)建的對(duì)象與在構(gòu)造函數(shù)外創(chuàng)建的對(duì)象沒(méi)有什么不同。 前言 最近在細(xì)讀Javascript高級(jí)程序設(shè)計(jì),對(duì)于我而言,中文版,書(shū)中很多地方翻譯的差強(qiáng)人意,所以用自己所理解的,嘗試解讀下。如有紕漏或錯(cuò)誤,會(huì)非常感謝您的指出。文中絕大部分內(nèi)容引用自《JavaScri...

    xuexiangjys 評(píng)論0 收藏0
  • JS面向對(duì)象程序設(shè)計(jì)_創(chuàng)建對(duì)象_工廠(chǎng)模式,構(gòu)造函數(shù)模式,原型模式-0

    摘要:構(gòu)造函數(shù)模式中的構(gòu)造函數(shù)可以創(chuàng)建特定類(lèi)型的對(duì)象。默認(rèn)情況下,所有的函數(shù)原型對(duì)象都會(huì)自動(dòng)獲得一個(gè)構(gòu)造函數(shù)屬性,該屬性指向?qū)傩运诤瘮?shù)。要明確的一點(diǎn),這個(gè)連接存在于實(shí)例對(duì)象與構(gòu)造函數(shù)的原型對(duì)象之間,而不是存在于實(shí)例對(duì)象與構(gòu)造函數(shù)之間。 前言:最近在細(xì)讀Javascript高級(jí)程序設(shè)計(jì),對(duì)于我而言,中文版,書(shū)中很多地方翻譯的差強(qiáng)人意,所以用自己所理解的,嘗試解讀下。如有紕漏或錯(cuò)誤,會(huì)非常感謝...

    RaoMeng 評(píng)論0 收藏0
  • js面向對(duì)象入門(mén)

    摘要:簡(jiǎn)單來(lái)理解對(duì)象就是由屬性和方法來(lái)組成的面向?qū)ο蟮奶攸c(diǎn)封裝對(duì)于一些功能相同或者相似的代碼,我們可以放到一個(gè)函數(shù)中去,多次用到此功能時(shí),我們只需要調(diào)用即可,無(wú)需多次重寫(xiě)。 什么是對(duì)象 我們先來(lái)看高程三中是如何對(duì)對(duì)象進(jìn)行定義的 無(wú)序?qū)傩缘募?,其屬性可以包括基本值、?duì)象或者函數(shù),對(duì)象是一組沒(méi)有特定順序的的值。對(duì)象的沒(méi)個(gè)屬性或方法都有一個(gè)俄名字,每個(gè)名字都映射到一個(gè)值。 簡(jiǎn)單來(lái)理解對(duì)象就是由屬...

    sihai 評(píng)論0 收藏0

發(fā)表評(píng)論

0條評(píng)論

最新活動(dòng)
閱讀需要支付1元查看
<