不一樣的面向?qū)ο罄渡梗琷avaScript原型揭秘

一、導讀

本篇文章將說清楚javaScript的原型帖鸦、原型鏈機制芝薇,說的不對評論區(qū)砸板磚~
如果你看了很多篇博客仍然搞不清楚prototype 、_ _ proto _ _作儿、new洛二、constructor的關(guān)系,請往下看攻锰!
如果你剛從java換坑到j(luò)avaScript晾嘶,請往下看!
如果你還在滿口“實例”口注、“構(gòu)造”去理解/閱讀javaScript代碼的話变擒,請往下看!
總之寝志,請往下看娇斑!

二策添、[[prototype]] 屬性才真正的叫做原型

JS中所有的對象被創(chuàng)建時會被賦予一個特殊的隱式屬性——[[prototype]] ,它是另一個對象的引用毫缆,并且?guī)缀跛械膶ο蟮?[[prototype]] 屬性都是非空的值唯竹。一個對象的 [[prototype]] 屬性值就被稱為這個對象的原型。一個對象成為另一個對象的原型苦丁,就稱這兩個對象被一條原型鏈關(guān)聯(lián)起來浸颓。如果某個對象A,它的 [[prototype]] 屬性值是對象B的引用的話旺拉,就稱B成為了A的原型产上。
JS函數(shù)的prototype 屬性、_ _ proto _ _屬性等其他的和“proto”字眼沾邊兒的蛾狗,我們統(tǒng)統(tǒng)不稱為原型晋涣。所以下文任何地方出現(xiàn)的“原型”都只表示 [[prototype]] 屬性。
值得一提沉桌,上面說所有對象都會有 [[prototype]] 屬性谢鹊。嚴謹點說是除了null和undefined外所有JS對象都有,包括引用類型和數(shù)字留凭、字符串這些基本類型佃扼。聲明let a = 'hello' 那a也是有原型的,默認是String.prototype

2.1蔼夜、用JS標準api -- getPrototypeOf兼耀、setPrototypeOf訪問原型

開頭講到,如果某個對象A挎扰,它的 [[prototype]] 屬性值是對象B的引用翠订,就稱B成為了A的原型,A的這個 [[prototype]] 屬性是隱式的(JS里應(yīng)該稱為非公有屬性)遵倦,也就是說不能通過.點操作符去訪問它尽超,它是JS底層機制。為了靈活性梧躺,JS標準依然提供了兩個api去訪問它(setter和getter)-- getPrototypeOf似谁、setPrototypeOf,Object.getPrototypeOf(A) 返回對象A的原型掠哥,Object.setPrototypeOf(A,B) 把B設(shè)置成A的原型
從這兩個api可以看出巩踏,A的原型不是生來就唯一確定的,可以中途修改续搀,所以對象和它原型的關(guān)系完全不是“類和實例的關(guān)系”塞琼,如果用類和實例的思維先入為主的理解話,很多現(xiàn)象解釋不通禁舷。比如彪杉,我定義了一個Bird()函數(shù)用來創(chuàng)建一只鳥毅往,語句let myBird = new Bird()執(zhí)行后,如果認為myBird是Bird的實例好像一點都不違和派近,好像Bird就是myBird的構(gòu)造函數(shù)攀唯,原型就約等于類。但事實是myBird這只鳥的原型是可以改的渴丸,let frog = {}; Object.setPrototypeOf(myBird,frog)侯嘀,frog(青蛙)成了這只鳥的原型,這個時候你還能說原型是類嗎?顯然是解釋不通的谱轨。這種“死胡同”的現(xiàn)象有很多戒幔,造成混淆的一個因素是new操作符,它和java里面的new可不是一回事土童,后面會解釋溪食。所以理解JS原型鏈的第一步是摒棄“類”、“實例”娜扇、“構(gòu)造”這些思想。

我認為“貸款人--擔保人”是比較合適的例子去形容原型關(guān)系:A去銀行辦貸款栅组,銀行要求A填寫一個擔保人B雀瓢,如果A沒錢還款,銀行就找擔保人還款玉掸。B就可以稱為A的原型刃麸。這里A可以指定父母、朋友司浪,甚至是一家公司(和A不是一個數(shù)據(jù)類型)作為自己的擔保人泊业,A也可以隨時找銀行申請重新指定擔保人。這里“銀行找A還款無果再找B還款”的動作就類似于訪問A的屬性啊易,不存在的話則沿著原型鏈往上找吁伺,找到則返回,一直找到原型鏈盡頭(和面向?qū)ο笾性L問子類屬性找不到后訪問父類租谈、祖父類的現(xiàn)象很像)篮奄。這個動作在JS被稱為委托(本篇不準備展開講委托)

