[深入02] 原型鏈

導(dǎo)航

  • 2021/07/21更新


    constructor.png
  • 2021/07/22更新


    原型鏈.png

[深入01] 執(zhí)行上下文
[深入02] 原型鏈
[深入03] 繼承
[深入04] 事件循環(huán)
[深入05] 柯里化 偏函數(shù) 函數(shù)記憶
[深入06] 隱式轉(zhuǎn)換 和 運(yùn)算符
[深入07] 瀏覽器緩存機(jī)制(http緩存機(jī)制)
[深入08] 前端安全
[深入09] 深淺拷貝
[深入10] Debounce Throttle
[深入11] 前端路由
[深入12] 前端模塊化
[深入13] 觀察者模式 發(fā)布訂閱模式 雙向數(shù)據(jù)綁定
[深入14] canvas
[深入15] webSocket
[深入16] webpack
[深入17] http 和 https
[深入18] CSS-interview
[深入19] 手寫Promise
[深入20] 手寫函數(shù)

[react] Hooks

[部署01] Nginx
[部署02] Docker 部署vue項(xiàng)目
[部署03] gitlab-CI

[源碼-webpack01-前置知識(shí)] AST抽象語法樹
[源碼-webpack02-前置知識(shí)] Tapable
[源碼-webpack03] 手寫webpack - compiler簡單編譯流程
[源碼] Redux React-Redux01
[源碼] axios
[源碼] vuex
[源碼-vue01] data響應(yīng)式 和 初始化渲染

構(gòu)造函數(shù)的缺點(diǎn)

  • 通過構(gòu)造函數(shù)生成實(shí)例對象非剃,屬性和方法都生成在實(shí)例上压真,多個(gè)實(shí)例之間屬性和方法不能共享

prototype屬性

  • javaScript的繼承機(jī)制的設(shè)計(jì)思想:原型對象的所有屬性和方法都能被實(shí)例對象所共享
  • 所有的函數(shù)都有一個(gè)prototype屬性,指向一個(gè)對象
  • 對于構(gòu)造函數(shù)來說:在構(gòu)造函數(shù)生成實(shí)例的時(shí)候维雇,構(gòu)造函數(shù)的prototype屬性會(huì)成為實(shí)例對象的原型

原型對象

  • 原型對象上的屬性不是實(shí)例對象自身的屬性,只要修改原型對象,變動(dòng)就會(huì)立刻反應(yīng)到所有實(shí)例對象上
  • 如何實(shí)例對象和原型對象有同名的屬性和方法暂氯,則實(shí)例對象讀取該屬性時(shí),會(huì)讀取自身的屬性亮蛔,而不會(huì)讀取原型上的屬性
  • 原型對象的作用:定義所有實(shí)例共享的屬性和方法

原型鏈

  • js規(guī)定痴施,所有對象都有原型對象
  • 所有對象都可以成為其他對象的原型,原型對象也是對象究流,也有自己的原型辣吃,形成一個(gè)鏈條
  • 一層層上溯,最終都會(huì)上溯到Object.prototype芬探,即Object構(gòu)造函數(shù)的prototype屬性
  • 即所有對象都繼承了 Object.prototype 對象上的屬性和方法神得,這就是所有對象都具有valueOf和toString的原因
  • Object.prototype的原型是null,null沒有任何屬性和方法偷仿,也沒有自己的原型哩簿,原型鏈終止
  • null是為了防止死鏈

覆蓋 overriding

  • 讀取對象的屬性時(shí),自身和原型上有同名的屬性和方法酝静,優(yōu)先讀取自身屬性节榜,這叫做覆蓋
  • 讀取對對象的屬性時(shí),自身沒有會(huì)到原型上找别智,原型沒有會(huì)到原型的原型上找宗苍,直到Object.prototype,還是沒有返回undefined
  • 一級級向上薄榛,在整個(gè)原型鏈上尋找某個(gè)屬性讳窟,對性能是有影響的。
  • overriding:覆蓋的意思

function A(){} //---------------------- 定義構(gòu)造函數(shù)A
A.prototype = new Array() // ---------- 將A.prototype指向?qū)嵗龜?shù)組敞恋,那么A的實(shí)例就能繼承數(shù)組原型鏈上的屬性和方法
A.prototype.constructor = A // -------- 修改prototype的同時(shí)丽啡,也要修改constructor防止意外
const a = new A()
a.push(1)
a instanceof Array // true

