摘要:對(duì)象詳解對(duì)象深度剖析,深度理解對(duì)象這算是醞釀很久的一篇文章了。用空構(gòu)造函數(shù)設(shè)置類名每個(gè)對(duì)象都共享相同屬性每個(gè)對(duì)象共享一個(gè)方法版本,省內(nèi)存。
js對(duì)象詳解(JavaScript對(duì)象深度剖析,深度理解js對(duì)象)
這算是醞釀很久的一篇文章了。
JavaScript作為一個(gè)基于對(duì)象(沒有類的概念)的語(yǔ)言,從入門到精通到放棄一直會(huì)被對(duì)象這個(gè)問(wèn)題圍繞。
平時(shí)發(fā)的文章基本都是開發(fā)中遇到的問(wèn)題和對(duì)最佳解決方案的探討,終于忍不住要寫一篇基礎(chǔ)概念類的文章了。
本文探討以下問(wèn)題,在座的朋友各取所需,歡迎批評(píng)指正:
創(chuàng)建對(duì)象
__proto__與prototype
繼承與原型鏈
對(duì)象的深度克隆
一些Object的方法與需要注意的點(diǎn)
ES6新增特性
下面反復(fù)提到實(shí)例對(duì)象和原型對(duì)象,通過(guò)構(gòu)造函數(shù) new 出來(lái)的本文稱作 實(shí)例對(duì)象,構(gòu)造函數(shù)的原型屬性本文稱作 原型對(duì)象。
創(chuàng)建對(duì)象字面量的方式:
var myHonda = {color: "red", wheels: 4, engine: {cylinders: 4, size: 2.2}}
就是new Object()的語(yǔ)法糖,一樣一樣的。
工廠模式:
function createCar(){ ? ? ? var oTemp = new Object(); ? ? ? oTemp.name = arguments[0]; ? //直接給對(duì)象添加屬性,每個(gè)對(duì)象都有直接的屬性 ? ? ? oTemp.age = arguments[1]; ? ? ? oTemp.showName = function () { ? ? ? ? ? ? alert(this.name); ? ? ? };//每個(gè)對(duì)象都有一個(gè) showName 方法版本 ? ? return oTemp; }; var myHonda = createCar("honda", 5)
只是給new Object()包了層皮,方便量產(chǎn),并沒有本質(zhì)區(qū)別,姑且算作創(chuàng)建對(duì)象的一種方式。
構(gòu)造函數(shù):
function Person(name, age, sex) { this.name = name; this.age = age; this.sex = sex; this.getName = function() { return this.name; }; } var rand = new Person("Rand McKinnon", 33, "M");
上面構(gòu)造函數(shù)的 getName 方法,每次實(shí)例化都會(huì)新建該函數(shù)對(duì)象,還形成了在當(dāng)前情況下并沒有卵用的閉包,所以構(gòu)造函數(shù)添加方法用下面方式處理,工廠模式給對(duì)象添加方法的時(shí)候也應(yīng)該用下面的方式避免重復(fù)構(gòu)造函數(shù)對(duì)象
function Person(name, age, sex) { this.name = name; this.age = age; this.sex = sex; this.getName = getName } function getName() { return this.name; };
構(gòu)造函數(shù)創(chuàng)建對(duì)象的過(guò)程和工廠模式又是半斤八兩,相當(dāng)于隱藏了創(chuàng)建新對(duì)象和返回該對(duì)象這兩步,構(gòu)造函數(shù)內(nèi) this 指向新建對(duì)象,沒什么不同。
最大不同點(diǎn): 構(gòu)造函數(shù)創(chuàng)造出來(lái)的對(duì)象 constructor 屬性指向該構(gòu)造函數(shù),工廠模式指向 function Object(){...}。
構(gòu)造函數(shù)相當(dāng)于給原型鏈上加了一環(huán),構(gòu)造函數(shù)有自己的 prototype,工廠模式就是個(gè)普通函數(shù)。說(shuō)到這兒我上一句話出現(xiàn)了漏洞,工廠模式的 constructor 指向哪得看第一句話 new 的是什么。
構(gòu)造函數(shù)直接調(diào)用而不 new 的話,就看調(diào)用時(shí)候 this 指向誰(shuí)了,直接調(diào)用就把屬性綁到 window 上了,通過(guò) call 或者 apply 綁定到其他對(duì)象作用域就把屬性添加到該對(duì)象了。
原型模式:
構(gòu)造函數(shù)雖然在原型鏈上加了一環(huán),但顯然這一環(huán)啥都沒有啊,這樣一來(lái)和工廠模式又有什么區(qū)別?加了一環(huán)又有什么意義?原型模式浮出水面。
function Car(){} //用空構(gòu)造函數(shù)設(shè)置類名 Car.prototype.color = "blue";//每個(gè)對(duì)象都共享相同屬性 Car.prototype.doors = 3; Car.prototype.drivers = new Array("Mike","John"); Car.prototype.showColor = function(){ ? ? ? ? alert(this.color); };//每個(gè)對(duì)象共享一個(gè)方法版本,省內(nèi)存。 //構(gòu)造函數(shù)的原型屬性可以通過(guò)字面量來(lái)設(shè)置,別忘了通過(guò) Object.defineProperty()設(shè)置 constructor 為該構(gòu)造函數(shù) function Car(){} Car.prototype = { color:"blue", doors:3, showColor:function(){ ? ? ? ? alert(this.color); } } Object.defineProperty(Car.prototype, "constructor", { enumerable:false, value:Car }) //(不設(shè)置 constructor 會(huì)導(dǎo)致 constructor 不指向構(gòu)造函數(shù),直接設(shè)置 constructor 會(huì)導(dǎo)致 constructor 可枚舉)
使用原型模式注意動(dòng)態(tài)性,通過(guò)構(gòu)造函數(shù)實(shí)例化出的對(duì)象,他的原型對(duì)象是構(gòu)造函數(shù)的 prototype ,如果在他的原型對(duì)象上增加或刪除一些方法,該對(duì)象會(huì)繼承這些修改。例如,先通過(guò)構(gòu)造函數(shù) A 實(shí)例化出對(duì)象 a ,然后再給 A.prototype 添加一個(gè)方法,a 是可以繼承這個(gè)方法的。但是給 A.prototype 設(shè)置一個(gè)新的對(duì)象,a 是不會(huì)繼承這個(gè)新對(duì)象的屬性和方法的。聽起來(lái)有點(diǎn)繞,修改 A.prototype 相當(dāng)于直接修改 a 的原型對(duì)象,a 很自然的會(huì)繼承這些修改,但是重新給 A.prototype 賦值的話,修改的是構(gòu)造函數(shù)的原型,并沒有影響 a 的原型對(duì)象!a 被創(chuàng)建出來(lái)以后原型對(duì)象就已經(jīng)確定了,除非直接修改這個(gè)原型對(duì)象(或者這個(gè)原型對(duì)象的原型對(duì)象),否則 a 是不會(huì)繼承這些修改的!
Object.create()
傳入要?jiǎng)?chuàng)建對(duì)象實(shí)例的原型對(duì)象,和原型模式幾乎是一個(gè)意思也是相當(dāng)于在原型鏈上加了一環(huán),區(qū)別在于這種方式創(chuàng)建的對(duì)象沒有構(gòu)造函數(shù)。這種方式相當(dāng)于:
function object(o){ function F(){} F.prototype = o; return new F() }
相當(dāng)于構(gòu)造函數(shù)只短暫的存在了一會(huì),創(chuàng)建出來(lái)的對(duì)象的 constructor 指向 原型對(duì)象 o 的 constructor !
混合模式:
使用原型模式時(shí),當(dāng)給實(shí)例對(duì)象設(shè)置自己專屬的屬性的時(shí)候,該實(shí)例對(duì)象會(huì)忽略原型鏈中的該屬性。但當(dāng)原型鏈中的屬性是引用類型值的時(shí)候,操作不當(dāng)有可能會(huì)直接修改原型對(duì)象的屬性!這會(huì)影響到所有使用該原型對(duì)象的實(shí)例對(duì)象!
大部分情況下,實(shí)例對(duì)象的多數(shù)方法是共有的,多數(shù)屬性是私有的,所以屬性在構(gòu)造函數(shù)中設(shè)置,方法在原型中設(shè)置是合適的,構(gòu)造函數(shù)與原型結(jié)合使用是通常的做法。
還有一些方法,無(wú)非是工廠模式與構(gòu)造函數(shù)與原型模式的互相結(jié)合,在生成過(guò)程和 this 指向上做一些小變化。
class 方式:
見下面 ES6 class 部分,只是一個(gè)語(yǔ)法糖,本質(zhì)上和構(gòu)造函數(shù)并沒有什么區(qū)別,但是繼承的方式有一些區(qū)別。
這兩個(gè)到底是什么關(guān)系?搞清楚 實(shí)例對(duì)象 構(gòu)造函數(shù) 原型對(duì)象 的三角關(guān)系,這兩個(gè)屬性的用法就自然清晰了,順便說(shuō)下 constructor。
構(gòu)造函數(shù)創(chuàng)建的實(shí)例對(duì)象的 constructor 指向該構(gòu)造函數(shù)(但實(shí)際上 constructor 是對(duì)應(yīng)的原型對(duì)象上的一個(gè)屬性!所以實(shí)例對(duì)象的 constructor 是繼承來(lái)的,這一點(diǎn)要注意,如果利用原型鏈繼承,constructor 將有可能指向原型對(duì)象的構(gòu)造函數(shù)甚至更上層的構(gòu)造函數(shù),其他重寫構(gòu)造函數(shù) prototype 的行為也會(huì)造成 constructor 指向問(wèn)題,都需要重設(shè) constructor),構(gòu)造函數(shù)的 prototype 指向?qū)?yīng)的原型對(duì)象,實(shí)例對(duì)象的 __proto__ 指對(duì)應(yīng)的原型對(duì)象,__proto__是瀏覽器的實(shí)現(xiàn),并沒有出現(xiàn)在標(biāo)準(zhǔn)中,可以用 constructor.prototype 代替??紤]到 Object.create() 創(chuàng)建的對(duì)象,更安全的方法是 Object.getPrototpyeOf() 傳入需要獲取原型對(duì)象的實(shí)例對(duì)象。
我自己都感覺說(shuō)的有點(diǎn)亂,但是他們就是這樣的,上一張圖,看看能不能幫你更深刻理解這三者關(guān)系。
當(dāng)訪問(wèn)一個(gè)對(duì)象的屬性時(shí),如果在對(duì)象本身找不到,就會(huì)去搜索對(duì)象的原型,原型的原型,知道原型鏈的盡頭 null,那原型鏈?zhǔn)窃趺存溒饋?lái)的?
把 實(shí)例對(duì)象 構(gòu)造函數(shù) 原型對(duì)象 視為一個(gè)小組,上面說(shuō)了三者互相之間的關(guān)系,構(gòu)造函數(shù)是函數(shù),可實(shí)例對(duì)象和原型對(duì)象可都是普通對(duì)象啊,這就出現(xiàn)了這樣的情況:
這個(gè)小組的原型對(duì)象,等于另一個(gè)小組實(shí)例對(duì)象,而此小組的原型對(duì)象又可能是其他小組的實(shí)例對(duì)象,這樣一個(gè)個(gè)的小組不就連接起來(lái)了么。舉個(gè)例子:
function Super(){ this.val = 1; this.arr = [1]; } function Sub(){ // ... } Sub.prototype = new Super();
Sub 是一個(gè)小組 Super 是一個(gè)小組,Sub 的原型對(duì)象鏈接到了 Super 的實(shí)例對(duì)象。
基本上所有對(duì)象順著原型鏈爬到頭都是 Object.prototype , 而 Object.prototype 就沒有原型對(duì)象,原型鏈就走到頭了。
判斷構(gòu)造函數(shù)和原型對(duì)象是否存在于實(shí)例對(duì)象的原型鏈中:
實(shí)例對(duì)象 instanceof 構(gòu)造函數(shù),返回一個(gè)布爾值,原型對(duì)象.isPrototypeOf(實(shí)例對(duì)象),返回一個(gè)布爾值。
上面是最簡(jiǎn)單的繼承方式了,但是有兩個(gè)致命缺點(diǎn):
所有 Sub 的實(shí)例對(duì)象都繼承自同一個(gè) Super 的實(shí)例對(duì)象,我想傳參數(shù)到 Super 怎么辦?
如果 Super 里有引用類型的值,比如上面例子中我給 Sub 的實(shí)例對(duì)象中的 arr 屬性 push 一個(gè)值,豈不是牽一發(fā)動(dòng)全身?
下面說(shuō)一種最常用的組合繼承模式,先舉個(gè)例子:
function Super(value){ // 只在此處聲明基本屬性和引用屬性 this.val = value; this.arr = [1]; } // 在此處聲明函數(shù) Super.prototype.fun1 = function(){}; Super.prototype.fun2 = function(){}; //Super.prototype.fun3... function Sub(value){ Super.call(this,value); // 核心 // ... } Sub.prototype = new Super(); // 核心
過(guò)程是這樣的,在簡(jiǎn)單的原型鏈繼承的基礎(chǔ)上, Sub 的構(gòu)造函數(shù)里運(yùn)行 Super ,從而給 Sub 的每一個(gè)實(shí)例對(duì)象一份多帶帶的屬性,解決了上面兩個(gè)問(wèn)題,可以給 Super 傳參數(shù)了,而且因?yàn)槭仟?dú)立的屬性,不會(huì)因?yàn)檎`操作引用類型值而影響其他實(shí)例了。不過(guò)還有個(gè)小缺點(diǎn): Sub 中調(diào)用的 Super 給每個(gè) Sub 的實(shí)例對(duì)象一套新的屬性,覆蓋了繼承的 Super 實(shí)例對(duì)象的屬性,那被覆蓋的的那套屬性不就浪費(fèi)了?豈不是白繼承了?最嚴(yán)重的問(wèn)題是 Super 被執(zhí)行了兩次,這不能忍(其實(shí)也沒多大問(wèn)題)。下面進(jìn)行一下優(yōu)化,把上面例子最后一行替換為:
Sub.prototype = Object.create(Super.prototype); // Object.create() 給原型鏈上添加一環(huán),否則 Sub 和 Super 的原型就重疊了。 Sub.prototype.constructor = Sub;
到此為止,繼承非常完美。
其他還有各路繼承方式無(wú)非是在 簡(jiǎn)單原型鏈繼承 --> 優(yōu)化的組合繼承 路程之間的一些思路或者封裝。
通過(guò) class 繼承的方式:
通過(guò) class 實(shí)現(xiàn)繼承的過(guò)程與 ES5 完全相反,詳細(xì)見下面 ES6 class的繼承 部分。
JavaScript的基礎(chǔ)類型是值傳遞,而對(duì)象是引用傳遞,這導(dǎo)致一個(gè)問(wèn)題:
克隆一個(gè)基礎(chǔ)類型的變量的時(shí)候,克隆出來(lái)的的變量是和舊的變量完全獨(dú)立的,只是值相同而已。
而克隆對(duì)象的時(shí)候就要分兩種情況了,簡(jiǎn)單的賦值會(huì)讓兩個(gè)變量指向同一塊內(nèi)存,兩者代表同一個(gè)對(duì)象,甚至算不上克隆克隆。但我們常常需要的是兩個(gè)屬性和方法完全相同但卻完全獨(dú)立的對(duì)象,稱為深度克隆。我們接下來(lái)討論幾種深度克隆的方法。
說(shuō)幾句題外的話,業(yè)界有一個(gè)非常知名的庫(kù) immutable ,個(gè)人認(rèn)為很大程度上解決了深度克隆的痛點(diǎn),我們修改一個(gè)對(duì)象的時(shí)候,很多時(shí)候希望得到一個(gè)全新的對(duì)象(比如Redux每次都要用一個(gè)全新的對(duì)象修改狀態(tài)),由此我們就需要進(jìn)行深度克隆。而 immutable 相當(dāng)于產(chǎn)生了一種新的對(duì)象類型,每一次修改屬性都會(huì)返回一個(gè)全新的 immutable 對(duì)象,免去了我們深度克隆的工作是小事,關(guān)鍵性能特別好。
歷遍屬性
function clone(obj){ var newobj = obj.constructor === Array ? [] : {}; // 用 instanceof 判斷也可 if(typeof obj !== "object" || obj === null ){ return obj } else { for(var i in obj){ newobj[i] = typeof obj[i] === "object" ??cloneObj(obj[i]) : obj[i];? // 只考慮 對(duì)象和數(shù)組, 函數(shù)雖然也是引用類型,但直接賦值并不會(huì)產(chǎn)生什么副作用,所以函數(shù)類型無(wú)需深度克隆。 } } ??return newobj; };
原型式克隆
function clone(obj){ function F() {}; F.prototype = obj; var f = new F(); for(var key in obj) ? { ? ??if(typeof obj[key] =="object") ? ??{ ? ????f[key] = clone(obj[key]) ? } ??} return f ; }
這種方式不能算嚴(yán)格意義上的深度克隆,并沒有切斷新對(duì)象與被克隆對(duì)象的聯(lián)系,被克隆對(duì)象作為新對(duì)象的原型存在,雖然新對(duì)象的改變不會(huì)影響舊對(duì)象,但反之則不然!而且給新對(duì)象屬性重新賦值的時(shí)候只是覆蓋了原型中的屬性,在歷遍新對(duì)象的時(shí)候也會(huì)出現(xiàn)問(wèn)題。這種方式問(wèn)題重重,除了實(shí)現(xiàn)特殊目的可以酌情使用,通常情況應(yīng)避免使用。
json序列化
var newObj = JSON.parse(JSON.stringify(obj));
這是我最喜歡的方式了!簡(jiǎn)短粗暴直接!但是最大的問(wèn)題是,畢竟JSON只是一種數(shù)據(jù)格式所以這種方式只能克隆屬性,不能克隆方法,方法在序列化以后就消失了。。。
一些Object的方法與需要注意的點(diǎn)Object 自身的方法:
設(shè)置屬性,Object.defineProperty(obj, prop, descriptor) 根據(jù) descriptor 定義 obj 的 prop 屬性(值,是否可寫可枚舉可刪除等)。
Object.getOwnPropertyDescriptor(obj, prop) 返回 obj 的 prop 屬性的描述。
使對(duì)象不可拓展,Object.preventExtensions(obj),obj 將不能添加新的屬性。
判斷對(duì)像是否可拓展,Object.isExtensible(obj)。
密封一個(gè)對(duì)象,Object.seal(obj),obj 將不可拓展且不能刪除已有屬性。
判斷對(duì)象是否密封,Object.isSealed(obj)。
凍結(jié)對(duì)象,Object.freeze(obj) obj 將被密封且不可修改。
判斷對(duì)象是否凍結(jié),Object.isFrozen(obj)。
獲取對(duì)象自身屬性(包括不可枚舉的),Object.getOwnPropertyNames(obj),返回 obj 所有自身屬性組成的數(shù)組。
獲取對(duì)象自身屬性(不包括不可枚舉的),Object.keys(obj),返回 obj 所有自身可枚舉屬性組成的數(shù)組。
當(dāng)使用for in循環(huán)遍歷對(duì)象的屬性時(shí),原型鏈上的所有可枚舉屬性都將被訪問(wèn)。
只關(guān)心對(duì)象本身時(shí)用Object.keys(obj)代替 for in,避免歷遍原型鏈上的屬性。
獲取某對(duì)象的原型對(duì)象,Object.getPrototypeOf(object),返回 object 的原型對(duì)象。
設(shè)置某對(duì)象的原型對(duì)象,Object.setPrototypeOf(obj, prototype),ES6 新方法,設(shè)置 obj 的原型對(duì)象為 prototype ,該語(yǔ)句比較耗時(shí)。
Object.prototype 上的方法:
檢查對(duì)象上某個(gè)屬性是否存在時(shí)(存在于本身而不是原型鏈中),obj.hasOwnProperty()?是唯一可用的方法,他不會(huì)向上查找原型鏈,只在 obj 自身查找,返回布爾值。
檢測(cè)某對(duì)象是否存在于參數(shù)對(duì)象的原型鏈中,obj.isPrototypeOf(obj2),obj 是否在 obj2 的原型鏈中,返回布爾值。
檢測(cè)某屬性是否是對(duì)象自身的可枚舉屬性,obj.propertyIsEnumerable(prop),返回布爾值。
對(duì)象類型,obj.toString(),返回 "[object type]" type 可以是 Date,Array,Math 等對(duì)象類型。
obj.valueOf(),修改對(duì)象返回值時(shí)的行為,使用如下:
function myNumberType(n) { this.number = n; } myNumberType.prototype.valueOf = function() { return this.number; }; myObj = new myNumberType(4); myObj + 3; // 7ES6新增特性
判斷兩個(gè)值是否完全相等,Object.is(value1, value2),類似于 === 但是可以用來(lái)判斷 NaN。
屬性和方法簡(jiǎn)寫:
// 屬性簡(jiǎn)寫 var foo = "bar"; var baz = {foo}; baz // {foo: "bar"} // 等同于 var baz = {foo: foo}; // 方法簡(jiǎn)寫 function f(x, y) { return {x, y}; } // 等同于 function f(x, y) { return {x: x, y: y}; } f(1, 2) // Object {x: 1, y: 2}
合并對(duì)象:
Object.assign(target, [...source]);將 source 中所有和枚舉的屬性復(fù)制到 target。
多個(gè) source 對(duì)象有同名屬性,后面的覆蓋前面的。
var target = { a: 1 }; var source1 = { b: 2 }; var source2 = { c: 3 }; Object.assign(target, source1, source2); target // {a:1, b:2, c:3}
注意一點(diǎn),該命令執(zhí)行的是淺克隆,如果 source 中有屬性是對(duì)象,target 中會(huì)復(fù)制該對(duì)象的引用。
常用于給對(duì)象添加屬性和方法(如給構(gòu)造函數(shù)的原型添加方法),克隆、合并對(duì)象等。
獲取對(duì)象自身的值或鍵值對(duì)(做為Object.keys(obj)的補(bǔ)充不包括不可枚舉的):
Object.keys(obj)返回 obj 自身所有可枚舉屬性的值組成的數(shù)組。
Object.entries(obj)返回 obj 自身所有可枚舉鍵值對(duì)數(shù)組組成的數(shù)組,例如:
var obj = { foo: "bar", baz: 42 }; Object.entries(obj) // [ ["foo", "bar"], ["baz", 42] ] // 可用于將對(duì)象轉(zhuǎn)為 Map 結(jié)構(gòu) var obj = { foo: "bar", baz: 42 }; var map = new Map(Object.entries(obj)); map // Map { foo: "bar", baz: 42 }
拓展運(yùn)算符:
取出對(duì)象所有可歷遍屬性,舉例:
let z = { a: 3, b: 4 }; let n = { ...z }; n // { a: 3, b: 4 } // 可代替 Object.assign() let ab = { ...a, ...b }; // 等同于 let ab = Object.assign({}, a, b);
可用于解構(gòu)賦值中最后一個(gè)參數(shù):
let { x, y, ...z } = { x: 1, y: 2, a: 3, b: 4 }; x // 1 y // 2 z // { a: 3, b: 4 } // 可以這樣理解,把 z 拆開以后就等于后面對(duì)象未被分配出去的鍵值對(duì)。
Null 傳導(dǎo)運(yùn)算符:
const firstName = message?.body?.user?.firstName || "default"; // 代替 const firstName = (message && message.body && message.body.user && message.body.user.firstName) || "default";
class:
ES6 引入了 class 關(guān)鍵字,但并沒有改變對(duì)象基于原型繼承的原理,只是一個(gè)語(yǔ)法糖,讓他長(zhǎng)得像傳統(tǒng)面向?qū)ο笳Z(yǔ)言而已。
以下兩個(gè)寫法完全等價(jià):
function Point(x, y) { this.x = x; this.y = y; } Point.prototype.toString = function () { return "(" + this.x + ", " + this.y + ")"; }; //定義類 class Point { constructor(x, y) { this.x = x; this.y = y; } toString() { return "(" + this.x + ", " + this.y + ")"; } } // 類中定義的方法就是在原型上
有兩點(diǎn)區(qū)別, class 中定義的方法是不可枚舉的,class 必須通過(guò) new 調(diào)用不能直接運(yùn)行。
class 不存在變量提升,使用要在定義之后。
class 中的方法前加 static 關(guān)鍵字定義靜態(tài)方法,只能通過(guò) class 直接調(diào)用不能被實(shí)例繼承。
如果靜態(tài)方法包含 this 關(guān)鍵字,這個(gè) this 指的是 class,而不是實(shí)例。注意下面代碼:
class Foo { static bar () { this.baz(); } static baz () { console.log("hello"); } baz () { console.log("world"); } } Foo.bar() // hello
父類的靜態(tài)方法,可以被子類繼承,目前 class 內(nèi)部無(wú)法定義靜態(tài)屬性。
設(shè)置靜態(tài)屬性與實(shí)例屬性新提案:
class 的實(shí)例屬性可以用等式,寫入類的定義之中。
靜態(tài)屬性直接前面加 static 即可。
class MyClass { myProp = 42; static myStaticProp = 42; }
class 的繼承:
通過(guò) extends 實(shí)現(xiàn)繼承,注意 super 關(guān)鍵字
class ColorPoint extends Point { constructor(x, y, color) { super(x, y); // 調(diào)用父類的constructor(x, y) this.color = color; } toString() { return this.color + " " + super.toString(); // 調(diào)用父類的toString() } }
extends 可以繼承其他類或任何有 prototype 屬性的函數(shù)。
super 會(huì)從父類獲取各路信息綁定到子類的 this。
子類自己沒有 this 對(duì)象,要先繼承父類的實(shí)例對(duì)象然后再進(jìn)行加工,所以要在 constructor 里調(diào)用 super 繼承 this 對(duì)象后才能使用 this。
ES5 的繼承,實(shí)質(zhì)是先創(chuàng)造子類的實(shí)例對(duì)象 this,然后再將父類的方法添加到 this 上面(Parent.apply(this))。ES6 的繼承機(jī)制完全不同,實(shí)質(zhì)是先創(chuàng)造父類的實(shí)例對(duì)象 this(所以必須先調(diào)用 super 方法創(chuàng)建和繼承這個(gè) this,并綁定到子類的 this),然后再用子類的構(gòu)造函數(shù)修改this。
這條理由也是造成了 ES6 之前無(wú)法繼承原生的構(gòu)造函數(shù)(Array Function Date 等)的原型對(duì)象,而使用 class 可以。因?yàn)?ES5 中的方法是先實(shí)例化子類,再把父類的屬性添加上去,但是父類有很多不能直接訪問(wèn)的屬性或方法,這就糟了,而通過(guò) class 繼承反其道而行之先實(shí)例化父類,這就自然把所有屬性和方法都繼承了。
super 作為對(duì)象時(shí),在普通方法中,指向父類的原型對(duì)象;在靜態(tài)方法中,指向父類。
通過(guò) super 調(diào)用父類的方法時(shí),super 會(huì)綁定子類的 this。
constructor 方法會(huì)被默認(rèn)添加:
class ColorPoint extends Point { } // 等同于 class ColorPoint extends Point { constructor(...args) { super(...args); } }
Object.getPrototypeOf(object),獲取某對(duì)象的原型對(duì)象,也可以獲取某類的原型類。
class 的 __proto__與prototype
子類的__proto__屬性,表示構(gòu)造函數(shù)的繼承,總是指向父類。
子類prototype屬性的__proto__屬性,表示方法的繼承,總是指向父類的 prototype 屬性。
相當(dāng)于子類本身繼承父類,子類的原型對(duì)象繼承自父類的原型對(duì)象。
new.target:
用在構(gòu)造函數(shù)或者 class 內(nèi)部,指向調(diào)用時(shí) new 的構(gòu)造函數(shù)或者 class。
文章版權(quán)歸作者所有,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請(qǐng)注明本文地址:http://systransis.cn/yun/91978.html
摘要:本文所實(shí)現(xiàn)的完整代碼存放在。這就是所謂的算法。兩個(gè)樹的完全的算法是一個(gè)時(shí)間復(fù)雜度為的問(wèn)題。如果有差異的話就記錄到一個(gè)對(duì)象里面。如和的不同,會(huì)被所替代。這牽涉到兩個(gè)列表的對(duì)比算法,需要另外起一個(gè)小節(jié)來(lái)討論。 作者:戴嘉華 轉(zhuǎn)載請(qǐng)注明出處并保留原文鏈接( https://github.com/livoras/blog/issues/13 )和作者信息。 目錄: 1 前言 2 對(duì)前端應(yīng)用狀...
摘要:忍者級(jí)別的函數(shù)操作對(duì)于什么是匿名函數(shù),這里就不做過(guò)多介紹了。我們需要知道的是,對(duì)于而言,匿名函數(shù)是一個(gè)很重要且具有邏輯性的特性。通常,匿名函數(shù)的使用情況是創(chuàng)建一個(gè)供以后使用的函數(shù)。 JS 中的遞歸 遞歸, 遞歸基礎(chǔ), 斐波那契數(shù)列, 使用遞歸方式深拷貝, 自定義事件添加 這一次,徹底弄懂 JavaScript 執(zhí)行機(jī)制 本文的目的就是要保證你徹底弄懂javascript的執(zhí)行機(jī)制,如果...
摘要:的翻譯文檔由的維護(hù)很多人說(shuō),阮老師已經(jīng)有一本關(guān)于的書了入門,覺得看看這本書就足夠了。前端的異步解決方案之和異步編程模式在前端開發(fā)過(guò)程中,顯得越來(lái)越重要。為了讓編程更美好,我們就需要引入來(lái)降低異步編程的復(fù)雜性。 JavaScript Promise 迷你書(中文版) 超詳細(xì)介紹promise的gitbook,看完再不會(huì)promise...... 本書的目的是以目前還在制定中的ECMASc...
摘要:返回一個(gè)對(duì)象先說(shuō)一個(gè)最常用的方法構(gòu)造函數(shù)獲取所有節(jié)點(diǎn)獲取節(jié)點(diǎn)的長(zhǎng)度作用于鏈?zhǔn)秸{(diào)用測(cè)試上面的代碼就可以實(shí)現(xiàn)的效果因?yàn)槲覜]讀過(guò)源碼。 es5版本:(過(guò)兩天寫es6 版本的)由淺入深地講,先說(shuō)使用, function person(age, sex) { this.age = age; this.sex = sex; this.clothes ...
閱讀 2675·2021-11-11 16:54
閱讀 3677·2021-08-16 10:46
閱讀 3454·2019-08-30 14:18
閱讀 3049·2019-08-30 14:01
閱讀 2733·2019-08-29 14:15
閱讀 2021·2019-08-29 11:31
閱讀 3096·2019-08-29 11:05
閱讀 2602·2019-08-26 11:54