编写可维护的javascript读后笔记(二)

编写可维护的javascript读后笔记(二)

本篇主要写第二部分 编程的实践,包括八章内容。

第五章 UI层的松耦合

在web开发中,用户界面(User interface,UI)是有三个彼此隔离又相互作用的层定义的。
(1) HTML用来定义页面的数据和语义
(2)CSS用来给页面添加样式 创建视觉特征
(3)javascript用来给页面添加行为的。

三个层面达到松耦合会给后续代码的维护带来很大的方便,可以做到更改其中一个不会影响到其他。而且对找问题也会分清界限。
1. 将javascript从CSS中抽离;
避免CSS表达式的使用。浏览器会以高频率重复计算CSS表达式,这严重影响了性能。而且对查找问题造成了一定的困扰,因为遇到javascript报错一般都会到js文件中查找问题。很少会想到去CSS文件中查找。

2.将CSS从Javascript中抽离
很多地方可能会需要在js代码中控制样式。用element.style=”“这种方式控制样式,这样就违背了松耦合的理念,可以使用控制类名的方式 将CSS从javascript中抽离开。
例如:

// 把需要控制的样式写在一个类名里
.red {
    color : white;
}

// 在js文件里 找到要添加该样式的相应元素,赋予.red类名
element.className+="red";       //原生写法
element.classList.add("red");   //HTML5的写法
$(element).addClass("red");     // Jquery中的写法

3.将javascript从HTML中抽离

(1)不好的写法

<button onclick="doSomething()" id="btn">Click Me</button>

这是DOM0级的事件写法 把javascript代码写在了HTML。不方便调试和维护。js代码应该都是在一个外部文件中,获取DOM节点 绑定事件。

(2)好的写法

// 定义一个专门的js文件 内容如下 然后用script标签 引入到HTML文件中
function doSomething() {
}

var btn=document.getElementById("btn");
btn.addEventListener("click",doSomething,false);

4.将HTML从javascript中抽离
有些地方会涉及到动态创建DOM节点插入到页面中,这有可能会涉及到在javascript中写HTML代码。

一般的解决方法是采用模板的方法 引入一个外部模板 动态填充内容。

第六章 避免使用全局变量

当脚本中的全局变量和全局函数越来越多时,发生命名冲突的概率也会越来越高。尤其是当多人团队中。全局变量有可能会重复 相互影响。

1.对于函数来说,确保你的函数不会对全局变量有依赖,将增强你的代码的可测试性。

2.意外的全局变量
不小心省略var语句可能意味着在不知情的情况下修改某个已存在的全局变量,如name变量。因为name实际上是window的一个默认属性,window.name属性常用语框架(frame)和Iframe场景中。不小心改变name会影响到站点的链接导航。

function doSomething() {
    var a=10;
        name=a + 1; //不好的写法 意外的创建了全局变量
}

3.单全局变量方式
但全局变量方式已经在各种流行的javascript类库中广泛使用,如YUI、jQuery、Dojo等。所有的方法和属性都附着在定义的一个全局对象上。

4.模块
模块是一种基于单全局变量的扩充方法。
两种流行的类型是“YUI模块”和 “异步模块定义(简称:AMD Asynchronous Module Definition)”;

// AMD 模块语法
define("module-name",["dependency1","dependency2"],
    function(dependency1,dependency2) {
    //模块正文
    var Books = {};
    Book.info = {
        author : "nike",
        price : 10
    }
    return Books;
});

