关闭 x
IT技术网
    技 采 号
    ITJS.cn - 技术改变世界
    • 实用工具
    • 菜鸟教程
    IT采购网 中国存储网 科技号 CIO智库

    IT技术网

    IT采购网
    • 首页
    • 行业资讯
    • 系统运维
      • 操作系统
        • Windows
        • Linux
        • Mac OS
      • 数据库
        • MySQL
        • Oracle
        • SQL Server
      • 网站建设
    • 人工智能
    • 半导体芯片
    • 笔记本电脑
    • 智能手机
    • 智能汽车
    • 编程语言
    IT技术网 - ITJS.CN
    首页 » JavaScript »编写更加稳定、可读性强的JavaScript代码

    编写更加稳定、可读性强的JavaScript代码

    2015-02-09 00:00:00 出处:Concurrency 的博客
    分享

    每个人都有自己的编程风格,也无可避免的要去感受别人的编程风格——修改别人的代码。”修改别人的代码”对于我们来说的一件很痛苦的事情。因为有些代码并不是那么容易阅读、可维护的,让另一个人来修改别人的代码,或许最终只会修改一个变量,调整一个函数的调用时机,却需要花上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];
    })();

    大概就想到这么些了,我突然发现我不太推荐的代码,都是我写的代码,囧。假如各位也还有更多有趣的代码,希望各位看官能掏出来让小弟见识见识。

    上一篇返回首页 下一篇

    声明: 此文观点不代表本站立场;转载务必保留本文链接;版权疑问请联系我们。

    别人在看

    hiberfil.sys文件可以删除吗?了解该文件并手把手教你删除C盘的hiberfil.sys文件

    Window 10和 Windows 11哪个好?答案是:看你自己的需求

    盗版软件成公司里的“隐形炸弹”?老板们的“法务噩梦” 有救了!

    帝国CMS7.5编辑器上传图片取消宽高的三种方法

    帝国cms如何自动生成缩略图的实现方法

    Windows 12即将到来,将彻底改变人机交互

    帝国CMS 7.5忘记登陆账号密码怎么办?可以phpmyadmin中重置管理员密码

    帝国CMS 7.5 后台编辑器换行,修改回车键br换行为p标签

    Windows 11 版本与 Windows 10比较,新功能一览

    Windows 11激活产品密钥收集及专业版激活方法

    IT头条

    智能手机市场风云:iPhone领跑销量榜,华为缺席引争议

    15:43

    大数据算法和“老师傅”经验叠加 智慧化收储粮食尽显“科技范”

    15:17

    严重缩水!NVIDIA将推中国特供RTX 5090 DD:只剩24GB显存

    00:17

    无线路由大厂 TP-Link突然大裁员:补偿N+3

    02:39

    Meta 千万美金招募AI高级人才

    00:22

    技术热点

    微软已修复windows 7/windows 8.1媒体中心严重漏洞 用户可下载安

    卸载MySQL数据库,用rpm如何实现

    windows 7中使用网上银行或支付宝支付时总是打不开支付页面

    一致性哈希算法原理设计

    MySQL数字类型中的三种常用种类

    如何解决SQL Server中传入select语句in范围参数

      友情链接:
    • IT采购网
    • 科技号
    • 中国存储网
    • 存储网
    • 半导体联盟
    • 医疗软件网
    • 软件中国
    • ITbrand
    • 采购中国
    • CIO智库
    • 考研题库
    • 法务网
    • AI工具网
    • 电子芯片网
    • 安全库
    • 隐私保护
    • 版权申明
    • 联系我们
    IT技术网 版权所有 © 2020-2025,京ICP备14047533号-20,Power by OK设计网

    在上方输入关键词后,回车键 开始搜索。Esc键 取消该搜索窗口。