2016年4月21日 星期四

JavaScript Note: prototype 更新釋疑

最常跟小弟交流的網友,大部分來自工控界。其實小弟也作過一陣子時下流行的「全棧工程師(Full Stack Developer)」,寫過 PHP + MySQL + jQuery + Google Map API,下面是小弟過去的作品截圖:



不過,小弟對這個作品不是很滿意,在這一行也沒作很久,所以這成為小弟的黑歷史之一,哈哈哈~

But,最近越來越多客戶反應,希望能夠同時在 PC/平板/手機上監控他們的設備。傳統方案顯然很有問題,因為同時開發 PC/iOS/Android App 的成本太高了。

Java 不是小弟考慮的方案,因為 Google 與 Oracle Java 官司未了,而 iOS 上主流是 Objective-C 與 Swift,Windows 上我幹嘛不用 C#,再說小弟寫編譯式語言(C++)多年已經十分厭倦...:(

如今 server side node.js 現身,與 client side (Browser) 合而為一,外加 HTML5 加持,泰山北斗已現又何必案牘勞形,工程師一魚兩吃,老闆僱用成本減半,win win!

廢話不多說,下面就是小弟的心得筆記,沒意外未來將會有一系列,算是寫給我自己看的,歡迎留言討論

Q: 將 prototype 屬性換成新物件,無法更新之前的實例?

A:


取自 「JavaScript 深入精要(JavaScript Enlightement)」P.95,原文是:

Replacing the prototype property with a new object does not update former instances


var Foo = function Foo(){};

Foo.prototype.x = 1;

var FooInstance = new Foo();

console.log(FooInstance.x); // logs 1, as you think it would

// now let’s replace/override the prototype object with a new Object() object
Foo.prototype = {x:2};

console.log(FooInstance.x); // logs 1, WHAT? Shouldn't it log 2, we just updated prototype
/* FooInstance still references the same state of the prototype object that 
was there when it was instantiated. */

// create a new instance of Foo()
var NewFooInstance = new Foo();

// the new instance is now tied to the new prototype object value (i.e. {x:2};)
console.log(NewFooInstance.x); // logs 2 

看完這個範例不免讓人驚恐,如果更新 prototype 無法影響到所有的 instance,那不是無法施展禁斷密技 Monkey Patch 了嗎?

再看一個例子:


var Foo = function(){};
Foo.prototype.x = 1;
var foo1 = new Foo;
console.log(foo1.x); //log 1
Foo.prototype.x = 2;
var foo2 = new Foo;
console.log(foo2.x); //log 2
console.log(foo1.x); //log 2

這似乎推翻了書上的說法,更新 prototype 的確會影響所有 instance,我們再增加一些程式碼,看看修改 Foo.prototype 是否能影響 Foo 的子代 Bar:

//parent
var Foo = function(){};
Foo.prototype.x = 1;
var foo1 = new Foo;
console.log(foo1.x); //log 1
Foo.prototype.x = 2;
var foo2 = new Foo;
console.log(foo2.x); //log 2
console.log(foo1.x); //log 2
//child
var Bar = function(){};
//inherit from Foo
Bar.prototype = Object.create(Foo.prototype);
var bar = new Bar;
Foo.prototype.x = 3;
console.log(bar.x); //log 3
console.log(foo1.x); //log 3
console.log(foo2.x); //log 3

從執行結果來看也沒問題。所以結論應該是:

  • 只更新 prototype 的一部份,會影響所有的 instance 與子代。
  • 「完全替換」 prototype(如第一個範例 prototype = {x:3}),只會影響替換後新建立的 instance與子代。
事實上原文開頭這樣寫:

「You might think that you can replace the prototype property entirely anytime and that all instances will be updated...」

好吧,小弟承認自己是個英語渣,不過這個細微之處的確很容易讓人混淆。另外,書上的建議是:

一旦你開始建立 instance 之後,就不應該將物件的 prototype (完全)替換成新的物件」。

沒有留言:

張貼留言