顯示具有 Javascript 標籤的文章。 顯示所有文章
顯示具有 Javascript 標籤的文章。 顯示所有文章

2015年12月25日 星期五

[JavaScript] JavaScript inheritance patterns

JavaScript 要怎麼做繼承,整理了一些寫法:
  • ES2015 (ES6)
  • Function Pattern
  • 不 return、使用 call (or apply) + new 的方法實作 (all in one)
  • Pseudo-Classical pattern 
環境:Chrome v47.0.2526.106

一些基礎知識 (須看懂這裡,下面才看的懂QQ)


__proto__:用來指向(pointing) prototype 的 property。

prototype:每個 object 都有 prototype,prototype 自己也是 object,Object 會繼承他們的 prototype 的屬性和方法。

Object.prototype是最上層的 prototype chain。

Object.create(prototype):建立一個 new object 並把其 __proto__ 設定成指定 object 的 properties。

Object.setPrototypeOf(obj, proto):設定 obj 的 prototype 為指定的 prototype,原型為 object.__proto__ = prototype。

prototype chain (__proto__ chain 或原型鏈):JavaScript 會先找 Instance 自己有沒有 property,沒有會經由 __proto__ 去找上一層的 prototype,沒有再找上一層,直到 Object.prototype。

下述範例 Car 的 prototype 就包含了 name、driver、getName()、run()。
var Car = function(name) {
    this.name = name || "car";
};
Car.prototype = {
    name    : "",
    driver  : "",
    getName : function() {
        return this.name;
    },
    run     : function () {
        console.log(this.name + " is running");
    },
};
把 Car 印出如下圖,他的上一層是 Function.prototyp,top 是 Object.prorotype


要呼叫 像 getName 需要使用 Car.prototype.getName() 才能呼叫,
直接 Car.getName() 會有錯誤 Uncaught TypeError: Car.getName is not a function。
不過要呼叫 Function.prorotype.call() 就可以直接使用 Car.call() 去呼叫 。
要呼叫 Object.prorotype.isPrototypeOf() 可以直接使用 Car.isPrototypeOf() 去呼叫 。
表示經由 __proto__往上尋找。

function: 把一個變數宣告為 function 時,如下
var Car = function(name) {
    this.name = name;
};
如圖,會繼承 Function.prototype,並且會把 Car 的 prototype.constructor 指向 Car 自己這個 function 。

prototype.constructor:指向 function 本身。本來以為跟 new constructor 會優先找這個並由這裡開始做 constructor 進入點,但實驗在 Chrome 時 new 不會看這個。

new constructor:
用實例來說明
var car = new Car(); 

console.log(car instanceof Car); //true


會先檢查 Car 是不是 function,需要是 function ,因為會拿來做 constructor。
  1. 建立 Object。
  2. 繼承 Car。
  3. 呼叫 Constructor (Car function)。
  4. 需要注意,如果 Constructor (Car function)有 return value 時 car 會被這個 return value 取代掉。
很像下列程式
var car = {};
car.__proto__ = Car.prototype; // 等同 Object.setPrototypeOf(car, Car.prototype);
Car.call(car, "CAR");

console.log(car instanceof Car); // true

或著

var car = Object.create(Car.prototype);
Car.call(car, "CAR");

console.log(car instanceof Car); // true
很接近不等於,差別是用 new 下圖是 Car,但用上列程式結果是 Object。
結果如下圖 (這裡 prototype.constructor 不見,是因為程式是直接設定 prototype 所以洗掉了)
用 new  constructor                          用 Object.create or __proto__ or Object.setPrototypeOf









ES 2015 (ES6)


ES2015 新增和支援 class、extends、constructor、super、set、get、static 等保留字,可以像其他語言寫出一個簡單的 class 並且使用 extend 來繼承母類別。最大的好處是類似 c++、java、php 等直覺寫法。不過目前尚未支援像是 public、protected、private 等 access modifiers,所以無法設定 private method。

