javascript對象(上篇)

var a = 1;

console.log(typeof a);// 'number'

var b = '1';

console.log(typeof b);// 'string'

var c = true;

console.log(typeof c);// 'boolean'

var d = null;

console.log(typeof d);// 'object'

var e = undefined;

console.log(typeof e);// 'undefined'

函數對象

typeof是用來判斷變量類型

instancaof是用來檢測這個實例是不是由這個類所創建,換言之,就是檢測這個實例對象是不是這個類new出來的。

constructor是每一個實例對象都擁有的屬性,而這個屬性也相當于是一個指針,它指向于創建當前對象的對象。

從下面的代碼可以看出,在javascript中函數即是對象。

var o = new Object();

console.log(typeof o);// 'object'

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

console.log(o.constructor === Object);// true

console.log(o instanceof Function);// false

console.log(o.constructor === Function);// false

var f = new Function();

console.log(typeof f);// 'function'

console.log(f instanceof Function);// true

console.log(f.constructor === Function);// true

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

console.log(f.constructor === Object);// false

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

console.log(Function.constructor === Object);// false

console.log(Function instanceof Function);// true

console.log(Function.constructor === Function);// true

javascript對象可以任意自定義屬性和方法,沒有c++和java中的class的約束。

var o = {

name:'nexus', // 定義一個屬性

showMsg:function(){ // 定義一個方法

console.log('my name is ' + this.name);

}

};

console.log(o.name);// 'nexus'

o.showMsg();// 'my name is nexus'

例子中新建了一個對象o,其中o對象有屬性name和方法showMsg。

o.name就是'neuxs'。

o.showMsg()就是運行o的showMsg函數,其中調用showMsg函數的宿主是o,所以此時的this是o,this.name也就是'nexus',打印'my name is nexus'。

構造函數

例子中定義了一個函數,返回一個對象,其中包括屬性和方法,屬性name和age通過行參自定義。

function Person(name,age){

var o = new Object();

o.name = name;

o.age = age;

o.showMsg = function(){

console.log(this.name + ':' + this.age);

};

return o;

}

var p = Person('nexus',18);

console.log(p.name);// 'nexus'

console.log(p.age);// 18

p.showMsg();// 'nexus : 18'

函數還可以作為構造函數使用,像上面所述的 Object 和 Array 原生構造函數一樣,在運行時會自動出現在執行環境中:

function Person(name,age){

this.name = name;

this.age = age;

this.showMsg = function(){

console.log(this.name + ':' + this.age);

};

}

var p = new Person('nexus',18);

console.log(p.name);// 'nexus'

console.log(p.age);// 18

console.log(p.showMsg());// 'nexus : 18'

要創建 Person 的新實例,必須使用 new 操作符。

創建一個新對象,

將構造函數的作用域賦給新對象(因此 this 就指向了這個新對象),

執行構造函數中的代碼(為這個新對象添加屬性和方法),

返回新對象。

// 作為普通函數調用,添加到 window

Person('nexus',18);

console.log(window.name);// 'nexus'

console.log(window.age);// 18

window.showMsg();// 'nexus : 18'

// 當作構造函數使用,新建一個對象p

var p = new Person('nexus', 18);

console.log(p.name);// 'nexus'

console.log(p.age);// 18

p.showMsg();// 'nexus : 18'

// 在另一個對象的作用域中調用

var o = {};

Person.call(o,'nexus',18);

console.log(o.name);// 'nexus'

console.log(o.age);// 18

o.showMsg(); // 'nexus : 18'

構造函數雖然簡潔明了,但是存在缺點:

var p1 = new Person('nexus',18);

var p2 = new Person('nexus',18);

console.log(p1.name == p2.name);// true

console.log(p1.age == p2.age);// true

console.log(p1.showMsg == p2.showMsg);// false

我們可以發現,當每次建立一個新對象的時候,每個 Person 實例都包含一個不同的 showMsg() 方法,即使它們的內容相同,也需要另外開辟內存來保存。