/*
* 想要使用AMD模块 需要使用一个与之兼容的模块加载器,如requireJs。
* 
* requireJs模块加载器添加了一个全局函数require(),专门用来加载指定
* 的依赖和执行回调函数。
* 
* 调用require时会首先立即加载依赖,这些依赖都加载完成后会即可执行
* 回调函数。
* /
require(["module-name"],function(books) {
    console.log(books.author);
});

5.零全局变量
使用零全局变量的场景比较少,当你的脚步非常短且不需要和其他代码产生交互,可以考虑使用零全局变量的方式实现代码。其实现是依靠一个立即执行函数并传入window对象实现的。

function(win) {
    "user strict";          //用严格模式来避免意外创建全局变量
    var doc = win.document;

    // 定义其他的变量

    // 其他的相关代码

}(window));

第七章 事件处理

事件处理是javascript的一个核心功能。怎么样使你的事件处理程序独立又可测试是一个需要注意地方。

1.好的事件处理程序应该将应用逻辑从事件处理程序中抽离出来。

// 不好的写法
function handleClick(event) {
    var popup=document.getElementById("popup");
    popup.style.left = event.clientX + "px";
    popup.style.top = event.clientY + "px";
    popup.className = "reveal";
}

addEventListener(element, "click",handleClick);


// 好的写法
var myHandleClick = {
    handleCLick : function(event) {
        this.showPopup(event);
    },
    showPopup : function(event) {
        var popup = document.getElementById("popup");
        popup.style.left = event.clientX + "px";
        popup.style.top = event.clientY + "px";
        popup.className = "reveal";
    }
};

addEventListener(element, "click",
    function(event) {
    myHandleClick.handleClick(event);
});

2.好的事件处理程序不应该分发event对象。
上述案例event对象被分发了几次,从匿名事件处理函数传入了myHandleCLick.handleClick(),然后又传入了myHandleClick.showPopup()函数中,改进办法如下:

var myHandleClick = {
    handleCLick : function(event) {
        /**
         * 最好让事件处理程序成为接触event对象的唯一函数
         * 所以关于阻止事件默认行为和阻止冒泡也在这里写
         * /
        event.preventDefault();
        event.stopPropagation();

        // 应用逻辑函数的调用.
        this.showPopup(event.clientX,event.clientY);
    },

    // showPopup函数只需要两个event对象的信息clientX和clientY.
    showPopup : function(x,y) {
        var popup = document.getElementById("popup");
        popup.style.left = x + "px";
        popup.style.top = y + "px";
        popup.className = "reveal";
    }
};

addEventListener(element, "click",function(event) {
    myHandleClick.handleClick(event);
});

第八章 避免“空”比较

变量和空(null)比较 并起不到什么作用。
如:

// 下面这段代码显然是想判断一下a是否是数组,但是通过a与null的相比,并不能判断a是不是数组。因为字符串 数值等也不等于null.到了a.sort()时就会报错。

if(a !==null) {
    a.sort();
    a.foreach(item) {
        //一些操作
    }
}

有一种例外就是需要用到跟null值比较:获取元素节点时:

// 如果dom不存在,通过document.getElementById()获得的值是null

var ele = document.getElementById("id");
if(ele !== null) {
    // 一些操作
}

javascript提供了很多方法来检测变量的真实值。
1.对于字符串、数值、布尔值和undefined 这种简单类型的数据可以使用typeof运算符来检测
语法:typeof variable
(1)对于字符串,typeof 返回 “string”。
(2)对于数字,typeof 返回 “number”。
(3)对于布尔值,typeof 返回 “boolean”。
(4)对于undefined, typeof 返回 “undefined”。

2.检测引用值时,如Object,Array,Date,Error,RegExp等 typeof检测出来都是“object”,typeof 检测null时也是得出“object”。检测引用值类型最好的方法是用instanceof。
语法: value instanceof constructor

3.由于使用instanceof检测函数和数组 在遇到跨帧时(跨frame)会有些问题。所以函数和数组有其更好的检测方式
(1)function函数的检测方法 用typeof;

 typeof 变量 === "function";

 然而在IE8及以下版本中,typeof检测DOM节点的方法(document.getElementById()等)会返回“object”,所以检测DOM节点方法可以用in 检测 如 “querySelectAll” in document;判断是否存在document.querySelectAll的方法。除了这种检测function最好用typeof.

(2)检测数组

Object.prototype.toString.call(value) === "[object Array]";

Array.isArray(value); IE9+ Firefox4+ Safari5+ Opera10.5+和Chrome都实现了这个检测数组的方法。其内部实现原理也是用了上面的toString方法。

4.检测属性

判断属性是否存在最好的方法是使用in运算符。In运算符仅仅会简单的判断属性是否存在,而不会去读属性的值。(包括原型链上的属性)
如果只想检测实例对象的某个属性是否存在,不查找原型链。则使用hasOwnProperty()方法

// 对于所有非DOM对象来说
object.hasOwnProperty(key);

// 如果不确定是否为DOM对象
"hasOwnProperty" in object && object.hasOwnProperty(key);

第九章 将配置数据从代码中分离出来

数据是不应该影响指令的运行的。精心设计的应用会将关键数据从主要的源码中抽离出来的。

1.抽离出配置数据的好处:
(1)以后需要更改数据时 不用再更改javascript代码了,安全性
(2)以后更改出现多次的值时,只需要更改一处即可 方便。

2.那么什么数据需要抽离出来呢?
(1) URL
(2)需要展现给用户的字符串
(3)重复的值
(4)设置(比如每页的配置项)
(5)任何可能发生变更的值

3.案例
抽离数据前的代码:

// 配置数据抽离前 数据跟代码混在一块

function validate(arg) {
    if(!arg) {
        alert("Invalid Value");
        location.href = "/errors/invalid.php";
    }
}

function toggleSelected(element) {
    if(hasClass(element,"selected")) {
        removeClass(element,"selected");
    }else{
        addClass(element,"selected");
    }
}

抽离数据后的代码

// 抽离数据后的代码 改变数据只需改变config对象值即可
var config = {
    MSG_INVALID : "Invalid Value",
    URL_INVALID : "/errors/invalid.php",
    CSS_SELECTED : "selected"
}

function validate(arg) {
    if(!arg) {
        alert(config.MSG_INVALID);
        location.href = config.URL_INVALID;
    }
}

function toggleSelected(element) {
    if(hasClass(element,config.CSS.SELECTED)) {
        removeClass(element,config.CSS.SELECTED);
    }else{
        addClass(element,config.CSS.SELECTED);
    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值