無標題文章

##**理解對象**##

---

###**屬性類型**

> ?JavaScript中有兩種屬性類型 分別是 數據屬性和訪問器屬性

#### **數據屬性**

數據屬性具有四個特征值

[Configurble] 表示能否修改特性或者是通過delete重新定義屬性 默認為true

[Enumerable] 表示能否通過forin遍歷循環返回屬性

[Writable] 表示能否修改這個屬性值 默認為true

[Value] 包含這個屬性的數據值

> 如果要修改屬性的特征值 那么可以通過以下的方法

```

var person = {

? ?name:"zhang"

}

Object.defineProperty(person,"name",{

? ?writable:false,

? ?value:"zhao" //小寫

});

person.name = "wang";

console.log(person.name)

```

> 但要注意的是如果你修改了屬性的Configurble 你將無法再修改這個屬性的特性。

####**訪問器屬性**

> 訪問器屬性不包含數據值,它們包含一塊getter和setter函數,在讀取和調用時會調用這些函數,從而負責返回 和 處理。訪問器有下面四個特性。

訪問器有下面四個特性。

> [Configurble] 表示能否修改特性或者是通過delete重新定義屬性 默認為true

? [Enumerable] 表示能否通過forin遍歷循環返回屬性

? [Get] 在讀取時調用的函數 ,默認為undefined

? [Set] 在寫入時調用的函數, 默認為undefined

```

var person = {

? ?name:"zhang",

? ?_year:2004,

? ?edition:1

}

Object.defineProperty(person,"year",{

? ?get: function () {

? ? ? ?return this._year;

? ?},

? ?set: function (newValue) {

? ? ? ?if(newValue > 2004){

? ? ? ? ? ?this._year = newValue; //這里的設置新值 設置的是_year的值

? ? ? ? ? ?this.edition += newValue - 2004;//每次更新同時去設置版本

? ? ? ?}

? ?}

});

//這里的year就是一個典型的**訪問器屬性**,他不保存實際的value 但是有擁有getset方法,他只能通過這種方法去定義。而數據函數則是修改特性需要調用,生成是不需要的

person.year = 2005; //其實是調用了year屬性的set方法 然后實質上設置了_year的值 并且跟新版本

alert(person.year); //調用year的get方法 返回了_year的值

alert(person.edition);

//所以實質上 _year 和 year 是兩個完全不同的值.這是訪問器屬性的常見用法 get set一個屬性 影響其他屬性。

```

####**定義多個屬性**

> 在一些情況下我們需要為一個對象的多個屬性設置特性,當然js也為我們提供了方便的方法 `Object.defineProperties()`.它提供了兩個參數,第一個參數代表了你要修改的對象,第二個參數也是一個對象 其中的屬性和第一個對象中需要修改的一一對應

```

var cat = {};

Object.defineProperties(cat,{

? ?_year:{

? ? ? ?writable:true,

? ? ? ?value:2004

? ?},

? ?edtions:{

? ? ? ?wirtable:true,

? ? ? ?value:1

? ?},

? ?year:{

? ? ? ?get: function () {

? ? ? ? ? ?return this._year;

? ? ? ?},

? ? ? ?set: function (newValue) {

? ? ? ? ? ?if(newValue > 2004){

? ? ? ? ? ? ? ?this._year = newValue; //這里的設置新值 設置的是_year的值

? ? ? ? ? ? ? ?this.edition += newValue - 2004;//每次更新同時去設置版本

? ? ? ? ? ?}

? ? ? ?}

? ?}

});

alert(cat.year);

alert(cat.edtions);

```

####**讀取屬性的特性**

> 既然有定義方法,自然有讀取方法 `Object.getOwnPropertyDescriptor()` 獲得自身特性描述符,返回一個具有四個屬性的對象,讀取不同對象的時候,他的屬性也不一樣。具體看上面的列表。

###**創建對象**

> 雖然用Obj構造函數或對象字面量可以創建單個對象,但是當你需要創建大量對象的時候,就會非常的麻煩并且產生大量重復的代碼。

####**工廠模式**

> 用函數來封裝特定接口創建對象,但這種方法無法知道他屬于什么類型的對象

```

function createPerson(name,age,job) {

? ?var o = new Object();

? ?o.name = name;

? ?o.age = age;

? ?o.job = job;

? ?o.sayName = function () {

? ? ? ?alert(this.name);

? ?}

? ?return o;

}

var tom = createPerson("tom",19,"doctor");

```

####**構造函數模式**

> 我們都知道,我們可以通過構造函數快捷構造特定的對象 比如Object和Array,此外,我們可以創建自己的構造函數。

```

function Person(name,age,job) {

? ?this.name = name;

? ?this.age = age;

? ?this.job = job;

? ?this.sayName = function () {

? ? ? ?alert(name);

? ?}

}

var tom = new Person("tom",90,"doctor");

var jerry = new Person("jerry",18,"teacher");

```

> 以上是一個典型的構造函數 我們可以和工廠模式做一個簡單的對比

> 第一 沒有顯示的創建對象

> 第二 直接通過this來給對象添加屬性

> 第三 沒有通過return把對象返回

> 第四 構造函數的首字母應大寫

既然生成了構造函數,我們可以通過new關鍵字來調用這個構造函數,當new的時候 其實發現了以下步驟

> 1. 創建了一個新的對象

> 2. ?把這個構造函數的作用域連接到這個新生成的對象 也就是說 這個函數內的this 指向新對象

> 3. ?執行構造函數的代碼(添加屬性和方法)

> 4. ?返回構造完畢的對象

在上方的例子中tom和jerry分別保存了一個**Person的一個實例** 他們其實都有一個constructor(構造函數)屬性,該屬性指向了他們的**構造函數Person**.

但實質上這種方法可能并不是非常的保險,我們可以使用更加可靠的`instanceof`操作符 就像下面這樣

```

console.log(tom instanceof Person);//true

console.log(tom instanceof Object);//true

console.log(jerry instanceof Person);//true

console.log(jerry instanceof Object);//true

//所有對象都繼承自Object

```

**構造函數模式相比工廠模式最大的優勢在于他提供了一個訪問對象類型的方法**

> 構造函數也是函數,這意味這個函數能夠被new的方式調用,同樣可以被當成普通的方式來調用,在普通情況下 他的所有特征會和普通函數表現的完全一樣。以下是一個簡單的例子。

```

Person("drivd",66,"ko");

console.log(window.name);//drivd

var cat = {};

Person.call(cat,"cat",10,"catch mourse");

console.log(cat.name);

//或者使用以下的方法 在對象內把函數置入到對象之中

cat.fnc = Person;

cat.fnc("wang",15,22);

```

> 首先是一個在全局內調用這個Person函數的情況下 Person函數正常執行 這里的**this指向了**運行他的作用域對象 也就是**window對象**。所以window對象的name就被設置成了drivd.

> 第二是在其他對象的作用域情況下,通過call方法來增加作用域 進入函數時**this指向為cat對象**。

**構造函數的問題**

```

this.sayName = function () {

? ? ? ?alert(name);

? ?}

```

> 在創建實例時,都會調用構造函數內的方法,但是每次在構造方法的時候,它實質上是**創建一個新的函數**(也就是一個對象)**然后讓sayName指向這個new出來的函數**,不同的Person實例中的方法雖然函數名和做的事情完全相同,但他們并不指向同一個函數對象。所以當你比較(==)兩個相同類型不同實例的相同方法時,**他們并不相同**。

>

> 所以我們需要一種能夠把一些函數封裝 然后在構造函數內部可以直接引用,這樣就可以讓不通實例的相同方法的指針指向同一個函數。也許把方法寫在全局變量之中,在內部調用是種方法。他達到了兩者方法里面存放一個指針 并且指向了同一個函數,但這種方法的**封裝性**實在差勁,并且并不是很好管理(如果需要非常多函數的情況下)。

####**原型模式**

>我們所創建的所有對象都擁有著一個屬性`prototype`,他包含了一個指針,指向了這個對象的原型對象,而這個原型對象的作用是提供一種類型的不同實例所共享的方法和屬性。讓我們接下來深入了解原型的本質。

**理解原型的本質**

> 首先當我們創建的一個新的函數,馬上按特定規則創造一個當前函數的`prototype`屬性,這個屬性指向了函數的原型對象。而原型對象也會生成`constructor`屬性,他指向了prototype屬性所在的函數。而在沒有實例生成的時候 這個原型屬性里其實除了`constructor`沒有任何東西

>