創建兩個完成同樣任務的 showMsg 方法沒有必要,這個時候就需要使用prototype。

prototype 原型

我們創建的每個函數都有一個 prototype(原型)屬性。使用原型的好處是可以讓所有對象實例共享它所包含的屬性和方法。換句話說,不必在構造函數中定義對象實例的信息,而是可以將這些信息直接添加到原型中。

function Person(){}

Person.prototype.name = 'nexus';

Person.prototype.age = 18;

Person.prototype.showMsg = function(){

console.log(this.name + ':' + this.age);

};

var p1 = new Person();

p1.showMsg();// 'neuxs : 18'

var p2 = new Person();

p2.showMsg();// 'neuxs : 18'

console.log(p1.showMsg == p2.showMsg);// true

showMsg() 方法和所有屬性直接添加到了 Person 的 prototype 屬性中,構造函數變成了空函數。即使如此,也仍然可以通過調用構造函數來創建新對象,而且新對象還會具有相同的屬性和方法。但與前面的例子不同的是,新對象的這些屬性和方法是由所有實例共享的。換句話說,p1 和 p2 訪問的都是同一組屬性和同一個 showMsg() 函數。

function Person(){}

Person.prototype.name = 'nexus';

Person.prototype.age = 18;

Person.prototype.showMsg = function(){

console.log(this.name + ':' + this.age);

};

var p1 = new Person();

console.log(p1.name);// 'nexus',來自原型

p1.name = "noa";

console.log(p1.name);// 'noa',來自實例

delete p1.name;

console.log(p1.name);// 'nexus',來自原型

delete p1.name;

console.log(p1.name);// 'nexus',來自原型,delete不能刪除原型屬性

當訪問 p1.name 時,需要讀取它的值,因此就會在這個實例上搜索一個名為 name 的屬性。這個屬性確實存在,于是就返回它的值而不必再搜索原型了

使用 delete 操作符刪除了 p1.name,之前它保存的 "noa" 值屏蔽了同名的原型屬性。把它刪除以后,就恢復了對原型中 name 屬性的連接。

從代碼中看出 delete 操作符只能刪除對象的實例name屬性,無法刪除原型的 name 屬性。

function Person(){}

Person.prototype.name = 'nexus';

Person.prototype.age = 18;

Person.prototype.showMsg = function(){

console.log(this.name + ':' + this.age);

};

var p = new Person();

console.log(p instanceof Object);? ? ? // true

console.log(p.constructor === Object);? // false

console.log(p instanceof Person);? ? ? // true

console.log(p.constructor === Person);? // true

重寫整個原型對象 Person.prototype

function Person(){}

Person.prototype = {

name : 'nexus',

age : 18,

showMsg : function () {

console.log(this.name + ':' + this.age);

}

};

Person.prototype.name = 'nexus';

Person.prototype.age = 18;

Person.prototype.showMsg = function(){

console.log(this.name + ':' + this.age);

};

var p = new Person();

console.log(p instanceof Object);? ? ? // true

console.log(p.constructor === Object);? // true

console.log(p instanceof Person);? ? ? // true

console.log(p.constructor === Person);? // false

Person.prototype 重新設置為一個新對象。實例 p.constructor 屬性不再指向 Person 了,而是指向 Object 構造函數。

如果 constructor 的值真的很重要,可以像下面設置。

function Person(){}

Person.prototype = {

constructor : Person,

name : 'nexus',

age : 18,

showMsg : function () {

console.log(this.name + ':' + this.age);

}

};

Person.prototype.name = 'nexus';

Person.prototype.age = 18;

Person.prototype.showMsg = function(){

console.log(this.name + ':' + this.age);

};

var p = new Person();

console.log(p instanceof Object);? ? ? // true

console.log(p.constructor === Object);? // false

console.log(p instanceof Person);? ? ? // true

console.log(p.constructor === Person);? // true

動態性原型

我們對原型對象所做的任何修改都能夠立即反映出來:

function Person(){}

var p = new Person();

Person.prototype.sayHi = function(){

console.log('hi');

};

p.sayHi();? // 'hi'

