搜索
首页web前端js教程教你编写更加稳定、可读性强的JavaScript代码的示例

每个人都有自己的编程风格,也无可避免的要去感受别人的编程风格——修改别人的代码。”修改别人的代码”对于我们来说的一件很痛苦的事情。因为有些代码并不是那么容易阅读、可维护的,让另一个人来修改别人的代码,或许最终只会修改一个变量,调整一个函数的调用时机,却需要花上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
Python vs. JavaScript:您应该学到哪种语言?Python vs. JavaScript:您应该学到哪种语言?May 03, 2025 am 12:10 AM

选择Python还是JavaScript应基于职业发展、学习曲线和生态系统:1)职业发展:Python适合数据科学和后端开发,JavaScript适合前端和全栈开发。2)学习曲线:Python语法简洁,适合初学者;JavaScript语法灵活。3)生态系统:Python有丰富的科学计算库,JavaScript有强大的前端框架。

JavaScript框架:为现代网络开发提供动力JavaScript框架:为现代网络开发提供动力May 02, 2025 am 12:04 AM

JavaScript框架的强大之处在于简化开发、提升用户体验和应用性能。选择框架时应考虑:1.项目规模和复杂度,2.团队经验,3.生态系统和社区支持。

JavaScript,C和浏览器之间的关系JavaScript,C和浏览器之间的关系May 01, 2025 am 12:06 AM

引言我知道你可能会觉得奇怪,JavaScript、C 和浏览器之间到底有什么关系?它们之间看似毫无关联,但实际上,它们在现代网络开发中扮演着非常重要的角色。今天我们就来深入探讨一下这三者之间的紧密联系。通过这篇文章,你将了解到JavaScript如何在浏览器中运行,C 在浏览器引擎中的作用,以及它们如何共同推动网页的渲染和交互。JavaScript与浏览器的关系我们都知道,JavaScript是前端开发的核心语言,它直接在浏览器中运行,让网页变得生动有趣。你是否曾经想过,为什么JavaScr

node.js流带打字稿node.js流带打字稿Apr 30, 2025 am 08:22 AM

Node.js擅长于高效I/O,这在很大程度上要归功于流。 流媒体汇总处理数据,避免内存过载 - 大型文件,网络任务和实时应用程序的理想。将流与打字稿的类型安全结合起来创建POWE

Python vs. JavaScript:性能和效率注意事项Python vs. JavaScript:性能和效率注意事项Apr 30, 2025 am 12:08 AM

Python和JavaScript在性能和效率方面的差异主要体现在:1)Python作为解释型语言,运行速度较慢,但开发效率高,适合快速原型开发;2)JavaScript在浏览器中受限于单线程,但在Node.js中可利用多线程和异步I/O提升性能,两者在实际项目中各有优势。

JavaScript的起源:探索其实施语言JavaScript的起源:探索其实施语言Apr 29, 2025 am 12:51 AM

JavaScript起源于1995年,由布兰登·艾克创造,实现语言为C语言。1.C语言为JavaScript提供了高性能和系统级编程能力。2.JavaScript的内存管理和性能优化依赖于C语言。3.C语言的跨平台特性帮助JavaScript在不同操作系统上高效运行。

幕后:什么语言能力JavaScript?幕后:什么语言能力JavaScript?Apr 28, 2025 am 12:01 AM

JavaScript在浏览器和Node.js环境中运行,依赖JavaScript引擎解析和执行代码。1)解析阶段生成抽象语法树(AST);2)编译阶段将AST转换为字节码或机器码;3)执行阶段执行编译后的代码。

Python和JavaScript的未来:趋势和预测Python和JavaScript的未来:趋势和预测Apr 27, 2025 am 12:21 AM

Python和JavaScript的未来趋势包括:1.Python将巩固在科学计算和AI领域的地位,2.JavaScript将推动Web技术发展,3.跨平台开发将成为热门,4.性能优化将是重点。两者都将继续在各自领域扩展应用场景,并在性能上有更多突破。

See all articles

热AI工具

Undresser.AI Undress

Undresser.AI Undress

人工智能驱动的应用程序,用于创建逼真的裸体照片

AI Clothes Remover

AI Clothes Remover

用于从照片中去除衣服的在线人工智能工具。

Undress AI Tool

Undress AI Tool

免费脱衣服图片

Clothoff.io

Clothoff.io

AI脱衣机

Video Face Swap

Video Face Swap

使用我们完全免费的人工智能换脸工具轻松在任何视频中换脸!

热工具

螳螂BT

螳螂BT

Mantis是一个易于部署的基于Web的缺陷跟踪工具,用于帮助产品缺陷跟踪。它需要PHP、MySQL和一个Web服务器。请查看我们的演示和托管服务。

WebStorm Mac版

WebStorm Mac版

好用的JavaScript开发工具

SecLists

SecLists

SecLists是最终安全测试人员的伙伴。它是一个包含各种类型列表的集合,这些列表在安全评估过程中经常使用,都在一个地方。SecLists通过方便地提供安全测试人员可能需要的所有列表,帮助提高安全测试的效率和生产力。列表类型包括用户名、密码、URL、模糊测试有效载荷、敏感数据模式、Web shell等等。测试人员只需将此存储库拉到新的测试机上,他就可以访问到所需的每种类型的列表。

适用于 Eclipse 的 SAP NetWeaver 服务器适配器

适用于 Eclipse 的 SAP NetWeaver 服务器适配器

将Eclipse与SAP NetWeaver应用服务器集成。

Dreamweaver Mac版

Dreamweaver Mac版

视觉化网页开发工具