2018年5月28日 星期一

惱人的 javascript context 和 this

寫 Javascript 的程式,被它的 this 搞得實在惱火,寫個小程式來測一下

<!DOCTYPE html>
<html>
<head>
    <meta charset="UTF-8">
</head>
<body>
測試 javascript this 所指的 context
</body>
<script type="text/javascript">
    var tmodel = (function ()
    {
        var myself;

        var set_myself = function() {
            myself = this;
        };

        var self;
        var v1 = 'tmodel v1';

        var tst = {
            self: this,
            fun1: function () {
                console.log('tst.fun1()');
                console.log(this);
                console.log(this.self);
                if (typeof v1 !== "undefined") {
                    console.log(v1);
                } else {
                    console.log('v1 is undefined!');
                }
            }
        };

        var fun2 = function () {
            console.log('tmodel.fun2()');
            console.log(this);
            console.log(myself);
            console.log(tst.self);
            if (typeof v1 !== "undefined") {
                console.log(v1);
            } else {
                console.log('v1 is undefined!');
            }

            tst.fun1();
        };

        var fun3 = function () {
            self = this;
            var v1 = 'fun3 var';
            console.log('tmodel.fun3()');
            console.log(this);
            console.log(tst.self);
            if (typeof v1 !== "undefined") {
                console.log(v1);
            } else {
                console.log('v1 is undefined!');
            }
            fun2();
        };

        /*
         * fun1.this => tst
         * tst.this => Window,這個不會改變
         * tmodel.v1
         */
        tst.fun1();

        return {
            set_myself: set_myself,
            fun2: fun2,
            fun3: fun3
        }
    })();

    tmodel.set_myself();

    /*
     * fun2.this => tmodel
     * 在執行 tst.fun1 中
     * fun1.this => tst
     */
    tmodel.fun2();

    /*
     * fun3.this => tmodel
     * fun3.v1 => local variable
     * 在執行 fun2 中
     * fun2.this => Window
     * 可直接存取 tmodel.v1
     * 無法存取 fun3.v1
     * 在執行 tst.fun1 中
     * fun1.this => tst
     * 可直接存取 tmodel.v1
     * 無法存取 fun3.v1
     */
    tmodel.fun3();

    console.log('tmodel');
    console.log(tmodel);

</script>
</html>

js_this_tst.php:24 tst.fun1()
js_this_tst.php:25 {self: Window, fun1: ƒ}
js_this_tst.php:26 Window {postMessage: ƒ, blur: ƒ, focus: ƒ, close: ƒ, frames: Window, …}
js_this_tst.php:28 tmodel v1
js_this_tst.php:36 tmodel.fun2()
js_this_tst.php:37 Window {postMessage: ƒ, blur: ƒ, focus: ƒ, close: ƒ, frames: Window, …}
js_this_tst.php:38 undefined
js_this_tst.php:39 Window {postMessage: ƒ, blur: ƒ, focus: ƒ, close: ƒ, frames: Window, …}
js_this_tst.php:41 tmodel v1
js_this_tst.php:24 tst.fun1()
js_this_tst.php:25 {self: Window, fun1: ƒ}
js_this_tst.php:26 Window {postMessage: ƒ, blur: ƒ, focus: ƒ, close: ƒ, frames: Window, …}
js_this_tst.php:28 tmodel v1
js_this_tst.php:52 tmodel.fun3()
js_this_tst.php:53 Window {postMessage: ƒ, blur: ƒ, focus: ƒ, close: ƒ, frames: Window, …}
js_this_tst.php:54 Window {postMessage: ƒ, blur: ƒ, focus: ƒ, close: ƒ, frames: Window, …}
js_this_tst.php:56 fun3 var
js_this_tst.php:36 tmodel.fun2()
js_this_tst.php:37 Window {postMessage: ƒ, blur: ƒ, focus: ƒ, close: ƒ, frames: Window, …}
js_this_tst.php:38 undefined
js_this_tst.php:39 Window {postMessage: ƒ, blur: ƒ, focus: ƒ, close: ƒ, frames: Window, …}
js_this_tst.php:41 tmodel v1
js_this_tst.php:24 tst.fun1()
js_this_tst.php:25 {self: Window, fun1: ƒ}
js_this_tst.php:26 Window {postMessage: ƒ, blur: ƒ, focus: ƒ, close: ƒ, frames: Window, …}
js_this_tst.php:28 tmodel v1
js_this_tst.php:74 內部執行 BEGIN 
js_this_tst.php:75 Window {postMessage: ƒ, blur: ƒ, focus: ƒ, close: ƒ, frames: Window, …}
js_this_tst.php:36 tmodel.fun2()
js_this_tst.php:37 Window {postMessage: ƒ, blur: ƒ, focus: ƒ, close: ƒ, frames: Window, …}
js_this_tst.php:38 undefined
js_this_tst.php:39 Window {postMessage: ƒ, blur: ƒ, focus: ƒ, close: ƒ, frames: Window, …}
js_this_tst.php:41 tmodel v1
js_this_tst.php:24 tst.fun1()
js_this_tst.php:25 {self: Window, fun1: ƒ}
js_this_tst.php:26 Window {postMessage: ƒ, blur: ƒ, focus: ƒ, close: ƒ, frames: Window, …}
js_this_tst.php:28 tmodel v1
js_this_tst.php:52 tmodel.fun3()
js_this_tst.php:53 Window {postMessage: ƒ, blur: ƒ, focus: ƒ, close: ƒ, frames: Window, …}
js_this_tst.php:54 Window {postMessage: ƒ, blur: ƒ, focus: ƒ, close: ƒ, frames: Window, …}
js_this_tst.php:56 fun3 var
js_this_tst.php:36 tmodel.fun2()
js_this_tst.php:37 Window {postMessage: ƒ, blur: ƒ, focus: ƒ, close: ƒ, frames: Window, …}
js_this_tst.php:38 undefined
js_this_tst.php:39 Window {postMessage: ƒ, blur: ƒ, focus: ƒ, close: ƒ, frames: Window, …}
js_this_tst.php:41 tmodel v1
js_this_tst.php:24 tst.fun1()
js_this_tst.php:25 {self: Window, fun1: ƒ}
js_this_tst.php:26 Window {postMessage: ƒ, blur: ƒ, focus: ƒ, close: ƒ, frames: Window, …}
js_this_tst.php:28 tmodel v1
js_this_tst.php:78 -- 內部執行 END --
js_this_tst.php:36 tmodel.fun2()
js_this_tst.php:37 {set_myself: ƒ, fun2: ƒ, fun3: ƒ}
js_this_tst.php:38 {set_myself: ƒ, fun2: ƒ, fun3: ƒ}
js_this_tst.php:39 Window {postMessage: ƒ, blur: ƒ, focus: ƒ, close: ƒ, frames: Window, …}
js_this_tst.php:41 tmodel v1
js_this_tst.php:24 tst.fun1()
js_this_tst.php:25 {self: Window, fun1: ƒ}
js_this_tst.php:26 Window {postMessage: ƒ, blur: ƒ, focus: ƒ, close: ƒ, frames: Window, …}
js_this_tst.php:28 tmodel v1
js_this_tst.php:52 tmodel.fun3()
js_this_tst.php:53 {set_myself: ƒ, fun2: ƒ, fun3: ƒ}
js_this_tst.php:54 Window {postMessage: ƒ, blur: ƒ, focus: ƒ, close: ƒ, frames: Window, …}
js_this_tst.php:56 fun3 var
js_this_tst.php:36 tmodel.fun2()
js_this_tst.php:37 Window {postMessage: ƒ, blur: ƒ, focus: ƒ, close: ƒ, frames: Window, …}
js_this_tst.php:38 {set_myself: ƒ, fun2: ƒ, fun3: ƒ}
js_this_tst.php:39 Window {postMessage: ƒ, blur: ƒ, focus: ƒ, close: ƒ, frames: Window, …}
js_this_tst.php:41 tmodel v1
js_this_tst.php:24 tst.fun1()
js_this_tst.php:25 {self: Window, fun1: ƒ}
js_this_tst.php:26 Window {postMessage: ƒ, blur: ƒ, focus: ƒ, close: ƒ, frames: Window, …}
js_this_tst.php:28 tmodel v1
js_this_tst.php:111 tmodel
js_this_tst.php:112 {set_myself: ƒ, fun2: ƒ, fun3: ƒ}

