首先是對“對象”的理解:
對象是一組沒有特定屬性的值,對象的每一個屬性或方法都有一個名字,而每一個名字都映射到一個值,其中值可以是數據或函數。每一個對象都是基于一個引用類型創建的,這個引用類型可以是原生類型,也可以是開發人員自定義的類型。——高程
(好的,這里說的比較不容易理解)
(不急,接下來再看)
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()); //輸出屬性和方法的值
優缺點:
優點:簡單
缺點:產生大量代碼,封裝性差
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 語句,在后臺返回
規范
函數名和實例化構造名相同且大寫(非強制,主要是為了和普通函數區分開來)
通過構造函數創建對象,必須使用 new 運算符
優缺點
優點: 創建自定義的構造類型意味著將來可以將它的實例標識為一種特定的類型,(可以用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高級程序設計》
文章版權歸作者所有,未經允許請勿轉載,若此文章存在違規行為,您可以聯系管理員刪除。
轉載請注明本文地址:http://specialneedsforspecialkids.com/yun/95166.html
摘要:要用作原型的對象。函數對象可以創建普通對象,這個我們上面講過了回顧一下這是一個自定義構造函數普通對象沒法創建函數對象,凡是通過創建的對象都是函數對象,其他都是普通對象通常通過創建,可以通過來判斷。 關于js的原型和原型鏈,有人覺得這是很頭疼的一塊知識點,其實不然,它很基礎,不信,往下看要了解原型和原型鏈,我們得先從對象說起 創建對象 創建對象的三種方式: 對象直接量 通過對象直接量創建...
摘要:所以覺得把這個執行的詳細過程整理一下,幫助更好的理解。類似的語法報錯的如下圖所示三預編譯階段代碼塊通過語法分析階段之后,語法都正確的下回進入預編譯階段。另開出新文章詳細分析,主要介紹執行階段中的同步任務執行和異步任務執行機制事件循環。 一、概述 js是一種非常靈活的語言,理解js引擎的執行過程對于我們學習js是非常有必要的。看了很多這方便文章,大多數是講的是事件循環(event loo...
摘要:所以覺得把這個執行的詳細過程整理一下,幫助更好的理解。類似的語法報錯的如下圖所示三預編譯階段代碼塊通過語法分析階段之后,語法都正確的下回進入預編譯階段。另開出新文章詳細分析,主要介紹執行階段中的同步任務執行和異步任務執行機制事件循環。 一、概述 js是一種非常靈活的語言,理解js引擎的執行過程對于我們學習js是非常有必要的。看了很多這方便文章,大多數是講的是事件循環(event loo...
摘要:在基于原型的面向對象方式中,對象則是依靠構造函數和原型構造出來的。來看下面的例子優點與單純使用構造函數不一樣,原型對象中的方法不會在實例中重新創建一次,節約內存。 我們所熟知的面向對象語言如 C++、Java 都有類的的概念,類是實例的類型模板,比如Student表示學生這種類型,而不表示任何具體的某個學生,而實例就是根據這個類型創建的一個具體的對象,比如zhangsan、lisi,由...
摘要:對象創建的三種方式字面量創建方式系統內置構造函數方式自定義構造函數構造函數原型實例之間的關系實例是由構造函數實例化創建的,每個函數在被創建的時候,都會默認有一個對象。 JS 對象創建的三種方式 //字面量創建方式 var person= { name:jack } //系統內置構造函數方式 var person= new Object(); person.name = jack; ...
摘要:執行上下文作用域鏈和內部機制一執行上下文執行上下文是代碼的執行環境,它包括的值變量對象和函數。創建作用域鏈一旦可變對象創建完,引擎就開始初始化作用域鏈。 執行上下文、作用域鏈和JS內部機制(Execution context, Scope chain and JavaScript internals) 一、執行上下文 執行上下文(Execution context EC)是js代碼的執...
閱讀 705·2021-11-18 10:02
閱讀 2241·2021-11-15 18:13
閱讀 3160·2021-11-15 11:38
閱讀 2947·2021-09-22 15:55
閱讀 3675·2021-08-09 13:43
閱讀 2447·2021-07-25 14:19
閱讀 2457·2019-08-30 14:15
閱讀 3449·2019-08-30 14:15