如以下範例 (這裡是使用 Babel 來編輯)。
class Car {
    constructor(name){
        this.name = name || "car";
    }
    getName() {
        return this.name;
    }
    run () {
        console.log("car is running");
    }
    set driver(name) {
        this.driver = name;
    }
    get driver() {
        return this.driver;
    }
}
class Formula extends Car {
    constructor(name, color){
        super(name || "F1");
        this.color = color;
    }
    getColor() {
        return this.color;
    }
    run () {
        console.log("F1 is running");
    }
}
實驗結果,符合預期。
var car = new Car("i am a car");
console.log(car.getName());  // CAR
car.run();                   // CAR is running
car.driver = "toolman";
console.log(car.driver);     // toolman
console.log(car.getColor()); // Uncaught TypeError: car.getColor is not a function
console.log(car instanceof Car);     //true
console.log(car instanceof Formula); //false

var f1 = new Formula("i am a F1", "red");
console.log(f1.getName());   // F91
f1.run();                    // red F91 is running
console.log(f1.getColor());  // red
f1.driver = "prettygirl";
console.log(f1.driver);      // prettygirl
console.log(f1 instanceof Car);     //true
console.log(f1 instanceof Formula); //true

Function pattern(或稱 Factory constructor patter)


使用 Closure 的技巧直接在 function return 實做好的 object,子類別也是直接呼叫父類別 function 取得的 object 後,擴增完成,再 return。這個方法好處在於能實作出 private variable(name、color) 和 method (_run) 的效果。但缺點會失去繼承的結構,所以無法使用 instanceof 來判斷 instance 是屬於那個 class 產生的,或是那個 class 的子類別。(使用此 pattern 可以不用 new ,因為有沒有用結果都一樣)。
另外其實也可以自己連接 __proto__ 到父的 prototype 讓其有繼承關係。
var Car = function(setname) {
    var that = {};
    var name = setname || "car";
    that.getName = function() {
        return name;
    };
    var _run = function () {
        return name + " is running";
    };
    that.run = function () {
        console.log(_run());
    };
    that.driver = "";
    return that;
};

var Formula = function(name, color) {
    var that = Car(name);
    that.getColor = function() {
        return color;
    };
    that.run = function() {
        console.log(color + " " + that.getName() +" is running");
    };
    return that;
}
實驗結果 (其他結果跟 ES2015 一樣就不重複了,看 instanceof 的結果)
發現不屬於任何 class
var car = Car("i am a car");
console.log(car instanceof Car);     //false
console.log(car instanceof Formula); //false

var f1 = Formula("i am a F1", "red");
console.log(f1 instanceof Car);     //false
console.log(f1 instanceof Formula); //false
左邊是 car 的 instance                                       右邊是 f1 的 instance

可以由圖看出上一層直接是 Object 了,所以無法用 instanceof 判斷是不是從 Car or Formula 繼承的 instance。

不 return,使用 call (or apply) + new 的方法實作 (or all in one)


這個方法跟上述很接近,利用 call (or apply )+ new(需要用 new constructor 上述有寫 new 的行為) 所以直接使用 this 並且不 return 。也可以實作出有 private 效果的 method、variable,所以關鍵在子類別的 Car.call(this, arg1)把 this extend。優點在於能實作出 private 效果,有記住自己是由那個 class 所產生,無法判斷父類別是誰,所以像 design pattern 常用的 abscract class 就比較不適合。
一樣也可以自己連接 __proto__ 到父的 prototype 讓其有繼承關係。
var Car = function(setname) {
    var name = setname || "car";
    this.getName = function() {
        return name;
    };
    var _run = function () {
        return name + " is running";
    };
    this.run = function () {
        console.log(_run());
    };
    this.driver = "";
};

