天天看點

js對象——建立對象(1)總結擴充:

建立多個相似對象時使用的方法:

1. 工廠模式
//工廠模式
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 person=createPerson("Jim",22,"Doctor");

           
  • 缺點:工廠模式雖然解決了建立多個相似對象的問題,但卻沒有解決對象識别的問題(怎樣知道一個對象的類型)。
2.構造函數模式
//構造函數模式
function Person(name,age,job){
  this.name=name;
  this.age=age;
  this.job=job;
  this.sayName=function(){
    alert(this.name);
  }
}

var person=new Person("Jim",22,"Doctor");
           
  • 構造函數模式相對于工廠模式有幾個顯著的差別

    (1) 沒有顯式地建立對象

    (2)直接将屬性和方法賦給了this對象

    (3)沒有return語句

    (4)函數名的首字母大寫(Person和createPerson):按照慣例,構造函數始終都是以大寫字母開頭,而非構造函數則應該以小寫字母開頭

  • 解決對象識别問題

    (1)constructor

    上面的例子使person儲存這個Person的示執行個體,是以有了一個constructor(構造函數)屬性,該屬性指向Person

    alert( person.constructor == Person ); //true

    對象的constructor屬性最初是用來辨別對象類型的,但是檢測對象類型還是instanceof操作符更加可靠

    (2) instanceof

    alert( person instanceof Object ); //true

    alert( person instanceof Person); //true

    這個例子裡建立的對象既是Object執行個體又是Person的執行個體
  • new Person()這條語句經曆了什麼

    (a)建立一個新對象

    (b)将構造函數的作用域賦給新對象(是以this就指向了這個新對象)

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

    (d)傳回新對象

  • 構造函數的問題

    ECMAScript中的函數是對象,是以每定義一個函數,也就是執行個體化了一個對象。而構造函數在每次建立的時候都會完成同樣任務的function執行個體(sayName),這是很沒有必要的。是以可以把函數定義轉移到構造函數外部來解決這個問題。

function Person(name,age,job){
  this.name=name;
  this.age=age;
  this.job=job;
  this.sayName=sayName;
}
function sayName(){
  alert(this.name);
}
           

這樣做确實解決了兩個函數做同一件事的問題,可是新問題是:(a)全局函數不再“全局”了,而隻能由Person對象調用(b)沒有了“封裝性”。如果對象需要定義很多方法,那麼就要定義很多個的全局函數,于是我們的自定義的引用類型就絲毫沒有封裝性可言了。而接下來的原型模式就可以解決這個問題

3.原型模式
//原型模式
function Person(){}
Person.prototype.name="Jim";
Person.prototype.age=22;
Person.protoType.job="Doctor";
Person.protoType.sayName=function(){
  alert(this.name);
}
var person=new Person();
person.sayName();   //Jim
           
  • 原型模式的好處

    可以讓所有對象執行個體共享它所包含的屬性和方法。換句話說,不必在構造函數中定義對象執行個體的資訊,而是可以讓這些資訊直接添加到原型對象中。

  • 原型對象是什麼

    (1)無論什麼時候,隻要建立了一個新函數,就會根據一組特定的規則為該函數建立一個prototype屬性,這個屬性指向函數的原型對象。在預設情況下,所有的原型對象都會自動獲得一個constructor(構造函數)的屬性,這個屬性包含一個指向prototype屬性所在的函數的指針, 如下:

    alert( Person.prototype.constructor==Person ); //true

    (2)在我們調用

    person.sayName();

    的時候,會先後執行兩次搜尋,先找執行個體再找原型。

    首先解釋器會問:“執行個體person有sayName屬性嗎?”

    答曰:“沒有”

    然後繼續搜尋,再問“person的原型裡有sayName屬性嗎?”

    答曰:“有”

    于是他就讀取那個儲存在原型對象中的函數

    (3)當給person主動添加屬性時,就會屏蔽掉原型的屬性(但不會被替代,如果其他Person對象調用name屬性,還會預設為Jim),如:

function Person(){}
Person.prototype.name="Jim";
Person.prototype.age=22;
Person.protoType.job="Doctor";
Person.protoType.sayName=function(){
  alert(this.name);
}
var person=new Person();
person.name="Tom";
alert(person.name);   //Tom
           

此時person的name被一個新值給屏蔽了

(4)使用delete操作符删除 執行個體 屬性

person.name="Tom";
alert(person.name);   //Tom
delete person.name;
alert(person.name);   //Jim
           

(5)hasOwnProperty()方法檢測一個屬性時存在于執行個體中還是原型中

var person=new Person();
alert(person.hasOwnProperty("name"))  //false:存在原型中
person.name="Tom";
alert(person.hasOwnProperty("name"))  //true:存在對象執行個體中
           

(6)判斷對象中傳入的參數類型(隻要有prototype屬性的對象都能用)Object.prototype.toString.call(options)

使用方法:https://www.cnblogs.com/wyaocn/p/5796142.html

例如判斷傳入的參數是否為對象

