JavaScript 原型式物件導向
Read From
You Don't Know JS: Prototypes
Prototype
-
[[Prototype]]
-
.__proto__
-
.prototype
[[Prototype]] link
JavaScript 的內部參考特性
代表物件對 "上層" 物件的參考
[[Prototype]] link
-
every object has [[Prototype]]
-
except Object.create(null)
-
-
Top of [[Prototype]]-
usualy Object.prototype
-
- Usage:
- for Property lookup
{}._proto_
-
Just like [[Prototype]]
- Not for every browser
.prototype
-
every function has one
- instance's [[Prototype]] will link to .prototype
._proto_ vs .prototype
| .prototype | .__proto__ |
|---|---|
| For function (constructor) | For instances |
Prototype Chain
-
think as "Property lookup"
- use when Key is not on object
Prototype Chain - [[Get]]
-
查找該物件上是否有同名 key,一找到則:
- 如果是正常的資料存取器就 return value
- 如果有設定存取器描述器,就執行 Getter Function
-
若沒有,往 [[Prototype]] 鏈結 尋找是否有同名 key,一找到則:
- 如果是正常的資料存取器就 return value
- 如果有設定存取器描述器,就執行 Getter Function
- 若到達 Prototype Chain 頂端都沒有,則回傳 undefined
Prototype Chain - [[Put]]
-
查找該物件上是否有同名 key,一找到則檢查:
- 是否為 setter,是的話執行
- writable 是否為 false,是的話失敗
- 都不是的話就正常賦值
-
若沒有,往 [[Prototype]] 鏈結尋找是否有同名 key,一找到則:
- 是否為 setter,是的話永遠 都執行那個設值器
-
writable 是否為 false,是的話失敗 ( 嚴格模式 TypeError / 非嚴格模式 無聲失敗 )
-
為了強化 繼承概念 的錯覺才如此設計
- 導致了奇怪的行為:=不可寫入,Object.defineProperty 可以
-
為了強化 繼承概念 的錯覺才如此設計
- 都不是的話就創建新屬性來產生 遮蔽(shadow) 屬性 的效果
- 若到達 Prototype Chain 頂端都沒有,就創建新屬性,並賦值到此屬性上
Prototype Class
-
宣告 類別 & 建構器
-
設定 類別藍圖
-
產出類別實體
function Foo() {};
Foo.prototype.prop = 'prototype prop';
var foo = new Foo();
console.log(foo.prop); // "prototype prop"
constructor call - new
-
Every Function can be constructor
constructor call - new
-
產生 (建構) 一個新物件 -
新物件的 [[Prototype]] 指向建構函式的 prototype 屬性 -
新物件 會被設為 建構函式的 this -
預設下,執行完建構函式後就會 return 該新物件
function Foo() {};
Foo.prototype.prop = 'prototype prop';
var foo = new Foo();
console.log(foo.prop); // "prototype prop"
Prototype Class
Difference
Classic OO
...
...
foo
Foo
.prototype
...
...
foo
(data members & actions)
Prototype Class
Difference
{
fooAction1: function() {},
fooAction2: function() {}
}[[Prototype]]
JavaScript OO
...
...
Change?
Dangerous
foo
Prototype Class
Difference
- 沒有拷貝動作,只有建立物件連結
- 改變 [[Prototype]] 內容,所有實體會一起改變
- 由物件來定義物件的藍圖 (weird!)
- .constructor 的不安全性
.constructor
-
可更改
- 原型物件上可以沒有
- 可能被遮蔽
- 可能錯誤鏈結到上層物件
function Foo() {};
Foo.prototype.prop = 'prototype prop';
var foo = new Foo();
foo.constructor === Foo; // true
function Bar() {};
Bar.prototype = {};
var bar = new Bar();
bar.constructor === Bar; // false
bar.constructor === Object; // true
Prototype inherit
- Child.prototype.__proto__ = Parent.prototype
- 子類別 建構器先呼叫父類別的建構器 (super())

child
Child
.prototype
Parent
.prototype
(data members & actions)
(data members & actions)
Prototype inherit
- Child.prototype.__proto__ = Parent.prototype
- 子類別 建構器先呼叫父類別的建構器 (super())
function Parent() {
this.name = 'name';
}
Parent.prototype.getName = function() {
console.log('parent\'s name:' + this.name);
}
function Child() {
Parent.call(this, arguments); // call parent exmplicitly, not very well
this.label = 'label';
}
Child.prototype = Object.create(Parent.prototype);
Child.prototype.constructor = Child;
Child.prototype.getLabel= function() {
console.log('child\'s label:' + this.label);
}
var child = new Child();
child.getName();
child.getLabel();
Object.create()
-
新建一個物件
-
其原型物件設為指定的物件
var childProto = Object.create(Parent.prototype);Object.create(null)
-
真正的空物件
var emptyObj = Object.create(null);Object.create() polyfill
Object.create = function(proto) {
function F() {};
F.proto = proto;
return F.proto;
}
不考慮 enumerable: false 的狀況下
Prototype inherit
Difference
- 沒有拷貝動作,只有建立物件連結
- 改變 [[Prototype]] 內容,所有實體會一起改變
- 由物件來定義物件的藍圖 (weird!)
- .constructor 的不安全性
- 藉由 "留著空洞" 讓 父類別補上差異
Prototype Class
Difference
{
childAction1: function() {},
childAction2: function() {}
}{
parentAction1: function() {},
parentAction2: function() {}
}{
childAction1: function() {},
parentAction1: function() {},
childAction2: function() {},
parentAction2: function() {}
}[[Prototype]]
Classic OO
JavaScript OO
child
child
introspection
-
obj instanceof Foo; -
Foo.prototype.isPrototypeOf(obj); -
Object.getPrototypeOf(obj) === Foo.prototype; -
obj.__proto__ === Foo.prototype; -
obj.constructor
JavaScript Prototype OO
By Chang Henry
JavaScript Prototype OO
- 16