var Formula = function(name, color) {
    Car.call(this, name || "F1");
    this.getColor = function() {
        return color;
    };
    this.run = function() {
        console.log(color + " " + this.getName() +" is running");
    };
}
實驗結果,記得自己是由那個 class 衍生出來的,但無法判斷父類別是什麼。
var car = new Car("i am a car");
console.log(car instanceof Car);     //true
console.log(car instanceof Formula); //false

var f1 = new Formula("i am a F1", "red");
console.log(f1 instanceof Car);     //false
console.log(f1 instanceof Formula); //true
左邊是 car 的 instance                                                        右邊是 f1 的 instance

 

由圖可以看出
car -> Car -> Object
f1 -> Formula -> Object
的關係,符合 instanceof 測試結果。

Pseudo-Classical pattern


過去最標準的作法,使用 prototype 來設定 variable 和 method 並利用 Object.setPrototypeOf() 或 __proto__ 指向父類別來實現繼承。
使用 prototype 好處是在 new 時 prototyp 是參考型別( __proto__ 指向 prototype),不會重複建立 method 和 variable,較 function pattern 有效率和節省 memory。
這個方法優點是有完整的繼承關係,而且跟前述幾種方法 (除 ES2015,用 babel 模擬,非瀏覽器內建實作) 在 instance 結構上也是最省的。缺點要記得 extend 。

function extends(child, parent) {
    Object.setPrototypeOf(child.prototype, parent.prototype);
    // 等同 child.prototype.__proto__ = parent.prototype;
}

var Car = function(name) {
    this.name = name || "car";
};
Car.prototype = {
    constructor : Car,
    name    : "",
    driver  : "",
    getName : function() {
        return this.name;
    },
    run     : function () {
        console.log(this.name + " is running");
    },
};

var Formula = function(name, color) {
    this.name = name;
    this.color = color;
};
Formula.prototype = {
    constructor : Formula,
    getColor : function() {
        return this.color;
    },
    run      : function() {
        console.log(this.color + " " + this.name +" is running");
    }
};
extends(Formula, Car);
實驗結果
var car = new Car("i am a car");
console.log(car instanceof Car);     //true
console.log(car instanceof Formula); //false

var f1 = new Formula("i am a F1", "red");
console.log(f1 instanceof Car);     //true
console.log(f1 instanceof Formula); //true
左邊是 car 的 instance                                                        右邊是 f1 的 instance

由圖可以看出
car -> Car -> Object
f1 -> Formula -> Car -> Object
的關係,符合 instanceof 測試結果。


再來依據前述的一些觀念,可能會有人問為什麼不把 extend 放在子類別人?如下程式。
這樣好處是好讀比較會記得。但缺點是設定 __proto__是 cost 很大的動作(),放進去後,每次 new 都會重設一次,所以一般還是放在外面居多。不過這點我也是抱著懷疑,因為 javascript 在 set object 時,不是都是 call by sharing? reference to the object
function extend(child, parent) {
    Object.setPrototypeOf(child.__proto__, parent.prototype);
    // 等同 child.__proto__.__proto__ = parent.prototype;
}

var Car = function(name) {
    this.name = name || "car";
};
Car.prototype = {
    constructor : Car,
    name    : "",
    driver  : "",
    getName : function() {
        return this.name;
    },
    run     : function () {
        console.log(this.name + " is running");
    },
};

var Formula = function(name, color) {
    extend(this, Car);
    this.name = name;
    this.color = color;

};
Formula.prototype = {
    constructor : Formula,
    getColor : function() {
        return this.color;
    },
    run      : function() {
        console.log(this.color + " " + this.name +" is running");
    }
};

結論

方法 2,3 都可以用 pseudo classical 的 extend 類似的方法去完成繼承關係。這幾個差別就看實際上的要不要 private 應用、怎麼寫法自己喜歡、或是在不在意使不使用 prototype 的效能上的差異。好壞差異不大。未來當然都是使用 ES2015 最好的。


Reference:


