摘要:返回新對象構造函數與其他函數的唯一區別,就在于調用它們的方式不同。在默認情況下,所有原型對象都會自動獲得一個構造函數屬性,這個屬性是一個指向屬性所在函數的指針。
對象 1.對象的定義
“無序屬性的集合,其屬性可以包含基本值,對象或者函數”。每個屬性都是一個名/值對。屬性名是字符串,因此我們可以把對象看成是從字符串到值的映射。
2.對象的創建=======
通過new創建對象
new運算符創建并初始化一個新對象。關鍵字new后跟隨一個函數調用,這個函數被稱為構造函數。
var person = new Object(); //創建一個空對象 person.name = "Jack"; //添加屬性 person.age = 20; person.sayName = function(){ //添加方法 alert(this.name); }
對象字面量
對象字面量是一個表達式。每次運算都創建并初始化一個新的對象。每次計算對象直接量的時候,也都會計算它的每個屬性的值。
var person = { name: "Jack", age: 20, sayName: function(){ alert(this.name); } };
原型
每一個JS對象(null除外)都與另一個對象相關聯?!傲硪粋€”對象就是我們所熟知的原型,每一個對象都從原型繼承屬性和方法。
所有通過對象字面量創建的對象都具有同一個原型對象,可以通過Object.prototype獲得對原型對象的引用。通過關鍵字new和構造函數調用創建的對象的原型就是構造函數的prototype屬性的值。
Object.prototype沒有原型,不繼承任何屬性。
Object.create()
ES5定義了一個名為Object.create的方法,他創建一個新對象,其中第一個參數是這個對象的原型,第二個參數用以對對象的屬性進行進一步描述。
可以通過任意原型創建新對象,換句話說,可以使任意對象可繼承。
var o1 = Object.create(Object.prototype); var o2 = Object.create(null); //不繼承任何屬性和方法
new一個對象
擴展:new一個對象時做了什么?
舉例:使用new關鍵字創建對象new ClassA()
1.創建一個空對象
var obj = {};
2.設置新對象的__proto__指向構造函數的原型對象
obj.__proto__ = ClassA.prototype;
3.將構造函數的作用域賦給新對象
ClassA.call(obj);
4.執行構造函數中的代碼并返回新對象
3.屬性類型數據屬性
數據屬性包含一個數據值的位置。在這個位置可以讀取和寫入值。
[[Configurable]]:表示能否通過 delete 刪除屬性從而重新定義屬性,能否修改屬性的特性,或者能否把屬性修改為訪問器屬性。
[[Enumerable]]:表示能否通過 for-in循環遍歷屬性。
[[Writable]]:表示能否修改屬性的值。
[[Value]]:包含這個屬性的數據值。
訪問器屬性
訪問器屬性不包含數據值,包含一對兒getter和setter函數(非必需)。訪問器屬性不能直接定義,要通過Object.defineProperty()來定義。
[[Get]]:在讀取屬性時調用的函數。
[[Set]]:在寫入屬性時調用的函數。
定義多個屬性
利用Object.defineProperties()方法可以通過描述符一次定義多個屬性。這個方法接收兩個對象參數:第一個對象是要添加和修改其屬性的對象,第二個對象的屬性與第一個對象中要添加或修改的屬性一一對應。
讀取屬性的特性
使用 ES5 的 Object.getOwnPropertyDescriptor()方法,可以取得給定屬性的描述符。這個方法接收兩個參數:屬性所在的對象和要讀取其描述符的屬性名稱。返回值是一個對象。
使用小結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 person1=createPerson("Jack",20,"Software Engineer"); var person2=createPerson("Tom",22,"Project Manager");
缺點:沒有解決對象識別的問題,即怎么知道一個對象的類型。
構造函數模式
像Object和Array這樣的原生構造函數,在運行時會自動出現在執行環境。此外,也可以創建自定義的構造函數,從而定義自定義對象類型的屬性和方法。
function Person(name,age,job){ this.name=name; this.age=age; this.job=job; this.sayName=function(){ alert(this.name); }; } var person1=new Person("Jack",20,"Software Engineer"); var person2=new Person("Tom",22,"Project Manager");
與工廠模式比:
沒有顯式地創建對象
直接將屬性和方法賦予了this對象
沒有return語句
要創建Person實例,必須使用new操作符,用這種方式調用的構造函數會經歷以下幾個步驟:
創建一個新的空對象
新對象的_proto_指向構造函數的原型對象
將構造函數的作用域賦值給新對象
執行構造函數內部的代碼,將屬性添加給person中的this對象。
返回新對象
構造函數與其他函數的唯一區別,就在于調用它們的方式不同。
缺點:構造函數內部的方法會被重復創建,不同實例內的同名函數是不相等的??赏ㄟ^將方法移到構造函數外部解決這一問題,但面臨新問題:封裝性不好。
原型模式:
我們創建的每個函數都有一個prototype屬性,這個屬性是一個指針,指向一個對象,而這個對象的用途是包含可以由特定類型的所有實例共享的屬性和方法。(prototype就是通過調用構造函數而創建的那個對象實例的原型對象)。
使用原型對象的好處是可以讓所有對象實例共享它所包含的屬性和方法。換句話說,不必在構造函數中定義對象實例的信息,而是可以將這些信息直接添加到原型對象中。
function Person(){ } Person.prototype.name="Jack"; Person.prototype.age=20; Person.prototype.job="Software Engineer"; Person.prototype.sayName=function(){ alert(this.name); }; var person1=new Person(); person1.sayName();//"Jack"
理解原型對象
無論什么時候,只要創建了一個新函數,就會根據一組特定的規則為該函數創建一個 prototype屬性,這個屬性指向函數的原型對象。在默認情況下,所有原型對象都會自動獲得一個 constructor(構造函數)屬性,這個屬性是一個指向 prototype 屬性所在函數的指針。
原型對象的問題:
它省略了為構造函數傳遞初始化參數這一環節,結果所有實例在默認情況下都將取得相同的屬性值,雖然這會在一定程度帶來一定的不便,但不是最大的問題,最大的問題是由其共享的本性所決定的。
對于包含基本值的屬性可以通過在實例上添加一個同名屬性隱藏原型中的屬性。然后,對于包含引用數據類型的值來說,會導致問題。
組合使用構造函數模式和原型模式
這是創建自定義類型的最常見的方式。
構造函數模式用于定義實例屬性,而原型模式用于定義方法和共享的屬性。所以每個實例都會有自己的一份實例屬性的副本,但同時共享著對方法的引用,最大限度的節省了內存。同時支持向構造函數傳遞參數。
function Person(name,age,job){ this.name=name; this.age=age; this.job=job; this.friends=["S","C"]; } Person.prototype={ constructor:Person, sayName:function(){ alert(this.name); } }; var person1=new Person(...);
動態原型模式
function Person(name,age,job){ this.name=name; this.age=age; this.job=job; if(typeof this.sayName!="function"){ Person.prototype.sayName=function(){ alert(this.name); }; } }
這里只有sayName()不存在的情況下,才會將它添加到原型中,這段代碼只會在初次調用構造函數時才執行。這里對原型所做的修改,能夠立刻在所有實例中得到反映。
5.繼承JS實現繼承的幾種方式
定義一個父類
//定義一個動物類 function Animal(name){ //屬性 this.name = name || "Animal"; //實例方法 this.sleep = function(){ console.log(this.name + " is sleeping~"); } } //原型方法 Animal.prototype.eat = function(food){ console.log(this.name + " is eating " + food); }
原型鏈繼承
構造函數、實例對象和原型對象的關系:
如果使一個原型對象等于另一個對象的實例,此時的原型對象將包含一個指向另一個原型的指針,相應地,另一個原型中也包含著一個指向另一個構造器函數的指針。假如另一個原型又是另一個類型的實例,如此層層遞進,就構成了實例與原型的鏈條。這就是原型鏈的基本概念。
function Cat(){ } Cat.prototype = new Animal(); Cat.prototype.name = "cat"; // Test Code var cat = new Cat(); console.log(cat.name); console.log(cat.eat("fish")); console.log(cat.sleep()); console.log(cat instanceof Animal); //true console.log(cat instanceof Cat); //true
原型鏈的問題:
來自原型對象的引用屬性被所有實例共享
創建子類實例時,無法向父類構造函數傳遞參數
構造函數繼承
這種繼承方式的基本思想是在子類構造函數的內部調用父類的構造函數。
function Cat(){ Animal.call(this); this.name = name || "Tom"; } // Test Code var cat = new Cat(); console.log(cat.name); console.log(cat.sleep()); console.log(cat instanceof Animal); // false console.log(cat instanceof Cat); // true
構造函數繼承的問題:
實例并不是父類的實例,只是子類的實例
方法都在構造函數中定義,函數復用無從談起
組合繼承
思路是使用原型鏈實現對原型屬性和方法的繼承,借用構造函數實現對實例屬性的繼承。
function Cat(){ Animal.call(this); this.name = name || "Tom"; } Cat.prototype = new Animal(); Cat.prototype.constructor = Cat; // Test Code var cat = new Cat(); console.log(cat.name); console.log(cat.sleep()); console.log(cat instanceof Animal); // true console.log(cat instanceof Cat); // true
組合繼承的問題: 調用了兩次父類構造函數,生成了兩份實例(子類實例將子類原型上的同名屬性屏蔽了),會多消耗一點內存。
寄生組合式繼承
通過寄生方式,砍掉父類的實例屬性,這樣,在調用兩次父類的構造的時候,就不會初始化兩次實例方法/屬性,避免的組合繼承的缺點。
function Cat(){ Animal.call(this); this.name = name || "Tom"; } (function(){ //創造一個沒有實例方法的類 var Super = function(){}; Super.prototype = Animal.prototype; Cat.prototype = new Super(); Cat.prototype.constructor = Cat; })(); // Test Code var cat = new Cat(); console.log(cat.name); console.log(cat.sleep()); console.log(cat instanceof Animal); // true console.log(cat instanceof Cat); //true
缺點: 稍顯復雜。
拷貝繼承
function Cat(){ var animal = new Animal(); for(var p in animal){ Cat.prototype[p] = animal[p]; } Cat.prototype.name = this.name || "Tom"; } // Test Code var cat = new Cat(); console.log(cat.name); console.log(cat.sleep()); console.log(cat instanceof Animal); // false console.log(cat instanceof Cat); // true
拷貝繼承存在的問題:
效率較低,內存占用高(因為要拷貝父類的屬性)
無法獲取父類不可枚舉的方法(for-in不能訪問到不可枚舉方法)
6.對象屬性的遍歷1.for...in
for...in循環遍歷對象自身和繼承的可枚舉屬性(不含Symbol屬性)
2.Object.keys(obj)
Object.keys返回一個數組,包括對象自身的(不含繼承的)所有可枚舉屬性(不含Symbol屬性)
3.Object.getOwnPropertyNames(obj)
Object.getOwnPropertyNames返回一個數組,包含對象自身的所有屬性(不含Symbol屬性,但是包括不可枚舉屬性)
4.Object.getOwnPropertySymbols(obj)
Object.getOwnPropertySymbols返回一個數組,包含對象自身的所有Symbol屬性。
5.Reflect.ownKeys(obj)
Reflect.ownKeys返回一個數組,包含對象自身的所有屬性,不管屬性名是Symbol還是字符串,也不管是否可枚舉。
對象的拷貝分為淺拷貝和深拷貝,簡單來說,淺復制只復制一層對象的屬性,而深復制則遞歸復制了所有層級。深拷貝和淺拷貝最根本的區別在于是否是真正獲取了一個對象的復制實體,而不是引用。
淺拷貝的實現
function shallowCopy(obj1) { var obj2 = {}; for (var prop in obj1) { if (obj1.hasOwnProperty(prop)) { obj2[prop] = obj1[prop]; } } return obj2; }
深拷貝的實現
// 方法一 JSON.parse(JSON.stringify(obj)); // 方法二 function deepCopy(obj1, obj2){ var obj2 = obj2 || {}; for(var prop in obj1) { if(typeof obj1[prop] === "object") { if(obj1[prop].constructor === Array) { obj2[prop] = []; } else { obj2[prop] = {}; } deepCopy(obj1[prop], obj2[prop]); } else { obj2[prop] = obj1[prop]; } } return obj2; }
本篇小結是對對象做一個系統的梳理,主要是在秋招之前供自己復習,有錯誤的地方還請大家指出。
文章版權歸作者所有,未經允許請勿轉載,若此文章存在違規行為,您可以聯系管理員刪除。
轉載請注明本文地址:http://specialneedsforspecialkids.com/yun/96177.html
摘要:性能訪問字面量和局部變量的速度是最快的,訪問數組和對象成員相對較慢變量標識符解析過程搜索執行環境的作用域鏈,查找同名標識符。建議將全局變量存儲到局部變量,加快讀寫速度。優化建議將常用的跨作用域變量存儲到局部變量,然后直接訪問局部變量。 缺陷 這本書是2010年出版的,這本書談性能是有時效性的,現在馬上就2018年了,這幾年前端發展的速度是飛快的,書里面還有一些內容考慮IE6、7、8的東...
摘要:最近在全力整理高性能的文檔,并重新學習一遍,放在這里方便大家查看并找到自己需要的知識點。 最近在全力整理《高性能JavaScript》的文檔,并重新學習一遍,放在這里方便大家查看并找到自己需要的知識點。 前端開發文檔 高性能JavaScript 第1章:加載和執行 腳本位置 阻止腳本 無阻塞的腳本 延遲的腳本 動態腳本元素 XMLHTTPRequest腳本注入 推薦的無阻塞模式...
摘要:一簡介是一種解釋性的腳本語言代碼不進行編譯,主要用來向頁面添加交互行為,主要由三部分組成核心,包含基本語法文檔對象模型瀏覽器對象模型是一種弱類型語言,可用修飾所有的變量不加時是全局變量二常見事件頁面或圖片加載完成時點擊提交按鈕時注意是在添加 一.簡介 javascript是一種解釋性的腳本語言(代碼不進行編譯),主要用來向HTML頁面添加交互行為,主要由三 部分組成:ECMAScrip...
摘要:為什么需要原型鏈為了實現繼承,具有相同特性的代碼不需要重復編寫,放在構造函數里面,實例化的對象都會擁有里面的屬性了,也就是可以共享屬性和方法。 一段簡單代碼引入 function Foo() {}; var f1 = new Foo(); showImg(https://segmentfault.com/img/bV4yXs?w=1176&h=944); 1.概念簡單理解 Foo...
摘要:則是把類似的異步處理對象和處理規則進行規范化,并按照采用統一的接口來編寫,而采取規定方法之外的寫法都會出錯。這個對象有一個方法,指定回調函數,用于在異步操作執行完后執行回調函數處理。到目前為止,已經學習了創建對象和用,方法來注冊回調函數。 Promise 本文從js的異步處理出發,引入Promise的概念,并且介紹Promise對象以及其API方法。 js里的異步處理 可以參考這篇文章...
閱讀 889·2023-04-26 02:16
閱讀 1197·2019-08-30 15:55
閱讀 2788·2019-08-30 15:53
閱讀 3382·2019-08-29 15:38
閱讀 2884·2019-08-29 13:42
閱讀 1979·2019-08-26 13:34
閱讀 1833·2019-08-26 10:10
閱讀 3075·2019-08-23 14:40