調用 p.sayHi() 時,首先會在實例中搜索名為 sayHi 的屬性,在沒找到的情況下,會繼續搜索原型。因為實例與原型之間的連接只不過是一個指針,而非一個副本

如果是重寫整個原型對象,那么情況就不一樣了。

通過 p.name 沒有發生改變可以得出結論:調用構造函數時會為實例添加一個指向最初原型的 Prototype 指針,而把原型修改為另外一個對象就等于切斷了構造函數與最初原型之間的聯系。

function Person(){}

var p = new Person();

Person.prototype.name = 'nexus';

Person.prototype = {

constructor: Person,

name:'noa',

sayHi : function () {

console.log('hi');

}

};

console.log(p.name);// 'nexus'

p.sayName();// Uncaught TypeError: p.sayName is not a function

原生對象的原型

我們同樣可以給原生對象添加方法,這里展示一下如何實現一個Array.prototype.map函數的原型方法。

if (!Array.prototype.map) {

Array.prototype.map = function (callback, thisArg) {

// 調用宿主是 null 或者 undefined,則拋出 TypeError 異常

if (this == null) {

throw new TypeError('Array.prototype.map called on null or undefined');

}

// 如果 callback 不是函數,則拋出 TypeError 異常

if (typeof callback !== 'function') {

throw new TypeError(callback + ' is not a function');

}

// 將 O 賦值為調用 map 方法的數組

var O = Object(this);

// 將 len 賦值為數組 O 的長度

var len = O.length >>> 0;

// 如果參數 thisArg 有值,則將 T 賦值為 thisArg ,否則T為 undefined

var T;

if (thisArg) {

T = thisArg;

}

// 創建新數組 A,長度為原數組 O 長度 len

var A = new Array(len);

// 將 k 賦值為0,遍歷開頭

var k = 0;

// 當 k < len 時,執行循環

while (k < len) {

var kValue, mappedValue;

// 遍歷 O,k 為原數組索引

// 那些從來沒被賦過值或者使用 delete 刪除的索引則不會被調用。

if (k in O) {

// kValue 為索引 k 對應的值

kValue = O[k];

// 執行callback,this 指向 T,參數有三個,分別是 kValue:值,k :索引,O :原數組

mappedValue = callback.call(T, kValue, k, O);

// 返回值添加到新數組A中.

A[k] = mappedValue;

}

// k自增1

k++;

}

// 返回新數組A

return A;

};

}

構造函數和原型結合

構造函數用于定義實例屬性,而原型用于定義方法和共享的屬性。

function Person(name, age){

this.name = name;

this.age = age;

this.friends = ['a', 'b'];

}

Person.prototype = {

constructor : Person,

sayName : function(){

console.log(this.name);

}

}

var p1 = new Person('nexus', 18);

var p2 = new Person('noa', 19);

p1.friends.push('c');

console.log(p1.friends);// ['a','b','c']

console.log(p2.friends);// ['a','b']

console.log(p1.friends === p2.friends);// false

console.log(p1.sayName === p2.sayName);// true

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念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

推薦閱讀更多精彩內容

  • ##**理解對象**## --- ###**屬性類型** > JavaScript中有兩種屬性類型 分別是 數據屬...
    nullunde閱讀 310評論 0 0
  • 第5章 引用類型(返回首頁) 本章內容 使用對象 創建并操作數組 理解基本的JavaScript類型 使用基本類型...
    大學一百閱讀 3,261評論 0 4
  • # javascript 筆記 ## 1 - confign (sudo) - node - npm init -...
    wwzwwz閱讀 190評論 0 0
  • 工廠模式類似于現實生活中的工廠可以產生大量相似的商品,去做同樣的事情,實現同樣的效果;這時候需要使用工廠模式。簡單...
    舟漁行舟閱讀 7,798評論 2 17
  • 雨天總有一種特有的能力,去牽引你的思緒。打開空間隨意的翻看著每個人的小心事,高中很喜歡的語文老師發了一段...
    啊啾吧閱讀 615評論 2 4