原型鏈
- 創建 (聲明) 對象有幾種方法
- 原型、構造函數、實例、原型鏈
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屬性。
三種方法的優缺點
- 功能:都能實現對象的聲明,並能夠賦值和取值
- 繼承性:內置方法創建的對象繼承到__proto__屬性上
- 隱藏屬性:三種聲明方法會默認為內部的每個成員(屬性或方法)生成一些隱藏屬性,這些隱藏屬性是可以讀取和可配置的,屬性分類見下面:
- 屬性讀取:
Object.getOwnPropertyDescriptor()或getOwnPropertyDescriptors() - 屬性設置:
Object.definePropertype或Object.defineProperties
二. 原型鏈的關系
下圖仔細觀察原型、構造函數、對象實例、原型鏈之間的關系。

1.對象實例
只要是對象就是一個實例;回顧上面的創建對象的幾種方式,任何一個實例對象都有一個隱式原型__proto__對象。
2.構造函數
凡是通過關鍵字new來操作后邊的函數,這個函數就是構造函數;准確的說任何一個函數只要被new使用了,后面這個函數就可以被叫做構造函數。
構造函數可以使用new運算符來生成一個實例;
3.原型對象
任何一個函數都有一個prototype屬性,他是函數所獨有的,這個prototype指的就是顯式原型對象;
任何一個實例對象都有一個__proto__對象。他是對象所獨有的,這個__proto__指的就是隱式原型對象;
__proto__ 是原型鏈查詢中實際用到的,它總是指向 prototype;
prototype 是函數所獨有的,在定義構造函數時自動創建,它總是被__proto__所指。
4.原型鏈
每個對象都可以有一個原型_proto_,這個原型還可以有它自己的原型,以此類推,形成一個原型鏈。查找特定屬性的時候,我們先去這個對象里去找,如果沒有的話就去它的原型對象里面去,如果還是沒有的話再去向原型對象的原型對象里去尋找知道終點null...... 這個操作被委托在整個原型鏈上,這個就是我們說的原型鏈了
原型鏈是通過什么來實現這個往上找的過程呢?
通過prototype這個原型和__proto__屬性來完成原型鏈的查找的;

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
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 運算符

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;
}
};
