欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

一文徹底理解js原生語法prototype,__proto__和constructor

 更新時(shí)間:2021年10月24日 14:56:37   作者:__冬冬來了__  
作為一名前端工程師,必須搞懂JS中的prototype、__proto__與constructor屬性,相信很多初學(xué)者對(duì)這些屬性存在許多困惑,容易把它們混淆,下面這篇文章主要給大家介紹了關(guān)于js原生語法prototype,__proto__和constructor的相關(guān)資料,需要的朋友可以參考下

1 前言

寫了幾篇vue的源碼注釋(并不算解析...), 感覺到了對(duì)原型的理解是不夠的, 在js中, 原型是非常重要的, 只要你想在js這座山上往上爬, 它就會(huì)嘲笑你, 你把我搞會(huì)了么? 如果沒有, 它就給你加個(gè)十倍重力. 如果搞懂了, 那肯定是能月薪過萬, 贏取白富美, 走向人生巔峰的啦~~~


這篇文章講的都是我自己的理解, 應(yīng)該是原創(chuàng)的(我有99%把握, 除非是我之前看過文章記到腦子里了, 沒法給到引用了, 聯(lián)系我可以加上), 但是如果有人借鑒我的這篇文章, 希望給到一個(gè)這篇文章的鏈接. 其實(shí)我只是想我的文章能有更多的閱讀量, 我想月薪過萬, 贏取白富美, 走向人生巔峰~~~

2 前置知識(shí)點(diǎn)

2.1 數(shù)據(jù)類型

js共有7種數(shù)據(jù)類型

從可不可以讀取屬性, 可以分為兩類

  • 可以讀取屬性:
    • 自身可以有屬性: object
    • 自身不可以有屬性: string,number,boolean,symbol
  • 不可以讀取屬性: null,undefined

null,undefined類型, 讀取和設(shè)置屬性都是非法的, 直接報(bào)錯(cuò).

只有object能有自有屬性, 可以讀取屬性和設(shè)置屬性

string,number,boolean,symbol類型可以讀取屬性, 其實(shí)是先構(gòu)造成包裝對(duì)象, 再讀取屬性, 設(shè)置屬性也是一樣, 可以理解設(shè)置到了會(huì)立即銷毀的包裝對(duì)象上, 就是可以設(shè)置, 但是沒有任何實(shí)質(zhì)效果.

2.2 判斷是否是自身屬性(hasOwnProperty)

hasOwnProperty方法是繼承來的, 用來判斷該對(duì)象自身上是否有這個(gè)屬性, 有就行, 不管是什么值

const obj = { a: 1 }
const o = Object.create(obj)
o.b = 1
o.c = void 0
console.log('a', o.a, o.hasOwnProperty('a')) // 可以讀取到值, 繼承而來, 但不是自身屬性
console.log('b', o.b, o.hasOwnProperty('b')) // 可以讀取到值, 自身屬性
console.log('c', o.c, o.hasOwnProperty('c')) // 讀取到undefined, 自身屬性
console.log('d', o.d, o.hasOwnProperty('d')) // 讀取到undefined, 不是自身屬性, 也沒有繼承到這個(gè)屬性

3 一點(diǎn)小思考

程序就是數(shù)據(jù)結(jié)構(gòu)與算法, 好的程序最好是使用最小的內(nèi)存存儲(chǔ)數(shù)據(jù), 使用最快的時(shí)間完成運(yùn)行得到結(jié)果.

復(fù)用數(shù)據(jù)可以達(dá)到減少內(nèi)存使用的目的, 例如a和b需要完成一樣的功能, 就可以復(fù)用同一個(gè)方法(屬性).

那么就需要解決一個(gè)問題, 這個(gè)復(fù)用的方法存在哪里, a和b怎樣能找到它.

在js中的解決方案是, a和b都由函數(shù)(這里先叫Function吧)構(gòu)造而來, 復(fù)用的方法存放在函數(shù)身上(prototype屬性里).

(因?yàn)橹挥袠?gòu)造函數(shù)身上需要存放復(fù)用的方法, 所以prototype只有可構(gòu)造的函數(shù)上才有, 箭頭函數(shù)不是用來構(gòu)造的, 它就沒有, 其它對(duì)象, 如果連函數(shù)都不是, 就更不會(huì)有這個(gè)屬性了)