最需要特別注意的是,在 fun3 中呼叫 fun2,fun2 的 this 指到 Window。

為了在 tmodel 中,能夠存取自身的 context,用一個 myself 的變數來存放自身的 context。但是因為在被呼叫之前,此 object 尚未建立,因此無裡面指定 myself,只能在它的外面執行。可能有更好的解決辦法,但我目前只能找到此方法。

這是為了解決 KnockoutJS 的 computed 變數的問題。



2018年5月19日 星期六

自訂 PHPstorm 的 BrowserRefresh 功能

If You are developing a PHP web site using PhpStorm in this approach currently, i.e. save the changes, and then switch to the browser and refresh the page, here is a quicker way for you. You can press a key-combination, Ctrl+Shift+R, for example, to save the change and refresh the browser.

雖然 PHPstorm 有個 LiveEdit,好像很強大,但是不會用。就參考先前用過的 Sublime 的 BrowserRefresh 功能,想辦法把它搬到 PHPstorm 來用。

我的作法,主要是參考 Sublime Text 的 BrowserRefresh 功能,在 Linux 下,使用 xdotool 來送出瀏覽器的 refresh 指令。在 Sublime Text 下,要有程式底子,才能看懂它在做什麼。但是在 PHPstorm,就簡單多了,只要建好 script 檔,確定功能正常,然後點一點滑鼠就 OK 了。

先建立可透過 xdotool 送出 refresh 指令的 script,並測試確定功能正常。

在 [Settings] > [Tools] > [External Tools] 下,新增 BrowserReferesh,並且,在 Program 欄位,填上 script 的 full path。

然後,在  [Settings] > [External Tools] > [External Tools] 下,指定快捷鍵,例如,我是指定 Ctrl+Shift+R。雖然,這個已被使用,但幾乎我要用的組合,都被用了,也就不管,用自己習慣的吧。



BrowserReferesh 的程式碼
(chromium-refresh)
#!/bin/bash
xdotool search --sync --onlyvisible --class 'chromium' windowfocus key 'F5'  windowactivate


(firefox-refresh)
#!/bin/bash
xdotool search --sync --onlyvisible --class 'firefox' windowfocus key 'F5'  windowactivatet 

對於 Chrome,有些困擾,會更新到 LINE 的擴充功能,就 chromium 了。因為 Chrome 和 Chromium 才有 Knockout JS 的 extension 可用。而 Firefox 改版之後,一些舊的 extension 都不能用了,另外,在我的 Linux 下,可能是設定的關係,版型變得怪怪的,要改網頁都抓不準,也是促成改用 chromium 的原因。

嗯,自從改用 PHPstorm 寫 PHP 程式,再也回不去了,現在根本就不想用 Sublime Text 了。

網誌存檔