摘要:另外,首字母大寫是我們用以區(qū)分構(gòu)造函數(shù)和普通函數(shù)的語法習(xí)慣,沒有強(qiáng)制規(guī)定一定要大寫。這個(gè)對(duì)象和構(gòu)造函數(shù)的指向的是同一個(gè)對(duì)象。
prototype是JavaScript比較難理解的一部分,繞來繞去的。不理出頭緒來,理解原型的概念是件頭疼的事情。
為了方便后面的說明,先從對(duì)象說起。
JavaScript里任何事物都是對(duì)象,不管什么,都是從Objec衍生出來的。function,array,string,{},都是對(duì)象。只是大家的功能各有不同。Object就像女媧,JavaScript世界的任何事物,都是它“創(chuàng)造”的。這里的對(duì)象是廣義的泛指的對(duì)象,是一切事物的統(tǒng)稱。
狹義對(duì)象狹義對(duì)象是指一般的對(duì)象類型,通過var p = new Object()或者通過var p = {}的方式創(chuàng)建出來的東西。用來表示某一種事物,包含事物的屬性和方法。后面我們說的對(duì)象就是狹義對(duì)象,專門指JavaScript里產(chǎn)生出來的實(shí)例對(duì)象。其他的函數(shù)、數(shù)組等我們只從它本身的功能角度來看待,不當(dāng)作對(duì)象,看成是執(zhí)行功能、儲(chǔ)存數(shù)據(jù)的一種工具。
對(duì)象的產(chǎn)生 對(duì)象的創(chuàng)建我們創(chuàng)建一個(gè)對(duì)象最常用的方法時(shí)var p = {},這只是JavaScript創(chuàng)建對(duì)象的快捷方式,其根本是通過var p = new Object()的方式產(chǎn)生。這種方式是通過構(gòu)造函數(shù)來創(chuàng)建對(duì)象的。
構(gòu)造函數(shù) 構(gòu)造函數(shù)創(chuàng)建對(duì)象如何通過構(gòu)造函數(shù)來創(chuàng)建對(duì)象呢?網(wǎng)上有很多資料,這里簡(jiǎn)單敘述一下。有如下構(gòu)造函數(shù):
function Person(name) { this.name = name; // 屬性 this.run = function() { // 方法 console.log("I"m running"); } }
上面是一個(gè)簡(jiǎn)單的構(gòu)造函數(shù)。構(gòu)造函數(shù)它首先是一個(gè)函數(shù),跟我們平常寫的函數(shù)是同一類。不同的是它里面有一個(gè)this指針,指向通過它產(chǎn)生的實(shí)例對(duì)象。另外,首字母大寫是我們用以區(qū)分構(gòu)造函數(shù)和普通函數(shù)的語法習(xí)慣,沒有強(qiáng)制規(guī)定一定要大寫。但為了方便理解最好用首字母大寫的命名習(xí)慣來命名。
var p = new Person();
通過new的方式創(chuàng)建實(shí)例。上面變量p就是一個(gè)實(shí)例對(duì)象,它包含了一個(gè)name屬性和一個(gè)run方法。用new 構(gòu)造函數(shù)創(chuàng)建對(duì)象的過程有兩步:
在內(nèi)存中開辟一塊內(nèi)存地址,用以存放新的實(shí)例對(duì)象
實(shí)例對(duì)象調(diào)用構(gòu)造函數(shù),那么里面的this指針指向這個(gè)實(shí)例,從而為對(duì)象設(shè)置了name屬性和run方法。
name屬性和run方法將是實(shí)例對(duì)象自身的東西,屬于對(duì)象自有。相當(dāng)于你看到的結(jié)果是這樣一個(gè)對(duì)象:
p = { name: "Pelemy", run: functioin() { cosole.log("I"m running"); } }實(shí)例對(duì)象重復(fù)創(chuàng)建的問題
按照上面的步驟創(chuàng)建對(duì)象,每執(zhí)行一次 new Person() 就會(huì)有一個(gè)新的對(duì)象產(chǎn)生。
var p1 = new Person(); // 新對(duì)象,新的內(nèi)存空間 var p2 = new Person(); // 新對(duì)象,新的內(nèi)存空間 console.log(p1 === p2); // false console.log(p1.run === p2.run) // false
每個(gè)對(duì)象都有自己name屬性和run方法。這里有個(gè)問題,每個(gè)實(shí)例對(duì)象run方法的實(shí)現(xiàn)都是一樣的,這是一個(gè)雷同的方法。雷同的方法每個(gè)人都有一個(gè),這很浪費(fèi)資源。如果有一個(gè)地方共享,同一種類的對(duì)象都去共享的地方取就好了,不需要每個(gè)都留一個(gè)備份在自己身上。為了處理這種情況,prototype產(chǎn)生了。
構(gòu)造函數(shù)的prototype屬性構(gòu)造函數(shù)中this指針設(shè)置的屬性和方法將是新實(shí)例對(duì)象自身擁有的屬性和方法,我們叫本地屬性和方法。為了使各個(gè)產(chǎn)生的實(shí)例對(duì)象不重復(fù)設(shè)置相同的屬性或方法,JavaScript把這部分放到了構(gòu)造函數(shù)的一個(gè)地方,這個(gè)地方就是構(gòu)造函數(shù)的prototype屬性指向的對(duì)象(注意這里的對(duì)象也是前面說的狹義對(duì)象)。prototype本意是原型、藍(lán)圖,在這里我認(rèn)為把它叫做“引用屬性”來理解更貼切。還是以前面的例子。
function Person(name) { this.name = name; // 定義本地屬性 } Person.prototype.run = function() { // 定義引用方法 console.log("I"m running"); }
這里可能會(huì)突然讓人頭疼。Person.prototype.run突然多了這么長(zhǎng)一串,連續(xù)三個(gè)點(diǎn)。我們一個(gè)個(gè)看。首先把prototype當(dāng)作一個(gè)屬性,就像我們常寫一個(gè)對(duì)象的屬性那樣,比如 car.color, car.speed。這里也一樣,prototype是構(gòu)造函數(shù)的一個(gè)屬性,它的值是一個(gè)對(duì)象
Person.prototype = { // properties and methods }
這個(gè)對(duì)象就是將來我們用來存放共享屬性和方法的。這些屬性和方法可以被實(shí)例對(duì)象引用。注意是引用,也就是自己沒有,指向那里去調(diào)用就行了。然后在這個(gè)對(duì)象里定義run方法
Person.prototype = { // properties and methods run: function() { console.log("I"m running"); } }
當(dāng)然,我們這里只是為了多定義一個(gè)run方法,而不是定義整個(gè)prototype的對(duì)象(這樣會(huì)把這個(gè)對(duì)象的其他方法擦掉,只剩下run方法)。所以定義整個(gè)引用方法的方式就是
object.run = ... 即
Person.prototype.run = ...
這樣新創(chuàng)建的實(shí)例再也不用自己定義這個(gè)方法,只要從共享對(duì)象上引用就好了。舉例:
function Person(name) { this.name = name; // 屬性 this.myfunction = function() { console.log("just work for me"); } } Person.prototype.run = function (distance) { console.log("I"ve run " + distance + " meters"); } var p1 = new Person("Pelemy"); var p2 = new Person("Summmy"); console.log(p1.name); // Pelemy p1.run(100); // I"ve run 100 meters console.log(p2.name); // Summy p2.run(200); // I"ve run 200 meters
p1,p2的本身沒有run方法(構(gòu)造函數(shù)里沒定義),但都能執(zhí)行到,他們是引用prototype里的run方法。執(zhí)行方法時(shí),先在本地查找是否有這個(gè)方法,沒有則向上尋找prototype對(duì)象是否有,有則執(zhí)行這個(gè)方法。這里可以通過hasOwnProperty方法來判斷本地是否有這個(gè)方法,沿用上面的例子
console.log(p1.hasOwnProperty("run")); // false console.log(p1.hasOwnProperty("myfunction"); // true; console.log(p1.hasOwnProperty("name"); // true;
把構(gòu)造函數(shù)的定義視為本地屬性定義,把prototype屬性對(duì)象視為引用屬性定義,這樣分開來理解,就會(huì)輕松多了。
對(duì)象的引用對(duì)象與構(gòu)造函數(shù)的prototype屬性的關(guān)系實(shí)例對(duì)象創(chuàng)建后,構(gòu)造函數(shù)中的屬性和方法成為本地屬性和方法,prototype中的屬性和方法成為引用屬性和方法。我想知道一個(gè)實(shí)例對(duì)象產(chǎn)生后,怎么知道這個(gè)對(duì)象是從哪里引用的?對(duì)象不像構(gòu)造函數(shù),生下來就有prototype屬性,連接著引用對(duì)象。但對(duì)象有一個(gè)只讀的屬性__proto__,指向的就是它的引用對(duì)象(這叫隱式原型對(duì)象)。這個(gè)對(duì)象和構(gòu)造函數(shù)的prototype指向的是同一個(gè)對(duì)象。因?yàn)橐脤?duì)象是放在那里供別人引用的,不會(huì)復(fù)制或重新產(chǎn)生,所以它是被直接定義到實(shí)例對(duì)象的__proto__的。
function Person(name) { this.name = name; // 屬性 } var p = new Person(); console.log(p.__proto__ === Person.prototype); // trueprototype對(duì)象怎么產(chǎn)生
這個(gè)prototype對(duì)象是怎么來的呢?構(gòu)造函數(shù)在JavaScript中產(chǎn)生時(shí),隨即就有一個(gè)它的引用對(duì)象(prototype屬性指向的對(duì)象)產(chǎn)生了。它是伴隨著構(gòu)造函數(shù)產(chǎn)生的。
延伸prototype是構(gòu)造函數(shù)生來就有的屬性,對(duì)象是沒有的。
構(gòu)造函數(shù)的prototype屬性的值是一個(gè)對(duì)象。即Person.prototype是一個(gè)對(duì)象
prototype的屬性和方法是共用屬性和方法,是所有實(shí)例對(duì)象都有,不同的屬性和方法在構(gòu)造函數(shù)實(shí)現(xiàn),按這樣創(chuàng)建對(duì)象就是類的繼承的方式了。產(chǎn)生的實(shí)例對(duì)象相當(dāng)于都從父類繼承過來,這就是為什么把引用的這個(gè)對(duì)象叫原型(不叫引用或共享)的原因了。
總結(jié)構(gòu)造函數(shù)中定義的屬性和方法是本地屬性和方法,prototype指向的對(duì)象定義的屬性和方法是引用屬性和方法。
prototype定義的屬性和方法能被實(shí)例共同引用,是共同的部分,相當(dāng)于每個(gè)對(duì)象都有和引用對(duì)象同樣的屬性和方法,而自身的方法就通過構(gòu)造函數(shù)來呈現(xiàn)。
文章版權(quán)歸作者所有,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請(qǐng)注明本文地址:http://specialneedsforspecialkids.com/yun/107654.html
摘要:深入理解原型與繼承看過不少書籍,不少文章,對(duì)于原型與繼承的說明基本上讓人不明覺厲,特別是對(duì)于習(xí)慣了面向?qū)ο缶幊痰娜藖碚f更難理解,這里我就給大家說說我的理解。 深入理解:JavaScript原型與繼承 看過不少書籍,不少文章,對(duì)于原型與繼承的說明基本上讓人不明覺厲,特別是對(duì)于習(xí)慣了面向?qū)ο缶幊痰娜藖碚f更難理解,這里我就給大家說說我的理解。 首先JavaScript是一門基于原型編程的語言...
摘要:這篇文章的的目的試圖通過最簡(jiǎn)單的表述讓大家理解和先把最重要的幾點(diǎn)列出來大家可以帶著這幾個(gè)核心要點(diǎn)閱讀下面的文章是用來在原型鏈上查找你需要的方法的實(shí)際對(duì)象所有的對(duì)象都有這個(gè)屬性這個(gè)屬性被引擎用作繼承使用根據(jù)的規(guī)范這個(gè)屬性應(yīng)該是一個(gè)內(nèi)在的屬性但 這篇文章的的目的試圖通過最簡(jiǎn)單的表述,讓大家理解prototype和__proto__ 先把最重要的幾點(diǎn)列出來,大家可以帶著這幾個(gè)核心要點(diǎn)閱讀下面...
摘要:如下所示在規(guī)范中,已經(jīng)正式把屬性添加到規(guī)范中也可以通過設(shè)置和獲取對(duì)象的原型對(duì)象對(duì)象之間的關(guān)系可以用下圖來表示但規(guī)范主要介紹了如何利用構(gòu)造函數(shù)去構(gòu)建原型關(guān)系。 前言 在軟件工程中,代碼重用的模式極為重要,因?yàn)樗麄兛梢燥@著地減少軟件開發(fā)的成本。在那些主流的基于類的語言(比如Java,C++)中都是通過繼承(extend)來實(shí)現(xiàn)代碼復(fù)用,同時(shí)類繼承引入了一套類型規(guī)范。而JavaScript是...
摘要:如果要理解基于原型實(shí)現(xiàn)面向?qū)ο蟮乃枷?,那么理解中得三個(gè)重要概念構(gòu)造函數(shù)原型原型鏈對(duì)幫助理解基于原型的面向?qū)ο笏枷刖惋@得尤為重要。函數(shù)對(duì)象的原型在中,函數(shù)是一種特殊的對(duì)象,所有的函數(shù)都是構(gòu)造函數(shù)的實(shí)例。 介紹 和java這種基于類(class-base)的面向?qū)ο蟮木幊陶Z言不同,javascript沒有類這樣的概念,但是javascript也是面向?qū)ο蟮恼Z言,這種面向?qū)ο蟮姆绞匠蔀?基...
摘要:理解文章中已經(jīng)比較全面的分析了在中的指向問題,用一句話來總結(jié)就是的指向一定是在執(zhí)行時(shí)決定的,指向被調(diào)用函數(shù)的對(duì)象。與和直接執(zhí)行原函數(shù)不同的是,返回的是一個(gè)新函數(shù)。這個(gè)新函數(shù)包裹了原函數(shù),并且綁定了的指向?yàn)閭魅氲摹? 理解 JavaScript this 文章中已經(jīng)比較全面的分析了 this 在 JavaScript 中的指向問題,用一句話來總結(jié)就是:this 的指向一定是在執(zhí)行時(shí)決定的,...
閱讀 2901·2021-11-23 09:51
閱讀 1547·2021-11-15 11:36
閱讀 3006·2021-10-13 09:40
閱讀 1863·2021-09-28 09:35
閱讀 13040·2021-09-22 15:00
閱讀 1367·2019-08-29 13:56
閱讀 2924·2019-08-29 13:04
閱讀 2698·2019-08-28 18:06