JavaScript進階3--對象原型

系列文章:算法

對象

在 JavaScript 中,共有6種類型主要類型:數組

  • string
  • number
  • boolean
  • null:typeof null 時會返回字符串"object",這是語言自己的一個bug。
  • undefined
  • object:包括函數、數組、內置對象等在內的子類型。就是鍵/值對的集合。typeof function 時會返回字符串"function"。

內容

對象的內容是由一些存儲在特定命名位置(任意類型的)值組成的,咱們稱之爲屬性。瀏覽器

⚠️注意:即便在對象的文字形式中聲明一個函數表達式,這個函數都不會「屬於」一個對象,他們只是對於相同函數的多個引用bash

屬性描述符

從 ES5 開始,全部的屬性都具有屬性描述符:閉包

  • writable:是否能夠修改屬性的值。值爲false時,至關於定義了一個空操做setter。函數

  • configurable:若是屬性是可配置的,就可使用 defineProperty(...) 進行修改屬性描述符。configurable的值設置爲false是單向操做,沒法撤銷!post

    ⚠️注意:即便 configurable:false,仍是能夠把 writable 的值從 true 改爲 false,但不能夠由 false 改爲 true。測試

  • enumerable:控制的是屬性是否會出如今對象的屬性枚舉中。ui

⚠️注意:屬性不必定包含值——也多是包含getter/setter的「訪問描述符」this

[[Get]] 和 [[Put]]

[[Get]]

屬性訪問時,對象默認的內置 [[Get]] 操做首先在對象中查找是否有名稱相同的屬性,遍歷可能存在的[[prototype]]鏈,若是不管如何也沒找到名稱相同的屬性,則返回值 undefined

⚠️注意:訪問變量時,沒找到會拋出一個 ReferenceError 異常。剛問對象屬性時,沒找到會返回 undefined

[[Put]]

當給屬性賦值:

  • 先檢查對象中是否存在這個屬性,若是有這個屬性, [[Put]]算法大體會檢查一下內容:
    1. 屬性是否有setter,若是有的話,調用 setter。
    2. 屬性的描述符中 writable 的值若是是false,如下兩種狀況:
      • 嚴格模式下,拋出 TypeError 異常。
      • 非嚴格模式下,靜默失敗。
    3. 若是都不是,則將該值設置爲屬性的值。
  • 若是對象沒有這個屬性,[[prototype]]鏈會被遍歷,若是 [[prototype]] 上找到了這個屬性。
    1. 若屬性的 writable: true,就會在對象裏添加一個新屬性。
    2. 若屬性的 writable: false,
      • 嚴格模式下,會拋出錯誤
      • 非嚴格模式下,會靜默失敗
    3. 若屬性有setter,那麼就會調用這個setter,屬性不會被添加到對象上。
  • 若是 [[prototype]] 上找不到這個屬性,這個屬性會直接添加到對象上。

原型

JavaScript 中的對象有一個特殊的 [[prototype]] 內置屬性,是對其餘對象的引用。全部普通的 [[prototype]] 最終都會指向內置的 Object.prototype。

全部的函數默認都會擁有一個名爲 prototype 的公有且不可枚舉的屬性,它會指向另外一個對象,這個對象被稱爲原型,這個對象默認有一個公有且不可枚舉的屬性.constructor,這個屬性引用的是對象關聯的函數。

[[prototype]]機制就是指對象中的一個內部連接引用另外一個對象。

原型繼承

JavaScript 會在兩個對象之間建立一個關聯,這樣一個對象就能夠經過委託來訪問另外一個對象的屬性和函數。

var bar = Object.create(foo); 會建立一個對象 bar 並把它關聯到 foo。

調用 Object.create(...)會憑空建立一個「新」對象,並把新對象內部的[[prototype]] 關聯到指定的對象...,這樣會直接把原始的關聯對象拋棄掉。

有如下幾種方法檢查兩個對象之間的關係:

  • instanceof:用於測試函數的 prototype 屬性是否出如今對象的原型鏈中任何位置。這個方法只能處理對象和函數之間的關係。
  • prototypeObj.isPrototypeOf(object): 檢查 prototypeObj 是否在 object 的原型鏈中出現。
  • getPrototypeOf: 返回指定對象的原型

⚠️注意:絕大多數(不是全部)的瀏覽器支持__proto__ 這個非標準的方法訪問內部的[[prototype]],__proto__存在於內置 Object.prototype 中,看起來像個屬性,其實更像一個 getter/setter。

好題練習

練習題1:

function Test() {
    Test.prototype.add = num => {
      this.number = num
    }
    Test.prototype.number = 0
  }

t1 = new Test()
t2 = new Test()
t1.add(12)
var a = t1.number
var b = t2.number
console.log(a, b) // => 0,12
複製代碼

每實例化一次Test,Test.prototype.add都會被從新賦值一次,而且賦值的函數爲箭頭函數,箭頭函數無自身的上下文,其中的this指向Test上下文中的this,即每次實例化的實例對象。因此,t2 實例化後 Test.prototype.add 裏的 this 指向t2,t1 調用 add 函數時,給 t2 的number 賦值。

練習題2:

var A = function() {};
A.prototype.n = 1;
var b = new A();
A.prototype = {
  n: 2,
  m: 3
}
var c = new A();

console.log(b.n);  // 1
console.log(b.m);  // undefined

console.log(c.n);  // 2
console.log(c.m);  // 3
複製代碼

⚠️注意:JavaScript 中的引用和其餘語言中的引用/指針不一樣,不能指向別的變量或者引用,只能指向

b 在實例化過程當中,b.__proto__ = A.prototype,此時 A.prototype = {n: 1},當 A.prototype = { n: 2, m: 3 }時,打破原型鏈,A.prototype 引用了新內存地址的對象,b.__proto__指向的依舊是舊內存地址的對象。若是是 A.prototype.n = 2; A.prototype.m = 3; 就不會指向新的引用。

練習題3:

var F = function() {};

Object.prototype.a = function() {
  console.log('a');
};

Function.prototype.b = function() {
  console.log('b');
}

var f = new F();

f.a();  // a
f.b();  // TypeError: f.b is not a function

F.a(); // a
F.b(); // b
複製代碼

f的原型鏈:f => f.__proto__ => F.prototype => F.prototype.__proto__ => Object.prototype => Object.prototype.__proto__ => null

F的原型鏈:F => F.__proto__ => Function.prototype => Function.prototype.__proto__ => Object.prototype => Object.prototype.__proto__ => null

練習題4:

function Person(name) {
    this.name = name
}
let p = new Person('Tom');

question1: p.__proto__等於什麼? // p.__proto__ === Person.prototype
question2: Person.__proto__等於什麼? // Person.__proto__ === Function.prototype
複製代碼

以爲有收穫的,點個贊再走吧~

相關文章
相關標籤/搜索