constructor

  • prototype對象有一個(gè)constructor屬性,默認(rèn)指向prototype屬性所在的構(gòu)造函數(shù)
  • 因?yàn)閏onstructor屬性在prototype對象上耳舅,所以constructor屬性被所有實(shí)例繼承
  • 作用:
    • (1) constructor的作用是碌上,可以得知一個(gè)實(shí)例對象,到底是由哪個(gè)構(gòu)造函數(shù)產(chǎn)生的
    • (2) constructor的另一個(gè)作用:可以用一個(gè)實(shí)例新建另一個(gè)實(shí)例
  • 注意:<font color=red>constructor表示原型對象和構(gòu)造函數(shù)之間的關(guān)連關(guān)系浦徊,如果修改原型對象馏予,一般會(huì)同時(shí)修改constructor屬性,防止引用的時(shí)候報(bào)錯(cuò)盔性,=> 因?yàn)樾薷牧藀rototype之后霞丧,A.prototype.constructor指向的已經(jīng)不是A了,而是最新賦值給prototype對象的對象指向的那個(gè)constructor</font>
  • 修改原型對象要一同修改構(gòu)造函數(shù)冕香,防止引用報(bào)錯(cuò)S汲ⅰ:笤ァ!M荒恰4炷稹!愕难!
  • name屬性
    • constructor.name => 構(gòu)造函數(shù)名
<script>
  function A(){}
  const a = new A()
  console.log(A.prototype.constructor === A) // true
  console.log(a.constructor === A.prototype.constructor) // true早龟,constructor屬性在prototype對象上,所以能被實(shí)例繼承并訪問
  console.log(a.constructor === A) // true猫缭,constructor的作用是可以確定實(shí)例由哪個(gè)構(gòu)造函數(shù)產(chǎn)生
  console.log(a.constructor === RegExp) // false
  console.log(a.hasOwnProperty('constructor'), "a.hasOwnProperty('constructor')") // constructor不是實(shí)例自身屬性葱弟,繼承的
  console.log(new a.constructor(), 'constructor的另一個(gè)作用: 一個(gè)實(shí)例可以借助constructor新建另一個(gè)實(shí)例')
  const b = new a.constructor()
  console.log( b instanceof A) // true , 即b是A的其中一個(gè)實(shí)例
</script>

constructor
constructor表示原型對象和構(gòu)造函數(shù)之間的關(guān)聯(lián)關(guān)系猜丹,修改prototype芝加,需要同時(shí)修改constructor,防止引用出錯(cuò)

<script>
  function A () {
    console.log('A構(gòu)造函數(shù)')
  }
  console.log(A.prototype, '修改之前的A.prototype')
  console.log(A.prototype.constructor, '修改之前的A.prototype.constructor') // A
  console.log(A.prototype.constructor.name, '修改之前的A.prototype.constructor.name => 修改前的constrctor名稱')
  A.prototype = {name: 'woow_wu7'}
  console.log(A.prototype.constructor, '修改之后的A.prototype.constructor') // Object
  console.log(A.prototype.constructor.name, '修改之后的A.prototype.constructor.name => 修改后的constrctor名稱')
  A.prototype.constructor = A
  console.log(A.prototype.constructor, '修改prototype后射窒,要重新指定A.prototype.constructor=A,防止引用出錯(cuò)')
</script>

instanceof

  • 返回一個(gè)布爾值藏杖,表示對象是否為某個(gè)構(gòu)造函數(shù)的實(shí)例
  • instanceof左邊是實(shí)例對象,右邊是構(gòu)造函數(shù)
  • <font color=red> instanceof會(huì)檢查右邊構(gòu)造函數(shù)的原型對象prototype轮洋,是否在左邊對象的原型鏈上</font>
  • <font color=red> 由于instanceof檢查整個(gè)原型鏈制市,因此同一個(gè)實(shí)例對象,可能對多個(gè)構(gòu)造函數(shù)返回true</font>
  • 有一個(gè)特殊情況:如果左邊對象的原型鏈上只有null(即左邊對象的原型對象是null)弊予,這時(shí)instanceof判斷就會(huì)失真
  • instanceof的一個(gè)用處就是判斷值類型,(但是只能用于判斷對象开财,不能用于判斷原始類型的值)
  • 對于undefined和null汉柒,instanceOf運(yùn)算符總是返回false
  • <font color=red>利用instanceof可以巧妙的解決,調(diào)用構(gòu)造函數(shù)時(shí)忘記加new命令的問題</font>
