[深刻02] 原型鏈

導航

[深刻01] 執行上下文
[深刻02] 原型鏈
[深刻03] 繼承
[深刻04] 事件循環
[深刻05] 柯里化 偏函數 函數記憶
[深刻06] 隱式轉換 和 運算符
[深刻07] 瀏覽器緩存機制(http緩存機制)
[深刻08] 前端安全
[深刻09] 深淺拷貝
[深刻10] Debounce Throttle
[深刻11] 前端路由
[深刻12] 前端模塊化
[深刻13] 觀察者模式 發佈訂閱模式 雙向數據綁定
[深刻14] canvas
[深刻15] webSocket
[深刻16] webpack
[深刻17] http 和 https
[深刻18] CSS-interview
[react] Hooks前端

[部署01] Nginx
[部署02] Docker 部署vue項目
[部署03] gitlab-CIvue

[源碼-webpack01-前置知識] AST抽象語法樹
[源碼-webpack02-前置知識] Tapable
[源碼-webpack03] 手寫webpack - compiler簡單編譯流程java

構造函數的缺點

  • 經過構造函數生成實例對象,屬性和方法都生成在實例上,多個實例之間屬性和方法不能共享

prototype屬性

  • javaScript的繼承機制的設計思想:原型對象的全部屬性和方法都能被實例對象所共享
  • 全部的函數都有一個prototype屬性,指向一個對象
  • 對於構造函數來講:在構造函數生成實例的時候,構造函數的prototype屬性會成爲實例對象的原型

原型對象

  • 原型對象上的屬性不是實例對象自身的屬性,只要修改原型對象,變更就會馬上反應到全部實例對象上
  • 如何實例對象和原型對象有同名的屬性和方法,則實例對象讀取該屬性時,會讀取自身的屬性,而不會讀取原型上的屬性
  • 原型對象的做用:定義全部實例共享的屬性和方法

原型鏈

  • js規定,全部對象都有原型對象
  • 全部對象均可以成爲其餘對象的原型,原型對象也是對象,也有本身的原型,造成一個鏈條
  • 一層層上溯,最終都會上溯到Object.prototype,即Object構造函數的prototype屬性
  • 即全部對象都繼承了 Object.prototype 對象上的屬性和方法,這就是全部對象都具備valueOf和toString的緣由
  • Object.prototype的原型是null,null沒有任何屬性和方法,也沒有本身的原型,原型鏈終止
  • null是爲了防止死鏈

覆蓋 overriding

  • 讀取對象的屬性時,自身和原型上有同名的屬性和方法,優先讀取自身屬性,這叫作覆蓋
  • 讀取對對象的屬性時,自身沒有會到原型上找,原型沒有會到原型的原型上找,直到Object.prototype,仍是沒有返回undefined
  • 一級級向上,在整個原型鏈上尋找某個屬性,對性能是有影響的。
  • overriding:覆蓋的意思
function A(){} //---------------------- 定義構造函數A
A.prototype = new Array() // ---------- 將A.prototype指向實例數組,那麼A的實例就能繼承數組原型鏈上的屬性和方法
A.prototype.constructor = A // -------- 修改prototype的同時,也要修改constructor防止意外
const a = new A()
a.push(1)
a instanceof Array // true

複製代碼

constructor

  • prototype對象有一個constructor屬性,默認指向prototype屬性所在的構造函數
  • 由於constructor屬性在prototype對象上,因此constructor屬性被全部實例繼承
  • 做用:
    • (1) constructor的做用是,能夠得知一個實例對象,究竟是由哪一個構造函數產生的
    • (2) constructor的另外一個做用:能夠用一個實例新建另外一個實例
  • 注意:constructor表示原型對象和構造函數之間的關連關係,若是修改原型對象,通常會同時修改constructor屬性,防止引用的時候報錯,=> 由於修改了prototype以後,A.prototype.constructor指向的已經不是A了,而是最新賦值給prototype對象的對象指向的那個constructor
  • 修改原型對象要一同修改構造函數,防止引用報錯!!!!!!!
  • name屬性
    • constructor.name => 構造函數名
<script>
  function A(){}
  const a = new A()
  console.log(A.prototype.constructor === A) // true
  console.log(a.constructor === A.prototype.constructor) // true,constructor屬性在prototype對象上,因此能被實例繼承並訪問
  console.log(a.constructor === A) // true,constructor的做用是能夠肯定實例由哪一個構造函數產生
  console.log(a.constructor === RegExp) // false
  console.log(a.hasOwnProperty('constructor'), "a.hasOwnProperty('constructor')") // constructor不是實例自身屬性,繼承的
  console.log(new a.constructor(), 'constructor的另外一個做用: 一個實例能夠藉助constructor新建另外一個實例')
  const b = new a.constructor()
  console.log( b instanceof A) // true , 即b是A的其中一個實例