那么需要給a,b 和Function建立起聯(lián)系, 因?yàn)閍,b需要到Function身上找它們可以用的復(fù)用方法

在js中的實(shí)現(xiàn)是通過constructor屬性,即a.constructor, b.constructor可以找到Function

所以通過a.constructor.prototype可以找到它可以復(fù)用的方法的存放地址, 為了快速找到j(luò)s提供了一種快捷方法a.__proto__一步到位找到, 即a.constructor.prototype和a.__proto__找到的是同一個(gè)對(duì)象, 當(dāng)然它倆是全等的啦.

// 它倆都不是自有屬性, 我也不知道怎么從這倆屬性上找到原型對(duì)象的了, 肯定是魔法.....
const obj = {}
console.log(obj.hasOwnProperty('__proto__')) // false
console.log(obj.hasOwnProperty('constructor')) // false

(所以, 如果手動(dòng)修改了constructor,prototype,__proto__的指向, 那么你得清楚你在干什么)

(我不知道js的設(shè)計(jì)者是不是這樣想的, 哈哈, 我就這樣認(rèn)為, 這樣好理解多了)

(這個(gè)過程稱之為繼承, 而且是一個(gè)鏈?zhǔn)竭^程, 即可以a.constructor.prototype.constructor.prototype.constructor.prototype這樣查找, 直到找到最頂端, 這個(gè)過程可以由a.__proto__.__proto__.__proto__加速, 這個(gè)就叫做原型鏈, js的繼承只有這一種實(shí)現(xiàn)方式.)

(上面只是引導(dǎo)思考過程, 其實(shí)查找原型對(duì)象并不會(huì)通過a.constructor.prototype去找, 而是直接通過__proto__查找)

3.1 修改 constructor

const Dog = function () {}
const dog = new Dog()

dog.constructor = 0

console.log(dog.hasOwnProperty('constructor')) // true
console.log(dog.constructor) // 0

console.log(dog.__proto__.constructor) // [Function: Dog]

總結(jié), 修改了這個(gè)屬性, 增加了找到構(gòu)造它的構(gòu)造函數(shù)的難度, 不能直接獲取了, 需要到原型對(duì)象上去讀取.

如果它自身的這個(gè)屬性和原型上的這個(gè)屬性都被修改了, 那么也只是找不到它的構(gòu)造函數(shù)了而已, 不會(huì)有別的影響.

3.1.1 instanceof

instanceof關(guān)心的是原型鏈, 跟constructor沒有關(guān)系

印證上面的點(diǎn), 修改constructor屬性, 除了讓實(shí)例找不到構(gòu)造它的構(gòu)造函數(shù), 沒有別的影響了. 如果需要通過實(shí)例找到它的構(gòu)造函數(shù), 就需要維護(hù)好它倆的關(guān)系.

// 語法是
// a instanceof b
// 這個(gè)操作符是判斷 a 的原型鏈上是否有  b.prototype, 因?yàn)橐袛?b.prototype 所以 b 必需是一個(gè) 可構(gòu)造的函數(shù), 否則會(huì)報(bào)錯(cuò)
const fn = function () {}
const o = Object.create(fn.prototype)
// 此時(shí) o 的原型鏈上有 fn.prototype, 因?yàn)?o.__proto__ === fn.prototype
console.log(o instanceof fn) // true
const emptyObj = {}
fn.prototype = emptyObj
// 此時(shí) o 的原型鏈上已經(jīng)沒有 fn.prototype 了, 因?yàn)榇藭r(shí) o.__proto__ 已經(jīng)不再和 fn.prototype 相等了
console.log(o instanceof fn) // false
o.__proto__ = emptyObj
// 修正了 o.__proto__ 就好了
console.log(o instanceof fn) // true

3.1.2 isPrototypeOf

現(xiàn)在有個(gè)新的api, 實(shí)現(xiàn)的功能和instanceof一致, 但是更加語義化一些, 直接判斷對(duì)象是否在另一個(gè)對(duì)象的原型鏈上

const fn = function () {}
const o = Object.create(fn.prototype)
console.log(fn.prototype.isPrototypeOf(o)) // true