instanceof

function A(){}
const a = new A()
console.log(a instanceof A, 'instanceof的原理是檢查右邊構(gòu)造函數(shù)的原型對象是否在左邊對象的原型鏈上')
console.log(A.prototype.isPrototypeOf(a), 'instanceof等價(jià)于這樣')


var d = new Date();
d instanceof Date
// true
// d既是Date的實(shí)例责鳍,也是Object的實(shí)例
d instanceof Object 
// true
// 因?yàn)閕nstanceof檢查的是右邊構(gòu)造函數(shù)的實(shí)例碾褂,是否在左邊實(shí)例對象的原型鏈上,檢查整個(gè)原型鏈
// 所以同一個(gè)實(shí)例历葛,可能對多個(gè)構(gòu)造函數(shù)返回ture



var obj = Object.create(null); // 以null為原型對象創(chuàng)建實(shí)例
typeof obj // "object"
Object.create(null) instanceof Object // false

installof可以解決調(diào)用構(gòu)造函數(shù)時(shí)正塌,忘記加new命令的情況

function A() {
  if(!(this instanceof A)) { // 如果this不是A的實(shí)例,說明不是new命令調(diào)用的恤溶,那么執(zhí)行new
    return new A()
  }
  else {
    this.name = 'woow_wu7'
  }
}
const a = new A()
console.log(a)

Object.getPrototypeOf

  • 返回參數(shù)對象的原型對象
  • Object.getPrototypeOf 是獲取原型對象的標(biāo)準(zhǔn)方法
  • 注意:<font color=red>參數(shù)是對象乓诽,也可以是函數(shù),因?yàn)楹瘮?shù)也是對象咒程,在es6中可以判斷類的繼承鸠天,Object.getPrototypeOf(ColorPoint) === Point
    // true,ColorPoint是一個(gè)類帐姻,即是一個(gè)函數(shù)</font>
Object.getPrototypeOf
- 獲取參數(shù)對象的原型對象稠集,是獲取原型對象的標(biāo)準(zhǔn)方法

Object.getPrototypeOf(Object.prototype) === null // true
Object.getPrototypeOf(Function.prototype) === Object.prototype // true
Object.getPrototypeOf({}) === Object.prototype // true

Object.setPrototypeOf

  • 將第一個(gè)參數(shù)對象的原型設(shè)置為第二個(gè)參數(shù)對象 Object.setPrototypeOf(現(xiàn)有對象奶段,原型對象)
  • 返回值:返回第一個(gè)參數(shù)對象
  • 參數(shù):第一個(gè)參數(shù)是現(xiàn)有對象,第二個(gè)參數(shù)是原型對象
const a = {}
const b = {name: 'woow_wu7'}
const res = Object.setPrototypeOf(a, b) // 將b設(shè)置成a的原型剥纷,注意返回值是a
Object.getPrototypeOf(a) === b // true
console.log(a.name)
console.log(res) // a, 返回值是a
  • new命令可以用Object.setPrototypeOf來模擬
var F = function () {
  this.foo = 'bar';
};

var f = new F();
// 等同于
var f = Object.setPrototypeOf({}, F.prototype);
// 返回值f是第一個(gè)參數(shù) {}
// Object.setPrototypeOf({}, F.prototype) => 相當(dāng)于 {}.__proto__ = F.prototype
F.call(f);

Object.create

  • 生成實(shí)例對象
    • 通過new命令執(zhí)行構(gòu)造函數(shù)的方式生成(構(gòu)造函數(shù)其實(shí)就是普通的函數(shù)痹籍,只是用new命令調(diào)用時(shí),this指向了實(shí)例晦鞋,并且首字母大寫來區(qū)分蹲缠,不大寫也行,但約定俗成)
    • 通過一個(gè)對象生成鳖宾,有時(shí)候只能拿到一個(gè)對象吼砂,要生成實(shí)例對象。Object.create鼎文,該實(shí)例完全繼承原型對象的屬性和方法
  • <font color=red>Object.create() 以參數(shù)對象為原型返回實(shí)例對象渔肩,該實(shí)例完全繼承原型對象的屬性和方法</font>
手動(dòng)實(shí)現(xiàn)一個(gè) Object.create