> 第二步發生在實例調用構造函數的過程中,創建的實例時,實例內部也會產生一個prototype屬性,然后內部產生一個指針,他指向了構造函數所指向的原型對象,(盡管實例的這個屬性對腳本不可見,不可直接訪問)。要注意的是,這個連接存在于實例與原型對象之間,這連接與構造函數并無太大關系。

>

> 對于實例可以用`Person.prototype.isPrototypeOf(Person1)` 的方法來查看是否和某個構造函數的原型建立了鏈接, 如果兩者確實有鏈接,返回true,

>

> 在EMCAScript中新增加了一個方法 它能讓我們快速的返回一個對象的原型對象 `Object.getPrototypeOf(person1)`

>

> 當實例的屬性被我們讀取時,他首先訪問自己本身有沒有這個屬性,如果沒有的情況下,就繼續查詢他的原型,如果在原型中找到了這個屬性,他的值就會被返回。

>

> 如果在某個實例的自身和他的原型中有同名的屬性,那么自身的屬性將會屏蔽原型的屬性。所以即使刪除這個屬性,修改的也永遠是其自身的屬性,他不會修改原型的屬性,我們可以通過刪除對象自身的某個屬性,來讓取消對原型屬性的屏蔽

>

> `person1.hasOwnProperty("name")`繼承自Object 他會檢查參數是否是對象其中的一個屬性,他不會去檢查對象的原型。

```

function Person() {

}

Person.prototype.name = "wang";

Person.prototype.age = 20;

Person.prototype.job = "T";

Person.prototype.sayName = function () {

? ?alert(this.name);

};

var person1 = new Person();

var person2 = new Person();

alert(person1.hasOwnProperty("name")); //false

person1.name = "zhang";

person1.sayName();//這里的sayName會查找到原型并且原型方法內的this指向為person1

alert(person1.hasOwnProperty("name")); //true

```

**原型與in操作符**

> 其實在之前我們就在forin中使用了in操作符 其實在檢測某個屬性是否屬于某個對象的時候,這個操作符同樣可以使用,他的特性與`hasOwnProperty()`不同,如果該屬性屬于該對象的原型對象,同樣會返回true,如果原型不曾擁有,返回false。

>

> 現在我們有了檢測屬性的兩種方法 1 檢測屬性是否屬于對象本身 2 檢測屬性對象是否屬于對象本身或原型。那么我們缺少第三種。檢測對象是否屬于原型。其實實現的方法很簡單 無法就是達成了兩個條件 **第一** hasOwnProperty()返回false,該屬性在本身不存在,**第二** in操作符返回true,在原型中存在。所以以下就是一個簡單方法

```

function hasPrototypeProperty(obj,name) {

? ?return !obj.hasOwnProperty(name) && (name in obj);//如果條件都達成 返回true

}

```

> 在我們通過forin循環遍歷屬性的時候,原型中的屬性也會被遍歷出來,(除非原型中屬性的特效標記為無法遍歷),但是這一點在IE8以一下版本中有一個BUG,**屏蔽不可枚舉屬性的實例屬性不會出現在forin循環之中**,也就是說,如果原型中的某個方法屏蔽in循環,然后在實例自身中覆蓋同名方法,連這個方法自身也會屏蔽In.無法發現。

>要取得一個對象中所有可以通過forin循環的屬性,可以通過`Object.keys()`方法,他接受一個對象參數,返回一個包含所有可forin遍歷的屬性標識符字符串數組。他的順序是forin中的出場順序

>

>如果想取得一個對象內的所有實例屬性 可以使用`Object.getOwnPropertyNames(Person)`他的參數是一個對象,他的返回值會包含非常多的屬性,比如caller argunments coustrusta

>當然上方的返回值都只會是自身的屬性,不會去查找他們的原型。

**更簡單的原型語法**

在很多時候重復的調用.prototype屬性的封裝性并不是很好,我們可以通過創建一個新對象并設置的方法來

```

function Person() {

}

Person.prototype = {

? ?name:"null",

? ?age:0,

? ?job:"job"

? ?sayName:function () {

? ? ? ?alert(this.name);

? ?}

}

```

這個方法 唯一問題在于 Person的原型的constructor不再指向Person構造函數,因為我們實質上創建了一個新的對象并把構造函數的prototype指針指向了他,之前默認創建的prototype對象已經被覆蓋,新對象里也沒有constructor這個屬性,辦法就是在新的原型對象內重新指定一下constructor

**原型的動態性**

