js對象(一)——建立對象

首先是對「對象」的理解:

對象是一組沒有特定屬性的值,對象的每個屬性或方法都有一個名字,而每個名字都映射到一個值,其中值能夠是數據或函數。每個對象都是基於一個引用類型建立的,這個引用類型能夠是原生類型,也能夠是開發人員自定義的類型。——高程

(好的,這裏說的比較不容易理解)javascript

(不急,接下來再看)java

JavaScript中,一切都是對象,函數也是對象,數組也是對象,可是數組是對象的子集,而對於函數來講,函數與對象之間有一種「雞生蛋蛋生雞」的關係。

全部的對象都是由Object繼承而來,而Object對象倒是一個函數。對象都是由函數來建立的。數組

好比,在控制檯中
輸入 typeof Object 結果是"function",
輸入 typeof Function 結果仍是"function".
圖片描述瀏覽器

(好的,是否是更懵逼了,不急,如今先看一下怎麼建立對象以及各類方法的孰優孰劣)安全

建立對象

0. 最基本的模式:

var box=new Object(); //建立一個 Object 對象
box.name='Lee'; //建立一個 name 屬性並賦值
box.age= 100; //建立一個 age 屬性並賦值
box.run= function(){ //建立一個run()方法並返回值 
 return this.name + this.age; 
}; 
console.log(box.run()); //輸出屬性和方法的值

優缺點:app

  • 優勢:簡單
  • 缺點:產生大量代碼,封裝性差

1.工廠模式:

fuction creatPerson(name,age,job){
 var  o = new Object();  //建立對象
 o.name = name;   //添加屬性
 o.age = age;
 o.job = job;
 o.sayName = function(){  //添加方法
 console.log(this.name);
 }
  return o; //返回對象引用
}

var person1 = creatPerson("Nicholas",29,"engineer");//實例化
var person2 = creatPerson("Mike",28,"teacher");

優缺點:函數

  • 優勢:解決了建立多個類似對象的問題.
  • 缺點:但卻沒有解決對象識別問題,即怎樣知道一個對象的類型。也就是,由於根本沒法搞清楚他們究竟是哪一個對象的實例(這個能夠和下面的構造函數模式做對比)

2.構造函數模式

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

//或
function Person(name,age,job){
    this.name = name;
    this.age = age;
    this.job = job;
    this.sayName = sayName;//注意這裏不要寫括號,要實現引用地址一致
}
function sayName(){
    console.log(this.name);
    
}//這裏是在外面寫一個,但這種方法會有做用域問題,十分不推薦
 var person1 = new Person("Nicholas",29,"Engineer");//通常這樣實例化
 
 var o = new Object;;
 Person.call(o,"Kkresten",25,"Nurse");//對象冒充法實例化

區別:學習

  • 沒有寫出new Object,可是後臺會自動 var obj = new Object,而this就至關於obj
  • 沒有 renturn 語句,在後臺返回

規範this

  • 函數名和實例化構造名相同且大寫(非強制,主要是爲了和普通函數區分開來)
  • 經過構造函數建立對象,必須使用 new 運算符

優缺點spa

  • 優勢: 建立自定義的構造類型意味着未來能夠將它的實例標識爲一種特定的類型,(能夠用instanceof 來驗證),便可識別(這裏就能夠和上面工廠模式做對比了,這也是比工廠模式更強的地方)
  • 缺點:每一個方法都要在每一個實例上建立一遍,大可沒必要(當函數在內部時),

全局做用域中定義的函數只能被某個對象調用,這讓全局做用域有點名存實亡,並且,若是對象須要定義不少方法,那麼就要定義不少個全局函數,因而,這個自定義的引用類型就絲毫沒有封可言了(函數定義在外部時)。(因此函數在內部和外部都有缺點)

(好的,學到這裏,你已經大致掌握了怎麼建立一個對象,接下來將開始學習建立對象高大上的方法和概念)

3. 原型模式

咱們建立的每一個函數都有一個prototype(原型)屬性,這個屬性是一個指針,指向一個對象,(屬性值是對象)而這個對象的用途是包含能夠由特定類型的全部實例共享的屬性和方法。:prototype 經過 調用構造函數而建立的那個對象的原型對象。使用原型的好處可讓全部對象實例共享它所包含的屬性和方法。也就是說,沒必要在構造函數中定義對象信息,而是能夠直接將這些信息 添加到原型中。———高程

(好的,又回到了懵逼的狀態了,不急,先經過例子和圖來了解一下先)

function Person(){//構造函數
}

Person.prototype.name = "Nicholas";//添加原型屬性
Person.prototype.age = 29;
Person.prototype.job = "software Engineer";
Person.prototype.sayName = function() {//添加原型方法
   console.log(this.name);
}

var person1 = new Person(); //實例化
person1.sayName(); //「Nicholas」
var person2 = new Person(); //實例化
person2.sayName();//"Nicholas"

以下圖:
圖片描述
圖片描述

而我本身畫了個圖來加深一下認識(結合高程裏的那段話)
圖片描述
(是否是有點懂了,接下來再逐個仔細分析)

1.對於[[Prototype]]

  • 每個對象都有一個這樣的隱藏屬性,它引用了建立這個對象的函數的prototype原型對象,咱們來看一張圖:

圖片描述
注意:函數也是對象,天然它也有__proto__。
在控制檯中,咱們發現:
圖片描述

即函數的__proto__是函數類型。(也就說函數的原型對象是函數,而函數也是對象,因此函數的原型仍是對象)(這裏聽着有點繞,可是能夠先跳過)

還要注意一個特例,以下圖:
圖片描述

這裏,一切對象繼承自Object,而咱們又知道Object.prototype是它的原型對象,是一個對象,可是這個對象的__proto__卻爲null,是否說明構建Object對象的函數沒有原型對象,由於對象都是由函數建立的

(對於函數與對象的關係和涉及到的原型鏈的相關知識,還挺大挺深的,將單獨做爲一個話題來討論。若是這裏有點看得暈,能夠先只是知道prototype是什麼就能夠了)

注意: __proto__這個指針沒有標準的方法訪問,IE 瀏覽器在腳本訪問[[Prototype]]會不能識別,火狐和谷歌瀏覽器及其餘某些瀏覽器均能識別。雖然能夠輸出,但沒法獲取內部信息。([[Prototype]] 也可寫爲__proto__)雖然沒法訪問到,可是能夠經過: Object.isPrototypeOf(person1)判斷這個實例對象是否指向它的原型對象 ;而咱們也知道Person.prototype就是Object類型,即一個原型對象

//承接上面的代碼
Person.prototype.isPrototypeOf(person1);//true
Person.prototype.isPrototypeOf(person2);//true

2.對於原型模式的執行流程:

①先檢查這個對象自身有無這個屬性;若是有,直接使用它。

②若是沒法在對象自身找到須要的屬性,就會繼續訪問對象的[[Prototype]]鏈,找到則直接使用,再也不查找下去;若是一直找不到,最後就會返回undefined

3.能夠經過 hasOwnProperty()方法檢測屬性是否存在實例中,也能夠經過 in 來判斷 實例或原型中是否存在屬性;能夠經過Object.keys()方法或Object.getOwnPropertyNames()來獲得實例屬性,具體見高程。

4.優缺點:每添加一個屬性和方法就要敲一遍Person.prototype,並且視覺上說封裝性不夠好。固然優勢就是解決了上面構造函數的問題。

5.更簡單的原型模式

function Person(){
}
 Person.prototype = {  //將 Person.prototype 設置爲等於一個以對象字面量形式建立的新對象
    name : "Nicholas",
    age:  29,
    job:  "software Engineer",
    sayName : function() {
       console.log(this.name);
    }
 }
//(但constructor屬性再也不指向Person了,而是指向Object構造函數)
//但能夠這樣手動設置:
 function Person(){
}
 Person.prototype = {  
    constructor : Person,//手動設置
    name : "Nicholas",
    age: 29,
    job:  "software Engineer",
    sayName : function() {
       console.log(this.name);
    }
 }
//由於按上面的方式會致使它的[[Enumerable]]特性被設置爲true,因此還能夠像下面這樣
 function Person(){
}
 Person.prototype = {  
    name : "Nicholas",
    age: 29,
    job:  "software Engineer",
    sayName : function() {
       console.log(this.name);
    }
 }
Object.definePrototype(Person.prototype,"constructor"),{
   enumerable :  false;
   value : Person
  }
}

6.原型的動態性:

//承接上面的Person構造函數
var friend = new Person();
Person.prototype.sayhi = function(){
    alert("hi");
};
friend.sayhi(); //"hi"沒有問題,雖然是在實例以後添加的屬性,可是根據原型模式的搜索機制,會找到原型中的這個方法,緣由:實例與原型是鬆散鏈接的
//可是:若是是這樣:
 function Person(){
}
 var friend = new Person();
 Person.prototype = {  
    name : "Nicholas",
    age:  29,
    job:  "software Engineer",
     sayName : function() {
       console.log(this.name);
    }
 }
 friend.sayName();//Uncaught TypeError: friend.sayName is not a function,雖然有將重寫的原型的指針指向Person原型對象,可是很實際上卻以下圖:

圖片描述

6.優缺點:

  • 優勢:解決了構造函數出現的問題(強大的類型識別)
  • 缺點:共享了引用類型的值,這個就是不多有人單獨使用原型模式的緣由。好比下面:
function Person(){
}
 Person.prototype = {  
    constructor : Person,
    name : "Nicholas",
    age:  29,
    job:  "software Engineer",
    friend:["Mike","Jeny"],
    sayName : function() {
       console.log(this.name);
     }
}
var person1 = new Person();
var person2 = new Person();
person1.friend.push("Van");
console.log(person1.friend);//"Mike,Jeny,Van"
console.log(person2.friend);//"Mike,Jeny,Van"

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

function Perosn(name,age,job){
 this.name = name;
 this.age = age;
 this.job = job;
 this.friends = ["Shelby","Court'];
}
Person.prototype = {
 constructor : Person,
 sayName : function(){
    console.log(this.name);
  }
}
var person1 = new Person("Nicholas",29," Engineer");

優缺點:

  • 優勢:解決了引用類型實例共享的問題
  • 缺點:封裝性不夠好

## 5.動態原型模式 ##

function Person(name,age,job){
//屬性
    this.name = name; 
    this.age = age;
    this.job = job;
//方法
if(typeof this.sayname != "function"){
  Person.prototype.sayname = function(){
   console.log(this.name);//只有在sayName方法不存在的狀況下才會被添加到原型中
  }
}
//這段代碼在初次調用構造函數時纔會執行,此後,原型已經初始化
var friend  = new Person("Nicholas",29,"Engineer");

優缺點:

  • 優勢:既獲得了封裝,又實現了原型方法共享,而且屬性都保持獨立。能夠說是很是完美了,其實說白了這種方法就是解決上面構造函數的方法不須要每次都建立一遍的問題。
  • 缺點:不能使用對象字面量重寫原型,會使以前定義的原型對象的方法失效。

(好了,學到這裏,大概經常使用的建立對象的方法就已經掌握了,接下來還有兩種不經常使用的方法能夠了解一下)

6.寄生構造函數模式

function Person(name,age,job){
 var o  = new Object();
 o.name = name;
 o.age = age;
 o.job = job;
 o.sayName = function(){
    console.log(this.name);
  }
  return o;
}
var friend = new Person("Nicholas",29,"Software Engineer");

function SpecialArray(){
//建立數組
var values = new Array();
//用push方法初始化數組的值
values.push.apply(values,arguments);
//添加方法
values.toPipedString = function(){
 return this.join("|");
}
//返回數組
 return values;
}
var colors = new SpecialArray("red","blue","green");
console.log(colors.toPipedString()); //"red|blue|green"

優缺點:

  • 構造函數返回的對象與在構造函數外部建立的對象沒有什麼不一樣,爲此不能依賴instanceof操做符來肯定對象的類型:
console.log(friend instanceof Person) // false

所以,可使用其餘模式的狀況下不使用此類型

7.穩妥構造函數模式

function Person(name,age,job){
 //建立要返回的對象
 var o = new Object();
//能夠在這裏定義私有變量和函數
//添加方法
 o.sayName = function(){
 console.log(name);
 }
//返回對象
  return o;
}
var friend =  Person("Nicholas",29,"Software Engineer");
friend.sayName();

區別:

  • 不引用this的對象
  • 不使用new操做符

優勢:安全

(好了,js對象的建立就大概有這幾種方法,其實最經常使用的貌似仍是構造函數的模式,可是原型相關的東西也是必需要掌握的)

最後,歡迎你們圍觀指正!

參考:《javascript高級程序設計》

相關文章
相關標籤/搜索