Object's prototype

__proto__

  • 所有 Object 都有這個 property
  • 指向一個 Object
    • 可以把它指向的 Object 想像成 父物件
      • 因此 Object 們都可以使用其 __proto__ 中的變數/函數
      • __proto__ 物件也會有其 __proto__
        • 父類別的父類別
        • 形成一個 Prototype chain
    • 兩個物件可以有同一個 __proto__ 物件, 即繼承至同一個父類別的概念
  • Prototype chain
    • 當存取一個物件沒有的屬性, 會往上層去找其 proto 是否有該屬性, 直到走完整個 Prototype chain為止
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
var person = {
firstname: 'Default',
lastname: 'Default',
getFullName: function(){
return this.firstname + ' ' + this.lastname;
}
}

var john = {
firstname: 'John',
lastname: 'Doe'
}

// 實際上不要去設定prototype物件,可能會拖慢整個系統速度!
john.__proto__ = person; // 此時person內的this指向john
console.log(john.getFullName()); // 顯示 John Doe
console.log(john.firstname); // 顯示 John

var jane = {
firstname: 'Jane'
}

jane.__proto__ = person; // 此時person內的this指向jane .
console.log(jane.getFullName()); // 顯示 Jane Default
  • 除了 Primitive types 外, 其餘都是 Object, 因此一些 Build-in function 都掛載在他們的 __proto__ 上

    • Array: .forEach(), .map(), .reduce() … etc.
    • Function: .call(), .apply() … etc.
    • … etc.
  • Setting an object as another object’s __proto__

    • Using Object.create(${father})
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
var person = {
firstname: 'Default',
lastname: 'Default',
greet: function(){
return 'Hi ' + this.firstname;
}
}

var john = Object.create(person); // 指定person為john的'原型'.
console.log(john); // 顯示 Object {} , john 目前本身沒有任何屬性!
console.log(john.greet()); // 顯示 Hi Default , 因為john的prototype有greet/firstname/lastname屬性!

john.firstname = 'John';
john.lastname = 'Doe';
console.log(john); // 顯示 Object {firstname: "John", lastname: "Doe"}
console.log(john.greet()); // 顯示 Hi John , john 本身就有firstname屬性 , 因會用prototype的firstname.

Constructor function 的 prototype

  • 建構子函數即函數物件, 因此本身有 __proto__ 這個屬性
  • 而函數物件相較於其他物件, 有一個特殊的屬性 prototype
    • 此屬性在 function 為建構子函數時使用
    • 透過該建構子函數建構出的物件的 __proto__, 即指向該建構子函數的 prototype屬性
    • 可以透過 prototype 屬性, 來設定由該建構子函數建構出來物件的共用變數/函數, 以節省記憶體空間
    • Example
1
2
3
4
5
6
7
8
9
10
11
12
13
14
function Person(firstname, lastname){
this.firstname = firstname;
this.lastname = lastname;
}

var john = new Person('John', 'Doe');

// 在物件已建立後才在原型上新增函數
Person.prototype.getFormalFullName = function(){
return this.lastname + ', ' + this.firstname;
}

// 物件本身沒有getFormalFullName(),往它的上一層去找其 __proto__
console.log(john.getFormalFullName()); // 顯示 Doe, John