面向?qū)ο?1 理解對象和創(chuàng)建對象

理解對象和創(chuàng)建對象

1.理解對象

  • 創(chuàng)建一個Object的實例,然為其添加屬性和方法(早期創(chuàng)建對象的模式)
var person = new Object();
person.name = "Nicholas";
person.age = 29;
person.job = "Software Engineer";

person.sayName = function(){
    alert(this.name);

}
  • 對象字面量語法重寫上述過程:
var person = {
    name: "Nicholas";
    age: 29;
    job: "Software Engineer";
    
    sayName: function(){
        alert(this.name);
    }
}

1.1 屬性類型

ECMAScript中有兩種 屬性(property) :數(shù)據(jù)屬性和訪問器屬性憎兽,用 特性(attribute) 來描述屬性的各種特征冷离。

數(shù)據(jù)屬性:

數(shù)據(jù)屬性包含一個數(shù)據(jù)值的位置,在這個位置可以讀取和寫入值纯命。

數(shù)據(jù)屬性有四個描述其行為的特性:

  • [[Enumerable]]:表示能否通過 for-in循環(huán)返回屬性西剥,默認值為true。
  • 要修改屬性默認的特性亿汞,必須使用ECMAScript的Object.defineProperty()方法
訪問器屬性

訪問器屬性不包含數(shù)據(jù)值瞭空,它們包含一對getter和setter函數(shù)(不是必須的)。在讀取訪問器屬性時疗我,會調(diào)用getter函數(shù)匙铡,在寫入訪問器屬性時,會調(diào)用setter函數(shù)并傳入新值碍粥。訪問器屬性有以下四個特性:

  • [[Enumerable]]:表示能否通過 for-in循環(huán)返回屬性鳖眼,默認值為true。
  • 訪問器屬性不能直接定義嚼摩,必須使用Object.defineProperty()方法來定義
