首頁  >  文章  >  web前端  >  教你寫更穩定、可讀性強的JavaScript程式碼的範例

教你寫更穩定、可讀性強的JavaScript程式碼的範例

黄舟
黄舟原創
2017-03-15 14:59:021237瀏覽

每個人都有自己的程式風格,也無可避免的要去感受別人的程式風格-修改別人的程式碼。 「修改別人的程式碼」對於我們來說的一件很痛苦的事情。因為有些程式碼並不是那麼容易閱讀、可維護的,讓另一個人來修改別人的程式碼,或許最後只會修改一個變數,調整一個函數的呼叫時機,卻需要花上1小時甚至更多的時間來閱讀、縷清別人的程式碼。本文一步步帶你重構一段獲取位置的」元件」——提升你的javascript程式碼的可讀性和穩定性。

本文內容如下:

  • 分離你的javascript程式碼

  • 函數不應該過度依賴外部環境

  • 語意化和重複使用

  • #元件應該專注於邏輯,行為只是封裝

  • 形成自己的風格的程式碼

#分離你的javascript程式碼

下面一段程式碼示範了難以閱讀/修改的程式碼:

(function (window, namespace) {
    var $ = window.jQuery;
    window[namespace] = function (targetId, textId) {
        //一个尝试复用的获取位置的"组件"
        var $target = $('#' + targetId),//按钮
            $text = $('#' + textId);//显示文本
        $target.on('click', function () {
            $text.html('获取中');
            var data = '北京市';//balabala很多逻辑,伪代码,获取得到位置中
            if (data) {
                $text.html(data);
            } else
                $text.html('获取失败');
        });
    }
})(window, 'linkFly');

這一段程式碼,我們暫且認可它已經構成一個」元件」。
上面的程式碼就是典型的一個方法搞定所有事情,一旦填充上內部的邏輯就會變得生活不能自理,而一旦增加需求,例如獲取位置返回的數據格式需要加工,那麼就要去裡面尋找處理資料的程式碼然後修改。

我們分離一下邏輯,得到程式碼如下:

(function (window, namespace) {
    var $ = window.jQuery,
        $target,
        $text,
        states= ['获取中', '获取失败'];
    function done(address) {//获取位置成功
        $text.html(address);
    }
    function fail() {
        $text.html(states[1]);
    }
    function checkData(data) {
        //检查位置信息是否正确
        return !!data;
    }
    function loadPosition() {
        var data = '北京市';//获取位置中
        if (checkData(data)) {
            done(data);
        } else
            fail();
    }
    var init = function () {
        $target.on('click', function () {
            $text.html(states[0]);
            loadPosition();
        });
    };
    window[namespace] = function (targetId, textId) {
        $target = $('#' + targetId);
        $text = $('#' + textId);
        initData();
        setData();
    }
})(window, 'linkFly');

函數不應該過度依賴外部環境

上面的程式碼中,我們已經把整個元件,切割成了各種函數(注意這裡我說的是函數,不是方法),這裡常出現一個新的問題:函數過度依賴不可控的變數。

變數$target和$text身為環境中的全域變量,從元件初始化便賦值,而我們切割後的程式碼大多數的操作方法都依賴$text,尤其是$text和done() 、fail()之間曖昧的關係,一旦$text相關的結構、邏輯改變,那麼我們的程式碼將會進行不小的改變。

和頁面/DOM相關的都是不可信賴的(例如$target和$text),一旦頁面結構改變,它的行為很大程度上也會隨之改變。而函數也不應該依賴外在的環境。
在不可控的變數上,我們應該解開函數和依賴變數上的關係,讓函數變得更專注自己區域的邏輯,更加的純粹。簡單的說:函數所依賴的外部變量,都應該透過參數傳遞到函數內部。
新的程式碼如下:

(function (window, namespace) {
    var $ = window.jQuery;
    //检查位置信息是否正确
    function checkData(data) {
        return !!data;
    }
    //获取位置中
    function loadPosition(done, fail) {
        var data = '北京市';//获取位置中
        if (checkData(data)) {
            done(data);
        } else
            fail();
    }
    window[namespace] = function (targetId, textId) {
       var  $target = $('#' + targetId),
            $text = $('#' + textId);
        var states = ['获取中', '获取失败'];
        $target.on('click', function () {
            $text.html(states[0]);
            loadPosition(function (address) {//获取位置成功
                $text.html(address);
            }, function () {//获取位置失败
                $text.html(states[1]);
            });
        });
    }
})(window, 'linkFly');

語意化與重複使用

變數states是一個陣列,它所描述的行為難以閱讀,每次看到states [0]都有一種分分鐘想捏死原作者的衝動,因為我們總是要記住變數states的值,在程式碼上,我們應該盡可能讓它可以很好的被閱讀。

另外,上面的程式碼中$text.html就是典型的程式碼重複,我們再一次的修改程式碼,請注意這次修改的程式碼中,我們所抽離的changeStateText()的程式碼位置,它並沒有被提升到上一層環境(也就是整個大閉包的環境)。

