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