Object._create = function(obj) {
  function F(){}
  F.prototype = obj // 新建一個(gè)構(gòu)造函數(shù),將構(gòu)造函數(shù)的prototype指向傳入的對象拇惋,執(zhí)行構(gòu)造函數(shù)周偎,即實(shí)例的原型指向了傳入的對象
  return new F()
}
  • 如果想要生成一個(gè)不繼承任何屬性和方法的對象,可以使用 Object.create(null)
  • <font color=red>如果Object.create()的參數(shù)為空撑帖,或者不是對象就會(huì)報(bào)錯(cuò)</font>

Object.prototype.isPrototypeOf

  • 實(shí)例對象的 isPrototypeOf 屬性用于判斷該對象是否是參數(shù)對象的原型

Object.prototype.__proto__

  • 實(shí)例對象的原型對象
  • 根據(jù)語言標(biāo)準(zhǔn)蓉坎,只有瀏覽器需要部署__proto__,其他環(huán)境中沒有__proto__屬性
  • 不建議使用__proto__
  • 而是用標(biāo)準(zhǔn)的Object.getPrototypeOf讀取原型胡嘿,Object.setPrototypeOf(現(xiàn)有對象蛉艾,原型對象)來設(shè)置原型

獲取原型對象的方法比較

- `obj.__proto__` // 只有瀏覽器才有,不建議使用
- obj.constructor.prototype // 手動(dòng)修改原型時(shí)衷敌,可能會(huì)失真
- Object.getPrototypeOf() // 推薦的獲取方法
直接修改原型對象時(shí)勿侯,需要同時(shí)修改constructor防止失真


function A(){}
const a = new A()

function B() {}
B.prototype = a
const b = new B()

b.constructor.prototype === a // false
// 因?yàn)椋篵.constructor === a.constructor === A.prototype.constructor === A
// 所以:b.constructor.prototype === A.prototype
// 結(jié)論:當(dāng)直接修改prototype屬性時(shí),一定要修改constructor屬性 =陕蕖V觥!C婷ァ1ァ!I嘟纭>蚱!Y骱帷Fㄒ!!D鸺8纯鳌!g缘铡5抻(重要)


// 如果是下面這樣則:
function A(){}
const a = new A()
function B() {}
B.prototype = a
B.prototype.constructor = B // 修改了prototype,同時(shí)修改constructor則引用不會(huì)出錯(cuò)
const b = new B()
b.constructor.prototype === a // true

Object.prototype.hasOwnProperty

  • 返回一個(gè)布爾值妇蛀,表示是否是對象自身的屬性耕突,不包含原型鏈上的屬性
Date.hasOwnProperty('length') // true
Date.hasOwnProperty('toString') // false

in運(yùn)算符

  • in運(yùn)算符返回一個(gè)布爾值,表示屬性在對象中是否存在评架,不區(qū)分自身屬性還是繼承的屬性
  • 注意:<font color=red>in 運(yùn)算符不區(qū)分自身屬性和繼承屬性</font>
例子:
function X(){}
X.prototype.name = 'woow_wu7';
let x = new X()
'name' in X // true
// 因?yàn)椋篿n 運(yùn)算符返回一個(gè)布爾值眷茁,表示屬性是否在對象中存在,不區(qū)分自身還是繼承
// 所以:'name' in X => 返回 true

for in 和 for of

  • for in 可以遍歷對象和數(shù)組
  • for of 只能遍歷數(shù)組
for in
- 用于數(shù)組纵诞,i表示:key
- 用于對象上祈,i表示:key
- 用于對象時(shí),for...in會(huì)遍歷自身屬性和繼承的屬性浙芙,
for of
- 用于數(shù)組:i表示 value


const objP = {sex: 'man'}
const obj = {name: 'woow_wu7', age: 20, address: 'hangzhou'};
Object.setPrototypeOf(obj, objP)
for(let i in obj) {
  console.log(i, 'for in 循環(huán) => 用于對象登刺,會(huì)遍歷自身和繼承的屬性') // name,age,address,sex
  if (obj.hasOwnProperty(i)) {
    console.log(i, '如果只希望遍歷自身屬性,可以用Object.prototype.hanOwnProperty(屬性名)來過濾')// name,age,address
  }
}

易錯(cuò)點(diǎn)總結(jié)

- constructor
    - constructor表示構(gòu)造函數(shù)和原型對象之間的關(guān)聯(lián)關(guān)系嗡呼,如果修改了原型對象纸俭,需要一起修改構(gòu)造函數(shù),防止引用出錯(cuò)南窗。
    -(每一個(gè)構(gòu)造函數(shù)都有一個(gè)prototype屬性揍很,prototype的constructor指向prototype所在的構(gòu)造函數(shù))
