prototype原型鏈詳解


原型鏈

  • 創建 (聲明) 對象有幾種方法
  • 原型、構造函數、實例、原型鏈
  • instanceof 的原理
  • new 運算符

一. 創建對象有幾種方法

1.字面量

var test2 = {x:123,y:345};
console.log(test2);//{x:123,y:345};
console.log(test2.x);//123
console.log(test2.__proto__.x);//undefined
console.log(test2.__proto__.x === test2.x);//false

2.構造函數new

// 方法1 #通過new Object聲明的一個對象
var test1 = new Object({x:123,y:345});
console.log(test1);//{x:123,y:345}
console.log(test1.x);//123
console.log(test1.__proto__.x);//undefined
console.log(test1.__proto__.x === test1.x);//false

// 方法2  #使用顯式構造函數創建對象
var M = function(name){ this.name = name;};
var o3 = new M('o3');  //M {name: "o3"}

new的作用: 1.創了一個新對象; 2.this指向構造函數; 3.構造函數有返回,會替換new出來的對象,如果沒有就是new出來的對象

3.內置方法

Obejct.create(obj,descriptor),obj是對象,describe描述符屬性(可選),創建一個具有指定原型且可選擇性地包含指定屬性的對象。

let p = {x:123,y:345};
let test = Object.create(p);
console.log(test);//{}
console.log(test.x);//123
console.log(test.__proto__.x);//3
console.log(test.__proto__.x === test.x);//true
console.log(test.__proto__ === p)  // true

Object.create方法是把參數中這個對象作為一個新對象的原型對象賦給test的,test本身不具備這個屬性的,通過原型鏈來連接這個原型對象的。(所以test對象本身沒有name這個屬性,只能通過原型鏈來找name屬性。

三種方法的優缺點

  1. 功能:都能實現對象的聲明,並能夠賦值和取值
  2. 繼承性:內置方法創建的對象繼承到__proto__屬性上
  3. 隱藏屬性:三種聲明方法會默認為內部的每個成員(屬性或方法)生成一些隱藏屬性,這些隱藏屬性是可以讀取和可配置的,屬性分類見下面:
  4. 屬性讀取:Object.getOwnPropertyDescriptor()或getOwnPropertyDescriptors()
  5. 屬性設置:Object.definePropertype或Object.defineProperties

二. 原型鏈的關系

下圖仔細觀察原型、構造函數、對象實例、原型鏈之間的關系。

原型鏈的關系

1.對象實例

​ 只要是對象就是一個實例;回顧上面的創建對象的幾種方式,任何一個實例對象都有一個隱式原型__proto__對象。

2.構造函數

凡是通過關鍵字new來操作后邊的函數,這個函數就是構造函數;准確的說任何一個函數只要被new使用了,后面這個函數就可以被叫做構造函數。

構造函數可以使用new運算符來生成一個實例;

3.原型對象

任何一個函數都有一個prototype屬性,他是函數所獨有的,這個prototype指的就是顯式原型對象;

任何一個實例對象都有一個__proto__對象。他是對象所獨有的,這個__proto__指的就是隱式原型對象;

__proto__ 是原型鏈查詢中實際用到的,它總是指向 prototype

prototype 是函數所獨有的,在定義構造函數時自動創建,它總是被__proto__所指。

4.原型鏈

每個對象都可以有一個原型_proto_,這個原型還可以有它自己的原型,以此類推,形成一個原型鏈。查找特定屬性的時候,我們先去這個對象里去找,如果沒有的話就去它的原型對象里面去,如果還是沒有的話再去向原型對象的原型對象里去尋找知道終點null...... 這個操作被委托在整個原型鏈上,這個就是我們說的原型鏈了

原型鏈是通過什么來實現這個往上找的過程呢?
通過prototype這個原型和__proto__屬性來完成原型鏈的查找的;

img

let obj1 = {name:'lucas'};   
obj1.__proto__ === Object.prototype  // true
obj1.__proto__.__proto__  			// null  #這是原型鏈頂端了
Object.prototype.__proto__  		// null #這是原型鏈頂端了

function Person(){}
Person.prototype.__proto__.__proto__  // null #這是原型鏈頂端了

let person = new Person();
person.__proto__.__proto__.__proto__  // null #這是原型鏈頂端了
person.__proto__  === Person.prototype  // true
function M (name) { 
    this.name = name; 
}//person是構造函數

var o3 = new M('o3') // personTwo是實例

原型關系詳解

原型對象都有一個默認的constructor屬性指向構造函數

三. instanceof 原理

instanceof主要用於判斷某個實例是否屬於某個類型,也可用於判斷某個實例是否是其父類型或者祖先類型的實例。

instanceof 主要的實現原理就是只要右邊變量的 prototype 在左邊變量的原型鏈上即可。因此,instanceof 在查找的過程中會遍歷左邊變量的原型鏈,直到找到右邊變量的 prototype,如果查找失敗,則會返回 false。

實例對象上有__proto__這個屬性,實例對象的這個屬性引用是它構造函數的原型對象(也就是找到的這個構造函數);
構造函數有prototype這個屬性,這個屬性引用的原型對象,在往下走,實例對象的__proto__這個屬性,其實是引用這個原型對象。

instanceof原理圖

模擬開發instanceof

function instanceof(left, right) {
    const rightVal = right.prototype
    const leftVal = left.__proto__
    // 若找不到就到一直循環到父類型或祖類型
    while(true) {
        if (leftVal === null) {
            return false
        }
        if (leftVal === rightVal) {
            return true
        }
        leftVal = leftVal.__proto__ // 獲取祖類型的__proto__
    }
}

四. new 運算符

new運算符

var new2 = function(func){

    //1.創建一個空對象,這個對象要繼承這個構造函數的原型對象(空對象要關聯構造函數的原型對象;)
    let o = Object.create(func.prototype);
    
    //2.執行構造函數
    let k = func.call(o);//call用來轉移上下文(this),把這個上下文轉成o對象
    
    //3.判斷構造函數的運行結果是不是對象類型
    if(typeof k ==='object'){
        return k;
    }else{
        return o;
    }
 };


免責聲明!

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



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