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