</script>
複製代碼
constructor
constructor表示原型對象和構造函數之間的關聯關係,修改prototype,須要同時修改constructor,防止引用出錯

<script>
  function A () {
    console.log('A構造函數')
  }
  console.log(A.prototype, '修改以前的A.prototype')
  console.log(A.prototype.constructor, '修改以前的A.prototype.constructor') // A
  console.log(A.prototype.constructor.name, '修改以前的A.prototype.constructor.name => 修改前的constrctor名稱')
  A.prototype = {name: 'woow_wu7'}
  console.log(A.prototype.constructor, '修改以後的A.prototype.constructor') // Object
  console.log(A.prototype.constructor.name, '修改以後的A.prototype.constructor.name => 修改後的constrctor名稱')
  A.prototype.constructor = A
  console.log(A.prototype.constructor, '修改prototype後,要從新指定A.prototype.constructor=A,防止引用出錯')
</script>
複製代碼

instanceof

  • 返回一個布爾值,表示對象是否爲某個構造函數的實例
  • instanceof左邊是實例對象,右邊是構造函數
  • instanceof會檢查右邊構造函數的原型對象prototype,是否在左邊對象的原型鏈上
  • 因爲instanceof檢查整個原型鏈,所以同一個實例對象,可能對多個構造函數返回true
  • 有一個特殊狀況:若是左邊對象的原型鏈上只有null(即左邊對象的原型對象是null),這時instanceof判斷就會失真
  • instanceof的一個用處就是判斷值類型,(可是隻能用於判斷對象,不能用於判斷原始類型的值)
  • 對於undefined和null,instanceOf運算符老是返回false
  • 利用instanceof能夠巧妙的解決,調用構造函數時忘記加new命令的問題
instanceof

function A(){}
const a = new A()
console.log(a instanceof A, 'instanceof的原理是檢查右邊構造函數的原型對象是否在左邊對象的原型鏈上')
console.log(A.prototype.isPrototypeOf(a), 'instanceof等價於這樣')


var d = new Date();
d instanceof Date
// true
// d既是Date的實例,也是Object的實例
d instanceof Object 
// true
// 由於instanceof檢查的是右邊構造函數的實例,是否在左邊實例對象的原型鏈上,檢查整個原型鏈
// 因此同一個實例,可能對多個構造函數返回ture



var obj = Object.create(null); // 以null爲原型對象建立實例
typeof obj // "object"
Object.create(null) instanceof Object // false

複製代碼
installof能夠解決調用構造函數時,忘記加new命令的狀況

function A() {
  if(!(this instanceof A)) { // 若是this不是A的實例,說明不是new命令調用的,那麼執行new
    return new A()
  }
  else {
    this.name = 'woow_wu7'
  }
}
const a = new A()
console.log(a)
複製代碼

Object.getPrototypeOf

  • 返回參數對象的原型對象
  • Object.getPrototypeOf 是獲取原型對象的標準方法
  • 注意:參數是對象,也能夠是函數,由於函數也是對象,在es6中能夠判斷類的繼承,Object.getPrototypeOf(ColorPoint) === Point // true,ColorPoint是一個類,便是一個函數
Object.getPrototypeOf
- 獲取參數對象的原型對象,是獲取原型對象的標準方法

Object.getPrototypeOf(Object.prototype) === null // true
Object.getPrototypeOf(Function.prototype) === Object.prototype // true
Object.getPrototypeOf({}) === Object.prototype // true
複製代碼

Object.setPrototypeOf

  • 將第一個參數對象的原型設置爲第二個參數對象 Object.setPrototypeOf(現有對象,原型對象)
  • 返回值:返回第一個參數對象
  • 參數:第一個參數是現有對象,第二個參數是原型對象
const a = {}
const b = {name: 'woow_wu7'}
const res = Object.setPrototypeOf(a, b) // 將b設置成a的原型,注意返回值是a
Object.getPrototypeOf(a) === b // true
console.log(a.name)
console.log(res) // a, 返回值是a
複製代碼
  • new命令能夠用Object.setPrototypeOf來模擬
var F = function () {
  this.foo = 'bar';
};

var f = new F();
// 等同於
var f = Object.setPrototypeOf({}, F.prototype);
// 返回值f是第一個參數 {}
// Object.setPrototypeOf({}, F.prototype) => 至關於 {}.__proto__ = F.prototype
F.call(f);
複製代碼