3.2 修改__proto__|prototype

先說一個(gè)總結(jié), 在構(gòu)造實(shí)例的時(shí)候, 會(huì)將這個(gè)實(shí)例的__proto__指向此時(shí)的構(gòu)造函數(shù)的prototype, 然后實(shí)例實(shí)際是繼承的是__proto__.(為什么強(qiáng)調(diào)此時(shí), 因?yàn)闃?gòu)造函數(shù)的prototype可能會(huì)被修改指向, 修改之后只會(huì)影響修改之后構(gòu)造的實(shí)例, 修改之前構(gòu)造的實(shí)例還會(huì)使用修改之前的prototype)

所以, 就可以理解到修改__proto__和prototype會(huì)有哪些影響了

1.修改__proto__的指向

只會(huì)影響它自己的繼承

const Dog = function () {}
const dog = new Dog()
const d = new Dog()
Dog.prototype.name = 'Dog'
dog.__proto__ = {
  name: '__proto__',
}
console.log(d.name) // Dog
console.log(dog.name) // __proto__

2.修改__proto__的屬性

會(huì)影響這一波段構(gòu)造的實(shí)例

const Dog = function () {}
const dog = new Dog()
const d = new Dog()
Dog.prototype.name = 'Dog'
console.log(d.name) // Dog
console.log(dog.name) // Dog
Dog.prototype = {
  name: 'after',
}
const dog1 = new Dog()
const d1 = new Dog()
console.log(d1.name) // after
console.log(dog1.name) // after
dog1.__proto__.name = '__proto__'
// 可以看到只影響了當(dāng)前這一段構(gòu)造的實(shí)例, 之前和之后的都不會(huì)被影響到, 因?yàn)檫@一段內(nèi)的是同一個(gè) Dog.prototype , 它們的 __proto__ 都是指向它的
console.log(d1.name) // __proto__
console.log(dog1.name) // __proto__
Dog.prototype = {
  name: 'new',
}
const dog2 = new Dog()
const d2 = new Dog()
console.log(d2.name) // new
console.log(dog2.name) // new

3.修改prototype的指向

會(huì)影響這一波段構(gòu)造的實(shí)例

4.修改prototype的屬性

會(huì)影響這一波段構(gòu)造的實(shí)例, 同修改 __proto__的屬性

4 修改和獲取原型對(duì)象的方式

4.1 修改

上面已經(jīng)講了修改prototype和__proto__

4.1.1 Object.create

const obj = {
  name: 'objName',
}

const o = Object.create(obj)
// 它相當(dāng)于 o.__proto__ = obj, 但是推薦使用`Object.create`

console.log(o.name) // objName
console.log(o.__proto__ === obj) // true

4.1.2 Object.setPrototypeOf

const obj = {
  name: 'objName',
}

const o = {}

Object.setPrototypeOf(o, obj)
// 它相當(dāng)于 o.__proto__ = obj, 但是推薦使用`Object.setPrototypeOf`
const proto = Object.getPrototypeOf(o)
console.log(proto === obj && proto === o.__proto__) // true
const obj1 = {}
o.__proto__ = obj1
const proto1 = Object.getPrototypeOf(o)
console.log(proto1 === obj1 && proto1 === o.__proto__) // true

總結(jié), 在什么時(shí)候使用Object.create, 在什么時(shí)候使用Object.setPrototypeOf呢, 首先它倆都是標(biāo)準(zhǔn)api, 都是建議使用的, 在創(chuàng)建對(duì)象的時(shí)候就要指定原型時(shí)使用Object.create, 需要?jiǎng)討B(tài)修改原型對(duì)象時(shí), 使用Object.setPrototypeOf

4.2 獲取

之前已經(jīng)講了, 通過 constructor.prototype和__proto__獲取了

4.2.1 Object.getPrototypeOf

const obj = {
  name: 'objName',
}

const o = {}

Object.setPrototypeOf(o, obj)

const proto = Object.getPrototypeOf(o)
console.log(proto === obj && proto === o.__proto__) // true

5 js 內(nèi)置原生構(gòu)造函數(shù)

