您好,登錄后才能下訂單哦!
這篇文章主要介紹“如何理解js原生語法prototype,__proto__和constructor”,在日常操作中,相信很多人在如何理解js原生語法prototype,__proto__和constructor問題上存在疑惑,小編查閱了各式資料,整理出簡單好用的操作方法,希望對大家解答”如何理解js原生語法prototype,__proto__和constructor”的疑惑有所幫助!接下來,請跟著小編一起來學習吧!
js共有7種數據類型
從可不可以讀取屬性, 可以分為兩類
可以讀取屬性:
自身可以有屬性: object
自身不可以有屬性: string,number,boolean,symbol
不可以讀取屬性: null,undefined
null,undefined類型, 讀取和設置屬性都是非法的, 直接報錯.
只有object能有自有屬性, 可以讀取屬性和設置屬性
string,number,boolean,symbol類型可以讀取屬性, 其實是先構造成包裝對象, 再讀取屬性, 設置屬性也是一樣, 可以理解設置到了會立即銷毀的包裝對象上, 就是可以設置, 但是沒有任何實質效果.
hasOwnProperty方法是繼承來的, 用來判斷該對象自身上是否有這個屬性, 有就行, 不管是什么值
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, 不是自身屬性, 也沒有繼承到這個屬性
程序就是數據結構與算法, 好的程序最好是使用最小的內存存儲數據, 使用最快的時間完成運行得到結果.
復用數據可以達到減少內存使用的目的, 例如a和b需要完成一樣的功能, 就可以復用同一個方法(屬性).
那么就需要解決一個問題, 這個復用的方法存在哪里, a和b怎樣能找到它.
在js中的解決方案是, a和b都由函數(這里先叫Function吧)構造而來, 復用的方法存放在函數身上(prototype屬性里).
(因為只有構造函數身上需要存放復用的方法, 所以prototype只有可構造的函數上才有, 箭頭函數不是用來構造的, 它就沒有, 其它對象, 如果連函數都不是, 就更不會有這個屬性了)
那么需要給a,b 和Function建立起聯系, 因為a,b需要到Function身上找它們可以用的復用方法
在js中的實現是通過constructor屬性,即a.constructor, b.constructor可以找到Function
所以通過a.constructor.prototype可以找到它可以復用的方法的存放地址, 為了快速找到js提供了一種快捷方法a.__proto__一步到位找到, 即a.constructor.prototype和a.__proto__找到的是同一個對象, 當然它倆是全等的啦.
// 它倆都不是自有屬性, 我也不知道怎么從這倆屬性上找到原型對象的了, 肯定是魔法..... const obj = {} console.log(obj.hasOwnProperty('__proto__')) // false console.log(obj.hasOwnProperty('constructor')) // false
(所以, 如果手動修改了constructor,prototype,__proto__的指向, 那么你得清楚你在干什么)
(我不知道js的設計者是不是這樣想的, 哈哈, 我就這樣認為, 這樣好理解多了)
(這個過程稱之為繼承, 而且是一個鏈式過程, 即可以a.constructor.prototype.constructor.prototype.constructor.prototype這樣查找, 直到找到最頂端, 這個過程可以由a.__proto__.__proto__.__proto__加速, 這個就叫做原型鏈, js的繼承只有這一種實現方式.)
(上面只是引導思考過程, 其實查找原型對象并不會通過a.constructor.prototype去找, 而是直接通過__proto__查找)
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]
總結, 修改了這個屬性, 增加了找到構造它的構造函數的難度, 不能直接獲取了, 需要到原型對象上去讀取.
如果它自身的這個屬性和原型上的這個屬性都被修改了, 那么也只是找不到它的構造函數了而已, 不會有別的影響.
instanceof關心的是原型鏈, 跟constructor沒有關系
印證上面的點, 修改constructor屬性, 除了讓實例找不到構造它的構造函數, 沒有別的影響了. 如果需要通過實例找到它的構造函數, 就需要維護好它倆的關系.
// 語法是 // a instanceof b // 這個操作符是判斷 a 的原型鏈上是否有 b.prototype, 因為要判斷 b.prototype 所以 b 必需是一個 可構造的函數, 否則會報錯 const fn = function () {} const o = Object.create(fn.prototype) // 此時 o 的原型鏈上有 fn.prototype, 因為 o.__proto__ === fn.prototype console.log(o instanceof fn) // true const emptyObj = {} fn.prototype = emptyObj // 此時 o 的原型鏈上已經沒有 fn.prototype 了, 因為此時 o.__proto__ 已經不再和 fn.prototype 相等了 console.log(o instanceof fn) // false o.__proto__ = emptyObj // 修正了 o.__proto__ 就好了 console.log(o instanceof fn) // true
現在有個新的api, 實現的功能和instanceof一致, 但是更加語義化一些, 直接判斷對象是否在另一個對象的原型鏈上
const fn = function () {} const o = Object.create(fn.prototype) console.log(fn.prototype.isPrototypeOf(o)) // true
先說一個總結, 在構造實例的時候, 會將這個實例的__proto__指向此時的構造函數的prototype, 然后實例實際是繼承的是__proto__.(為什么強調此時, 因為構造函數的prototype可能會被修改指向, 修改之后只會影響修改之后構造的實例, 修改之前構造的實例還會使用修改之前的prototype)
所以, 就可以理解到修改__proto__和prototype會有哪些影響了
1.修改__proto__的指向
只會影響它自己的繼承
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__的屬性
會影響這一波段構造的實例
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__' // 可以看到只影響了當前這一段構造的實例, 之前和之后的都不會被影響到, 因為這一段內的是同一個 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的指向
會影響這一波段構造的實例
4.修改prototype的屬性
會影響這一波段構造的實例, 同修改 __proto__的屬性
上面已經講了修改prototype和__proto__
const obj = { name: 'objName', } const o = Object.create(obj) // 它相當于 o.__proto__ = obj, 但是推薦使用`Object.create` console.log(o.name) // objName console.log(o.__proto__ === obj) // true
const obj = { name: 'objName', } const o = {} Object.setPrototypeOf(o, obj) // 它相當于 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
總結, 在什么時候使用Object.create, 在什么時候使用Object.setPrototypeOf呢, 首先它倆都是標準api, 都是建議使用的, 在創建對象的時候就要指定原型時使用Object.create, 需要動態修改原型對象時, 使用Object.setPrototypeOf
之前已經講了, 通過 constructor.prototype和__proto__獲取了
const obj = { name: 'objName', } const o = {} Object.setPrototypeOf(o, obj) const proto = Object.getPrototypeOf(o) console.log(proto === obj && proto === o.__proto__) // true
這些原生的構造函數的prototype屬性是不可寫, 不可枚舉, 不可配置的
console.log(Object.getOwnPropertyDescriptor(Object, 'prototype')) // { // value: [Object: null prototype] {}, // writable: false, // enumerable: false, // configurable: false // }
null, 必須是這家伙, 不然只能無限套娃了
然后其它所有對象都是從Object構造而來, 所以所有的對象都可以繼承到Object.prototype.
const obj = {} const o = new Object()
在上面的小思考中, 說到, js對象都是函數構造而來, 所以包括Object也是由Function構造來的, 甚至它自己都是由自己構造而來
console.log(Object.constructor === Function) // true // 這就離譜了, 第一個Function是從哪里來的呢???? console.log(Function.constructor === Function) // true
我再來一點小理解, 可能是在js內部做了小處理, 第一個Function是憑空變出來的.... 然后這個Function構造出了Object, 然后這個Object構造出了第一個原型對象Object.prototype, 然后再去修改一些引用關系.
其實最復雜的是Object和Function的關系
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
const arr = [ String, Array, Boolean, Number, Date, RegExp, Error, Promise, Map, Set, Symbol, Proxy, ] // 都是由Function構造而來
這個才是重點, 根據上面的理解, 我會再開一篇文章寫一下我理解的js的繼承, 這里就先留個坑
到此,關于“如何理解js原生語法prototype,__proto__和constructor”的學習就結束了,希望能夠解決大家的疑惑。理論與實踐的搭配能更好的幫助大家學習,快去試試吧!若想繼續學習更多相關知識,請繼續關注億速云網站,小編會繼續努力為大家帶來更多實用的文章!
免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。