JavaScript之對象

在ES6出現(xiàn)之前,JavaScript不能真正被稱為 面向?qū)ο蟮木幊陶Z言,因為 class 僅僅作為其保留字而非關(guān)鍵字,而ES6之后,引入了class,使程序員可以用自己更加熟悉的方式創(chuàng)建對象;

至于ES6和ES5有什么區(qū)別,應(yīng)該就是上面提到的可以讓程序員更爽地coding,而程序員爽了,根據(jù) 工作量守恒定律,總有某個事物要干更多的活,沒錯,就是計算機。

為了兼容某些不支持ES6的瀏覽器,我們可以引入 Babel 庫將ES6代碼 “編譯” 成ES5之后執(zhí)行,而對于支持ES6的瀏覽器,在其JavaScript引擎中會自動進行“編譯”操作;

所以,總體上看,ES6和ES5在功能上是等效的,即用ES6能完成的任務(wù),用ES5必然能夠完成,只是在語法上,ES6提供了更多的 語法糖,讓程序員嘗到甜頭;所以為了更好的理解JavaScript對象,我們回歸初心,從ES5中窺視JavaScript所創(chuàng)建的對象世界;

對象的創(chuàng)建

對象有兩個基本元素:屬性和方法;

屬性用于存儲數(shù)據(jù),方法用于存儲代碼;接下來,我們從簡單到復(fù)雜,來理解JavaScript創(chuàng)建對象的演變史。

創(chuàng)建Object對象并賦值時代

我們可以通過 new Object 創(chuàng)建Object,并為其賦屬性和方法來創(chuàng)建對象:

// 代碼段 1
var p = new Object();
p.name = "x";
p.age = 20;
p.say = function(){
  console.log("My name is",this.name,",this year is",this.age);
}

這樣我們就得到一個含有2個屬性,1個方法的對象;

通過執(zhí)行 p.say() 得到輸出 : My name is x ,this year is 20 ;

但是,這些幾行代碼非常松散,每一行代碼都像一條單獨的語句,為了體現(xiàn)這些屬性和方法是一個整體,而不是一個個獨立的存在,我們需要進入下一個時代;

字面量賦值時代

我們通過給一個變量賦一個字面量,即可創(chuàng)建一個對象:

// 代碼段 2
var p = {
  name : "x" ,
  age : 20 , 
  say : function(){
    console.log("My name is",this.name,",this year is",this.age);
  }
}

這樣,我們就創(chuàng)建好了一個對象,這個對象有了自己的屬性和方法;

通過 console.log(typeof p) ,輸出為object可知, p 的類型是一個對象;

代碼段1和代碼段2相比,代碼段2的結(jié)構(gòu)更為合理,所有的屬性和方法都用大括號包含,更利于閱讀,也體現(xiàn)了整體性,但功能上是等效的;

不過我們又發(fā)現(xiàn),每次要創(chuàng)建一個相似的對象,都需要寫一遍屬性名,非常地不優(yōu)雅,所以,我們又要進入下一個時代;

工廠模式時代

我們可以通過調(diào)用一個函數(shù)創(chuàng)建我們需要的對象,并返回該對象,從而使代碼可重用,這個函數(shù)就是傳說中的 工廠,代碼如下:

// 代碼 3
function createPerson(name,age){
  var t = new Object();
  t.name = name;
  t.age = age;
  t.say = function(){
    console.log("My name is",this.name,",this year is",this.age);
  }
  return t;
}
var p = createPerson("x",20);

代碼3是將代碼1變?yōu)榱斯S模式;

下面再將代碼2也變?yōu)楣S模式:

