javascript專欄簡單明了介紹原型與原型鏈。
在 JavaScript 中,原型與原型鍊是不可避免的重要概念,那麼怎麼去理解原型和原型鏈呢?以下是我對原型和原型鏈的理解和總結。也許有些理解還比較淺薄,隨著時間的推移和理解的深入,以後還會補充。如果大家發現我理解的有問題,歡迎大家在留言中指正。
在過去的學習過程中,我們曾透過學習物件導向語言java 了解到其有三個要素:封裝、繼承、多態。關於繼承,java 與 javascript 其實兩者並不完全一樣。
那麼 javascript 到底是如何設計出來的呢?早期,瀏覽器只能瀏覽網頁內容,而不能進行用戶交互,也就說當我們輸入帳號密碼進行登錄時,瀏覽器不能對其輸入內容進行判斷,需要通過服務器進行判斷,而網景公司為了解決這個問題,發明一種與java 搭配使用的輔助腳本語言,並在語法上有些類似。由此可以看出,javascript 受到 java 的影響,其都是物件類型,有物件就會牽涉到繼承機制,那麼JS的繼承機制是怎麼樣呢?
JS參考java的設計,使用new操作符產生對象,但與java不同的是new後面接的是 Construtor 而不是 Class 。
// java 中生成一个对象 Person p = new Person() // Person 指的是类名 // js 生成一个对象 function Person (age) { this.age = age this.nation = 'China' } var father = new Person(42) // Person 指的是构造函数 var mingming = new Person(11)复制代码
建構子也是普通函數,其也有 prototype 屬性,與一般函數的差異在於其要求首字母大寫。若建構子使用new運算元呼叫時,其需要執行四個步驟:
1. 建立新的物件
2. 將 this 指向這個新的物件
3. 執行建構函數,為新物件新增屬性與方法
4. 回傳這個新物件
function Food (name) { this.name = name this.eat = function () { console.log('eat') } } var food = new Food('banana')复制代码
任何函數都有一個 prototype 屬性,它指向 prototype 物件。那麼原型其實就是一個對象,在原型上定義的屬性,透過繼承(new 操作符實現),實例化的對像也擁有了該屬性。
原型與建構函數的關係:建構函式內有一個 prototype 屬性,透過此屬性可以存取原型。
以建構函數中的程式碼為例,Food 是建構函數,Food.prototype 是原型,food 就是參考 Food.prototype 產生的物件。
實例是指建構子在原型上建立可以"繼承"的屬性與方法,並透過new運算子建立的對象。
簡單來說,我們使用 new 運算子建立一個 food 實例,並且可以透過 instanceof 來檢驗實例與建構子之間的關係。
function Food (name) { this.name = name this.eat = function () { console.log('eat') } } var food = new Food('banana') // 实例化 var res = food instanceof Food // 检查 food 是否为 Food 实例 console.log(res) // true复制代码
當我們在原型上定義一個屬性時,實例也會"繼承"這個屬性。
function Food (name) { this.name = name this.eat = function () { console.log('eat') } } var food = new Food('banana') // 实例化 var res = food instanceof Food // 检查 food 是否为 Food 实例 console.log(res) // true // 原型定义属性 Food.prototype.type = 'object named Food' var foodRes = food.type // 实例继承的属性 console.log(foodRes) // object named Food复制代码
# 任何物件在建立時都會有一個 __proto__ 屬性,它指向產生目前物件的建構函數的原型物件。由於該屬性並非標準規定的屬性,所以不要隨便去更改該屬性的值,以免破壞原型鏈。也就是說,實例可以透過 __proto__ 屬性存取到原型。
对象中的 __proto__ 属性在所有实现中是无法访问到的,但是可以通过 isPrototypeOf() 方法来确定对象之间是否存在着这种关系。
function Food (name) { this.name = name this.eat = function () { console.log('eat') } } var food = new Food('banana') // 实例化 console.log(food.__proto__ === Food.prototype) // true console.log(Food.prototype.isPrototypeOf(food)) // true复制代码
构造函数可以通过 prototype 属性访问到原型,那么原型也是能够通过某种途径访问到构造函数的,其就是原型中的一个属性 constructor ,该属性并不是真正的构造函数,真正的构造函数是指 Constructor,两者不要混淆了。
function Food (name) { this.name = name this.eat = function () { console.log('eat') } } var food = new Food('banana') console.log(Food.prototype.constructor === Food) //true复制代码
关键:prototype 的 constructor 指向构造函数本身
那么构造函数、原型、实例三者的关系应该是这样的:
为了更好地理解这一过程,我通过一个故事给大家梳理一下:
1. 很久以前,有个雕刻家偶然看到一个很精致的花瓶(原型 Food.prototype)
2. 一天,他想通过大批生产复刻这个花瓶来发家致富,于是他先分析这个花瓶,还原了雕刻的过程,并设计出了一条生产线(构造器 Food)
3. 然后通过这条生产线,雕刻出许许多多的复刻花瓶。(实例 food)
proto 是任何对象都有的属性,在js中会形成一条 proto 连接起来的链条,递归访问 proto 直到值为 null ,这个搜索过程形成的链状关系就是原型链。
function Food (name) { this.name = name this.eat = function () { console.log('eat') } } var food = new Food('banana') // 实例化 // 原型链 console.log(food.__proto__) // Food {} console.log(food.__proto__.__proto__) // {} console.log(food.__proto__.__proto__.__proto__) // null复制代码
如下图:
1. 每创建一个函数都会有一个 prototype 属性,该属性是一个指针,指向一个对象,该对象为原型对象(Food.prototype)。
2. 原型对象上的默认属性 constructor 也是一个指针,指向其相关的构造函数。
3. 通过 new 操作符产生的实例对象都会有一个内部属性指向原型对象,该实例对象可以访问原型对象上的所有属性和方法。
4. 实例可以通过内部指针访问到原型对象,原型对象也可以通过 constructor 找到构造函数。
5. 每个构造函数都有一个原型对象,原型对象上包含一个指向构造函数的指针,实例包含一个指向原型对象的内部指针。
6. __proto___ 的指向取决于对象创建时的实现方式。
7. 构造函数实例,封装的函数,如果通过 new 操作符来调用则是构造函数,否则则不是。 8. 在整个原型链上寻找某个属性,对性能有影响,越是上层的原型对象,对性能的影响越大。
9. js中一切皆对象,通过 new Function 的是函数对象,其构造函数是 Function,而普通对象的构造函数则是 Object 。
10. 每一个对象都有 __proto__ 属性,而每一个函数对象才有 prototype 属性。
1.《JavaScript 高级程序设计》
2.developer.mozilla.org/zhCN/docs/W…
如果你仔细阅读完本文,相信你对 JavaScript 中的原型和原型链会有新的认识。如果你觉得对你有帮助,记得 点赞 哦!如果你发现我理解的有问题,也欢迎你在评论中指正出来。
相关免费学习推荐:javascript(视频)
以上是這麼看,原型與原型鏈 好像也不難的詳細內容。更多資訊請關注PHP中文網其他相關文章!