2.2、JS對象的 _ _ proto _ _屬性

_ _ proto _ _屬性想必大家非常熟悉割去,非JS標準窟却,是各大瀏覽器為了方便程序員訪問對象的原型而提供的顯式屬性,可以這樣方便的訪問對象A的原型:A. _ _ proto _ _呻逆,當然 _ _ proto _ _屬性也是可以修改的夸赫,A. _ _proto _ _ = B是有效的。這個屬性也許會成為你理解原型鏈的絆腳石咖城,到此為止你完全可以認為 _ _ proto _ _ 完全等效于 [[prototype]] 屬性茬腿,從目前來看他倆意義是一樣的呼奢,都是表示原型。但是下文將不再出現(xiàn) _ _ proto _ _ 滓彰,僅僅用[[prototype]] 表示原型控妻,一是希望大家將原型當做是JS對象間產(chǎn)生聯(lián)系的機制,而不是一個屬性揭绑,二是盡管各家瀏覽器都“不約而同”的使用了 _ _ proto _ _ 顯式的表示原型弓候,但是最終JS標準組也沒將它納入標準中,肯定有個中原因的他匪。
(不約而同實際都是瀏覽器廠商向市場的妥協(xié)菇存,帶頭大哥chrome用了 _ _ proto _ _ ,程序員寫的網(wǎng)頁在chrome上跑得風生水起邦蜜,在你xxx瀏覽器就報錯依鸥,用戶只會說xxx瀏覽器真垃圾,哪會說你chrome不按標準來呢悼沈,所以xxx瀏覽器只能跟上大哥腳步)贱迟。

2.3、JS函數(shù)的 prototype 屬性

先說結(jié)論:JS函數(shù)的prototype屬性命名是極為失敗的絮供,隨便換個名稱都能讓JS原型鏈更容易理解衣吠,它不表示函數(shù)的原型
無疑壤靶,JS函數(shù)的prototype 是原型鏈非常重要的一環(huán)缚俏。它是JS專門為函數(shù)賦予的一個屬性,并且是顯式屬性贮乳,它默認也是指向另一個對象忧换。前面講過,每個對象都有 [[prototype]] 屬性(原型)向拆,并且只有 [[prototype]] 屬性才表示原型亚茬。函數(shù)也是對象,所以它既有原型又有prototype屬性浓恳,所以千萬別被prototype這個名字給騙了才写,它不表示函數(shù)的原型,包括函數(shù)在內(nèi)的所有對象的原型有且只用[[prototype]] 屬性表示奖蔓。
許多博客解釋原型鏈喜歡用這個例子:

let Foo = function(){}
let myFoo = new Foo()

雖然赞草,new是關(guān)鍵,但我不打算管new操作吆鹤,希望大家先看看let Foo = function(){}的時候發(fā)生了什么厨疙,然后引出我的觀點。分析下面的代碼:

let Foo = function(){}
console.log(Foo.prototype) //{constructor: ?}疑务,是一個帶有constructor屬性的對象
Object.getPrototypeOf(Foo) === Foo.prototype //false
Object.getPrototypeOf(Foo) === Function.prototype //true

從上面的代碼可以看出沾凄,我聲明了一個name為Foo的函數(shù)梗醇,F(xiàn)oo便自動獲得prototype屬性,并且指向了一個包含constructor屬性的全新對象(constructor屬性后面再討論)撒蟀。再看一下叙谨,Object.getPrototypeOf(Foo) === Function.prototype 返回true說明JS系統(tǒng)指定Function.prototype這個對象成為了Foo的原型,F(xiàn)unction是JS內(nèi)置的函數(shù)保屯,常常被當做所謂的“構(gòu)造函數(shù)”使用(new Function())手负,也就是說Foo與系統(tǒng)中某些已經(jīng)存在的對象產(chǎn)生了聯(lián)系,一種稱為原型的聯(lián)系姑尺。我聲明了個函數(shù)竟终,系統(tǒng)就指派Function.prototype作為它的原型,顯然是根據(jù)數(shù)據(jù)類型去指派的切蟋,如果聲明一個字符串统捶,那么原型將被指派為String.prototype,JS數(shù)據(jù)類型就那么幾個柄粹,他們的原型也是能枚舉出來的喘鸟,我稱它為固有原型,下一章就做這個事驻右。

再回頭說說constructor屬性迷守,constructor屬性也是JS命名的一大敗筆,它妄想用“構(gòu)造函數(shù)/構(gòu)造器”的語義讓程序員以為它是構(gòu)造函數(shù)旺入,但它卻根本沒有“構(gòu)造”的含義在里面,是constructor這個名字誤導了我們凯力。前面說到茵瘾,let myBird = new Bird()讓程序員認為Bird是一個類的原因是new關(guān)鍵字,那么到目前為止我們又遇到一個因素了咐鹤,這個constructor變本加厲的誤導程序員拗秘。constructor屬性所指向的函數(shù)對象是和原型一樣,是可以更改的祈惶。
分析下面代碼:

let Bird= function(){}
let Frog= function(){}
let myBird = new Bird()
myBird.constructor === Bird //true
myBird.constructor = Frog //代碼生效雕旨,構(gòu)造函數(shù)重新指向到Frog
myBird.constructor === Bird //false

如果你把constructor 當做構(gòu)造函數(shù)去看待,那表示我很輕易的將小鳥的構(gòu)造函數(shù)改成了青蛙捧请,以后小鳥就是由青蛙構(gòu)造而來的(WTF)含義上根本說不通凡涩。另外,你會發(fā)現(xiàn)constructor 到目前為止幾乎沒有其他用武之地疹蛉,它純粹是湊數(shù)的屬性活箕,所以在你徹底理解原型鏈之前,請放下這個屬性可款,不要糾結(jié)育韩。

2.5克蚂、固有原型鏈

首先我認為:每一個對象都不是孤立的,每一個對象誕生之初就已經(jīng)身在一條原型鏈之中筋讨,只是根據(jù)自己的類型不同埃叭,身處的位置就不一樣,因此程序員每一次的聲明悉罕、賦值都意義重大赤屋,實際是解鏈再建鏈的過程。如果你按字面量給對象賦值時蛮粮,那么就表示讓系統(tǒng)給你分配原型益缎,這個系統(tǒng)分配的原型就可以稱為固有原型,如果你想自己指定原型就要用Object.create或者new操作符自行指定然想。先說一下讓系統(tǒng)分配原型的情況:

2.5.1莺奔、系統(tǒng)默認分配原型的情況

1、Number類型—— let obj0 = 1024
2变泄、String類型—— let obj1 = 'hello'
3令哟、Boolean類型—— let obj2 = true
4、Symbol類型—— let obj3 = Symbol('id')
5妨蛹、Function類型—— let obj4 = function ()
6屏富、Object類型—— let obj5 = {name:'張三'}
7、Array類型—— let obj6 = [1,2,3]
以上7種方式創(chuàng)建的對象我們在控制臺打印出來蛙卤,它們的原型是:

let obj0 = 1024
let obj1 = 'hello'
let obj2 = true
let obj3 = Symbol('id')
let obj4 = function (){}
let obj5 = {name:'張三'} 
let obj6 = [1,2,3]
Object.getPrototypeOf(obj0) === Number.prototype  //true
Object.getPrototypeOf(obj1) === String.prototype  //true
Object.getPrototypeOf(obj2) === Boolean.prototype  //true
Object.getPrototypeOf(obj3) === Symbol.prototype  //true
Object.getPrototypeOf(obj4) === Function.prototype  //true
Object.getPrototypeOf(obj5) === Object.prototype  //true
Object.getPrototypeOf(obj6) === Array.prototype  //true

拿let obj0 = 1024舉例狠半,可以看到,聲明obj0 并直接賦初值1024時颤难,JS系統(tǒng)就默認指定了obj0的原型為Number.prototype(嚴格上應(yīng)該說成obj0的原型和Number的prototype屬性指向了同一個對象神年,因為總不能拿內(nèi)存地址來解釋,只能暫時把這個對象就稱為Number.prototype)行嗤。前面說到已日,JS只有函數(shù)才有prototype屬性,所以Number是個函數(shù)栅屏,并且Number.prototype是個object

