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'

函數(shù)對象

typeof是用來判斷變量類型

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

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

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

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函數(shù),其中調(diào)用showMsg函數(shù)的宿主是o,所以此時的this是o,this.name也就是'nexus',打印'my name is nexus'。

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

例子中定義了一個函數(shù),返回一個對象,其中包括屬性和方法,屬性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'

函數(shù)還可以作為構(gòu)造函數(shù)使用,像上面所述的 Object 和 Array 原生構(gòu)造函數(shù)一樣,在運行時會自動出現(xiàn)在執(zhí)行環(huán)境中:

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'

要創(chuàng)建 Person 的新實例,必須使用 new 操作符。

創(chuàng)建一個新對象,

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

執(zhí)行構(gòu)造函數(shù)中的代碼(為這個新對象添加屬性和方法),

返回新對象。

// 作為普通函數(shù)調(diào)用,添加到 window

Person('nexus',18);

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

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

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

// 當(dāng)作構(gòu)造函數(shù)使用,新建一個對象p

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

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

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

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

// 在另一個對象的作用域中調(diào)用

var o = {};

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

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

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

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

構(gòu)造函數(shù)雖然簡潔明了,但是存在缺點:

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

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

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

prototype 原型

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

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 屬性中,構(gòu)造函數(shù)變成了空函數(shù)。即使如此,也仍然可以通過調(diào)用構(gòu)造函數(shù)來創(chuàng)建新對象,而且新對象還會具有相同的屬性和方法。但與前面的例子不同的是,新對象的這些屬性和方法是由所有實例共享的。換句話說,p1 和 p2 訪問的都是同一組屬性和同一個 showMsg() 函數(shù)。

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不能刪除原型屬性

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

使用 delete 操作符刪除了 p1.name,之前它保存的 "noa" 值屏蔽了同名的原型屬性。把它刪除以后,就恢復(fù)了對原型中 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 重新設(shè)置為一個新對象。實例 p.constructor 屬性不再指向 Person 了,而是指向 Object 構(gòu)造函數(shù)。

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

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

動態(tài)性原型

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

function Person(){}

var p = new Person();

Person.prototype.sayHi = function(){

console.log('hi');

};

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

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

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

通過 p.name 沒有發(fā)生改變可以得出結(jié)論:調(diào)用構(gòu)造函數(shù)時會為實例添加一個指向最初原型的 Prototype 指針,而把原型修改為另外一個對象就等于切斷了構(gòu)造函數(shù)與最初原型之間的聯(lián)系。

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

原生對象的原型

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

if (!Array.prototype.map) {

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

// 調(diào)用宿主是 null 或者 undefined,則拋出 TypeError 異常

if (this == null) {

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

}

// 如果 callback 不是函數(shù),則拋出 TypeError 異常

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

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

}

// 將 O 賦值為調(diào)用 map 方法的數(shù)組

var O = Object(this);

// 將 len 賦值為數(shù)組 O 的長度

var len = O.length >>> 0;

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

var T;

if (thisArg) {

T = thisArg;

}

// 創(chuàng)建新數(shù)組 A,長度為原數(shù)組 O 長度 len

var A = new Array(len);

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

var k = 0;

// 當(dāng) k < len 時,執(zhí)行循環(huán)

while (k < len) {

var kValue, mappedValue;

// 遍歷 O,k 為原數(shù)組索引

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

if (k in O) {

// kValue 為索引 k 對應(yīng)的值

kValue = O[k];

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

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

// 返回值添加到新數(shù)組A中.

A[k] = mappedValue;

}

// k自增1

k++;

}

// 返回新數(shù)組A

return A;

};

}

構(gòu)造函數(shù)和原型結(jié)合

構(gòu)造函數(shù)用于定義實例屬性,而原型用于定義方法和共享的屬性。

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

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

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

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