攻略前端面試官(三):JS的原型和原型鏈


本文在個人主頁同步更新~

背就完事了

介紹:一些知識點相關的面試題和答案
使用姿勢:看答案前先嘗試回答,看完后把答案收起來檢驗成果~

面試官:什么是構造函數

答:構造函數的本質是一個普通函數,他的特點是需要通過new關鍵字來調用,用來創建對象的實例。所有的引用類型,如[],{},function等都是由構造函數實例化而來。一般首字母大寫。

解析:首字母大寫只是約定俗成的規范。首字母小寫的函數也可以用作構造函數。

面試官:什么是原型和原型鏈

答:原型模式是JS實現繼承的一種方式。所有的函數都有一個prototype屬性,通過new生成一個對象時,prototype會被實例化為對象的屬性。所有的引用類型都有一個__proto__指向其構造函數的prototype。原型鏈的話,指的就是當訪問一個引用類型時,如果本身沒有這個屬性或方法,就會通過__proto__屬性在父級的原型中找,一級一級往上,直到最頂層為止。

解析:原型鏈最頂層Object的prototype__proto__指向為null。

面試官:如何理解constructor屬性

答:所有函數的原型對象都有一個constructor屬性指向函數本身。

解析:實例化的對象可以通過[].__proto__.constructor獲取到其構造函數。

面試官:描述new 操作符的執行過程

答:

  1. 創建一個空對象。
  2. 將這個空對象的__proto__指向構造函數的prototype
  3. 將構造函數的this指向這個對象。
  4. 執行構造函數中的代碼。
面試官:如何判斷一個變量是數組類型

答: 使用instanceof關鍵字 或者constructor屬性。

解析:instanceof的原理是判斷操作符左邊對象的原型鏈上是否有右邊構造函數的prototype屬性。

理解小幫手

介紹:總結性的圖表,代碼例子或筆試題目和解析,讓知識點更容易懂

關於構造函數和原型

構造函數:相當於java中“類”的存在,如原生JS中的Array, Function, String, Date等等,都是構造函數。例如new Date()通過new操作符進行調用,用來創建一個Date對象的實例。

一個便於理解的栗子,描述js通過原型模式實現繼承的過程

function Animal (name) {                 // 構造函數
    this.name = name
}

Animal.prototype.type = 'animal'         // 原型上的屬性和方法可以被繼承

Animal.prototype.eat = function () {
    console.log('eat')
}

let dog = new Animal('忠犬八公')          // 通過new 調用構造函數創建Animal的實例dog
console.log(dog.name)                    // 輸出:忠犬八公
console.log(dog.type)                    // 輸出:animal
dog.eat()                                // 輸出:eat

console.log(dog.__proto__)               // 輸出:{ type:'animal', eat: f, __proto__: ...}  
// dog.__proto__ 指向其構造函數Animal的prototype對象

一個關於原型的實用型例子

function Elem(id) {
    this.elem = document.getElementById(id)
}

Elem.prototype.html = function (val) {
    var elem = this.elem 
    if (val) {
        elem.innerHTML = val
        return this    // 鏈式編程
    }else{
        return elem.innerHTML
    }
}

Elem.prototype.on = function (type, fn) {
    var elem = this.elem
    elem.addEventListener(type, fn)
}

var div1 = new Elem('div1')
div1.html('灶門碳治郎').on('click', (e) => {
    alert('灶門碳治郎')
})

這個栗子,使用原型將對dom節點的操作封裝起來,只要創建一個Elem實例就輕松插入dom和添加事件監聽。

原型鏈

原型鏈

所有的引用類型會有一個__proto__屬性指向其構造函數的prototype,當訪問這個引用類型的變量和方法時,會通過__proto__屬性一層層往上找。如[]不止有構造函數Array原型上的方法,還有可以通過原型鏈找到Object原型上的方法。

關於instanceof 和 constructor

instanceof判斷操作符右邊的參數是否在左邊的原型鏈上。所以[] instanceof Object也為true

let obj = {}                                
let arr = []
console.log(typeof(obj))                    // object
console.log(typeof(arr))                    // object
console.log(obj instanceof Array)           // false
console.log(arr instanceof Array)           // true
console.log(obj.constructor === Array)      // false
console.log(arr.constructor === Array)      // true

通過以上代碼可以學習通過instanceof關鍵字和constructor 屬性進行數據類型判斷的使用方式。

知識延伸

先有雞還是先有蛋

JS究竟是先有Object還是先有Function呢?

console.log(Function instanceof Object)     // 輸出:true
console.log(Object instanceof Function)     // 輸出:true

Object和Function究竟是什么關系,這個問題一度困擾着我,直到我看到了這張圖

F&O

簡單理解為:

  1. FunctionObject的原型鏈上,因為Object是構造函數,他的__proto__指向Function的原型
  2. ObjectFunction的原型鏈上,因為Function是構造函數,他的__proto__指向的也是他自己的原型,然而Function.prototype本質上是一個對象,所以Function.prototype.__proto__指向Object.prototype

關於鏈式編程

上述“一個關於原型的實用例子”中,提到了鏈式編程,在此做簡單介紹

function Dog(){
    this.run = function(){
        alert('dog is run...')
        return this                    // 鏈式編程的關鍵
    }
    this.eat = function(){
        alert('dog is eat...')
        return this 
    }
    this.sleep = function(){
        alert('dog is sleep...')
        return this 
    }
}
var d1 = new Dog()
d1.run().eat().sleep()

通過以上代碼可以看出

  1. 鏈式編程的設計模式就是,調用的函數的時候,可以基於其返回值繼續調用其他方法
  2. 關鍵在於方法執行結束后需要有一個供繼續調用的返回值,如this等。

Kane -- 一切都是命運石之門的選擇


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM