Javascript风格要素 ⅠⅠ

Stella981
• 阅读 229

我们使用习惯用法可以使我们的意图更加的清晰和简洁。

使用==时,当心强制转换

考虑下面函数:

function gw(f){
    if (d.w.sv.checked == true) {
        zv = 'on';
    }
    else {
        zv = 'off';
    }
    procframe.location.replace("http://b.www.yahoo.com/module/wtr_tr.php?p=" +
    escape(f.p.value) +
    "&sv=" +
    zv);
    return false;
}

==运算符不应该被用着和true比较值,因为它要执行强制转换。如果我们想确定d.w.sv.checked是否是布尔值
true,我们必须用===运算符。如果我们仅在意一个值是真实存在的不是假的,最好不要用相等运算符。

例如,由于强制转换:1 == true是真,1 === true是假。==运算符隐藏了类型错误。

使用?:运算符选择两值之一

if语句通常被用来从两个值中选择一个。这应该是三元操作符?:最适合的。

zv = d.w.sv.checked ? 'on' : 'off';

绝不使用隐含的全局变量

变量zv不是作为一个var或函数参数来声明的,所以它是一个隐式的全局变量。如果这个页面的另一个函数使用了同样名字的全局变量,则可能得到一个 失败的结果。这样的臭虫(bug)是非常难以发现,却很容易避免。这个例子中,我们既可以声明zv为一个var,也可以发现它仅仅被使用过一次而整个去掉 它。

function gw(f){
    procframe.location.replace("http://b.www.yahoo.com/module/wtr_tr.php?p=" +
    escape(f.p.value) +
    "&sv=" +
    d.w.sv.checked ? 'on' : 'off');
    return false;
}

绝不使用?:运算符选择两种行为之一

我们常质疑那些返回一个常量的函数,但这有时是在浏览器环境下所必需的。

下面我们看一个不正确使用?:运算符的例子。它常被用于在两个任务间选择。

function u(o, z){
    var em = o.id.substring(1);
    var p = d.getElementById('e' + em);
    if (p) {
        (z == 0) ? p.style.backgroundColor = '#fff' : p.style.backgroundColor = '#989898';
    }
    p = d.getElementById('e' + (em - 1));
    if (p) {
        (z == 0) ? p.style.backgroundColor = '#fff' : p.style.backgroundColor = '#989898';
    }
}

对Z的判断是模糊不清的。Z正好等于0时我们选择#fff颜色,那么,Z不等于时?如上所述似乎指明的是前者,但它实际上是后者。在这个例子中幸运的是,我们大概想要的就是后者,所以它不是技术上错误(这次)。但是在文体上只糟糕的。

我们可以用if代替?:,但碰巧的是这些值对应的是同一个左值(lvalue),所以我们无需if就可以改正这个错误。

function u(o, z){
    var em = o.id.substring(1), p = d.getElementById('e' + em);
    if (p) {
        p.style.backgroundColor = z ? '#fff' : '#989898';
    }
    p = d.getElementById('e' + (em - 1));
    if (p) {
        p.style.backgroundColor = z ? '#fff' : '#989898';
    }
}

使用||运算符指定一个默认值

事件处理程序依赖于浏览器。理想情况下,应用程序应该通过公共库隔绝对浏览器的依赖。当没有这样的库时,就会有些函数发生如下情况:

function md(e){
    (window.event) ? ev = window.event : ev = e;
    (ev.target) ? sr = ev.target : sr = ev.srcElement;
    if (ev && sr && sr.id == "fp" || sr.id == "sb") 
        st = 1;
    if (sr.className.indexOf("pllist") < 0 && sr.className != "more" &&
    sr.className != "plinkc" &&
    sr.tagName != "scrollbar " &&
    _toClose &&
    _toCloseNorgie) {
        d.getElementById(_toClose).innerHTML = "";
        _toClose = "";
        _toCloseNorgie.parentNode.className = '';
        _toCloseNorgie = '';
    }
}

一些浏览器把事件对象作为一个参数传给事件管理程序。微软选用把事件对象放入到一个全局的事件变量中。在Javascript中,全局变量是全局对 象的成员。在浏览器中,全局对象始终包含一个window对象成员,其值是全局对象。当测试一个变量是否存在时,通过window访问全局变量是避免未定 义变量错误的一种方法。无论如何,做这样的测试不应该是必要的。

我们能通过问它是否是另外一种,来代替首先判断是否是微软事件。

ev = e || event;我们用||(默认)运算符。如果e是真,我们将有它的值,但是如果e是假,则我们将用event。

在下一个语句,我们又用||运算符去确定sr是哪个值。

我们应该用var去声明ev和sr来避免全局冲突:

function md(e){
    var ev = e || event, sr = ev.target || ev.srcElement;
    if (sr && (sr.id == 'fp' || sr.id == 'sb')) {
        st = 1;
    }
    if (sr.className.indexOf('pllist') < 0 && sr.className != 'more' &&
    sr.className != 'plinkc' &&
    sr.tagName != 'scrollbar ' &&
    _toClose &&
    _toCloseNorgie) {
        d.getElementById(_toClose).innerHTML = '';
        _toClose = '';
        _toCloseNorgie.parentNode.className = '';
        _toCloseNorgie = '';
    }
}

全局变量是魔鬼

下面我们看到另一个时间处理程序。正如你所料,它重复像前面一样破坏风格。

function kd(e){
    (window.event) ? ev = window.event : ev = e;
    (ev.target) ? el = ev.target : el = ev.srcElement;
    if (ev && el) {
        code = ev.keyCode;
        id = el.id;
    }
    else {
        return;
    }
    ctn = lt.id.substring(1);
    if (code == 13) {
        return;
    }
    else 
        if ((code == 191 || code == 222) && id != 'fp') {
            _ffs = 1;
            gk = 0;
        }
        else 
            if ((code < 31 || code > 41) &&
            (code < 16 || code > 18) &&
            code != 9 &&
            code != 8) {
                gk = 1;
            }
            else {
                gk = 0;
            }
    if (!_ffs && (id == 'fp' || id == 'st')) {
        if (code == 9) {
            if (box.value == '' || (box.value != '' && (at == 1 || ev.shiftKey))) {
                mt(ctn);
            }
            else 
                if (id == 'st' && box.value != '' && at == 0) {
                    at = 1;
                    mt(ctn);
                }
        }
        else 
            if (id == 'fp' && gk == 0 && (box.value == '' && st == 0) &&
            !ev.shiftKey &&
            !ev.ctrlKey &&
            !ev.altKey) {
                d.getElementById('mk').focus();
                d.getElementById('mk').blur();
            }
            else 
                if (gk == 1) {
                    at = 0;
                }
    }
    else 
        if ((id == 'mk2' && box.value != '' && ev.shiftKey && code == 9) ||
        (id == 'm6' && !ev.shiftKey && code == 9)) {
            d.getElementById('mk').focus();
        }
        else 
            if (!_ffs && gk == 1 && el.type != 'text' && !ev.ctrlKey && !ev.altKey) {
                box.value = '';
                box.focus();
            }
}
function mt(ctn){
    if ((ev && !ev.ctrlKey && !ev.altKey) || !ev) {
        if (ev.shiftKey) {
            nextTab = parseInt(ctn) - 1;
        }
        else {
            nextTab = parseInt(ctn) + 1;
        }
        if (nextTab == 0) {
            d.getElementById('mk').focus();
        }
        else 
            if (nextTab < 8) {
                t(d.getElementById('v' + nextTab));
            }
            else {
                return;
            }
    }
}

有意思的是它有一个同伴函数mt,它仅被kd调用。mt被传给一个参数ctn,但kd和mt之间的通讯大部分是通过全局变量。

使用内部函数避免全局变量

我们可以通过增加传递给mt的参数数量来除掉所有的全局变量。但代替方案,我们将使mt变成kd的内部函数。作为一个内部函数,mt能访问kd的所有变量。

function kd(e){
    var ev = e || event, el = ev.target || ev.srcElement, cnt, code = ev.keyCode, gk, id = el.id, ctn = lt.id.substring(1);
    
    function mt(){
        var nextTab;
        if (!ev.ctrlKey && !ev.altKey) {
            nextTab = parseInt(ctn) + ev.shiftKey ? -1 : 1;
            if (!nextTab) {
                d.getElementById('mk').focus();
            }
            else 
                if (nextTab < 8) {
                    t(d.getElementById('v' + nextTab));
                }
        }
    }
    
    if (code == 13) {
        return;
    }
    else 
        if ((code == 191 || code == 222) && id != 'fp') {
            _ffs = 1;
            gk = 0;
        }
        else 
            if ((code < 31 || code > 41) &&
            (code < 16 || code > 18) &&
            code != 9 &&
            code != 8) {
                gk = 1;
            }
            else {
                gk = 0;
            }
    if (!_ffs && (id == 'fp' || id == 'st')) {
        if (code == 9) {
            if (box.value == '' ||
            (box.value != '' && (at == 1 || ev.shiftKey))) {
                mt();
            }
            else 
                if (id == 'st' && box.value != '' && at == 0) {
                    at = 1;
                    mt();
                }
        }
        else 
            if (id == 'fp' && gk == 0 && (box.value == '' && st == 0) &&
            !ev.shiftKey &&
            !ev.ctrlKey &&
            !ev.altKey) {
                d.getElementById('mk').focus();
                d.getElementById('mk').blur();
            }
            else 
                if (gk == 1) {
                    at = 0;
                }
    }
    else 
        if ((id == 'mk2' && box.value != '' && ev.shiftKey && code == 9) ||
        (id == 'm6' && !ev.shiftKey && code == 9)) {
            d.getElementById('mk').focus();
        }
        else 
            if (!_ffs && gk == 1 && el.type != 'text' && !ev.ctrlKey &&
            !ev.altKey) {
                box.value = '';
                box.focus();
            }
}

在函数kd中,从两个地方调用函数mt。通过使它成为一个内部函数,我们能有效的减少kd所用到的全局变量的数目,这将降低了干扰其他组件的可能性。kd依旧是一个烂摊子,但它现在不是一无是处的烂摊子。

作者:Douglas Crockford
原文:The Elements of JavaScript Style Part Two
翻译:秦歌随网之舞

点赞
收藏
评论区
推荐文章
blmius blmius
2年前
MySQL:[Err] 1292 - Incorrect datetime value: ‘0000-00-00 00:00:00‘ for column ‘CREATE_TIME‘ at row 1
文章目录问题用navicat导入数据时,报错:原因这是因为当前的MySQL不支持datetime为0的情况。解决修改sql\mode:sql\mode:SQLMode定义了MySQL应支持的SQL语法、数据校验等,这样可以更容易地在不同的环境中使用MySQL。全局s
Jacquelyn38 Jacquelyn38
2年前
2020年前端实用代码段,为你的工作保驾护航
有空的时候,自己总结了几个代码段,在开发中也经常使用,谢谢。1、使用解构获取json数据let jsonData  id: 1,status: "OK",data: 'a', 'b';let  id, status, data: number   jsonData;console.log(id, status, number )
Wesley13 Wesley13
2年前
java将前端的json数组字符串转换为列表
记录下在前端通过ajax提交了一个json数组的字符串,在后端如何转换为列表。前端数据转化与请求varcontracts{id:'1',name:'yanggb合同1'},{id:'2',name:'yanggb合同2'},{id:'3',name:'yang
皕杰报表之UUID
​在我们用皕杰报表工具设计填报报表时,如何在新增行里自动增加id呢?能新增整数排序id吗?目前可以在新增行里自动增加id,但只能用uuid函数增加UUID编码,不能新增整数排序id。uuid函数说明:获取一个UUID,可以在填报表中用来创建数据ID语法:uuid()或uuid(sep)参数说明:sep布尔值,生成的uuid中是否包含分隔符'',缺省为
Stella981 Stella981
2年前
Android So动态加载 优雅实现与原理分析
背景:漫品Android客户端集成适配转换功能(基于目标识别(So库35M)和人脸识别库(5M)),导致apk体积50M左右,为优化客户端体验,决定实现So文件动态加载.!(https://oscimg.oschina.net/oscnet/00d1ff90e4b34869664fef59e3ec3fdd20b.png)点击上方“蓝字”关注我
Easter79 Easter79
2年前
Twitter的分布式自增ID算法snowflake (Java版)
概述分布式系统中,有一些需要使用全局唯一ID的场景,这种时候为了防止ID冲突可以使用36位的UUID,但是UUID有一些缺点,首先他相对比较长,另外UUID一般是无序的。有些时候我们希望能使用一种简单一些的ID,并且希望ID能够按照时间有序生成。而twitter的snowflake解决了这种需求,最初Twitter把存储系统从MySQL迁移
Wesley13 Wesley13
2年前
00:Java简单了解
浅谈Java之概述Java是SUN(StanfordUniversityNetwork),斯坦福大学网络公司)1995年推出的一门高级编程语言。Java是一种面向Internet的编程语言。随着Java技术在web方面的不断成熟,已经成为Web应用程序的首选开发语言。Java是简单易学,完全面向对象,安全可靠,与平台无关的编程语言。
Stella981 Stella981
2年前
Django中Admin中的一些参数配置
设置在列表中显示的字段,id为django模型默认的主键list_display('id','name','sex','profession','email','qq','phone','status','create_time')设置在列表可编辑字段list_editable
Wesley13 Wesley13
2年前
MySQL部分从库上面因为大量的临时表tmp_table造成慢查询
背景描述Time:20190124T00:08:14.70572408:00User@Host:@Id:Schema:sentrymetaLast_errno:0Killed:0Query_time:0.315758Lock_
Python进阶者 Python进阶者
1个月前
Excel中这日期老是出来00:00:00,怎么用Pandas把这个去除
大家好,我是皮皮。一、前言前几天在Python白银交流群【上海新年人】问了一个Pandas数据筛选的问题。问题如下:这日期老是出来00:00:00,怎么把这个去除。二、实现过程后来【论草莓如何成为冻干莓】给了一个思路和代码如下:pd.toexcel之前把这