typeof Number === 'function' //true
typeof Number.prototype === 'object' //true

那么Number和Number.prototype也都有各自的原型飘千。這里直接給出結(jié)果:


let obj = 1024執(zhí)行后,系統(tǒng)默認給obj分配的原型鏈

Number.prototype的原型是Object.prototype栈雳,Number的原型是Function.prototype护奈,F(xiàn)unction.prototype的原型也是Object.prototype,再往上游Object.prototype的原型就是null了哥纫,意味著鏈到頭兒了逆济。其他類型的對象的原型和Number類型對象如出一轍,用一張總圖表示:

各種類型的對象的原型

1、 Number.prototype > Object.prototype > null奖慌,這樣的一條原型鏈便是固有原型鏈抛虫,每一個變量對象的誕生,都伴隨著一條鏈的誕生简僧。任何一個對象一定處在一條原型鏈的其中一環(huán)建椰。
2、前面講到岛马,可以用setPrototypeOf這個api隨意設(shè)置對象的原型棉姐,固有的原型鏈也可以改,但是99.99%的情況下啦逆,你不會去改它的伞矩。MDN上說改變一個對象的原型,開銷比較大夏志,更合適的做法是用Object.create創(chuàng)造一個新的原型鏈乃坤。
3、前面講到Number沟蔑、String湿诊、Boolean、Symbol瘦材、Function厅须、Object、Array這些都是函數(shù)食棕,所以它們各自的原型都是Function.prototype朗和。
4、大部分情況下簿晓,typeof為object的對象眶拉,它的原型是Object.prototype,只是數(shù)組有些區(qū)別抢蚀,JS的怪異行為(缺陷)導致typeof [1,2,3] 等于object,而數(shù)組對象的原型是Array.prototype镰禾,而不是Object.prototype皿曲。
5、null是所有原型鏈的盡頭吴侦。null和undefined本身語義就表示“空的”屋休、“沒定義”,因此沒有原型备韧。嘗試用getPrototypeOf(null)劫樟、getPrototypeOf(undefined)會報語法錯。
6、雖說setPrototypeOf可以隨意設(shè)置叠艳,但是類型必須是object類型的對象奶陈、函數(shù)或者null)。嘗試設(shè)置setPrototypeOf({}附较,1024)會報語法錯吃粒。
7、前面講到拒课,嘗試訪問對象A的屬性徐勃,不存在的話則沿著原型鏈往上找,找到則返回早像,一直找到原型鏈盡頭僻肖。所以系統(tǒng)內(nèi)置的這幾個固有原型,上面承載了很多通用api卢鹦。比如:
number類型對象的原型Number.prototype臀脏,就被JS內(nèi)置了toFixed、toPrecision等api法挨,

let obj0 = 1024
obj0.toFixed(2)  //1024.00谁榜,精確到小數(shù)點后兩位

就是obj0對象去自己原型上借來的toFixed函數(shù)使用。


Number.prototype對象上內(nèi)置的api

2.6凡纳、自行指定原型

可以通過Object.create和new操作符窃植,在賦值時自行指定原型。

1荐糜、new操作符方式

再來看看前面的示例代碼:

let Foo = function(){}
let myFoo = new Foo()

我們知道了Foo是函數(shù)類型的對象巷怜,所以它有Foo.prototype屬性,并且Foo的原型是Function.prototype暴氏。new操作符會執(zhí)行Foo函數(shù)延塑,并且創(chuàng)造一個object類型的空對象,并返回答渔,同時把這個空對象的原型指定為Foo.prototype关带。所以myFoo是一個空的對象,它的原型是Foo.prototype沼撕。
1宋雏、new操作符會自動把返回的對象的原型指定為Foo的prototype。

let Foo = function(){}
let myFoo = new Foo()
Object.getPrototypeOf(myFoo) === Foo.prototype //true

2务豺、myFoo是空對象這一點很重要磨总,常常被忽略,(除非Foo執(zhí)行完有確實返回了對象)笼沥。myFoo之所以能訪問到Foo上的屬性蚪燕,完全是通過原型機制實現(xiàn)的娶牌,這和傳統(tǒng)面向?qū)ο蟮?code>實例化概念是完全不一樣的,傳統(tǒng)面向?qū)ο箢惗x的屬性馆纳,在類實例化時诗良,實例就實實在在的擁有了這個屬性。
3厕诡、無論Foo執(zhí)行完返回的類型是什么累榜,經(jīng)過new后都會變?yōu)閛bject類型。