Object.create

  • 生成實例對象
    • 經過new命令執行構造函數的方式生成(構造函數其實就是普通的函數,只是用new命令調用時,this指向了實例,而且首字母大寫來區分,不大寫也行,但約定俗成)
    • 經過一個對象生成,有時候只能拿到一個對象,要生成實例對象。Object.create,該實例徹底繼承原型對象的屬性和方法
  • Object.create() 以參數對象爲原型返回實例對象,該實例徹底繼承原型對象的屬性和方法
手動實現一個 Object.create

Object._create = function(obj) {
  function F(){}
  F.prototype = obj // 新建一個構造函數,將構造函數的prototype指向傳入的對象,執行構造函數,即實例的原型指向了傳入的對象
  return new F()
}
複製代碼
  • 若是想要生成一個不繼承任何屬性和方法的對象,可使用 Object.create(null)
  • 若是Object.create()的參數爲空,或者不是對象就會報錯

Object.prototype.isPrototypeOf

  • 實例對象的 isPrototypeOf 屬性用於判斷該對象是不是參數對象的原型

Object.prototype.__proto__

  • 實例對象的原型對象
  • 根據語言標準,只有瀏覽器須要部署__proto__,其餘環境中沒有__proto__屬性
  • 不建議使用__proto__
  • 而是用標準的Object.getPrototypeOf讀取原型Object.setPrototypeOf(現有對象,原型對象)來設置原型

獲取原型對象的方法比較

- `obj.__proto__` // 只有瀏覽器纔有,不建議使用
- obj.constructor.prototype // 手動修改原型時,可能會失真
- Object.getPrototypeOf() // 推薦的獲取方法
複製代碼
直接修改原型對象時,須要同時修改constructor防止失真


function A(){}
const a = new A()

function B() {}
B.prototype = a
const b = new B()

b.constructor.prototype === a // false
// 由於:b.constructor === a.constructor === A.prototype.constructor === A
// 因此:b.constructor.prototype === A.prototype
// 結論:當直接修改prototype屬性時,必定要修改constructor屬性 !!!!!!!!!!!!!!!!!!(重要)


// 若是是下面這樣則:
function A(){}
const a = new A()
function B() {}
B.prototype = a
B.prototype.constructor = B // 修改了prototype,同時修改constructor則引用不會出錯
const b = new B()
b.constructor.prototype === a // true
複製代碼

Object.prototype.hasOwnProperty

  • 返回一個布爾值,表示是不是對象自身的屬性,不包含原型鏈上的屬性
Date.hasOwnProperty('length') // true
Date.hasOwnProperty('toString') // false
複製代碼

in運算符

  • in運算符返回一個布爾值,表示屬性在對象中是否存在,不區分自身屬性仍是繼承的屬性
  • 注意:in 運算符不區分自身屬性和繼承屬性
例子:
function X(){}
X.prototype.name = 'woow_wu7';
let x = new X()
'name' in X // true
// 由於:in 運算符返回一個布爾值,表示屬性是否在對象中存在,不區分自身仍是繼承
// 因此:'name' in X => 返回 true
複製代碼

for in 和 for of

  • for in 能夠遍歷對象和數組
  • for of 只能遍歷數組
for in
- 用於數組,i表示:key
- 用於對象,i表示:key
- 用於對象時,for...in會遍歷自身屬性和繼承的屬性,
for of
- 用於數組:i表示 value


const objP = {sex: 'man'}
const obj = {name: 'woow_wu7', age: 20, address: 'hangzhou'};
Object.setPrototypeOf(obj, objP)
for(let i in obj) {
  console.log(i, 'for in 循環 => 用於對象,會遍歷自身和繼承的屬性') // name,age,address,sex
  if (obj.hasOwnProperty(i)) {
    console.log(i, '若是隻但願遍歷自身屬性,能夠用Object.prototype.hanOwnProperty(屬性名)來過濾')// name,age,address
  }
}
複製代碼

易錯點總結

- constructor
    - constructor表示構造函數和原型對象之間的關聯關係,若是修改了原型對象,須要一塊兒修改構造函數,防止引用出錯。
    -(每個構造函數都有一個prototype屬性,prototype的constructor指向prototype所在的構造函數)
- instanceof
    - 原理:instanceof是檢查(右邊構造函數的prototype屬性)是否在(左邊對象)的原型鏈上
    - instanceof失效的狀況:
        - 若是一個對象的__proto__屬性指向null,則instanceof就會失效
        - 由於右邊是構造函數的prototype => 終點是Object.prototype,是否在左邊對象的原型鏈上
        - Object.prototype.__prototo__ === null
        - Object.prototype instanceof Ojbect             // false  
        // Object.create(null) instanceof Object   // false,由於建立的實例沒有任何屬性和方法,也沒有原型
複製代碼

相關文章
相關標籤/搜索