var book = {
    _year:2004;
    edition: 1;
};
Object.defineProperty(book,"year",{
    get:function(){
        return this._year;
    },
    set: function(newValue){
        if(newValue > 2004){
            this._year = newValue;
            this.edition+=newValue -2004;
        }
    }
};
book.year = 2005; //對訪問器屬性year進行寫入钦讳,因此調(diào)用setter函數(shù)
alert(book.edition); //2

2 創(chuàng)建對象

Object構(gòu)造函數(shù)和對象字面量

前面提到了早期創(chuàng)建對象的兩種方法:Object構(gòu)造函數(shù)對象字面量矿瘦,但是這些方式有明顯的缺點:使用同一個接口創(chuàng)建很多對象,會產(chǎn)生大量的重復(fù)代碼愿卒。

工廠模式:

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 person1 = create("Nicholas", 29, "Software Engineer");

缺點:

  • 工廠模式雖然解決了創(chuàng)建多個相似對象會有大量重復(fù)代碼的問題缚去,但是卻沒有解決 對象識別 的問題(因為返回的都是Object類型)

構(gòu)造函數(shù)模式

ECMAScript中的構(gòu)造函數(shù)可以用來創(chuàng)建特定類型的對象。

function Person(name, age, job){
    this.name=name;
    this.age=age;
    this.job=job;
    this.sayName=function(){
        alert(this.name);
    }
}
// 注意將Person當做構(gòu)造函數(shù)琼开,需要使用new操作符來創(chuàng)建新對象
var person1 = new Person("xin",22,"Software Engineer");
var person2 = new Person("wu",22,"Software Engineer");

在上面的例子中易结,person1和person2分別保存著一個不同的實例,但是這兩個對象都有一個constructor(構(gòu)造函數(shù))屬性柜候,該屬性執(zhí)行Person搞动。

檢測對象類型:

  • 利用constructor屬性:
alert(person1.constructor == Person); //true
  • 利用instanceof操作符:
alert(person1 instanceof Object); //true
alert(person1 instanceof Person); //true

創(chuàng)建自定義的構(gòu)造函數(shù)意味著將來可以將它的事例標識為一種特定的類型

缺點:

  • 每個方法都要在每個實例上重新創(chuàng)建一遍。上例中渣刷,person1和person2中都有一個sayName的方法鹦肿,但是兩個實例中的方法不是同一個Function的實例。因此不同實例上的同名函數(shù)是不相等的辅柴。
  • 解決方法:把函數(shù)定義轉(zhuǎn)移到構(gòu)造函數(shù)外部箩溃。
function Person(name, age, job){
    this.name=name;
    this.age=age;
    this.job=job;
    this.sayName=sayName;//sayName屬性設(shè)置為全局的sayName函數(shù)
}
function sayName(){
        alert(this.name);
}
var person1 = new Person("xin",22,"Software Engineer");
var person2 = new Person("wu",22,"Software Engineer");
  • 修改后,person1和person2對象共享了在全局作用域中定義的同一個sayName()函數(shù)碌嘀。但是出現(xiàn)了新的問題:

    • 在全局作用域中定義的函數(shù)(sayName)實際上只能被某個對象(person1 person2)調(diào)用涣旨,讓全局作用域“名不副實”;

    • 如果對象需要定義很多方法股冗,則需要在全局作用域中定義很多全局函數(shù)开泽,是得這個 自定義的引用類型(自定義的構(gòu)造函數(shù)) 無封裝性可言。

    • 解決方法:原型模式

原型模式

每個函數(shù)都有一個prototype(原型) 屬性魁瞪,這個屬性是一個指針穆律,指向一個對象,而這個對象的用途是 包含可以由特定類型的所有實例共享的屬性和方法 导俘。

function Person(){
}
Person.prototype.name = "Nicholas";
Person.prototype.age = 29;
Person.prototype.job = "Software Engineer";
Person.ptototype.sayName = function(){
    alert(this.name);
}
var person1 = new Person();
person1.sayName(); //"Nicholas"

var person2 = new Person();
person2.sayName();//"Nicholas"
//person1和person2的屬性和方法是所有實例共享的
alert(person1.sayName == person2.sayName); //true
理解原型對象
image
原型屬性[[Prototype]]的訪問
  • 確定對象之間的關(guān)系 isPrototypeOf
alert(Person.prototype.isPrototypeOf(person1));//true
  • 獲取原型對象 Object.getPrototypeOf()
alert(Object.getPrototypeOf(person1) == Person.prototype); //true
alert(Object.getPrototypeOf(person1).name);//Nicholas
  • 每當代碼讀取某個對象的某個屬性時峦耘,都會執(zhí)行一次搜索,目標是具有給定名字的屬性旅薄。搜索 首先從對象實例本身開始 辅髓。如果在實例中找到了具有給定名字的屬性,則返回該屬性的值少梁。如果沒有找到洛口,則繼續(xù)搜索指針指向的原型對象,在原型對象中查找具有給定名字的屬性凯沪。如果在原型對象中找到了額這個屬性第焰,則返回該屬性的值。
  • 雖然可以通過對象實例訪問保存在原型中的值妨马,但卻不能通過對象重寫原型中的值挺举。如果在實例中添加了一個屬性杀赢,且該屬性與實例原型中的一個屬性同名,則該屬性會屏蔽原型中的那個屬性湘纵。
  • 可以使用hasOwnProperty()方法來檢測一個屬性是存在與實例中脂崔,還是存在與原型中。

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

  • 原型與in操作符:單獨使用in操作符時梧喷,in操作符會在通過原型能夠訪問給定屬性時返回true砌左,無論該屬性存在與實例中還是存在原型中。同時使用hasOwnProperty()方法和in操作符铺敌,就可以確定該屬性到底是存在與對象中汇歹,還是存在與原型中
alert( !person1.hasOwnProperty(name) && name in person1); //true
更簡單的原型語法:

前面的例子中每添加一個屬性和方法就要敲一遍Person.prototype适刀。為了減少不必要的輸入秤朗,更常見的方法是 用一個包含所有屬性和方法的對象字面量來重寫整個原型對象

function Person(){
}
Person.prototype = {
    name: "Nicholas",
    age: 29,
    job: "Software Engineer",
    sayName: function(){
        alert(this.name);
    }
}

注意
這里使用的語法煤蹭,本質(zhì)上完全重寫了默認的prototype對象笔喉,因此constructor屬性也就變成了新對象的constructor屬性(指向Object構(gòu)造函數(shù)),不再指向Person函數(shù)硝皂。使用instanceof操作符還能返回正確的結(jié)果常挚,但是通過constructor已經(jīng)無法確定對象的類型了。

var friend = new Person();
alert(friend.instanceof Person);//true
alert(friend.constructor == Person);//false

如果constructor屬性很重要稽物,可以將其設(shè)為適當?shù)闹?/p>

//方法一奄毡,但是會使constructor屬性的[[Enumerable]]特性變?yōu)閠rue
function Person(){
}
Person.prototype = {
    constructor:Person,
    name:"Nicholas",
    age:29,
    job:"Software Engineer",
    sayName:function(){
        alert(this.name);
    }
}
function Person(){
}
Person.prototype = {
    name:"Nicholas",
    job:"Software Engineer",
    age:29,
    sayName:function(){
        alert(this.name);
    }
}
Object.defindProperty(Person.prototype, "constructor",{ 
    enumerable: false,
    value: Person
});
原生對象的原型:

所有原生類型(Object,Array,String,等等)都在其構(gòu)造函數(shù)的原型上定義了方法。

原型對象的問題:
  • 省略了為構(gòu)造函數(shù)傳遞參數(shù)贝或,導(dǎo)致了所有實例在默認情況下都取得相同的屬性值吼过。
  • 原型對象的最大問題是由其共享屬性 的本質(zhì)所導(dǎo)致的:
function Person(){
}
Person.prototype = {
    constructor: Person,
    name: "Nicholas",
    age: 29,
    job: "Software Engineer",
    friends: ["Sheldon","Court"],
    sayName: function(){
        alert(this.name);
    }
};
var person1 = new Person();
var person2 = new Person();

person1.friends.push("van");
alert(person1.friends); //sheldon,court咪奖,van
alert(person2.friends); //sheldon盗忱,court,van
alert(person1.friends === person2.friends);//true

