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 變數的問題。



沒有留言:

張貼留言

網誌存檔