JavaScript 物件及物件的建立

前言
即使不瞭解Javascript的設計原理和繼承機制這些東西的時候,我們依然能很好的使用Javascript去設計前端頁面及功能。
這得力於傳統jquery時的前端開發我們把大部分的業務邏輯封裝在後臺,前端程式碼的邏輯性大大降低,我們只需要使用一點點j和ava語法類似的知識就能寫出所需的javascript程式碼。
這時候我們不需要考慮繼承,不需要考慮複雜的封裝,甚至連this關鍵字我們都用的極少。但在接觸了react,vue這些前端框架之後就有必要詳細的瞭解與學習Javascript了,Javascript已經變成一個獨立於後端的完整的前端框架的一個基礎。
物件及物件的建立
Javascript是一種基於物件(object-based)的語言,你遇到的所有東西幾乎都是物件。但是,它又不是一種真正的面向物件程式設計(OOP)語言,因為它的語法中沒有"子類"和"父類"的概念,也沒有"類"(class)和"例項"(instance)的區分。我們舉幾個例子,一下的都是物件。

這些基礎的資料型別也是物件但是不在本章的討論訪問之內。

這兩行都是等價的,都生成了一個名為fun1的(函式)物件。

這是我們比較常見的物件方式,用大括號的方式建立物件,採用鍵值對的方式描述物件屬性。

這個物件的結果其實和第三個物件是一樣的都是可以用{}來表述的。
我們可以很清楚的看出這都是物件,完全沒有類的概念,我們最後一個例子裡的Object是類麼?不,不是的。接下來就要說明一下物件的建立方式。
順便給大家推薦一個裙,它的前面是 537,中間是631,最後就是 707。想要學習前端的小夥伴可以加入我們一起學習,互相幫助。群裡每天晚上都有大神免費直播上課,如果不是想學習的小夥伴就不要加啦。
物件的建立方式只有一種,那就是

所以我們很清楚了,Object是一個建構函式(物件)。
這麼設計的原因是因為當初javascript的設計者並不打算引入"類"(class)的概念,因為一旦有了"類",Javascript就是一種完整的面向物件程式語言了,這好像有點太正式了,而且增加了初學者的入門難度。不過他在設計建立物件的方式上卻借鑑了C++和java的語法,引用了new關鍵字,但是javascript是沒有類的?那麼new後面跟著是什麼呢?它發現java與C++物件的建立都必須執行建構函式(constructor),於是乎他就做了簡化的設計,在Javascript語言中,new命令後面跟的不是類,而是建構函式。
所以以上四種範例其本質上都是javascript通過new 建構函式的方式隱式或顯式幫我們建立的。我們先詳細的瞭解一下建構函式的建立方式再細說範例中是如何隱式建立的物件。
建構函式 construct
示例:

7丨console.log(zs); //[object Object]
{[functions]: , __proto__: { },age: 23,name:"zhangsan",sex: "man"}
可以看到我們根據man建構函式建立了一個物件zs,物件有name,age和sex 屬性。在這裡面我們可以清楚的看到建構函式給物件設定屬性的方式是this關鍵字,this關鍵字動態指向的是建立的物件(關於this的其它問題會另起一章介紹),this關鍵字後跟著的屬性或方法就是該物件的屬性或方法。但是如果構造方法有返回值,那生成的物件就是構造方法的返回值了,所以大家在寫構造方法時要特別注意,示例如下:

看到zs的值變成了{info:‘error’},所以寫建構函式的時候最好也不要寫返回值。但是我發另一很奇怪的情況,示例如下

丨:"zhangsan",sex: "man"}
我們看到如果返回的是基本型別,整型,字串型別等的那建構函式還是生效了,這另我十分費解。不過只要記住在我們寫建構函式時不去寫返回值也就能規避這種問題。
建構函式建立的方式也不是完全完美的,比如看下面的示例中的sex屬性,如果通過man建構函式創建出來的物件sex都固定是“man”,那每個物件都要分配記憶體來儲存sex屬性是不是有些浪費呢?或者就是有些屬性要給所有物件共用的呢?比如物件的方法,完全不需要隨著物件變化,該如何處理?