function Person(name) {
	alert(Object.prototype.toString.call(name))
}
var Tim={
	age:11,
	job:"student",
}
var person=new Person(Tim);  //[object,object]
           
  • 原型與in操作符

    in操作符通過對象通路對象中的屬性時,傳回true ,無論該屬性是存在于執行個體中還是原型中

var person=new Person;
alert( "name" in person );    //true
person.name="Tom";    //将name寫在執行個體中
alert( "name" in person );    //true
           

是以想要确定該屬性是存在于原型中還是執行個體中,隻需要同時使用hasOwbProperty和in操作符

function hasPrototypeProperty(object,name){
  return !object.hasOwnProperty(name)&&(name in object);  //true:在原型中,false:在執行個體中
}
           

for-in循環,所有開發人員定義的屬性都是可枚舉的(IE8-除外),是以所有屬性都可以(執行個體屬性和原型屬性)使用for-in循環

for(var prop in person){
  if(prop=="name"){
    alert("Found name!")  ;  //IE除外
  }
}
           

IE8-的解決辦法見《JavaScript進階程式設計》(第三版)——人民郵電出版社,P153~154

  • 更簡單的原型文法
function Person(){}
Person.prototype={
  name:"Jim",
  age:22,
  job:"Doctor",
  sayName:function(){
    alert(this.name);
  }
}
           

注意,此時的constructor屬性不再指向Person了。前面介紹過,每建立一個函數,會同時建立它的prototype對象,這個對象會自動擷取constructor屬性,這裡本質上重寫了預設的prototype對象,此時constructor屬性指向Object構造函數,經管如此,instanceof操作符還能傳回正确的結果。

如果此時constructor的值很重要,可以在person.prototype中設定它,但會導緻constructor的預設不可枚舉特性被設定為可枚舉。

function Person(){}
Person.prototype={
  constructor:Person,
  name:"Jim",
  age:22,
  job:"Doctor",
  sayName:function(){
    alert(this.name);
  }
}
           

如果要相容ES5,可以試一下object.definProperty()

function Person(){}
Person.prototype={
  name:"Jim",
  age:22,
  job:"Doctor",
  sayName:function(){
    alert(this.name);
  }
};
//重設構造函數,隻适用于ES5相容的浏覽器
Object.definPrototype(Person.prototype,"constructor",{
  enumerable:false, //不可枚舉
  value:Person,
})
           
  • 原型對象的問題

    對于包含引用類型值的屬性來說,原型模式最大的問題——“共享”性,就非常突出了。

    看下面的例子

function Person(){}
Person.prototype={
  friends:["Amy","Sam"],
};
var person1=new Person();
var person2=new Person();
person1.friends.push("Van");
alert(person1.friends);     //"Amy,Sam,Van"
alert(person2.friends);     //"Amy,Sam,Van"
           

當沒有對Person的friends指派時,直接調用原型屬性并修改,就會直接導緻原型修改。

而這個問題證明我們很少有人單獨使用原型模式的問題所在。解決辦法看下面。

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

建立自定義類型最常見的方式,就是組合構造函數模式和原型模式。構造函數模式用于定義執行個體屬性,而原型模式用于定義方法和共享屬性

//組合使用構造模式和原型模式
function Person(name,age,job){
  this.name=name;
  this.age=age;
  this.job=job;
  this.friends=["Amy","Tom"];
}
Person.prototype={
  constructor:Person,
  sayName:function(){
    alert(this.name);
  }
}
var person1=new Person();
var person2=new Person();

person1.friends.push("Van");
alert(person1.friends);   //Amy,Tom,Van
alert(person2.friends);   //Amy,Tom
           

總結

在沒有類的情況下,可以使用下列模式建立對象:

(1)工廠模式:在函數裡面建立對象,并添加屬性和方法。但沒有解決對象識别問題,最後被構造函數模式所取代。

(2)構造函數模式:可以像建立内置對象執行個體一樣使用new操作符。但沒函數不局限于任何對象,是以沒有了封裝性。

(3)原型模式:使用構造函數的prototype屬性指定應該共享的屬性和方法。但單獨使用原型模式的話,就會使不需要共享的屬性和方法被共享

(4)組合使用構造函數模式和原型模式:使用構造函數定義執行個體屬性,使用原型定義共享的屬性和方法

-----------------------------------《JavaScript進階程式設計(第三版)》讀書筆記--------------------------------------------------------

擴充:

5. JSON格式的JS對象

  • 基本寫法

    遵照JSON原則 , PI , area相當于關鍵字,必須要打引号(ps:據說這是現在最流行的寫法之一,至于具體原因,以後弄懂了會補充)

var Circle={
   "PI":3.14159,
   "area":function(r){
     return this.PI * r * r;
  }
};
alert( Circle.area(1.0) );
           
  • 缺點:隻用來初始化對象
  • 優點:可以了解成封包, 且非常靈活,本身就是一種json格式,是以友善應用于json格式傳遞環境下