這些原生的構(gòu)造函數(shù)的prototype屬性是不可寫, 不可枚舉, 不可配置的

console.log(Object.getOwnPropertyDescriptor(Object, 'prototype'))
// {
//   value: [Object: null prototype] {},
//   writable: false,
//   enumerable: false,
//   configurable: false
// }

5.1 js 繼承的最頂端是什么

null, 必須是這家伙, 不然只能無限套娃了

然后其它所有對(duì)象都是從Object構(gòu)造而來, 所以所有的對(duì)象都可以繼承到Object.prototype.

const obj = {}
const o = new Object()

5.2 js 繼承的二等公民(Function)

在上面的小思考中, 說到, js對(duì)象都是函數(shù)構(gòu)造而來, 所以包括Object也是由Function構(gòu)造來的, 甚至它自己都是由自己構(gòu)造而來

console.log(Object.constructor === Function) // true
// 這就離譜了, 第一個(gè)Function是從哪里來的呢????
console.log(Function.constructor === Function) // true

我再來一點(diǎn)小理解, 可能是在js內(nèi)部做了小處理, 第一個(gè)Function是憑空變出來的.... 然后這個(gè)Function構(gòu)造出了Object, 然后這個(gè)Object構(gòu)造出了第一個(gè)原型對(duì)象Object.prototype, 然后再去修改一些引用關(guān)系.

其實(shí)最復(fù)雜的是Object和Function的關(guān)系

console.log(Object.__proto__ === Function.prototype) // true
console.log(Function.constructor === Function) // true
console.log(Function.__proto__ === Function.prototype) // true
console.log(Function.prototype.__proto__ === Object.prototype) // true

5.3 js 繼承的三等公民(內(nèi)置的其他構(gòu)造函數(shù))

const arr = [
  String,
  Array,
  Boolean,
  Number,
  Date,
  RegExp,
  Error,
  Promise,
  Map,
  Set,
  Symbol,
  Proxy,
]
// 都是由Function構(gòu)造而來

6 用戶定義的特定公民構(gòu)造函數(shù)

這個(gè)才是重點(diǎn), 根據(jù)上面的理解, 我會(huì)再開一篇文章寫一下我理解的js的繼承, 這里就先留個(gè)坑

7. 總結(jié)

這篇文章跟網(wǎng)上大多講constructor,prototype,__proto__的文章都有所不同, 我的立足點(diǎn)是從給定的一個(gè)可以讀取屬性的值開始, 在js中, 除了null和undefined, 其它所有的值都可以成為立足點(diǎn). 從這個(gè)立足點(diǎn)開始, 它的__proto__屬性記錄了它的原型對(duì)象, 這個(gè)原型對(duì)象是構(gòu)造它時(shí), 它的構(gòu)造函數(shù)的prototype屬性的值.

const a = 1
console.log(a.__proto__.constructor) // [Function: Number]

讀取一個(gè)值的屬性的值時(shí), 如果它自身有這個(gè)屬性, 那么直接返回這個(gè)屬性的值, 否則就會(huì)到它的__proto__對(duì)象上去找, 一直遞歸下去, 直到找到頂部null, 找到就返回它的值, 沒找到就返回undefined

這篇文章有三個(gè)理解點(diǎn),讓我茅塞頓開, 都是在我試驗(yàn)了好久突然得到的結(jié)論

  1. 以一個(gè)值為立足點(diǎn)開始分析
  2. 在構(gòu)造實(shí)例的時(shí)候, 會(huì)將這個(gè)實(shí)例__proto__指向此時(shí)的構(gòu)造函數(shù)的prototype
  3. 查找原型對(duì)象時(shí), 以__proto__為準(zhǔn)

8 最后