function createPerson(name,age){
  return {
    name:name,
    age : age,
    say = function(){
      console.log("My name is",this.name,",this year is",this.age);
    }
  };
}
var p = createPerson("x",20);
```
工廠模式的代碼要比前面兩個時代的代碼優(yōu)雅,我們只需要調(diào)用一個函數(shù)即可獲得我們想要的對象;

但是,我們又發(fā)現(xiàn)了一個新的問題(別問我為什么總是能發(fā)現(xiàn)新問題,因為就是有一雙善于觀察的眼睛,手動傲嬌 ^_^):

通過上述3中方式創(chuàng)建的對象在使用 `typeof` 時,返回的都是 `object`,而通過 `實例 instanceof 類` 只有在類為 `Object`時,才返回true,就是說,上面3中方法創(chuàng)建的對象都是無差別的對象,我們不能分辨出它們的類型;

這就麻煩了,比如我們有這么一個函數(shù):
```javascript
function seeDoctor( o ){
  if(o是人){
    請人醫(yī)治療
  }
  if(o是動物){
    請獸醫(yī)治療
  }
}
```
那我們創(chuàng)建的對象因為不能判斷其是人是獸,將不能選擇適合的治療方案;

要解決這個問題,有兩種思路:
- 給對象添加信息,即為每一個對象添加一個屬性 `type` ,用于指明其類型;
- 讓js解釋器能夠判斷其類型;

第一種方式比較 *丑陋*,我們需要管理更多的數(shù)據(jù),但比較容易理解;第二種是更優(yōu)雅的方法,也推動我們進入下一個時代;

### 構(gòu)造函數(shù)時代

構(gòu)造函數(shù)時代的主要任務(wù)是讓創(chuàng)建的對象自帶類別說明屬性,即通過`instanceof` 就能判斷出其所屬的類:
```javascript
// 類是一個函數(shù),約定:
// 普通函數(shù)第一個字母小寫,類函數(shù)第一個字母大寫
function Person(name,age){
  this.name = name;
  this.age = age;
  this.say = function(){
    console.log("My name is",this.name,",this year is",this.age);
  }
}
function Animal(){}
var p = new Person("x",20) ;
```
注意,創(chuàng)建對象時,必須使用關(guān)鍵字 **new** 。

此時,我們通過 `p instanceof Person`,返回的結(jié)果為 `true`,而通過 `p instanceof Animal` ,返回結(jié)果為 `false`,從而使對象實例自帶類型屬性;

完美! But,又雙叒叕發(fā)現(xiàn)了不足,我們用上述各種函數(shù)創(chuàng)建兩個對象:
```javascript
var p1 = new Person("x",20);
var p2 = new Person("y",21);
console.log(p1.say==p2.say) ; // 輸出的是false
```
我們發(fā)現(xiàn):雖然 *say* 函數(shù)的代碼相同,但兩個對象實例的 *say* 居然指向不同的代碼塊,如果我們有100個實例,相同的代碼塊就需要有100份,極大的內(nèi)存浪費,這是我們所不能忍受的,因此迫切希望下一個時代的到來!

### 構(gòu)造函數(shù)+原型時代

原型就是所有對象實例所共享的一個 **對象**,這個對象中的屬性就是共享屬性(在c++中稱為靜態(tài)變量),方法就是共享方法; 

```javascript
function Person(name,age){
  this.name = name;
  this.age = age;
}
Person.prototype.say = function(){
  console.log("My name is",this.name,",this year is",this.age);
}
var p = new Person("x",20);
```
上述代碼創(chuàng)建的對象實例 p 有自己的屬性name和age,以及共享的方法say;通過 `p.say()` 即可打印出:*My name is x ,this year is 20* ;

執(zhí)行 `p.say()` 的時候,p先搜索其自身是否有方法 `say`,如果有,就執(zhí)行,如果沒有,就搜索其原型對象是否有 `say` 方法,如果還是沒有,就搜索其原型對象的原型對象是否有say屬性(即沿著原型鏈搜索say方法,這也是繼承的實現(xiàn)機制),如果原型鏈上都沒有say方法,就拋出錯誤,否則,執(zhí)行搜索到的方法。

p通過屬性`p.__proto__` 指向原型對象 `Person.prototype` , 從而獲取原型上的所有屬性和方法;

如果p上也定義一個方法 `say`:
```javascript
p.say = function(){console.log("Hello,world");}
```
則該方法將會 **覆蓋** 原型上的say方法,即調(diào)用 `p.say` 輸出的將是 *Hello,world* ,而如果通過 `delete p.say` 刪除掉 `say` 屬性,則調(diào)用 `p.say` 時,執(zhí)行的代碼又是原型上的 say 代碼;

總結(jié):原型就是一個類所創(chuàng)建的所有對象實例共享的一個對象;

但是,原型對象的定義和構(gòu)造函數(shù)分開了,這又使結(jié)構(gòu)不太優(yōu)美,所以,我們又得進入下一個時代;

### 構(gòu)造原型時代

為了解決原型定義和構(gòu)造函數(shù)分離的問題,我們決定將原型定義放到構(gòu)造函數(shù)中,就出現(xiàn)了以下代碼:

```javascript
function Person(name,age){
  this.name = name;
  this.age = age;
  Person.prototype.say = function(){
    console.log("My name is",this.name,",this year is",this.age);
  }
}
var p = new Person("x",20);
```
OK,完成了原型定義和構(gòu)造函數(shù)的合并,結(jié)構(gòu)也變得更加優(yōu)美了,但是,又出現(xiàn)了一個問題:

每次執(zhí)行創(chuàng)建 Person 對象實例的時候,都要重新定義一遍 `Person.prototype.say` 方法,雖然這不會增加內(nèi)存泄漏(以前定義的say代碼由于沒有被引用,內(nèi)存塊將會被自動回收),但卻增加了cpu的工作量,所以我們需要進入下一個時代;

### 優(yōu)化構(gòu)造原型時代

為了避免 `Person.prototype.say` 函數(shù)的重復(fù)定義,我們可以先判斷該函數(shù)是否已定義,如果沒有定義,再對其進行定義:

```javascript
function Person(name,age){
  this.name = name;
  this.age = age;
  if(typeof(Person.prototype.say)=="undefined"){
    Person.prototype.say = function(){
      console.log("My name is",this.name,",this year is",this.age);
    } ;
  }
}
var p = new Person("x",20);
```
通過以上7個時代的迭代,我們終于在 JavaScript中創(chuàng)建了一個基本上符合我們要求的對象; 

完!

最后編輯于
?著作權(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)容

  • 前言 這是讀書筆記第二篇,看完之后突然發(fā)現(xiàn)自己對js的內(nèi)置的一些東西還是了解的不夠全面,很多方法見都沒見過,啥用都...
    不止前端閱讀 229評論 0 0
  • 中午,有個同事問我們需不需要買鞋,她說她領(lǐng)取了一家鞋店的優(yōu)惠券,有很多張,都是滿100減100的,優(yōu)惠力度特別的大...
    辰苓閱讀 1,251評論 0 2
  • copy和mutablecopy 源于對數(shù)據(jù)的復(fù)制需求,對于對象類型的數(shù)據(jù)來說,區(qū)別于直接持有這個數(shù)據(jù)對象的方式,...
    縱橫而樂閱讀 490評論 0 1
  • 職位描述 【崗位職責】 1、對房地產(chǎn)市場數(shù)據(jù)進行日常收集歸類統(tǒng)計;及時跟蹤和研究國家宏觀經(jīng)濟政策的走向,對國家重大...
    沒頭沒腦傻開心閱讀 579評論 0 2
  • 小洋洋出生后,我與寶爸基本沒有二人世界啦,有的都是三人世界。但這個周末的三人世界有點特別(∩_∩)。 上篇更文中寶...
    李梅樹閱讀 703評論 1 3

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