摘要:構(gòu)造函數(shù)模式中的構(gòu)造函數(shù)可以創(chuàng)建特定類型的對(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ì)于我而言,中文版,書中很多地方翻譯的差強(qiáng)人意,所以用自己所理解的,嘗試解讀下。如有紕漏或錯(cuò)誤,會(huì)非常感謝您的指出。文中絕大部分內(nèi)容引用自《JavaScript高級(jí)程序設(shè)計(jì)第三版》。
雖然可以通過Object構(gòu)造函數(shù)或?qū)ο笞置媪縼?lái)創(chuàng)建單個(gè)對(duì)象。
但是這些方式有明顯的缺點(diǎn): 使用同一個(gè)接口創(chuàng)建很多對(duì)象,會(huì)產(chǎn)生大量重復(fù)的代碼,從性能角度來(lái)講,也會(huì)占用大量的內(nèi)存。 為了解決這個(gè)問題,人們開始解決使用工廠模式。那什么是工廠模式呢?
工廠模式是計(jì)算機(jī)領(lǐng)域一種廣為人知的設(shè)計(jì)模式,這種設(shè)計(jì)模式抽象了創(chuàng)建具體對(duì)象的過程??紤]到在ECMAScript中無(wú)法創(chuàng)建類,開發(fā)者們就發(fā)明了一種函數(shù),用函數(shù)來(lái)封裝以特定接口創(chuàng)建對(duì)象的細(xì)節(jié)。
function createPerson(name, age, gender, job) { var o = new Object(); o.name = name; o.age = age; o.gender = gender; o.job = job; o.sayName = function(){ console.log(this.name); } return o; }
函數(shù)createPerson能夠根據(jù)接受的參數(shù)來(lái)構(gòu)建一個(gè)包含所有必要信息的Person對(duì)象。 可以無(wú)數(shù)次地調(diào)用這個(gè)函數(shù),而每次都會(huì)返回帶有四個(gè)屬性和一個(gè)方法的對(duì)象。
就像工廠里生產(chǎn)產(chǎn)品的模具一樣,使用模具,就會(huì)生產(chǎn)出一個(gè)產(chǎn)品。
工廠模式,雖然解決了創(chuàng)建多個(gè)類似對(duì)象的問題,但卻沒有解決對(duì)象識(shí)別的問題, 別人沒看到你的源碼之前,怎么知道創(chuàng)建對(duì)象的類型呢?(即怎么知道一個(gè)對(duì)象的類型)。 隨著javaScript的發(fā)展,又一個(gè)新設(shè)計(jì)模式出現(xiàn)。
構(gòu)造函數(shù)模式ECMAScript中的構(gòu)造函數(shù)可以創(chuàng)建特定類型的對(duì)象。像Object和Array這樣的原生構(gòu)造函數(shù), 在運(yùn)行時(shí)會(huì)自動(dòng)出現(xiàn)在執(zhí)行環(huán)境中。
我們也可以創(chuàng)建自定義的構(gòu)造函數(shù),從而定義 - 自定義對(duì)象類型的屬性和方法。
例如: 我們可使用構(gòu)造函數(shù)模式將前面的例子重寫如下。
function Person(name, age, gender, job) { this.name = name; this.age = age; this.gender = gender; this.job = job; this.sayName = function() { console.log(this.name); } } var person1 = new Person("Shaw", "Secret, "Male", "Designer"); var person2 = new Person("Roc", 18, "Female", "Engineer");
在這個(gè)例子中, Person函數(shù)取代了createPerson函數(shù)。 Person函數(shù)里面的代碼除了和createPerson函數(shù)中相同的部分外,還存在以下不同之處:
沒有顯式地創(chuàng)建對(duì)象
直接將屬性和方法賦予==this對(duì)象==
沒有return語(yǔ)句
此外,還應(yīng)該注意到函數(shù)名Person使用的是大寫字母P。 這又是一種約定俗成,構(gòu)造函數(shù)始終都應(yīng)該以一個(gè)大寫字母開頭, 而非構(gòu)造函數(shù)則應(yīng)該以一個(gè)小寫字母開頭。
這種做法借鑒了其他OO語(yǔ)言, 主要是為了區(qū)別于ECCMAScript中的其他函數(shù)。
==構(gòu)造函數(shù)本身也是函數(shù),只是可以用來(lái)創(chuàng)建對(duì)象而已。==
要?jiǎng)?chuàng)建Person的新實(shí)例對(duì)象,必須使用new操作符。以這種方式調(diào)用構(gòu)造函數(shù)實(shí)際上會(huì)經(jīng)歷一下4個(gè)步驟:
創(chuàng)建一個(gè)新對(duì)象,new操作符的作用。
將構(gòu)造函數(shù)的作用域賦予給新對(duì)象this。
執(zhí)行構(gòu)造函數(shù)中的代碼(為這個(gè)對(duì)象添加屬性,this對(duì)象就指向了這個(gè)新對(duì)象)。
返回新對(duì)象。
//實(shí)例過程偽代碼 var person = new Person(name, age, job); person = function Person(name, age, job) { this = new Object()// this是對(duì)象?。? this.name = name; this.age = age; this.job = job; this.sayName = function(){ return this.name; } return this; }() // this = { // name: name; // age: age; // job: job; // sayName: function() { // return this.name //} //} person = this = { name: name; age: age; job: job; sayName: function() { return this.name } }
在前面例子的最后,person1和person2分別保存著Person的一個(gè)不同的實(shí)例對(duì)象。 這兩個(gè)實(shí)例對(duì)象都有著一個(gè)constructor(構(gòu)造函數(shù)屬性)屬性,該屬性指向Person。
console.log(person1.constructor == Person); // true console.log(person2.constructor == Person); // true
==對(duì)象的constructor屬性最初是用來(lái)標(biāo)識(shí)對(duì)象類型的。==
提到檢測(cè)對(duì)象類型,還是instanceof操作符更可靠一些。 我們?cè)谶@個(gè)例子中創(chuàng)建的所有對(duì)象既是Object的實(shí)例對(duì)象,也是Person的實(shí)例對(duì)象,這一點(diǎn)可以通過instanceof操作符得到驗(yàn)證。
console.log(person1 instanceof Person); // true console.log(person2 instanceof Person); // true console.log(person1 instanceof Object); // true console.log(person2 instanceof Object); // true
創(chuàng)建自定義的構(gòu)造函數(shù)意味著將來(lái)可以將它的實(shí)例標(biāo)識(shí)為一種特定的類型; 而這正是構(gòu)造函數(shù)模式勝過工廠模式的地方。在這個(gè)例子中, person1和person2之所以同時(shí)是Object的實(shí)例,是因?yàn)樗袑?duì)象均繼承自O(shè)bject。
以這種方式定義的構(gòu)造函數(shù), 是定義在Global對(duì)象(在瀏覽器中是window對(duì)象)中的。
將構(gòu)造函數(shù)當(dāng)做普通函數(shù)
構(gòu)造函數(shù)與其他函數(shù)的唯一區(qū)別,就在于調(diào)用它們的方式不同。
==構(gòu)造函數(shù)就是函數(shù),不存在定義構(gòu)造函數(shù)的特殊語(yǔ)法。==
==任何函數(shù), 只要通過new操作符來(lái)調(diào)用,那它就可以作為構(gòu)造函數(shù)。==
==而任何函數(shù),只要不通過new操作符來(lái)調(diào)用,那它就是普通的函數(shù)。==
//當(dāng)做構(gòu)造函數(shù)使用 function Person(name, age, job) { this.name = name; this.age = age; this.job = job; this.sayName = function() { console.log(this.name); } } var person1 = new Person("xxx", 18, "Soft Engineer"); person1.sayName // "xxx" //作為普通函數(shù)調(diào)用 Person("Roc", 27, "Doctor"); window.sayName(); // "Greg" // 在另外一個(gè)對(duì)象的作用域中調(diào)用 var o = new Object(); Person.call(o,"Kris", 25, "Nurse"); o.sayName(); //"Kris";
這個(gè)例子的前兩行代碼展示了構(gòu)造函數(shù)的典型用法,即使用new操作符來(lái)創(chuàng)建一個(gè)新對(duì)象。接下來(lái)的兩行代碼展示了不使用new操作符調(diào)用Person()會(huì)出現(xiàn)什么結(jié)果:屬性和方法都被添加給window對(duì)象了。當(dāng)在全局作用中調(diào)用一個(gè)函數(shù)時(shí),this對(duì)象總是指向Global對(duì)象(瀏覽器中就是window對(duì)象)。因此,在調(diào)用完函數(shù)之后,可以通過window對(duì)象來(lái)調(diào)用sayName()方法,并且返回了“Greg”。最后,也可以使用call()(或者apply())在某個(gè)特殊對(duì)象的作用域調(diào)用Person()函數(shù)。 這里是在對(duì)象o的作用中調(diào)用的,因此調(diào)用后o就擁有了所有屬性和sayName()方法。
構(gòu)造函數(shù)的問題
構(gòu)造函數(shù)模式雖然好用,但也并非沒有缺點(diǎn)。使用構(gòu)造函數(shù)的主要問題在于, 就是每個(gè)方法都要在每個(gè)實(shí)例上重新創(chuàng)建一遍。在前面的例子中, person1和person2都有一個(gè)名為sayName()方法,但那兩個(gè)方法不是同一個(gè)Function的實(shí)例。 不要忘了,ECMAScript中的函數(shù)是對(duì)象,因此每定義一個(gè)函數(shù),也就是實(shí)例化了一個(gè)對(duì)象。 從邏輯角度講,此時(shí)的構(gòu)造函數(shù)也可以這樣定義。
function Person(name, age, job) { this.name = name; this.age = age; this.job = job; this.sayName = new Function("alert(this.name)"); // 與聲明函數(shù)在邏輯上是等價(jià)的。 }
從這個(gè)角度上來(lái)看構(gòu)造函數(shù),更容易明白每個(gè)Person實(shí)例化對(duì)象的sayName()方法,都包含一個(gè)不同的Function實(shí)例的本質(zhì)。換句話說(shuō),以這種話方式創(chuàng)建函數(shù),會(huì)導(dǎo)致不同的作用域鏈和標(biāo)識(shí)符解析,但創(chuàng)建Function新實(shí)例的機(jī)制仍然是相同的。
==不同實(shí)例上的同名函數(shù)是不相等的,一下代碼可以證明這一點(diǎn):==
alert(person1.sayName == person2.sayName); // false
然而,創(chuàng)建兩個(gè)完成同樣任務(wù)的Function實(shí)例的確沒有必要; 況且有this對(duì)象在,根本不用在執(zhí)行代碼前就把函數(shù)綁定到特定對(duì)象上面。因此,大可像下面那樣,通過把函數(shù)定義轉(zhuǎn)移到構(gòu)造函數(shù)外部解決這個(gè)問題。
function Person(name, age, job) { this.name = name; this.age = age; this.job = job; this.sayName = sayName; } function sayName(){ console.log(this.name); } var person1 = new Person("Nancy", 29, "Soft Engineer"); var person2 = new Person("Nanth",27, "Doctor");
在這個(gè)例子中, 我們把sayName函數(shù)的定義轉(zhuǎn)移到了構(gòu)造函數(shù)外部。
而在構(gòu)造函數(shù)內(nèi)部,我們將sayName屬性設(shè)置為全局的sayName函數(shù)。 這樣一來(lái),sayName屬性都包含一個(gè)指向函數(shù)的指針, 因此person1和person2對(duì)象就共享了在全局作用中定義的同一個(gè)sayName()函數(shù)。 這樣做確實(shí)解決了兩個(gè)函數(shù)做同一件事的問題,可是新問題來(lái)了: 在全局定義的函數(shù),實(shí)際上只能被某個(gè)對(duì)象調(diào)用,這樣全局作用域有點(diǎn)名不副實(shí)。而更讓人無(wú)法接受的是: 如果對(duì)象需要定義很多方法,那么就要定義很多個(gè)全局函數(shù),玉石我們這個(gè)自定義的引用類型就絲毫沒有封裝性可言了。
==好在,這些問題可以通過使用原型模式解決==
那么原型模式是什么呢?
原型模式==簡(jiǎn)而言之,每個(gè)函數(shù)都有一個(gè)prototype(原型)屬性,這個(gè)屬性指向一個(gè)對(duì)象,prototype屬性是一個(gè)對(duì)象!==
而這個(gè)對(duì)象的用途是可以包含由特定類型的所有實(shí)例對(duì)象所共享的屬性和方法。
==按照字面意思去理解,prototype就是通過調(diào)用構(gòu)造函數(shù)而創(chuàng)建的那個(gè)實(shí)例對(duì)象的原型對(duì)象。==
==使用原型對(duì)象的優(yōu)點(diǎn),可以讓所有的實(shí)例對(duì)象共享prototype上所包含的屬性和方法。
==換句話說(shuō),不必在構(gòu)造函數(shù)上定義所有實(shí)例對(duì)象的屬性和方法,而是可以把這些信息直接添加到函數(shù)的原型對(duì)象中。==
==記住,ECMAScript中,函數(shù)也是對(duì)象。==
function Person(){ } Person.prototype.name = "Shaw"; Person.prototype.age = 18; Person.prototype.job = "Soft Engineer"; Person.prototype.sayName = function(){ console.log(this.name); } var person1 = new Person(); person1.sayName(); // "Shaw" var person2 = new Person(); person2.sayName(); // "Shaw" console.log(person1.sayName == person2.sayName); // true
理解原型對(duì)象
無(wú)論什么時(shí)候,只要?jiǎng)?chuàng)建一個(gè)新函數(shù),就會(huì)根據(jù)一組特定的規(guī)則為該函數(shù)創(chuàng)建一個(gè)prototype屬性,這個(gè)屬性的值是一個(gè)對(duì)象,所以也可以稱作函數(shù)的原型對(duì)象。默認(rèn)情況下,所有的函數(shù)原型對(duì)象都會(huì)自動(dòng)獲得一個(gè)constructor(構(gòu)造函數(shù))屬性, 該屬性指向prototype屬性所在函數(shù)。 就拿前面的例子來(lái)說(shuō),Person.property.constructor指向Person。 而通過這個(gè)構(gòu)造屬性,我們還可以繼續(xù)為原型對(duì)象添加其他屬性和方法。
==創(chuàng)建自定義的構(gòu)造函數(shù)之后,其原型對(duì)象默認(rèn)只會(huì)取得constructor屬性;至于其他方法,則都是從Object繼承而來(lái)。==
==這也側(cè)面反映出, 函數(shù)在ECMAScript中是個(gè)對(duì)象。==
當(dāng)調(diào)用構(gòu)造函數(shù)創(chuàng)建一個(gè)新實(shí)例對(duì)象之后,該實(shí)例對(duì)象的內(nèi)部將包含一個(gè)指針(__proto__內(nèi)部屬性),指向構(gòu)造函數(shù)的原型對(duì)象。
ECMAScript-262第五版中管這個(gè)指針叫[[Prototype]]。雖然在腳本中沒有標(biāo)準(zhǔn)的方式訪問[[Prototype]], 但Firefox、Safari和Chrome在每個(gè)對(duì)象上都支持一個(gè)屬性__proto__;而在其他實(shí)現(xiàn)中,這個(gè)屬性對(duì)腳本是完全不可見的。
==要明確的一點(diǎn),這個(gè)連接存在于實(shí)例對(duì)象與構(gòu)造函數(shù)的原型對(duì)象之間, 而不是存在于實(shí)例對(duì)象與構(gòu)造函數(shù)之間。==
以使用Person構(gòu)造函數(shù)和Person.prototype創(chuàng)建實(shí)例的代碼為例:
//偽代碼 Person.prototype = { constructor: function Person(arguments){native code}, name: "Shaw", age: "18", job: "Soft Engineer", sayName: function() { console.log(this.name); //這里的this指向prototype } } Person.prototype.constructor => function Person(..){native code} //=> 指向的意思。 person1.__proto__ => Person.prototype person2.__proto__ => Person.prototype
以上偽代碼展示構(gòu)造函數(shù)以及它的原型對(duì)象,與它的兩個(gè)實(shí)例對(duì)象之間的關(guān)系。
在此, Person.prototype 構(gòu)造函數(shù)的原型屬性指向原型對(duì)象, 而Person.prototype.constructor又指回了Person。
原型對(duì)象中除了包含constructor屬性,還包括后來(lái)添加的其他屬性。
Person的每個(gè)實(shí)例對(duì)象-person1和person2都有一個(gè)內(nèi)部屬性,該屬性僅僅指向了Person.prototype;
換句話說(shuō):調(diào)用誰(shuí)創(chuàng)造了實(shí)例對(duì)象, 實(shí)例對(duì)象的__proto__就指向創(chuàng)造者的原型。 很像造人~~
至此,說(shuō)的都是與構(gòu)造函數(shù)的prototype打交道,和構(gòu)造函數(shù)本身沒有任何關(guān)系。
此外,要格外注意的是, 雖然這兩個(gè)實(shí)例都不包含屬性和方法,但我們可以調(diào)用person1.sayName()。這是通過查找對(duì)象屬性的過程來(lái)實(shí)現(xiàn)的。
我們可以通過isPrototypeOf()方法來(lái)確定對(duì)象之間是否存在這種關(guān)系。
從本質(zhì)上講,如果實(shí)例對(duì)象的__proto__屬性指向調(diào)用isPrototype()方法的原型對(duì)象(Person.prototype), 那么這個(gè)方法就返回true。
//示例代碼 function Person(){} var person1 = new Person(); var person2 = new Person(); console.log(Person.prototype.isPrototypeOf(person1)); // true, 實(shí)例化對(duì)象的原型鏈?zhǔn)欠裰赶驑?gòu)造函數(shù)的原型對(duì)象 console.log(Person.prototype.isPrototypeOf(person2)); // true。
我們用原型對(duì)象的isPrototypeOf()方法測(cè)試了person1和person2。因?yàn)閷?shí)例化對(duì)象內(nèi)部都有一個(gè)指向Person.prototype的指針,因此都返回了true。
吐槽- 這個(gè)方法有點(diǎn)難記~
==還好,ECMAScript 5 新增一個(gè)方法,叫Object.getPrototypeOf(), 這個(gè)方法比較直觀好記理解。
在所有的支持的實(shí)現(xiàn)中,這個(gè)返回[[Prototype]]的值。==
function Person(){}; var person1 = new Person(); var person2 = new Person(); var PersonConstructorProto = Object.getPrototypeOf(person1); //獲取實(shí)例對(duì)象指向的構(gòu)造函數(shù)的原型對(duì)象。 console.log(PersonConstructorProto); console.log(PersonConstructorProto == Person.prototype);// true
這里的第一行代碼返回的是Object.getPrototypeOf(實(shí)例對(duì)象)方法得到實(shí)例對(duì)象的原型對(duì)象。
==使用Object.getPrototypeOf()可以方便地獲取實(shí)例對(duì)象的原型對(duì)象,而這在利用原型實(shí)現(xiàn)繼承的情況下是非常重要的。==
支持這個(gè)方法的瀏覽器有IE9+、Firefox 3.5+、Safari 5+、Opera 12+和Chrome。
插一條: 查兼容 可以使用這個(gè)網(wǎng)站 caniuse.com。
每當(dāng)代碼讀取某個(gè)對(duì)象的屬性的時(shí)候,都會(huì)執(zhí)行一次搜索, 目標(biāo)是具有給定名字的屬性。 搜索首先從實(shí)例對(duì)象本身開始。
如果在實(shí)例對(duì)象中找到了具有給定名字的屬性,則返回實(shí)例對(duì)象屬性的值。
如果沒有找到,則沿著實(shí)例對(duì)象內(nèi)部的指針,搜索實(shí)例對(duì)象指向的構(gòu)造函數(shù)的原型對(duì)象, 如果在構(gòu)造函數(shù)的原型對(duì)象中找到了這個(gè)屬性,則返回該屬性的值。
//實(shí)例對(duì)象搜索屬性的過程 //也就是說(shuō), 我們調(diào)用person1.sayName()的時(shí)候,會(huì)先后執(zhí)行兩次搜索。 //首先,解析器會(huì)問:“實(shí)例對(duì)象person1有sayName屬性嗎?”; 答:“沒有”。 //然后,它繼續(xù)搜索,在問:“person1的原型上有sayName屬性嗎?”; 答:“有”。 //于是,它就讀取那個(gè)保存在原型對(duì)象中的函數(shù)。 //同理,當(dāng)我們調(diào)用person2.sayName()時(shí),將會(huì)出現(xiàn)相同的搜索過程,得到相同的結(jié)果。 //而這正是多個(gè)實(shí)例對(duì)象共享原型所保存的屬性和方法的基本原理。 function Person(){} Person.prototype.name = "Shaw"; Person.prototype.sayName = function(){ alert(this.name); } var person1 = new Person(); var person2 = new Person(); person1.sayName(); // "Shaw" person2.sayName(); // "Shaw"
==函數(shù)的原型對(duì)象,最初只包含constructor屬性, 而該屬性也是共享的, 因此可以通過對(duì)象實(shí)例訪問。==
== 雖然可以通過實(shí)例對(duì)象訪問保存在原型中的值,但不能通過實(shí)例對(duì)象重寫原型中的值。如果我們?cè)趯?shí)例對(duì)象中添加了一個(gè)屬性, 該屬性與實(shí)例對(duì)象的原型對(duì)象的一個(gè)屬性同名,那我們就在實(shí)例中創(chuàng)造該屬性,該屬性將會(huì)屏蔽原型中的那個(gè)屬性。==
function Person(){} Person.prototype.name = "Shaw"; Person.prototype.age = 28; Person.prototype.job = "Designer"; Person.prototype.sayName = function(){ console.log(this.name); } var person1 = new Person(); var person2 = new Person(); person1.name = "Roc"; console.log(person1.name); //"Roc" 來(lái)自person1實(shí)例 console.log(person2.name); //"Shaw" 來(lái)自 Person.prototype
在這個(gè)例子中, person1的name屬性值就是實(shí)例對(duì)象屬性name的值。無(wú)論訪問person1.name還是person2.name都能返回響應(yīng)的值,分別是實(shí)例對(duì)象自身的name屬性值:person1.name = "Roc" 和 person2.name = "Shaw", person2在自身實(shí)例對(duì)象上找不到name屬性,所以只能通過__proto__指針的指向,找到構(gòu)造函數(shù)原型對(duì)象的name屬性值。
當(dāng)為對(duì)象實(shí)例添加一個(gè)屬性時(shí),這個(gè)屬性就會(huì)屏蔽原型對(duì)象中保存的同名屬性;
==換句話說(shuō)。添加這個(gè)屬性只會(huì)阻止我們?cè)L問構(gòu)造函數(shù)原型對(duì)象的那個(gè)屬性, 但不會(huì)修改那個(gè)屬性。==
不過,可以使用delete操作符則可以完全去除實(shí)例屬性,從而能夠重新訪問原型中的屬性。
function Person(){ } Person.prototype.name = "Shaw"; Person.prototype.age = 28; Person.prototype.job = "Designer"; Person.prototype.sayName = function() { console.log(this.name) } var person1 = new Person(); var person2 = new Person(); person1.name = "Roc"; console.log(person1.name); // "Roc" 來(lái)自實(shí)例對(duì)象person1 console.log(person2.name); // "Shaw" 來(lái)自構(gòu)造函數(shù)的原型對(duì)象prototype.name delete person1.name; // 刪除實(shí)例對(duì)象上的name屬性, 還記得之前的章節(jié),說(shuō)對(duì)象的屬性數(shù)據(jù)特性中的configurable, enumerable,writable默認(rèn)都為true嗎? 所以這里可以通過delete操作符,刪除實(shí)例對(duì)象的屬性。 console.log(person1.name); // "Shaw" 只能去構(gòu)造函數(shù)的原型對(duì)象prototype找name屬性,找到了~~
在這個(gè)修改后的例子,我們使用delete操作符刪除了實(shí)例對(duì)象的person1的name屬性,把它刪除后,就恢復(fù)了實(shí)例對(duì)象對(duì)構(gòu)造函數(shù)原型對(duì)象中的name屬性連接。 再調(diào)用person1.name, 返回的就是原型中name屬性的值了。
使用hasOwnProperty()方法可以檢測(cè)一個(gè)屬性是存在于實(shí)例對(duì)象中,還是存在于構(gòu)造函數(shù)的原型中。
這個(gè)方法是從Object原生引用類型中繼承而來(lái)的,只在給定屬性存在于對(duì)象實(shí)例中時(shí),才會(huì)返回true。
function Person(){ } Person.prototype.name = "Shaw"; Person.prototype.age = 28; Person.prototype.job = "Designer"; Person.prototype.sayName = function(){ console.log(this.name); } var person1 = new Person(); var person2 = new Person(); console.log(person1.hasOwnProperty("name")); // false person1.name = "Roc"; console.log(person1.name); // "Roc" console.log(person1.hasOwnProperty("name")); // true console.log(person2.name); //"Shaw" console.log(person2.hasOwnProperty("name")); // false delete person1.name; console.log(person1.hasOwnProperty("name")); // false console.log(person1.name); // "Shaw"
通過使用Object.getOwnProperty()方法,就能很清楚地知道,訪問到的是實(shí)例對(duì)象屬性,還是實(shí)例對(duì)象的構(gòu)造函數(shù)原型對(duì)象。
調(diào)用person1.hasOwnProperty("name")時(shí), 只有當(dāng)person1重寫name屬性后才會(huì)返回true,因?yàn)橹挥羞@個(gè)時(shí)候name才是一個(gè)實(shí)例對(duì)象屬性,而非構(gòu)造函數(shù)的原型對(duì)象。
//偽代碼 實(shí)例對(duì)象訪問屬性或方法過程。 Person.prototype = { constructor: function Person(arguments){native code}, name: "Shaw", age: 28, job: "Designer", sayName: function(){ console.log(this.name); } } object person1 <= new from Person; object person2 <= new from Person; person1.__proto__ => Person.prototype; person2.__proto__ => Person.prototype; object person1.name = "Roc"; so person1.name, it is from object person1, the value it is "Roc" the "name" from object person1, so getOwnProperty, it is true. object person2.name, object person2 self not has "name" property, searching for => Person.prototype.name => find the "name" property, the value is "Shaw", get it. person2.name = " Shaw"; object person2 not has own Property "name", so person2.getOwnProperty("name"), it is false; Alright, let us delete object person1.name. Okay, object person1 do not has property "name". He only can find it from Person.prototype, is it has "name" property in Person.prototype. Yes, it did have. Okay~, the object person1.name => Person.prototype.name => "Shaw". Because we delete the "name" property of object person1. So object person1 not has Own Property. person1.hasOwnProperty("name"), it is false;
ECMAScript 5的 Object.getOwnPropertyDescriptor()方法可用于實(shí)例對(duì)象的屬性,當(dāng)然也可用于構(gòu)造函數(shù)。
==一定要記住函數(shù)也是對(duì)象啊~只不過它是一個(gè)特殊的對(duì)象~ 要不然原型對(duì)象上怎么繼承那么多Object的方法呢?==
var people = { name: "Shaw" } function Person(){ } Person.prototype.name = "Roc"; var PersonProp = Person.prototype; var peoplePropDes = Object.getOwnPropertyDescriptor(people, "name"); console.log(peoplePropDes.value); // "Shaw" var PersonPropDes = Object.getOwnPropertyDescriptor(PersonProp, "name"); console.log(PersonPropDes.value); //"Roc"
文章版權(quán)歸作者所有,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請(qǐng)注明本文地址:http://systransis.cn/yun/98169.html
摘要:目錄導(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é)講解完本章開始進(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 與...
摘要:在基于原型的面向?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 都有類的的概念,類是實(shí)例的類型模板,比如Student表示學(xué)生這種類型,而不表示任何具體的某個(gè)學(xué)生,而實(shí)例就是根據(jù)這個(gè)類型創(chuàng)建的一個(gè)具體的對(duì)象,比如zhangsan、lisi,由...
摘要:簡(jiǎn)單來(lái)理解對(duì)象就是由屬性和方法來(lái)組成的面向?qū)ο蟮奶攸c(diǎn)封裝對(duì)于一些功能相同或者相似的代碼,我們可以放到一個(gè)函數(shù)中去,多次用到此功能時(shí),我們只需要調(diào)用即可,無(wú)需多次重寫。 什么是對(duì)象 我們先來(lái)看高程三中是如何對(duì)對(duì)象進(jìn)行定義的 無(wú)序?qū)傩缘募希鋵傩钥梢园ɑ局?、?duì)象或者函數(shù),對(duì)象是一組沒有特定順序的的值。對(duì)象的沒個(gè)屬性或方法都有一個(gè)俄名字,每個(gè)名字都映射到一個(gè)值。 簡(jiǎn)單來(lái)理解對(duì)象就是由屬...
前言:最近在細(xì)讀Javascript高級(jí)程序設(shè)計(jì),對(duì)于我而言,中文版,書中很多地方翻譯的差強(qiáng)人意,所以用自己所理解的,嘗試解讀下。如有紕漏或錯(cuò)誤,會(huì)非常感謝您的指出。文中絕大部分內(nèi)容引用自《JavaScript高級(jí)程序設(shè)計(jì)第三版》。 2. 原型對(duì)象與in操作符 有兩種方式使用in操作符: 單獨(dú)使用和在for-in循環(huán)中使用。 單獨(dú)使用時(shí),in操作符會(huì)通過對(duì)象能夠訪問給定屬性時(shí)返回true。 ==...
摘要:當(dāng)作構(gòu)造函數(shù)來(lái)使用,作為普通函數(shù)來(lái)使用,當(dāng)在全局作用域中調(diào)用一個(gè)函數(shù)時(shí),對(duì)象總是指向?qū)ο?。調(diào)用構(gòu)造函數(shù)時(shí)會(huì)為實(shí)例添加一個(gè)指向最初原型的的指針,而把原型修改為另外一個(gè)對(duì)象就等于切斷了構(gòu)造函數(shù)于最初原型之間的聯(lián)系。 ECMA-262 把對(duì)象定義為 無(wú)序?qū)傩缘募?,其屬性可以包含基本值、?duì)象或者函數(shù)。 即對(duì)象是一組沒有特定順序的值。對(duì)象的每個(gè)屬性或方法都有一個(gè)名字,而每個(gè)名字都映...
閱讀 1835·2023-04-26 02:51
閱讀 2876·2021-09-10 10:50
閱讀 3077·2021-09-01 10:48
閱讀 3641·2019-08-30 15:53
閱讀 1829·2019-08-29 18:40
閱讀 417·2019-08-29 16:16
閱讀 2041·2019-08-29 13:21
閱讀 1828·2019-08-29 11:07