let Foo = function(){ 
    return 1024
 }
let myFoo = new Foo()
typeof myFoo //object

4灵嫌、既然new會把myFoo的原型指定為Foo的prototype壹罚,而函數(shù)才有prototype屬性贷盲,所以new只能作用函數(shù)對象祟偷,作用于其他類型的對象會報語法錯。

let Foo = {name:'張三'}
let myFoo = new Foo() //報Foo is not a constructor

所以到這里就能看清new的面目了盏缤,和構(gòu)造實例一點兒關(guān)系都沒有绪穆。再看看和new作用差不多的Object.create辨泳。

2、Object.create方式
let Foo = function(){}
let myFoo = Object.create(Foo)

1玖院、Object.create(Foo)執(zhí)行的結(jié)果是創(chuàng)造一個空的對象菠红,并返回,同時將返回的對象的原型指定為Foo本身

let Foo = function(){}
let myFoo = Object.create(Foo)
Object.getPrototypeOf(myFoo) === Foo //true

2难菌、Object.create(Foo)和new操作符不同试溯,不必要求Foo是函數(shù)。但是因為Foo要作為別人的原型郊酒,所以Foo就要滿足原型的類型限制(object對象遇绞、函數(shù)或者null),否則會報語法錯燎窘。
3摹闽、如果Foo是函數(shù),new操作符會執(zhí)行一次Foo褐健,而Object.create不會執(zhí)行Foo付鹿,因為它的本質(zhì)只是要取Foo本身作為它的原型而已,不需要Foo執(zhí)行蚜迅。因為Foo不會執(zhí)行舵匾,所以Foo函數(shù)返回任何都不會影響到myFoo,所以無論Foo是函數(shù)還是普通對象慢叨,Object.create(Foo)返回的永遠是空對象纽匙。

let Foo =  { age:24 }
let myFoo = Object.create(Foo)
myFoo.age //24
myFoo.hasOwnProperty('age')  //false

let Foo = function(){
  console.log('Foo執(zhí)行')
  return { age:24 }
}
let myFoo = Object.create(Foo)  //Foo沒執(zhí)行
myFoo.age //undefined
myFoo.hasOwnProperty('age')  //false

這點非常重要务蝠,myFoo能訪問Foo的屬性完全是因為Foo是myFoo的原型拍谐。一步小心就會造成下面這種情況:

let Foo =  { age:24 }
let myFoo = Object.create(Foo)
let myFoo1 = Object.create(Foo)
myFoo.age = 25
myFoo1.age //25

myFoo通過=賦值改變了原型鏈上的age屬性值,導致污染全局。

3轩拨、instanceof操作符

解釋了new操作符和構(gòu)造實例沒關(guān)系践瓷,還有一個和實例有關(guān)系的操作符——instanceof,它和實例也一點關(guān)系都沒有亡蓉。myFoo instanceof Foo 回答的不是myFoo是否是Foo的一個實例晕翠,而是回答Foo.prototype是否是myFoo原型鏈上的一環(huán),看兩個例子:

let Foo = function(){ }
let myFoo = new Foo()
Object.getPrototypeOf(myFoo) === Foo.prototype //true
myFoo instanceof Foo  //true,顯然Foo.prototype就是myFoo的原型(第一環(huán))砍濒。所以返回true淋肾。

接著這個例子,我們創(chuàng)造一個新的原型鏈:

let a0 = {age:24}
let a1 = Object.create(a0)
let a2 = Objece.create(a1)

經(jīng)過前文Object.create的作用和固有原型鏈的介紹爸邢,我們知道樊卓,JS為我們創(chuàng)建了這么一條原型鏈
a2 > a1 > a0 > Object.prototype > null
這時執(zhí)行:

Object.setPrototypeOf(myFoo,a2)  //把myFoo的原型指定為a2

那么myFoo的原型鏈就是:
myFoo > a2 > a1 > a0 > Object.prototype > null
再執(zhí)行:

Foo.prototype = a0 //Foo.prototype屬性重新賦值
myFoo instanceof Foo //true