(function (window, namespace) {
    var $ = window.jQuery;
    function checkData(data) {
        return !!data;
    }
    function loadPosition(done, fail) {
        var data = '北京市';//获取位置中
        if (checkData(data)) {
            done(data);
        } else
            fail();
    }
    window[namespace] = function (targetId, textId) {
        var $target = $('#' + targetId),
            $text = $('#' + textId),
            changeEnum = { LOADING: '获取中', FAIL: '获取失败' },
            changeStateText = function (text) {
                $text.html(text);
            };
        $target.on('click', function () {
            changeStateText(changeEnum.LOADING);
            loadPosition(function (address) {
                changeStateText(address);
            }, function () {
                changeStateText(changeEnum.FAIL);
            });
        });
    }
})(window, 'linkFly');

提及語意化,我們必須要知道目前整個程式碼的邏輯和語意:

在這整個元件中,所有的函數模組可以分為:工具和工具提供者。

上一層環境(整個大閉包)在我們的業務中扮演著工具的身份,它的任務是締造一套和獲取位置邏輯相關的工具,而在window[namespace])函數中,則是工具提供者的身份,它是唯一的入口,負責提供組件完整的業務給工具的使用者。
這裡的$text.html()在邏輯上並不屬於工具,而是屬於工具提供者使用工具後所得到的回饋,所以changeStateText()函數置於工具提供者window[namespace]()中。

组件应该关注逻辑,行为只是封装

到此为止,我们分离了函数,并让这个组件拥有了良好的语义。但这时候来了新的需求:当没有获取到位置的时候,需要进行一些其他的操作。这时候会发现,我们需要window[namespace]()上加上新的参数。
当我们加上新的参数之后,又被告知新的需求:当获取位置失败了之后,需要修改一些信息,然后再次尝试获取位置信息。
不过幸好,我们的代码已经把大部分的逻辑抽离到了工具提供者中了,对整个工具的逻辑影响并不大。
同时我们再看看代码就会发现我们的组件除了工具提供者之外,没有方法(依赖在对象上的函数)。也就是说,我们的组件并没有对象。

我见过很多人的代码总是喜欢打造工具提供者,而忽略了工具的本质。迎合上面的增加的需求,那么我们的工具提供者将会变得越来越重,这时候我们应该思考到:是不是应该把工具提供出去?

让我们回到最初的需求——仅仅只是一个获取位置的组件,没错,它的核心业务就是获取位置——它不应该被组件化。它的本质应该是个工具对象,而不应该和页面相关,我们从一开始就不应该关注页面上的变化,让我们重构代码如下:

(function (window, namespace) {
    var Gps = {
        load: function (fone, fail) {
            var data = '北京市';//获取位置伪代码
            this.check(data) ?
                done(data, Gps.state.OK) :
                fail(Gps.state.FAIL);
        },
        check: function (data) {
            return !!data;
        },
        state: { OK: 1, FAIL: 0 }
    };
    window[namespace] = Gps;
})(window, 'Gps');

在这里,我们直接捏死了工具提供者,我们直接将工具提供给外面的工具使用者,让工具使用者直接使用我们的工具,这里的代码无关状态、无关页面。

至此,重构完成。

形成自己风格的代码

之所以讲这个是因为大家都有自己的编程风格。有些人的编程风格就是开篇那种代码的…
我觉得形成自己的编程风格,是建立在良好代码的和结构/语义上的。否则只会让你的代码变得越来越难读,越来越难写。
****
单var和多var
我个人是喜欢单var风格的,不过我觉得代码还是尽可能在使用某一方法/函数使用前进行var,有时候甚至于为了单var而变得丧心病狂:由于我又过分的喜爱函数表达式声明,函数表达式声明并不会在var语句中执行,于是偶尔会出现这种边声明边执行的代码,为了不教坏小朋友就不贴代码了(我不会告诉你们其实是我找不到了)。

对象属性的屏蔽
下面的代码演示了两种对象的构建,后一种通过闭包把内部属性隐藏,同样,两种方法都实现了无new化,我个人…是不喜欢看见很多this的..但还是推荐前者。

(function () {
    //第一种,曝露了_name属性
    var Demo = function () {
        if (!(this instanceof Demo))
            return new Demo();
        this._name = 'linkFly';
    };
    Demo.prototype.getName = function () {
        return this._name;
    }

    //第二种,多一层闭包意味内存消耗更大,但是屏蔽了_name属性
    var Demo = function () {
        var name = 'linkFly';
        return {
            getName: function () {
                return name;
            }
        }
    }
});

巧用变量置顶[hoisting]
巧用函数声明的变量置顶特性意味着处女座心态的你放弃单var,但却可以让你的函数在代码结构上十分清晰,例如下面的代码:

(function () {
    var names = [];
    return function (name) {
        addName(name);
    }
    function addName(name) {
        if (!~names.indexOf(name))//如果存在则不添加
            names.push(name);
        console.log(names);// ["linkFly"]
    }
}())('linkFly');

if和&&
这种代码,在几个群里都见过讨论:

(function () {
    var key = 'linkFly',
        cache = { 'linkFly': 'http://www.cnblogs.com/silin6/' },
        value;
    //&&到底
    key && cache && cache[key] && (value = cache[key]);
    //来个if
    if (key && cache && cache[key])
        value = cache[key];
})();

以上是教你寫更穩定、可讀性強的JavaScript程式碼的範例的詳細內容。更多資訊請關注PHP中文網其他相關文章!

陳述:
本文內容由網友自願投稿,版權歸原作者所有。本站不承擔相應的法律責任。如發現涉嫌抄襲或侵權的內容,請聯絡admin@php.cn