詳解JavaScript的原型與原型鏈
詳解原型與原型鏈
其實,剛開始學JavaScript時,就有學過原型與原型鏈的相關知識了,只是當時還沒有養(yǎng)成寫筆記的習慣,導致現(xiàn)在已經(jīng)忘的七七八八了。
這邊文章真是花了很多心思,寫了兩天,看了很多篇篇博文,其中有小參考的,有解決一點疑惑的,但是最后只標注了一篇幫助最大的。
構造函數(shù)
實例的構造函數(shù)屬性(constructor
)指向其構造函數(shù)
function Person(name) { this.name = name } const person = new Person('clz') console.log(person.constructor === Person) // true
實例的構造函數(shù)并不是自身屬性,而是從原型對象上繼承的屬性
function Person(name) { this.name = name } const person = new Person('clz') console.log(person.constructor === Person) // true console.log(Person.prototype.constructor === Person) // true console.log(person.hasOwnProperty('constructor')) // false:constructor屬性并不是實例自身的屬性,而是繼承來的
原型對象
__proto__(隱式原型)
:每個對象(除了null
)都具有的屬性,該屬性指向該對象的原型prototype(顯式原型)
:只有函數(shù)對象才有的屬性,該屬性指向函數(shù)的原型對象
來看來看
const arr = [1, 2, 3] const obj = { name: 'clz' } function add(a, b) { return a + b } console.log(arr) console.log(obj) console.log(add)
紅框框中的[[prototype]]
和__proto__
意義相同,都是指對象的內(nèi)部屬性
而所有函數(shù)都擁有prototype
屬性,所以可以通過f.prototype
得到,那么自然也不需要通過[[prototype]]
顯示出來(畢竟prototype
是顯式原型,而__proto__
是隱式原型,好吧,這是我猜的)
箭頭函數(shù)沒有prototype
屬性
訪問原型
通過實例對象訪問原型對象有 3 種方法
obj.__proto__
obj.constructor.prototype
Object.getPrototypeOf(obj)
function Person(name) { this.name = name } const person = new Person('clz') const proto1 = person.__proto__ const proto2 = person.constructor.prototype const proto3 = Object.getPrototypeOf(person) const proto = Person.prototype // 原型 console.log(proto1 === proto) // true: 第一種方法 console.log(proto2 === proto) // true: 第二種方法 console.log(proto3 === proto) // true: 第三種方法
比較安全的做法是Object.getPrototypeOf(obj)
以下部分會涉及一丟丟原型鏈的知識(如果沒看懂,可以看下原型鏈再來看)
__proto__
屬性是私有屬性,存在瀏覽器兼容性問題,缺乏非瀏覽器環(huán)境的支持- 如果obj的
constructor
屬性被覆蓋,那么obj.constructor.prototype
將會失效。(因為obj自身是沒有constructor
屬性的,是通過原型鏈去它的原型上獲取constructor
屬性,所以覆蓋該屬性時,將不會再去原型鏈上查找)
function Person(name) { this.name = name } function Temp(name) { this.name = name } const person = new Person('clz') person.constructor = Temp const proto = Person.prototype // 原型 console.log(person.__proto__ === proto) // true: 第一種方法 console.log(person.constructor.prototype === proto) // false: 第二種方法 console.log(Object.getPrototypeOf(person) === proto) // true: 第三種方法
設置原型
設置原型對象有 3 種方法
obj.__proto__=prototypeObj
Object.setPrototypeOf(obj, prototypeObj)
Object.create(prototypeObj)
const proto = { // 原型對象 name: 'prototype' } // 第一種方法 const obj1 = {} obj1.__proto__ = proto // 設置原型 console.log(obj1.name) // prototype console.log(obj1.__proto__ === proto) // true // 第二種方法 const obj2 = {} Object.setPrototypeOf(obj2, proto) // 設置原型 console.log(obj2.name) // prototype console.log(obj2.__proto__ === proto) // true // 第三種方法 const obj3 = Object.create(proto) // 創(chuàng)建對象并設置原型 console.log(obj3.name) // prototype console.log(obj3.__proto__ === proto) // true
檢測原型
使用obj1.isPrototypeOf(obj2)
方法判斷obj1
是否為·obj2
的原型
const proto = { // 原型對象 name: 'prototype' } const proto1 = { name: 'prototype' } const obj = {} obj.__proto__ = proto // 設置原型 console.log(proto.isPrototypeOf(obj)) // true console.log(Object.prototype.isPrototypeOf(obj)) // true console.log(proto1.isPrototypeOf(obj)) // false
prototype、__proto__、constructor之間的關系
function Person(name) { this.name = name } const person = new Person('clz') console.log(person.__proto__ === Person.prototype) // true:因為創(chuàng)建person對象的構造函數(shù)是Person,所以person對象的隱式原型(__proto__)指向Person函數(shù)的原型(prototype) console.log(Person.prototype.constructor === Person) // true
同一個構造函數(shù)創(chuàng)建的多個實例的原型是同一個
function Person(name) { this.name = name } const person1 = new Person('clz') const person2 = new Person('clz') console.log(person1 === person2) // false: 不是同一個對象 console.log(person1.__proto__ === person2.__proto__) // true:同一個構造函數(shù)創(chuàng)建的實例對象的原型是同一個
原型鏈
由上面的知識可以知道,實例對象具有屬性__proto__
,會指向原型對象。而原型對象也是對象,所以也會有屬性__proto__
,也會繼續(xù)指向它的原型對象。
實例對象在查找屬性時,如果查找不到,就會沿著__proto__
去它的原型上查找,還找不到,則繼續(xù)去原型的原型上查找,直到找到或到最頂層為止。這就是原型鏈的概念。
對象本身的方法(第一層:把方法當成屬性)
function Person(name) { this.name = name this.listenMusic = function () { console.log('聽音樂') } } const person = new Person('clz') console.log(person) console.log('實例對象本身是否有l(wèi)istenMusic方法', person.hasOwnProperty('listenMusic')) person.listenMusic()
對象的原型上添加方法(第二層)
function Person(name) { this.name = name } Person.prototype.listenMusic = function () { console.log('聽音樂') } const person = new Person('clz') console.log(person) console.log('實例對象本身是否有l(wèi)istenMusic方法', person.hasOwnProperty('listenMusic')) person.listenMusic()
原型的原型上的方法(第三層)
function Person(name) { this.name = name } Person.prototype.__proto__.listenMusic = function () { console.log('聽音樂') } const person = new Person('clz') console.log(person) person.listenMusic()
但是呢,沒法玩第四層,因為已經(jīng)到頂了(Object.prototype
沒有原型(原型為null))
function Person(name) { this.name = name } Person.prototype.__proto__.__proto__.listenMusic = function () { console.log('聽音樂') } const person = new Person('clz') console.log(person) person.listenMusic()
person
-> Person.prototype
-> Object.prototype
-> null
那么,這里就來看看第三層是不是真的是Object.prototype
function Person(name) { this.name = name } Person.prototype.__proto__.listenMusic = function () { console.log('聽音樂') } const person = new Person('clz') console.log(person) console.log(Person.prototype.__proto__ === person.__proto__.__proto__) console.log(person.__proto__.__proto__ === Object.prototype) // 這里就是判斷處 person.listenMusic()
發(fā)現(xiàn),確實如此。
下面這張圖就是原型鏈的簡單圖(找不到是在哪里截的圖了,侵刪)
原型鏈的作用
為對象設置默認值
利用原型為對象設置默認值。當原型屬性與私有屬性同名時,刪除私有屬性之后,可以訪問原型屬性,即可以把原型屬性值作為初始化默認值。
function Person(name) { this.name = name } Person.prototype.name = '赤藍紫' const person = new Person('clz') console.log(person.name) // clz delete person.name console.log(person.name) // 赤藍紫
繼承
繼承內(nèi)容部分之后單獨寫。
擴展原型方法
Array.prototype.test = function () { console.log('擴展原型方法: 有風險') } const arr = [1, 2, 3] arr.test() // 擴展原型方法: 有風險
typeof
typeof是判斷類型時大多數(shù)人的選擇(當然也包括我啦),但是,判斷非基本數(shù)據(jù)類型(function
除外)時,只能得到Object
。(null也是,但是null這個屬于是歷史遺留bug了)。
js 在底層存儲變量的時候,會在變量的機器碼的低位1-3位存儲其類型信息
- 000:對象
- 010:浮點數(shù)
- 100:字符串
- 110:布爾
- 1:整數(shù)
null
:所有機器碼均為0
undefined
:用 −2^30 整數(shù)來表示
symbol
和bigint
是后來新增的數(shù)據(jù)類型
const num = 123 const str = 'hello' const bool = true const n = null const u = undefined const sym = Symbol(1) const big = BigInt(123) const fun = () => console.log(1) console.log(typeof num) // number console.log(typeof str) // string console.log(typeof bool) // boolean console.log(typeof n) // object console.log(typeof u) // undefined console.log(typeof sym) // symbol console.log(typeof big) //bigint console.log(typeof fun) //function
function除外的非基本數(shù)據(jù)類型
let arr = [] let obj = { name: 'clz' } let set = new Set([1, 2, 4]) console.log(typeof arr) console.log(typeof obj) console.log(typeof set)
清一色object
通過Object.prototype.toString.call(obj)
來識別對象類型。會返回"[object Type]"
來告訴你所指對象的類型
let arr = [] let obj = { name: 'clz' } let set = new Set([1, 2, 4]) console.log(Object.prototype.toString.call(arr)) // [object Array] console.log(Object.prototype.toString.call(obj)) // [object Object] console.log(Object.prototype.toString.call(set)) // [object Set]
instanceof
instanceof只要右邊變量的 prototype 在左邊變量的原型鏈上,就會返回true
function Person(name) { this.name = name } function Test(name) { this.name = name } const person = new Person('clz') console.log(person instanceof Person) // true console.log(person instanceof Object) // true console.log(person instanceof Test) // false
普通對象與函數(shù)對象
- 所有的函數(shù)都是通過new Function()來創(chuàng)建的,即是函數(shù)對象
- 其他的都是普通對象
function fn1() { } const fn2 = function () { } const fn3 = () => { } const fn4 = new Function() console.log(typeof fn1) //function console.log(typeof fn2) //function console.log(typeof fn3) //function console.log(typeof fn4) //function const obj1 = {} const obj2 = new Object() const obj3 = new fn1() console.log(typeof obj1); //object console.log(typeof obj2); //object console.log(typeof obj3); //object
上面的例子中,fn1
、fn2
、fn3
、fn4
是函數(shù)對象,obj1
、obj2
、obj3
是普通對象
- Object是構造函數(shù),即也是函數(shù),所以
Object
也是函數(shù)對象,相當于Function
的實例,即Object.__proto__ === Function.prototype
Object.prototype
是Object
構造函數(shù)的原型,處于原型鏈的頂端,Object.prototype.__proto__
已經(jīng)沒有可以指向的上層原型,因此其值為null
console.log(typeof Object) // function console.log(Object.__proto__ === Function.prototype) // true console.log(Object.prototype.__proto__) // null
Function.prototype
是Function
的原型,是所有函數(shù)對象的原型Function.prototype
是一個普通對象,所以Function.prototype.__proto__ === Object.prototype
Function
函數(shù)不通過任何東西創(chuàng)建,JS
引擎啟動時,添加到內(nèi)存中,所以**Function.__proto__ === Function.prototype
**
console.log(typeof Function) // function console.log(Function.prototype.__proto__ === Object.prototype) // true console.log(Function.__proto__ === Function.prototype) // true
經(jīng)典原型鏈圖
總結
本篇文章就到這里了,希望能夠給你帶來幫助,也希望您能夠多多關注腳本之家的更多內(nèi)容!
相關文章
JavaScript實現(xiàn)經(jīng)緯度轉換成地址功能
這篇文章主要介紹了JavaScript實現(xiàn)經(jīng)緯度轉換成地址,非常不錯,具有參考借鑒價值,需要的朋友可以參考下2017-03-03如何在JavaScript中等分數(shù)組的實現(xiàn)
這篇文章主要介紹了如何在JavaScript中等分數(shù)組的實現(xiàn),文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友們下面隨著小編來一起學習學習吧2020-12-12