為了解決這個問題,javascript為建構函式(函式型別物件)提供了prototype屬性。
prototype屬性
prototype只能用於函式物件,如果你給其它非函式物件增加prototype屬性,執行器會報出該物件無prototype屬性錯誤。
我們先來一個示例來看prototype如何使用

我們看到建構函式通過prototype屬性增加的屬性或方法是給所有物件共享的。我們把那些需要共享的方法和屬性放在prototype屬性裡,那些不需要共享的屬性和方法,就放在建構函式裡面。
而實現過程是這樣的,當例項物件一旦建立,將自動引用prototype物件的屬性和方法。也就是說,例項物件的屬性和方法,分成兩種,一種是本地的,另一種是引用的。
順便給大家推薦一個裙,它的前面是 537,中間是631,最後就是 707。想要學習前端的小夥伴可以加入我們一起學習,互相幫助。群裡每天晚上都有大神免費直播上課,如果不是想學習的小夥伴就不要加啦。
我們再看一個示例

我們看到只有通過prototype屬性修改sex物件才能更新全部物件的sex屬性,而單單修改某個物件的sex屬性是無法全域性修改的,對於這個我不知道是如何實現的,有知道的不吝賜教。
我們再看一個示例

這個示例只是prototype下屬性的修改方式改為對整個prototype屬性的物件進行設定。這種方式可以麼?可以,而且修改成這樣對上一個示例的輸出完全沒有影響。但是個人還是不推薦這種方式,因為prototype屬性還有一個特別的方法constructor。
constructor
我們先介紹下constructor屬性

constructor屬性是指向構造方法的引用,也就是說如下兩行程式碼是等價的。

看起來好像沒什麼用,但是我們知道prototype可以把自己的屬性傳遞給物件的。也就是說我們可以通過constructor來比較兩個物件是不是同一個構造器的例項,示例如下:

所以通過man.prototype={…}的方式設定共享屬性會破壞constructor屬性。雖然好像破壞了在我們的使用中不會產生任何影響,但我們還是要知道有這個屬性的存在及prototype的作用。
prototype還有一些其他方法,但是通過man.prototype={…}方式是破壞不了,只是因為所有物件的這些方法都是一致的,比如isPrototypeOf,hasOwnProperty方法,提供幾個示例,就不詳述了。
isPrototypeOf 示例是不是來自該建構函式

hasOwnProperty 每個例項物件都有該方法,用來判斷某一個屬性到底是本地屬性,還是繼承自prototype物件的屬性

我們再看一下最開始我們說的那個問題,所有的物件生成本質上都是通過建構函式生成的。檢視的方法很簡單,我們知道每個物件都有一個constructor方法,我們只要輸出每個物件的constructor方法就能找到他的建構函式。

其它問題
我們說了建構函式,什麼樣才能算是建構函式呢?所有的函式都可以是建構函式,所有的函式也只有函式可以擁有有prototype屬性。而函式也是物件,如上面例子的fun1函式,它也是由Function()建構函式生成的,fun1是函式物件,你可以把fun1當物件用,給他設定屬性,方法都是完全沒問題的,但是fun1.prototype設定的屬性也只能給new fun1()生成的物件使用,不能混淆了。
有說javascript的繼承機制是依靠prototype屬性(原型鏈{prototype chain}模式)來實現的,但是因為通過prototype設定“父物件”的屬性和方法,只能變成“繼承”共享的屬性和方法,每個物件自己私有的屬性和方法是無法繼承的,還是有一些不一樣的。而且說是共享只能說是統一賦值,我們通過物件去修改prototype提供的屬性並不會影響其他物件的該屬性。說是繼承但是不太一樣。
在理解javascript的物件及物件的建立時要牢記兩點,一個是javascript內(幾乎)所有的都是物件,並且物件都有建構函式和一些預設方法。比如我有時忘記這兩件事,寫一些奇怪的範例時就很懵逼,為什麼會出現這個結果。如我常忘記prototype屬性指向的也是物件,物件也就會有constructor,isPrototypeOf 等這些方法。
作者:spongeboblz
來源:CSDN