Foo.prototype重新指向了a0,a0位于myFoo的原型鏈上杠河,所以myFoo instanceof Foo返回true碌尔。同理把Foo.prototype指向a2、a1券敌、a0甚至Object.prototype唾戚,myFoo instanceof Foo都會返回true。所以instanceof 雖然有“實例”的語義待诅,卻和實例沒有關(guān)系叹坦,本質(zhì)還是原型鏈機制。

ES6有提出一個isPrototypeOf的api咱士,它的作用和instanceof很像立由,但也有點誤導人,F(xiàn)oo.isPropertyOf(myFoo)語義上好像是回答Foo是myFoo的原型嗎序厉?但是實際是回答Foo是myFoo原型鏈上的一環(huán)嗎锐膜?所以上面a1.isPrototypeOf(myFoo)a2.isPrototypeOf(myFoo)都會返回true。

總結(jié):

1弛房、JS的原型鏈機制和類-實例機制完全不同道盏,有相似之處,但不要用傳統(tǒng)面向?qū)ο蟮乃枷肴ダ斫釰S代碼文捶,遇到10個場景可能有9個都能解釋得通荷逞,但總有那么1個你解釋不了,比如前文講到的“鳥由青蛙構(gòu)造而來”一樣粹排。

2种远、JS只有[[prototype]]這隱式屬性才叫做原型,prototype是函數(shù)的一個屬性顽耳,專屬于函數(shù)坠敷,這個屬性和原型大有關(guān)系妙同。反之,有prototype屬性的對象一定是函數(shù)膝迎。

3粥帚、JS兩個對象之間通過原型鏈產(chǎn)生聯(lián)系,原型鏈上的對象之間不會發(fā)生復制限次,盡管你是我的原型芒涡,但你還是你,我還是我卖漫,我不會復制你身上的屬性到我身上费尽,我只是想辦法引用你。

4羊始、每個對象誕生之時就已經(jīng)處于一條原型鏈中依啰,如果不指定就由JS自動分配原型,JS根據(jù)賦值時數(shù)據(jù)類型來分配到具體的鏈上店枣,稱為固有原型鏈速警。原型鏈上可以定義通用的api。一旦發(fā)生賦值操作鸯两,如果賦值前后的類型不一致闷旧,就會發(fā)生斷鏈和接新鏈的操作,這種操作是有開銷的钧唐,所以盡量保持變量類型一致忙灼。

5、拋棄了“構(gòu)造”钝侠、“實例”该园,忘掉了_ _ proto _ _、constructor才能有助于理解原型帅韧。

6里初、new操作符和構(gòu)造實例一點兒關(guān)系都沒有,它的功能是創(chuàng)造新的原型鏈忽舟,和它功能很相似的是Object.create双妨,但是兩者有很多區(qū)別。

7叮阅、instanceof 也和實例沒有關(guān)系刁品,myFoo instanceof Foo 回答的不是myFoo是否是Foo的一個實例,而是回答Foo.prototype是否是myFoo原型鏈上的一環(huán)浩姥。

8挑随、JS標準小組努力把自己往傳統(tǒng)面向?qū)ο笊峡浚‥S6出的class勒叠,但是太倉促了兜挨,有時候某些機制簡直匪夷所思竞阐。

舉個匪夷所思的例子,看下面代碼:

let obj = {age:24}
Object.defineProperty(obj,'age',{
  writable:false,
  configurable:false,
  enumerable:true,
})
obj.age = 25  //writable為false暑劝,只讀,所以賦值不生效

let newObj = Object.create(obj)
newObj.hasOwnProperty('age') //false颗搂。前文分析過担猛,obj1是空對象,沒有age屬性
newObj.age = 26
newObj.hasOwnProperty('age') //false,為什么還是false丢氢?

Object.defineProperty是用來設(shè)置對象屬性的描述符傅联,將obj的age屬性的writable設(shè)置為false(只讀),所以用=賦值25不生效疚察,這個沒問題蒸走,但是用Object.create創(chuàng)建newObj后發(fā)現(xiàn)newObj .age = 26 竟然也沒有生效!貌嫡!newObj依然沒有age這個屬性比驻。這個很奇怪,newObj竟然被原型鏈對象內(nèi)的同名屬性影響到了岛抄,貌似“繼承”了writable:false别惦,但事實卻不是繼承,只是JS在刻意的模仿類屬性的繼承夫椭,這么模仿的結(jié)果就是讓程序員匪夷所思掸掸,不知道改怎么辦!蹭秋!
更令人費解的是扰付,newObj用=賦值沒生效,但用Object.defineProperty賦值又可以仁讨。接上面的代碼:

Object.defineProperty(newObj,'age',{
  value:27,
  writable:false,
  configurable:false,
  enumerable:true,
})
newObj.hasOwnProperty('age') //true
newObj.age //27

用Object.defineProperty賦值27是生效的羽莺,并且把writable改成true也是生效的。
還有一點是newObj對象age屬性的configurable不管原型上設(shè)置的是什么洞豁,都不會產(chǎn)生影響(不繼承禽翼,符合預期),有的會有繼承的現(xiàn)象發(fā)生族跛,有的又不會闰挡,我太難了~~。
JS發(fā)展十多年來礁哄,不少設(shè)計缺陷令人抓狂长酗,要修復bug,兼容是不可能了桐绒,沒有別的招夺脾,只能新推出嚴格模式之拨,或者靠TpyeScript力挽狂瀾。

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末咧叭,一起剝皮案震驚了整個濱河市蚀乔,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌菲茬,老刑警劉巖吉挣,帶你破解...
    沈念sama閱讀 206,378評論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異婉弹,居然都是意外死亡睬魂,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,356評論 2 382
  • 文/潘曉璐 我一進店門镀赌,熙熙樓的掌柜王于貴愁眉苦臉地迎上來氯哮,“玉大人,你說我怎么就攤上這事商佛『砀郑” “怎么了?”我有些...
    開封第一講書人閱讀 152,702評論 0 342
  • 文/不壞的土叔 我叫張陵良姆,是天一觀的道長出牧。 經(jīng)常有香客問我,道長歇盼,這世上最難降的妖魔是什么舔痕? 我笑而不...
    開封第一講書人閱讀 55,259評論 1 279
  • 正文 為了忘掉前任,我火速辦了婚禮豹缀,結(jié)果婚禮上伯复,老公的妹妹穿的比我還像新娘。我一直安慰自己邢笙,他們只是感情好啸如,可當我...
    茶點故事閱讀 64,263評論 5 371
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著氮惯,像睡著了一般叮雳。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上妇汗,一...
    開封第一講書人閱讀 49,036評論 1 285
  • 那天帘不,我揣著相機與錄音,去河邊找鬼杨箭。 笑死寞焙,一個胖子當著我的面吹牛,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播捣郊,決...
    沈念sama閱讀 38,349評論 3 400
  • 文/蒼蘭香墨 我猛地睜開眼辽狈,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了呛牲?” 一聲冷哼從身側(cè)響起刮萌,我...
    開封第一講書人閱讀 36,979評論 0 259
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎娘扩,沒想到半個月后着茸,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 43,469評論 1 300
  • 正文 獨居荒郊野嶺守林人離奇死亡畜侦,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 35,938評論 2 323
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了躯保。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片旋膳。...
    茶點故事閱讀 38,059評論 1 333
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖途事,靈堂內(nèi)的尸體忽然破棺而出验懊,到底是詐尸還是另有隱情,我是刑警寧澤尸变,帶...
    沈念sama閱讀 33,703評論 4 323
  • 正文 年R本政府宣布义图,位于F島的核電站,受9級特大地震影響召烂,放射性物質(zhì)發(fā)生泄漏碱工。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 39,257評論 3 307
  • 文/蒙蒙 一奏夫、第九天 我趴在偏房一處隱蔽的房頂上張望怕篷。 院中可真熱鬧,春花似錦酗昼、人聲如沸廊谓。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,262評論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽蒸痹。三九已至,卻和暖如春呛哟,著一層夾襖步出監(jiān)牢的瞬間叠荠,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 31,485評論 1 262
  • 我被黑心中介騙來泰國打工扫责, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留蝙叛,地道東北人。 一個月前我還...
    沈念sama閱讀 45,501評論 2 354
  • 正文 我出身青樓公给,卻偏偏與公主長得像借帘,于是被迫代替她去往敵國和親蜘渣。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 42,792評論 2 345

推薦閱讀更多精彩內(nèi)容