到此這篇關(guān)于js原生語法prototype,__proto__和constructor的文章就介紹到這了,更多相關(guān)js原生語法prototype,__proto__和constructor內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

  • 基于Html+CSS+JS實(shí)現(xiàn)手動(dòng)放煙花效果

    基于Html+CSS+JS實(shí)現(xiàn)手動(dòng)放煙花效果

    這篇文章主要介紹了利用Html+CSS+JavaScript實(shí)現(xiàn)的放煙花效果,文中一共實(shí)現(xiàn)了兩種方式:手動(dòng)和自動(dòng),文中的示例代碼講解詳細(xì),感興趣的可以試一試
    2022-01-01
  • 深入了解JavaScript 防抖和節(jié)流

    深入了解JavaScript 防抖和節(jié)流

    這篇文章主要介紹了JavaScript 防抖和節(jié)流,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下
    2019-09-09
  • mock.js模擬數(shù)據(jù)實(shí)現(xiàn)前后端分離

    mock.js模擬數(shù)據(jù)實(shí)現(xiàn)前后端分離

    這篇文章主要為大家詳細(xì)介紹了mock.js模擬數(shù)據(jù)實(shí)現(xiàn)前后端分離,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下
    2019-07-07
  • Rxjs?中處理錯(cuò)誤和抓取錯(cuò)誤的代碼案例

    Rxjs?中處理錯(cuò)誤和抓取錯(cuò)誤的代碼案例

    這篇文章主要介紹了Rxjs?中怎么處理和抓取錯(cuò)誤,本文,我們學(xué)習(xí)了如何使用?catchError?在數(shù)據(jù)流中抓取錯(cuò)誤,怎么去修改和返回?observable,或者使用?EMPTY?不去觸發(fā)組件中的錯(cuò)誤,需要的朋友可以參考下
    2022-08-08
  • 利用JS將圖標(biāo)字體渲染為圖片的方法詳解

    利用JS將圖標(biāo)字體渲染為圖片的方法詳解

    在軟件開發(fā)中肯定要用到圖標(biāo),比如下圖的?Groove?音樂中就用到了許多圖標(biāo)。一種獲取這些圖標(biāo)的方法是把?Groove?音樂截個(gè)圖,然后熟練地開啟?Photoshop,開始摳圖。這種方式很遜,效率也很低。本文將利用JS將圖標(biāo)字體渲染為圖片,需要的可以參考一下
    2022-05-05
  • Uniapp?實(shí)現(xiàn)全民分銷功能原理解析

    Uniapp?實(shí)現(xiàn)全民分銷功能原理解析

    這篇文章主要介紹了Uniapp?實(shí)現(xiàn)全民分銷功能,本篇文章主要介紹全民分銷功能實(shí)現(xiàn)原理,本文給大家介紹的非常詳細(xì),需要的朋友可以參考下
    2022-06-06
  • JavaScript函數(shù)的4種調(diào)用方法詳解

    JavaScript函數(shù)的4種調(diào)用方法詳解

    了解函數(shù)的調(diào)用過程有助于深入學(xué)習(xí)與分析JavaScript代碼。本文是JavaScript高級(jí)這個(gè)系列中的第三篇文章,主要介紹JavaScript中函數(shù)的四種使用形式
    2014-04-04
  • 使用apply方法處理數(shù)組的三個(gè)技巧[譯]

    使用apply方法處理數(shù)組的三個(gè)技巧[譯]

    本文要講的是使用apply方法處理數(shù)組的三個(gè)技巧,需要的朋友可以參考下
    2012-09-09
  • JavaScript中變量提升與函數(shù)提升經(jīng)典實(shí)例分析

    JavaScript中變量提升與函數(shù)提升經(jīng)典實(shí)例分析

    這篇文章主要介紹了JavaScript中變量提升與函數(shù)提升,結(jié)合實(shí)例形式分析了JavaScript中的變量提升與函數(shù)提升相關(guān)原理、使用方法及操作注意事項(xiàng),需要的朋友可以參考下
    2018-07-07
  • 微信小程序使用wx.request請(qǐng)求服務(wù)器json數(shù)據(jù)并渲染到頁面操作示例

    微信小程序使用wx.request請(qǐng)求服務(wù)器json數(shù)據(jù)并渲染到頁面操作示例

    這篇文章主要介紹了微信小程序使用wx.request請(qǐng)求服務(wù)器json數(shù)據(jù)并渲染到頁面操作,結(jié)合實(shí)例形式分析了微信小程序使用wx.request發(fā)送網(wǎng)絡(luò)請(qǐng)求及返回結(jié)果渲染到wxml界面相關(guān)操作技巧,需要的朋友可以參考下
    2019-03-03

最新評(píng)論