出現(xiàn)上述問題的原因在于:person1和person2的friends屬性共享一個數(shù)組羊赵。

組合使用構(gòu)造函數(shù)模式和原型模式

構(gòu)造函數(shù)模式用來定義實例屬性趟佃,原型模式用來定義方法和共享屬性。結(jié)果每個實例都會有自己的一份實例屬性的副本昧捷,但同時又共享著對方法的引用闲昭,最大限度的節(jié)省了內(nèi)存。

function Person(name,age,job){
    this.name = name;
    this.age = age;
    this.job = job;
    this.friends = ["sheldon","mary"];
}
Person.prototype = {
    constructor: Person,
    sayName: function(){
        alert(this.name);
    }
}

穩(wěn)妥構(gòu)造函數(shù)模式

  • 特點:1.新創(chuàng)建對象的實例方法不引用this靡挥;2.不使用new 操作符調(diào)用構(gòu)造函數(shù)
function Person(name,age,job){
    var o = new Object();
    o.sayName = function(){
        alert(name);
    }
    return o;
}
  • 以這種方式創(chuàng)建的對象中序矩,除了使用sayName()方法之外,沒有其他辦法訪問name的值跋破。
var friend = Person("Nicholas",29,"Software Engineer");
friend.sayName();

即使有其他代碼會給這個對象添加方法或數(shù)據(jù)成員贮泞,但也不可能有別的方法訪問傳入到構(gòu)造函數(shù)中的原始數(shù)據(jù)(name,job,age)楞慈。穩(wěn)妥構(gòu)造函數(shù)模式提供的這種安全性,使得它非常適合在某些安全環(huán)境中執(zhí)行啃擦。

使用穩(wěn)妥構(gòu)造函數(shù)模式創(chuàng)建的對象與構(gòu)造函數(shù)之間沒什么關(guān)系囊蓝,因此instanceof操作符對這種對象沒有什么意義

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市令蛉,隨后出現(xiàn)的幾起案子聚霜,更是在濱河造成了極大的恐慌,老刑警劉巖珠叔,帶你破解...
    沈念sama閱讀 216,843評論 6 502
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件蝎宇,死亡現(xiàn)場離奇詭異,居然都是意外死亡祷安,警方通過查閱死者的電腦和手機姥芥,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,538評論 3 392
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來汇鞭,“玉大人凉唐,你說我怎么就攤上這事』艚荆” “怎么了台囱?”我有些...
    開封第一講書人閱讀 163,187評論 0 353
  • 文/不壞的土叔 我叫張陵,是天一觀的道長读整。 經(jīng)常有香客問我簿训,道長,這世上最難降的妖魔是什么米间? 我笑而不...
    開封第一講書人閱讀 58,264評論 1 292
  • 正文 為了忘掉前任强品,我火速辦了婚禮,結(jié)果婚禮上屈糊,老公的妹妹穿的比我還像新娘的榛。我一直安慰自己,他們只是感情好另玖,可當我...
    茶點故事閱讀 67,289評論 6 390
  • 文/花漫 我一把揭開白布困曙。 她就那樣靜靜地躺著,像睡著了一般谦去。 火紅的嫁衣襯著肌膚如雪慷丽。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 51,231評論 1 299
  • 那天鳄哭,我揣著相機與錄音要糊,去河邊找鬼。 笑死妆丘,一個胖子當著我的面吹牛锄俄,可吹牛的內(nèi)容都是我干的局劲。 我是一名探鬼主播,決...
    沈念sama閱讀 40,116評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼奶赠,長吁一口氣:“原來是場噩夢啊……” “哼鱼填!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起毅戈,我...
    開封第一講書人閱讀 38,945評論 0 275
  • 序言:老撾萬榮一對情侶失蹤苹丸,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后苇经,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體赘理,經(jīng)...
    沈念sama閱讀 45,367評論 1 313
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,581評論 2 333
  • 正文 我和宋清朗相戀三年扇单,在試婚紗的時候發(fā)現(xiàn)自己被綠了商模。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 39,754評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡蜘澜,死狀恐怖施流,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情兼都,我是刑警寧澤嫂沉,帶...
    沈念sama閱讀 35,458評論 5 344
  • 正文 年R本政府宣布稽寒,位于F島的核電站扮碧,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏杏糙。R本人自食惡果不足惜慎王,卻給世界環(huán)境...
    茶點故事閱讀 41,068評論 3 327
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望宏侍。 院中可真熱鬧赖淤,春花似錦、人聲如沸谅河。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,692評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽绷耍。三九已至吐限,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間褂始,已是汗流浹背诸典。 一陣腳步聲響...
    開封第一講書人閱讀 32,842評論 1 269
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留崎苗,地道東北人狐粱。 一個月前我還...
    沈念sama閱讀 47,797評論 2 369
  • 正文 我出身青樓舀寓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親肌蜻。 傳聞我的和親對象是個殘疾皇子互墓,可洞房花燭夜當晚...
    茶點故事閱讀 44,654評論 2 354

推薦閱讀更多精彩內(nèi)容