http://www.w3schools.com/js/js_object_prototypes.asp
http://stackoverflow.com/questions/650764/how-does-proto-differ-from-constructor-prototype
http://dmitrysoshnikov.com/ecmascript/javascript-the-core/
http://davidshariff.com/blog/javascript-inheritance-patterns/
https://medium.com/@PitaJ/javascript-inheritance-patterns-179d8f6c143c#.qbayiakzb
http://es6.ruanyifeng.com/#docs/class
http://www.codedata.com.tw/javascript/essential-javascript-14-constructor/
http://www.codedata.com.tw/javascript/essential-javascript-15-prototype/
http://www.codedata.com.tw/javascript/essential-javascript-16-introspection/
http://www.codedata.com.tw/javascript/essential-javascript-18-class-based-oo-simulation/
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/new
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/create
https://developer.mozilla.org/zh-TW/docs/Web/JavaScript/Reference/Global_Objects/Object/setPrototypeOf

2015年12月17日 星期四

[Node.js] Electron 發佈成可執行檔 APP

Electron v0.36.0
https://github.com/atom/electron/blob/master/docs/tutorial/application-distribution.md
官網文件,看不懂 (未來應該會修正)

後來查了一下原來要裝 electron-packager
(Ref:https://www.youtube.com/watch?v=dz5SnmBzBXc (舊版 youtube 教學)

安裝 electron-packager

https://www.npmjs.com/package/electron-packager
# for use in npm scripts 
npm install electron-packager --save-dev
 
# for use from cli 
npm install electron-packager -g

修改 package.json

  "scripts": {
    "start": "electron main.js",
    "build_mac": "electron-packager . MyApp --platform=darwin --arch=x64 --version=0.36.0 --icon=myapp.icns --asar=true --ignore=node_modules/electron-* ",
    "build_win32": "electron-packager . MyApp --platform=win32 --arch=x64 --version=0.36.0 --icon=myapp.ico --asar=true --ignore=node_modules/electron-* ",
    "build_linux": "electron-packager . MyApp --platform=linux --arch=x64 --version=0.36.0 --icon=myapp.png --asar=true --ignore=node_modules/electron-* "
  },
electron-packager . MyApp 把目前目錄打包成 MyApp 這個名字的 app

--platform:平台
有那些 platform 可以看這個連結
https://github.com/atom/electron/releases

--arch:ia32, x64, all

--version:這裡指的是 electron 的版本
版本建議跟開發的 electron 的版本相同,此時我是使用 0.36.0

--icon:app 的 icon

--asar:true or false 把 source 封裝,比較不容易被看到原始碼

--ignore:忽略檔案,不要打包,建議把 electron 都忽略掉,因為打包後裡面就有了,可以減少檔案大小

要注意 node_modules 是要在 npm install 後的狀態下打包,不要清除

發佈

# Windows  x64
npm run build_win32

# MAC OS X 
npm run build_mac

# LINUX
npm run build_linux
執行完會在此目錄下產生一個新資料夾
像 win32 會出現 MarkApp-win32-x64 ,然後裡面有執行檔
另外此版本的 0.36.0 electron win32 還是怪怪的,開幾次就掛了,而且關不掉
建議先用 npm start 來執行





2015年12月16日 星期三

[Node.js] 初學 Node.js 注意這三點,會比較好上手

1. 同步和非同步執行順序抽像想像


node.js 是 single thread 所以不會有並行
所以 sync 部份跑完才會開始跑 async 或是在 even driven 才跑 async 有點像下圖



async 部份是看誰先排進 async query 內誰先優先執行
所以像
var async_function = function(val, callback){
    process.nextTick(function(){
        callback(val);
    });
};

async_function(1, function(val) {
    console.log(val);
    async_function(2, function(val) {
        console.log(val);
        async_function(3, function(val) {
            console.log(val);
        });
    });
});

console.log(100);
會列出
100
1
2
3
所以在同一個 async scope 會保證其順序性 1 > 2 > 3

再看一個範例,在 sync 等地方再加一個不同的 async scope
var async_function = function(val, callback){
    process.nextTick(function(){
        callback(val);
    });
};

async_function(1, function(val) {
    console.log(val);
    async_function(2, function(val) {
        console.log(val);
        async_function(3, function(val) {
            console.log(val);
        });
    });
});

console.log(100);

async_function(4, function(val) {
    console.log(val);
    async_function(5, function(val) {
        console.log(val);
        async_function(6, function(val) {
            console.log(val);
        });
    });
});
會列出
100
1
4
2
5
3
6
一樣在同一個 async scope 會保證其順序性
1 > 2 > 3
4 > 5 > 6
但不同 scope 會形成交叉,因為在 sync 的 scope 時 1>4 是這順序排進 queue
而印 1 後,2 被排進 queue 內,4 被執行
4印出後,5排進 queue 內,2被執行,以此類堆

推薦可以看下面這篇,會更清楚為什麼會這樣,雖然主要在講為什麼 async 會比較快,但圖片說明很棒,淺顯易懂
http://blogger.gtwang.org/2014/02/node-js-is-faster-than-java-for-concurrent-data-processing-operations.html

另外這篇在講 node.js 的 process queue
http://fred-zone.blogspot.tw/2012/03/nodejs.html


2. 區分是 sync 的 function 還是 async 的 function


這點 JSDC 2015 Jonathan 也有提到
http://www.jongleberry.com/async-control-flow/assets/player/KeynoteDHTMLPlayer.html#8

大部份只要有 callback 幾乎都是 async 的 function
除了 array iteration methods 操作,像 forEach、map...等
(Ref:https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/prototype#Iteration_methods)

另外很多 function 如果結尾有 sync 就是同步版的 function
像 fs 內就有,例如 fs.open 是 async 的版本 fs.openSync 就是 sync 的版本


3. Control Flow (俗稱 callback hell)


因為 callback 的方式導致閱讀程式變成很難
而且很多整理程式的方法,都不太適用,例如 design pattern

目前常聽到的解決是使用這三個方法來解:aync、promise、async/await

aync:
https://github.com/caolan/async
我個人目前都是用這個
比較常用的像有 waterfall

promise:
這裡指的是 ES6 的 promise
不過我也還沒研究:D
npm 有一些類似的 modules

async/await:
這裡指的是 ES7 的
npm 也有幾個,但都會降低效能
https://github.com/yortus/asyncawait#5-performance
像這個會降 20%左右

不過另一個經驗在大部份的 refactory 書(像易讀程式之美學)都有提到。
儘量把程式 function 化,讓 function 和 method 只做一件事,即使在 callback hell 中,也能讓程式變的比較好讀。




題外話
http://www.jongleberry.com/async-control-flow/assets/player/KeynoteDHTMLPlayer.html#12
有提到儘量使用 thrown new Error("boom") 取代 thrown "boom"
這樣才有 stack trace 能看

Reference:
http://blogger.gtwang.org/2014/02/node-js-is-faster-than-java-for-concurrent-data-processing-operations.html
http://fred-zone.blogspot.tw/2012/03/nodejs.html
http://www.jongleberry.com/async-control-flow/assets/player/KeynoteDHTMLPlayer.html#8
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/prototype#Iteration_methods
https://github.com/caolan/async
https://github.com/yortus/asyncawait#5-performance

[JavaScript] Vue.js 初探一些要注意的地方


此文件有可能因為時間的演進而與現況不合, Vue.js 正在演進中
目前記錄自己在寫時,碰到一些要注意的地方


1. 不支援巢狀 (nested)
這裡指的巢狀是指 Vue instance 內再包含 Vue instance
雖然 Vue 不會報錯,但是行為會怪怪的。
比較好的做法是 children 是使用 Vue.componet 包起來,這點跟 React 比較接近。

2. methods 的 function name 和 data 的 data name 是共用的
例如下列情況,有可能 foo function 呼叫不到,被 foo data 覆蓋了
data: {
    foo:''
}, 
method:{
      foo:function(){}
}

3. 如果 data binding 的資料為 array 需要注意
(Ref:http://cn.vuejs.org/guide/list.html#问题)
  1. 直接用索引操作,像 vm.items[0] = {msg:"hellow"};
    Vue 有對 array 新增 $set() 的method 來操作
    例如:this.items.$set(0, {msg:"hello"});
  2. 直接對 array 長度修改 (0為清空)
    如 vm.items.leangth = 0; 會建議使用 vm.items = []; 方式操作
另外也有對 array 新增 $remove() 的 method
該方法行為如下

var index = this.items.indexOf(item)
if (index !== -1) {
    this.items.splice(index, 1)
}


4. v-html (等同 innerHTML) 預設不會過濾 xss
建議直接用 {{variable}} or v-text (等同 textContent)方式設置


5. Vue.extend() 
帶進的 options 大多都跟 Vue instance 相同 (平常使用的 Vue() )
除了 data 和 el 這兩個 options
例如以下例子,MyComponet1 和 MyComponet2 會共用 data,這應該不是我們平常想做的
var data = { a: 1 }
var MyComponent1 = Vue.extend({
    data: data
})
var MyComponent2 = Vue.extend({
    data: data
})
建議做法如下,這樣 data 內的值在每個 Componet 會獨立
var MyComponent = Vue.extend({
    data: function () {
        return { a: 1 }
    }
})


6. Vue.componet 的 prop
prop 概念基本上跟 React 差不多,在 Vue 預設是單向的
但可以用關鍵字
.sync 強迫雙向,這表示 children componet 如果修改該 prop 的值,parent 也會更新
.once 只綁定一次,之後不管改 parent 或是 children 都不會變化

另外目前還有一點問題,是如果 prop 是 array or object 會變成雙向的


7. 如果想要當成一個完整 Web Componet (Vue.componet),直接由 Parent 像一般 HTML 元件向 children 取值

可以使用 v-ref 來設定 componet 別名
<div id="parent">
  <user-profile v-ref:profile></user-profile>
</div>
在 parent 時,可以如下方式呼叫 children 的 method
this.$refs.profile.method()
實際上如果不用 v-ref,Vue 自動也會在 this.$children 內 Reference 他的子元件
但比較麻煩的是,children 是個 array 不容易直接判斷你要取某個 Componet (需要回圈)

在 children 也有 this.$parent 去呼叫 parent 的 method


8. vue-validator 還在 Alpha 所以會有一些問題
所以目前蠻多要自己實作的,沒有 angular 那麼多已完成,可以用的
我使用 2.0.0-alpha.6 基本功能上上 required valid 的行為都是正常的
不過 Vue.validator 這個因為有 bug 而無法使用
但換到最新版 2.0.0-alpha.8 基本功能上就 valid 就壞了,還沒深入了解
是我寫法錯誤還是 bug ,至少寫法沿用 alpha 6 是會有錯誤發生






2015年12月9日 星期三

[JavaScript] HTML String 也能使用 CSS selector 取得資料


如果有一個 HTML 字串,或是來自其他網站的內容
在純前端 (Client) 的環境
不想使用正規表示法去取得某部份資料

也能利用 createHTMLDocument() 讓 HTML string 變成 DOMDocument 就能使用 querySelector or querySelectorAll 取得資料

利用 var doc = document.implementation.createHTMLDocument("Crawler");
會回傳一個 document object
再來利用 innerHTML 把 html string 插入
剩下就當成平常的 document 使用
程式碼如下





2015年11月7日 星期六

[JavaScript] 使用 css selector 來撰寫爬蟲程式



過去習慣在寫爬蟲程式時都是使用正規表示法來剖析網頁結構取得資料。

最近在寫 Chrome Extension 時 Chrome Extension 可以使用 chrome.tabs.executeScript 在一個 tab 內的內容執行自己寫的 JavaScript,在這個機制可以利用 Css Selector 來做網頁的剖析取得想要的資料,極其方便。

所以一個簡單的想法是,取得網頁資料->執行 JavaScript ->取得想要的資料

就找到 Node.js 有個 Project jsdom 符合此需求

此程式便是使用 jsom 的去 ted 網站取得演講資訊
完整的 Source

var jsdom = require('jsdom');
jsdom.env ( "http://tedxtaipei.com/talks/2013-ping-cheng-yeh/" ,
            ["//code.jquery.com/jquery-1.11.3.min.js"] ,
            function(err, window) {
                var $ = window.jQuery;
                var speech = {
                    title : $('.textover .title').text(),
                    description : $('.content-main .section p').text(),
                    video : $('.player .placeholder').attr('data-embed-code').match(/src="(.*?)"/i)[1],
                    speaker : {
                        name : $('.speaker .name text').text(),
                        anotherName : $('.speaker .another-name text').text(),
                        jobTitle : $('.speaker .job-title').text(),
                        image : $('.speaker .cover .image').attr('style').match(/url\((.*)\)/i)[1],
                        description : $('.speaker .description .content p').text()
                    }
               };
               console.log(speech);
}); 


題外話
php 可以用 DOMDocument

Reference:
jsdom

2015年10月22日 星期四

[JavaScript] 在 for loop 中,使用 var 宣告 object 並在 async function 呼叫該物件時的 scope 問題

之前在寫 javascript 有碰到在 for loop 中,使用 var 宣告 object 並在 async function (例如 event)呼叫該物件時的 scope 問題。最近有空把它整理出來。

以下面這個範例為例,正常預期是依據按鈕是 hello 1、hello 2....點支下去 alert 的分別是 hello 1、hello 2....等,跟按鈕顯二的文字一樣。

在下面的例子中,在 onclick event 中, alert 的都是 hello 5
但在 for loop 中時,console.log(say.hello()); 是符合預期的。
這應該 var scope 範圍,和 var say 物件和 var i 因為 call by reference 的關係,reference 到最後一個實體
(因使用 alert 的關係,要點擊右上角 edit in JSFiddle 才能正確執行)



最簡單的解法是使用 let 來宣告,如下例 (只有 chrome 和 edge 可以用)
只改 var say => let say 來說明

但這個方法最主要問題是目前還有許多瀏覽器未支援,並不適合用在客戶端的產品上
Ref: http://caniuse.com/#search=let



另一個想到解法是使用 array 的 forEach 讓其產生獨立的 scope 空間
沒 forEach 之類的怎麼辦!?只好硬幹建一個 array 塞進入 XD
如下



那還有沒有其他解法?

JavaScript.tw FB社群:施文祥、Yuren Ju 
提供的 babel,compiler 出來的方法
把 loop 內改成 function ,在 loop 中呼叫 function



JavaScript.tw FB社群:Ly Cheng 
提供使用匿名函數 + bind 方式來解



同理可證
直接整個用匿名函數也能解,但效能可能就不怎麼樣

2012年12月22日 星期六

[JavaScript] Variable dump

一個簡單的方法是
console.log(object);

另一個是寫個 dump 函數
function dump(obj) {
         var out = '';
         for (var i in obj) {
                 if (typeof obj[i] === "object")
                         out += i + ": {\n" + dump(obj[i]) + "}\n";
                 else
                         out += i + ": " + obj[i] + "\n";
         }
         return out;
}


Example:
<script>
        var book = {
                name: 'Javascript tutorial',
                price: 50,
                index: {
                        1: 'introduction',
                        2: 'hello world'
                },
                toString: function () { return "name: " + this.name + ",price: " + this.price; }
        }
        function dump(obj) {
                var out = '';
                for (var i in obj) {
                        if (typeof obj[i] === "object")
                                out += i + ": {\n" + dump(obj[i]) + "}\n";
                        else
                                out += i + ": " + obj[i] + "\n";
                }
                return out;
        }
        console.log(book);
        console.log(dump(book));
</script>