原型的動態性主要體現在增加和重定向兩個方面

```

function Person() {

}

var person1 = new Person();

Person.prototype.name = "person1";

alert(person1.name);

```

> 上面的例子首先創建一個person的實例person1 然后給person的原型添加的name屬性。即使增加屬性在創建實例之后,但依舊可以正常訪問原型中的屬性。這是因為獲取值只是查詢,他在自身內沒找到就回到原型中去找,找到了,返回。

```

Person.prototype = {

? ?name:"name"

}

alert(person1.name);//undefined 因為他訪問的仍舊是創建時綁定的原型對象。

```

> 但是在這種情況下,就會發生無法訪問的情況。因為實例原型的連接出現在構造函數的那一刻,他被綁定成原來的原型對象,而后來的原型對象被重新更改成另一個新創建的對象,也就是說,這個實例對象的原型仍舊是剛開始創建的那個原型對象。 和上面的例子對比,一個是在新的原型對象內創建屬性,而另一個則是在原本的原型中去添加屬性。

**原生對象的原型**

不僅僅是自創建的對象,就連很多原生對象(比如Object Array String)都使用了原型的方式來封裝方法,通過原生對象的原型不僅可以獲取原生方法的應用,也可以為原生對象添加新的方法。

```

String.prototype.startsWith = function (tex) {

? ?return this.indexOf(tex) == 0;

}

var text = "Hello World";

alert(text.startsWith("World"));

```

以上就為基本包裝類的原型添加了一個方法

**原型對象的問題**

原生模式其實也存在一些問題,首先他沒有傳參數然后初始化的環節,也就是說原型中的值默認值都相同而且會被共享。假設我們兩個實例的原型相同,且內部有一個數組,被兩個共享,那么我們操作其中一個實例的該數組,會改變到另外一個實例的該數組。(如果重新創建 則創建在對象內部 和原型無關)

**組合使用構造函數和原型模式**

構造函數用于定義實例屬性,而原型模式用來定義需要方法和共享的屬性

```

function Person(name,age) {

? ?this.name = name;

? ?this.age = age;

? ?this.friends = ["tom","jerry"]

}

Person.prototype = {

? ?sayName:function () {

? ? ? ?alert(this.name);

? ?}

}

var person1 = new Person("wang",15);

var person2 = new Person("zhang",12);

```

**動態原型模式**

如果把分離的原型和構造函數可能會造成一些困惑,可以把初始化原型和構造函數封裝在一起

```

function Person(name,age) {

? ?this.name = name;

? ?this.age = age;

? ?this.friends = ["tom","jerry"]

? ?if(this.sayName != "function"){

? ? ? ?Person.prototype.sayName = function () {

? ? ? ? ? ?alert(this.name);

? ? ? ?}//不能調用對象字面量的方式去重寫原型,只能增加方法,不然第一個創建的對象會被隔絕

? ?}

}

```

> 以上的原型初始化只會執行一次,在第一次調用構造函數生成實例時,this指向的是new出來的對象,這個對象有原型,但這個原型內部沒有sayName函數。所以會執行一次初始化,而第二次開始new出來的對象原型中已經有sayName函數,以后就不會調用了,

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 228,443評論 6 532
  • 序言:濱河連續發生了三起死亡事件,死亡現場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機,發現死者居然都...
    沈念sama閱讀 98,530評論 3 416
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 176,407評論 0 375
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 62,981評論 1 312
  • 正文 為了忘掉前任,我火速辦了婚禮,結果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當我...
    茶點故事閱讀 71,759評論 6 410
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發上,一...
    開封第一講書人閱讀 55,204評論 1 324
  • 那天,我揣著相機與錄音,去河邊找鬼。 笑死,一個胖子當著我的面吹牛,可吹牛的內容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 43,263評論 3 441
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 42,415評論 0 288
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當地人在樹林里發現了一具尸體,經...
    沈念sama閱讀 48,955評論 1 336
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 40,782評論 3 354
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發現自己被綠了。 大學時的朋友給我發了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 42,983評論 1 369
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 38,528評論 5 359
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質發生泄漏。R本人自食惡果不足惜,卻給世界環境...
    茶點故事閱讀 44,222評論 3 347
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 34,650評論 0 26
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 35,892評論 1 286
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 51,675評論 3 392
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 47,967評論 2 374

推薦閱讀更多精彩內容