Javascript基础知识篇(6): 高质量开发准则(上)

  很多从事web开发的项目都离不开Javascript,我个人也在目前的公司中用到了很多,但是感觉还不是很到位,尤其是对Javascript的开发规范上。可能有些朋友要说,我实现一个简单的的功能(如两个数相加),需要遵守什么规范吗?我回答:需要。还有的会问:我的项目进度非常紧张,我有必要按照你说的规范一步一步来吗?我还是回答:需要。为什么呢?我认为与其在项目开发完毕的维护过程中,反复的调试还不如提前遵守开发规范,养成良好的编码习惯。结合我个人一直以来的职业习惯,我总结了以下几点,作为抛砖引玉。保持一致的编码风格具体做法:

(1).函数都必须拥有函数名(匿名函数除外),并以tab作为函数内容的缩进,函数体应包含在{}中(最好前{在函数名后)。

function sayHello() {//注意大括号的位置
    alert("Hello, Miracle");
    return true;
}

(2).函数体即使只有一句话(switch除外),也建议用{}封闭起来。这样有利于以后需求变更和维护,以防止新手忘记添加{}。

if(x == y) {
    alert("We have a match!");
} else {
    alert("We have a problem!");
}
switch (prompt("What is your favorite fruit?", "banana")) {
    case "banana":
        alert("Well done. Bananas are full of potassium.");
        break;
    case "mango":
        alert("Exotic and tasty!");
        break;
    default:
        alert("That's not to my taste, but keep it up to get your 5 days.");
        break;
}

(3).注重大小写。一般准则如下:

a.大多数的变量和函数均以小写开头,采用驼峰法进行书写。

b.构造函数和类(实质是函数)均以大写开头。

c.所有的常量都是由大写组成并以"_"相连。

// Calendar构造函数
var Calendar = function() {
    // 大写标识常量
    var DAYS_IN_ONE_WEEK = 7;
    // 小写代表函数及变量
    var count;
    function add() {
    }
    // 变量及函数遵守驼峰法书写
    var currentDate;
    function updateCurrentDate() {
    }
}

(4).使用有描述语义的变量,避免使用"魔幻数"(即变量直接由一些常量数字计算而成)。

// currentDate代表"当前日期",userName代表"用户名"
var currentDate;
function setCurrentDate() {
}
var userName;
function updateUserName() {
}
// daysInAYear和seatsOnThePlane均为"魔幻数"
var daysInAYear = 52 * 7; 
var seatsOnThePlane = 25 * 6; 
//改进方案
var WEEKS_IN_A_YEAR = 52;
var DAYS_IN_A_WEEK = 7;
var daysInAYear = WEEKS_IN_A_YEAR * DAYS_IN_A_WEEK; 
var SEAT_ROWS_ON_PLANE = 25;
var SEATS_PER_ROW = 6;
var seatsOnThePlane = SEAT_ROWS_ON_PLANE * SEATS_PER_ROW;

(5).函数功能尽量单一,不要融入太多"复杂"逻辑,尽量按职责拆分成对应的子函数。

// 反馈用户对水果的爱好程度
function feedbackOnUsersFavoriteFruit() {
    // 用户喜欢的水果
    var favoriteFruit = prompt("What is your favorite fruit?", "None");
    var score = 0;
    // 根据喜欢的水果计算得分
    switch (favoriteFruit.toLowerCase()) {
        case "banana":
            score = 6;
            break;
        case "apple":
            score = 4;
            break;
        default:
            break;
    }
    //根据分数来统计结果
    if (score > 5) {
        alert("You picked one of my favorites too!");
    } else if (score > 0) {
        alert("Credit for choosing a fruit, at least!");
    } else {
        alert("Not sure about your choice of fruit!");
    }
}

从上面的函数我们看出:包含获取喜欢水果和计算得分两项职责(实际项目中很可能是多项职责),我们在这里加以拆分。

// 用户喜欢的水果
function getFavoriteFruit() {
    return prompt("What is your favorite fruit?", "None");
}
// 根据喜欢的水果计算得分
function getFruitScore(fruit) {
    var score = 0;
    switch (fruit.toLowerCase()) {
        case "banana":
            score = 6;
            break;
        case "apple":
            score = 4;
            break;
        default:
            break;
    }
    return score;
}
//根据分数来统计结果
function getMessageByScore(score) {
    var message = "";
    if (score > 5) {
        message = "You picked one of my favorites too!";
    } else if (score > 0) {
        message = "Credit for choosing a fruit, at least!";
    } else {
        message = "Not sure about your choice of fruit!";
    }
    return message;
}
//最开始的函数
function feedbackOnUsersFavoriteFruit() { 
    var favoriteFruit = getFavoriteFruit();
    var score = getFruitScore(favoriteFruit);
    alert(getMessageByScore(score));
}

(6).习惯利用scriptDoc为函数添加注释。包含项目文件描述(包含功能、作者及版本等),函数功能描述(包含参数,返回值,示例程序)。

/**
* @projectDescription 该文件用于日期对象操作
*
* @author Miracle He hmiinyu@sina.com
* @version 1.0
*/

/** 传入一个数并计算它的平方并返回四舍五入后的整数结果
*
* Examples:
* square(2.5); => 6
* square(2); => 4
* @param {Float} 传入计算的数
* @return {Integer} 返回四舍五入后的整数结果
*/
function square(number) {
    return Math.round(number * number);
}

(7).习惯使用"//TODO:[功能描述]"标记未完成的功能。以便于团队其他成员进行添加。

function checkPasswordStrength(password) {
    // TODO: 请完成密码强度检查
    return true;
}

(8).避免编写解决"不存在"问题的代码。即在项目开发中仅仅只编写与你问题相关的代码,而不是刻意添加与问题不相关的代码。如果你认为所做的代码能达到通用的层次,则建议将它封装到工具类中,而不是散列到各个业务文件中。这样可以更好的维护代码和提高重用性。举个例子,有时做项目经常碰到在前端页面获取MasterPage或者UserControl中某一个子控件时,在外部文件中不能使用$("#<%=id.ClientID%>")的方式来获取控件,因此我封装了一个函数并放到oa.lib.js中(oa是项目名,可自定义)。

/*DOM*/
oa.prototype.dom = {
    //根据ID获取对应的元素
    get: function (id, tag) {
        if (tag == undefined) return $("#" + id);
        switch (tag.toLowerCase()) {
            case "drop":
                return $("select[name$=" + id + "]");
            case "radio":
                return $("input[name$=" + id + "]");
            case "check":
            case "table":
                return $("table[id$=" + id + "]");
            case "input":
                return $("input[id$=" + id + "]");
            case "link":
                return $("a[id$=" + id + "]");
        }
    },
    //获取一组ID指定的元素
    gets: function (ids, tag) {
        var arr = ids; //ids可为数组或以逗号分隔的ID列表
        if (!(ids instanceof Array)) {
            arr = ids.split(',');
        }
        var elems = oa.dom.get(arr[0], tag);
        for (var i = 1; i < arr.length; i++) {
            elems = elems.add(oa.dom.get(arr[i], tag));
        }
        return elems;
    }
};
dom = oa.dom;

而当用户调用时,就很简单了。

<!--html-->
<asp:Content ID="Content3" ContentPlaceHolderID="content" runat="server">
     <asp:TextBox ID="txtSubject" runat="server" Text="科目"></asp:TextBox>
</asp:Content>
<!--javascript-->
dom.get("txtSubject", "input");

(9).保持Javascript(行为),HTML(结构),CSS(展现)分离,js和css以外部文件方式导入到html中。不要将三者混搭,这样极不专业也难以维护。我还是举个例子来说明。

<html xmlns="http://www.w3.org/1999/xhtml">
<head runat="server">
    <title>*****</title>
    <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
    <link href="Contents/styles/index.css" rel="stylesheet" type="text/css" />
    <script src="Scripts/lib/jquery-1.7.1.min.js" type="text/javascript"></script>
    <script src="Scripts/plugins/jquery.include.js" type="text/javascript"></script>
    <script src="Scripts/import/index.js" type="text/javascript"></script>
    <link rel="shortcut icon" href="Contents/images/common/favicon.ico" type="image/x-icon" />
</head>
<body>
    <form id="form1" runat="server">
    <div class="top">
        <iframe src="Shared/Header.aspx" width="100%" scrolling="no" marginwidth="0" marginheight="0"
            frameborder="0" height="85px" id="header"></iframe>
    </div>
    <div class="content">
        <div class="side">
            <iframe src="Shared/Menu.aspx" height="100%" frameborder="0" scrolling="auto" id="menu">
            </iframe>
        </div>
        <div class="bar">
            <iframe src="Shared/MenuBar.aspx" height="100%" frameborder="0" scrolling="auto" id="menubar">
            </iframe>
        </div>
        <div class="main">
            <iframe src="Shared/PageContainer.aspx" height="100%" frameborder="0" scrolling="auto"
                id="page"></iframe>
        </div>
    </div>
    <div class="bottom">
        Copyright ? 2007 - 2012 ***. All Rights Reserved
    </div>
    </form>
</body>
</html>

然后是外部文件(js,css),我这里就不列出具体内容了。

(10).尽量编写有"防护性"的代码,即对你编写的代码保持很好的逻辑判断和异常处理。下面我们来看一个示例。

var hr = document.getElementById("horizontal-rule");
if (hr) { // 如果对象存在的前提下执行
    hr.parentNode.removeChild(hr); 
}

当你对你的代码不太确认时,请添加异常处理,以更好的进行调试。

try {
    // 以下代码不合法将导致异常
    document.getElementById("horizontal-rule").propertyName.toString();
} catch (error) {
    alert("An error occurred.");
)

请注意,才这里catch中的参数error,包含异常产生的原因和类型等信息。

try {
    // varx 不是有效的关键字,因此将抛出语法错误异常
    varx x = 0;
} catch (error) {
    // 以下列举六种对应的错误
    if (error instanceOf TypeError) {
        // 类型错误...
    } else if (error instanceOf SyntaxError) {
        // 输出错误消息
        alert(error.message); 
    } else if (error instanceOf RangeError) {
        // 超界错误
    } else if (error instanceOf EvalError) {
        // eval()执行错误
    } else if (error instanceOf ReferenceError) {
        // 非法引用错误
    } else if (error instanceOf URIError) {
        // URI解码错误
    }
}

有时为了满足业务需要,也可以自定义异常。

// 定义自定义异常
var domObjectNotFoundException = new Error("DOM object not found");
function getDOMObjectById(id) {
    var domObject = document.getElementById(id);
    if (!domObject) {
        // 当对象不存在时抛出错误
        throw domObjectNotFoundException;
    }
    return domObject;
}
try {
    // 假定我们获取一个不存在的对象
    getDOMObjectById("this-id-is-not-on-the-page").style.width = "100px";
} catch (error) {
    if (error instanceOf domObjectNotFoundException) {
        // 输出错误
        alert(error.message); 
    } else {
        // 做其他错误处理
    }
}

类似于很多面向对象的高级语言,你还能使用try/catch/finally形式,在finally在放置你必须执行的代码,不管是否产生异常。

try {
    getDOMObjectById("this-id-is-not-on-the-page").style.width = "100px";
} catch (error) {
    alert(error.message);
} finally {
    alert("不管你的对错,我依然光顾!");
}

后续还有很多规范,我会逐步总结下来....

posted @ 2012-05-18 17:33  Miracle He  阅读(2370)  评论(1编辑  收藏  举报