- instanceof
    - 原理:instanceof是檢查(右邊構(gòu)造函數(shù)的prototype屬性)是否在(左邊對象)的原型鏈上
    - instanceof失效的情況:
        - 如果一個(gè)對象的__proto__屬性指向null,則instanceof就會(huì)失效
        - 因?yàn)橛疫吺菢?gòu)造函數(shù)的prototype => 終點(diǎn)是Object.prototype,是否在左邊對象的原型鏈上
        - Object.prototype.__prototo__ === null
        - Object.prototype instanceof Ojbect             // false  
        // Object.create(null) instanceof Object   // false万伤,因?yàn)閯?chuàng)建的實(shí)例沒有任何屬性和方法女轿,也沒有原型
image
image

2021/07/24更新

  • null沒有任何屬性和方法
  • 如何生成一個(gè)沒有任何屬性和方法的對象
    • Object.create(null)
  • 如何模擬一個(gè)Object.create
  • 修改prototype屬性時(shí),一定要同時(shí)修改constructor屬性壕翩,防止引用出錯(cuò),不然會(huì)指向被賦值對象的構(gòu)造函數(shù)的prototype上的constructor


    20210724165301.jpg
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末傅寡,一起剝皮案震驚了整個(gè)濱河市放妈,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌荐操,老刑警劉巖芜抒,帶你破解...
    沈念sama閱讀 206,378評論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異托启,居然都是意外死亡宅倒,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,356評論 2 382
  • 文/潘曉璐 我一進(jìn)店門屯耸,熙熙樓的掌柜王于貴愁眉苦臉地迎上來拐迁,“玉大人蹭劈,你說我怎么就攤上這事∠哒伲” “怎么了铺韧?”我有些...
    開封第一講書人閱讀 152,702評論 0 342
  • 文/不壞的土叔 我叫張陵,是天一觀的道長缓淹。 經(jīng)常有香客問我哈打,道長,這世上最難降的妖魔是什么讯壶? 我笑而不...
    開封第一講書人閱讀 55,259評論 1 279
  • 正文 為了忘掉前任料仗,我火速辦了婚禮,結(jié)果婚禮上伏蚊,老公的妹妹穿的比我還像新娘立轧。我一直安慰自己,他們只是感情好丙挽,可當(dāng)我...
    茶點(diǎn)故事閱讀 64,263評論 5 371
  • 文/花漫 我一把揭開白布肺孵。 她就那樣靜靜地躺著,像睡著了一般颜阐。 火紅的嫁衣襯著肌膚如雪平窘。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 49,036評論 1 285
  • 那天凳怨,我揣著相機(jī)與錄音瑰艘,去河邊找鬼。 笑死肤舞,一個(gè)胖子當(dāng)著我的面吹牛紫新,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播李剖,決...
    沈念sama閱讀 38,349評論 3 400
  • 文/蒼蘭香墨 我猛地睜開眼芒率,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了篙顺?” 一聲冷哼從身側(cè)響起偶芍,我...
    開封第一講書人閱讀 36,979評論 0 259
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎德玫,沒想到半個(gè)月后匪蟀,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 43,469評論 1 300
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡宰僧,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 35,938評論 2 323
  • 正文 我和宋清朗相戀三年材彪,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 38,059評論 1 333
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡段化,死狀恐怖嘁捷,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情穗泵,我是刑警寧澤普气,帶...
    沈念sama閱讀 33,703評論 4 323
  • 正文 年R本政府宣布,位于F島的核電站佃延,受9級特大地震影響现诀,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜履肃,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,257評論 3 307
  • 文/蒙蒙 一仔沿、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧尺棋,春花似錦封锉、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,262評論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至荆残,卻和暖如春奴艾,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背内斯。 一陣腳步聲響...
    開封第一講書人閱讀 31,485評論 1 262
  • 我被黑心中介騙來泰國打工蕴潦, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人俘闯。 一個(gè)月前我還...
    沈念sama閱讀 45,501評論 2 354
  • 正文 我出身青樓潭苞,卻偏偏與公主長得像,于是被迫代替她去往敵國和親真朗。 傳聞我的和親對象是個(gè)殘疾皇子此疹,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 42,792評論 2 345

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