编写高质量 JavaScript -- 知识点小记
一: 团队合作避免JS冲突
脚本中的变量随时存在冲突的风险,
1. 解决办法---用匿名函数将脚本包起来,让变量的作用域控制在匿名函数之内
如:
- <script type="text/javascript">
- (function(){
- var a=123,b="12121";
- })();
- </script>
- .....
- <script type="text/javascript">
- (function(){
- var a=123,b="asdasa";
- })();
- </script>
此番改善之后,匿名function里面的变量作用域不再是window,而是局限在函数内。
2. 有时各个函数之间变量又要进行通讯,故又要改善(利用全局作用域)
如:
- <script type="text/javascript"> var str; </script>
- <script type="text/javascript">
- (function(){
- var a=123,str = b = "12121";
- })();
- </script>
- .....
- <script type="text/javascript">
- (function(){
- var a=123,b=str;
- })();
- </script>
3. 但如此一来,工程变大,全局变量未免过多难以管理,(利用hash对象作为全局变量)
如:
- <script type="text/javascript"> var GLOBAL={}; </script>
- <script type="text/javascript">
- (function(){
- var a=123,b = "12121";
- GLOBAL.str = a;
- GLOBAL.str2 = b;
- })();
- </script>
- .....
- <script type="text/javascript">
- (function(){
- var a=GLOBAL.str;
- var b = GLOBAL.str2;
- })();
- </script>
4. 不过就算如此,如果工程变大,不同工程师完成不同的函数,变量难免也会冲突,
故又继续改善(GLOBAL 属性不是直接挂在GLOBAL属性对象上,而是挂在此命名函数的命名空间下)
如:
- <script type="text/javascript"> var GLOBAL={}; </script>
- <script type="text/javascript">
- (function(){
- var a=123,b = "12121";
- GLOBAL.A = {};
- GLOBAL.A.str = a;
- GLOBAL.A.str2 = b;
- })();
- </script>
- .....
- <script type="text/javascript">
- (function(){
- var a=GLOBAL.A.str;
- var b = GLOBAL.A.str2;
- })();
- </script>
如此种种,函数内属性越复杂,
又可以进一步扩展GLOBAL挂载方式.....
如 GLOBAL.A={};
GLOBAL.A.CAT={};
GLOBAL.A.DOG={};
GLOBAL.A.DOG.name="aa";
GLOBAL.A.CAT.name="bb";
5. 进一步,又可以给此命名空间定义方式封装成函数,方便以后调用:
如:
- <script type="text/javascript">
- var GLOBAL={};
- GLOBAL.namespace = function(str){
- var arr=str.split("."), o = GLOBAL;
- for(i=(arr[0]="GLOBAL") ? 1 : 0 ; i<arr.length; i++){
- o[arr[i]] = o[arr[i]] || {};
- o = o[arr[i]];
- }
- }
- </script>
- <script type="text/javascript">
- (function(){
- var a=123,b = "12121";
- GLOBAL.namespace("A.CAT");
- GLOBAL.namespace("A.DOG");
- GLOBLA.A.CAT.name="aa";
- GLOBLA.A.DOG.name="bb";
- })();
- </script>
二:
方便JS程序执行:
1. 给程序一个统一的入口===== window.onload 或DOMReady
(先把所有函数定义部分放入 init函数中,最后再加载 init()即可使用
如:在DOM节点加载进来之前就调用会出错
- <script type="text/javascript">
- alert(document.getElementById("test").innerHTML);
- </script>
- <div id="test">hello ! </div>
改善方式:
调换位置,在DOM节点加载进来之后再调用即可
- <div id="test">hello ! </div>
- <script type="text/javascript">
- alert(document.getElementById("test").innerHTML);
- </script>
或者,使用window.onload事件,window对象会在网页内元素全部加载完毕之后才触发onload时间
- <script type="text/javascript">
- window.onload= function(){
- alert(document.getElementById("test").innerHTML);
- }
- </script>
- <div id="test">hello ! </div>
但此种方式,仍有不足。
window.onload要等到网页元素全部加载才进行
而DOMReady则只要页面内所有DOM节点皆全部生成即可进行。
DOMReady 方式原生JS并不支持,要使用第三方类库(JS框架)
如jQuery的方式:
$(document).ready(init); // init() 是一个函数...
$(function(){ ...}); /// 等等等等..
当然了,我们也可以用原生JS模拟DOMReady ,事实上很简单,就是:
- <script type="text/javascript">
- function init(){
- alert(document.getElementById("test").innerHTML);
- }
- </script>
- <div id="test">hello ! </div>
- <script type="text/javascript">
- init();
- </script>
- </body>
如此一来 body标签加载完成之前才调用函数,这种方式也不错!
2. CSS 文件与 JS 文件的位置
因为JS是阻塞型的,所以一般” CSS放在页头,Javascript放在页尾“
(这样一来,网页先呈现给用户,再慢慢加载页面里面的脚本,减少页面空白的时间)
三. Javascript 分层概念
一般来说,我们要把Javascript分层三层: base层 --> common层 --> page 层
1. page层
page 层提供统一的接口,可以在这里实现封装不同浏览器下Javascript的差异,依靠它来完成跨浏览器兼容的工作!
还可以扩展Javascript语言底层提供的接口,以便提供出更多有用的接口(主要是为common page 层提供)
各种问题类举:
<1> 在IE中,它只视DOM节点为childNodes中的一员,
但在FireFox中,它会将包括空白.换行等文本信息在内的信息也当做childNodes的一员。
未解决此问题,
可用 document.all 方式处理,它是IE支持的属性,FireFox不支持
代码如:
- <ul>
- <li id="item1"></li>
- <li id="item2"></li>
- <li id="item3"></li>
- </ul>
- <script type="text/javascript">
- var item1 = document.getElementById("item1");
- var nextNode = item1.nextSibling;
- if(document.all){
- while(true){
- if(nextNode.nodeType == 1){
- break;
- }
- else{
- if(nextNode.nextSibling){
- nextNode = nextNode.nextSibling;
- }
- else{ break; }
- }
- }
- }
- alert(nextNode.id);
- //////////////////////////////////////////////////
- var item2 = document.getElementById("item2");
- var nextNode = item2.nextSibling;
- if(!document.all){
- while(true){
- if(nextNode2.nodeType == 1){
- break;
- }
- else{
- if(nextNode2.nextSibling){
- nextNode2 = nextNode2.nextSibling;
- }
- else{ break; }
- }
- }
- }
- alert(nextNode2.id);
- </script>
哦对,好像有点冗余,那就把这个功能封装成函数吧!
如:
- <ul>
- <li id="item1"></li>
- <li id="item2"></li>
- <li id="item3"></li>
- </ul>
- <script type="text/javascript">
- function getNextNode(node){
- node = typeof node=="string" ? document.getElementById("node") : node;
- var nextNode = node.nextSibling;
- if(!nextNode)
- return NULL;
- if(!document.all){
- while(true){
- if(nextNode.nodeType == 1){
- break;
- }
- else{
- if(nextNode.nextSibling){
- nextNode = nextNode.nextSibling;
- }
- else{ break; }
- }
- }
- }
- return nextNode;
- } //funciton over
- var nextNode = getNextNode("item1");
- var nextNode2 = getNextNode("item1");
- alert(nextNode.id);
- alert(nextNode2.id);
- </script>
<2> 透明度的问题:
IE下透明度是通过滤镜实现的,但在FireFox下透明度是通过CSS 的opacity属性实现的
我们把它封装起来
代码如:
- <style type="text/css">
- #test1 { background:blue; height: 100px;}
- #test1 { background:blue; height: 100px;}
- </style>
- <div id="test1"></div>
- <div id="test2"></div>
- <script type="text/javascript">
- function setOpacity(node,level){ // level 为滤镜程度
- node = typeof node=="string" ? document.getElementById("node") : node;
- if(document.all){
- node.style.filter = 'alpha(opacitu= ' + level + ')';
- }
- else{
- node.style.opacity = level / 100;
- }
- }
- setOpacity("test1",20); //test1.style.filter = 'alpha(opacitu=20)';
- setOpacity("test2",80); //test2.style.opacity = 0.8;
- </script>
<3> event 对象的问题:
IE下 event对象是作为window的属性作用于全局作用域的,但在FireFox中 event对象是作为事件的参数存在的
所以,为了兼容性,一般考虑用一个变量指向event对象,继而通过这个变量访问event对象
不理解? 代码如下
- <script type="text/javascript">
- var btn = document.getElementById("btn");
- btn.onclick = function(e){
- e = window.event || e;
- // ..........接下来你就可以用e了, 比如 e.target 等
- </script>
另一方面,派生事件的对象在IE下是通过event对象的srcElement属性访问的
在FireFox下是通过event对象的target属性访问的
如代码:
- <script type="text/javascript">
- var btn = document.getElementById("btn");
- btn.onclick = function(e){
- e = window.event || e;
- var el = e.srcElement || e.target;
- alert(el.tagName);
- </script>
<4> 冒泡问题的处理
首先理解概念---> 对于事件流,浏览器中的事件模型分为两种:捕获型和冒泡型事件
事件的冒泡: Javascript对这种先触发子容器监听事件,后触发父容器监听事件的现象。
事件的捕获: 即相反于冒泡(先父后子)
比如代码中 <div id="wrapper">
<input type="button" value="click me" id="btn" />
</div>
我们为id=wrapper绑定事件1,为id=btn绑定事件2,
如此一来,我们的结果却是: 无论点哪里,触发的都是事件1 (因为事件2触发得很快就会迅速转变为事件1)
为了解决,要阻止(对子容器)事件的冒泡机制:IE下通过设置event对象的cancelBubble 为true 实现
FireFox 通过调用event对象的stopPropagation方法实现
封装成函数即是:
- <script type="text/javascript">
- function stopPropagation(e){
- e = window.event || e;
- if(document.all){
- e.cancelBubble = true;
- }
- else{
- e.stopPropagation();
- }
- }
- /////////////////////////调用
- wrapper.onclick = function(){
- ..............
- }
- btn.onlcick = function(e){
- ................
- stopPropagation(e);
- }
- </script>
<5>事件监听的处理:
可以简单地使用 onXXX 的方式,
如 btn.onclick = function(){ .......};
但onXXX方法没有叠加作用,后面定义的onXXX会把前面的覆盖掉,为解决此类问题:
IE下引入 attachEvent方法,FireFox 下引入addEventListener方法。
IE下引入 detachEvent方法,FireFox 下引入removeEventListener方法。
如:oSpan.detachEvent("onclick",fnClick);
oSpan.removeEventListener("click",fnClick);
好,我们就把这个功能封装一下:
代码如:
- <script type="text/javascript">
- function on(node,eventType,handeler){
- node = typeof node=="string" ? document.getElementById("node") : node;
- if(document.all){
- node.attachEvent("on"+ eventType,handeler);
- }
- else{
- node.addEventListener(eventType,handeler,false); // 其中第三个参数:true--捕获型,false--冒泡型
- }
- }
- var btn = document.getElementById("btn");
- on(btn,"click",funciton(){
- alert(1);
- });
- on(btn,"click",funciton(){
- alert(2);
- });
- </script>
<6> 其他小功能:
函数封装实现:
- <script type="text/javascript">
- function trim(ostr){ //trim() 去空格
- return ostr.replace(/^\s+|\s+$/g,"");
- }
- function isNumber(s){
- return !isNaN(s);
- }
- function isString(s){
- return typeof s === "string";
- }
- function isBoolean(s){
- return typeof s === "boolean";
- }
- function isFunction(s){
- return typeof s === "function";
- }
- function isNull(s){
- return s === null;
- }
- function isUndefined(s){
- return typeof s === "undefined";
- }
- function isEmpty(s){
- return /^\s*$/.test(s);
- }
- function isArray(s){
- return s instanceof Array;
- }
- function get(node){ // get() 代替ducument.getElementById() ...alert(get("test1").innerHTML);
- node = typeof node=="string" ? document.getElementById("node") : node;
- return node;
- }
- function $(node){ // $() 代替ducument.getElementById() ...alert($("test1").innerHTML);
- node = typeof node=="string" ? document.getElementById("node") : node;
- return node;
- }
- // 原生JS没有getElementByClassName, 那就给它实现一个呗...
- // getElementByClassName函数接收3个参数,第一个参数为class名(必选),第二个为父容器,缺省值为body节点,第三个参数为DOM节点的标签名。
- // 函数 实现如下
- function getElementByClassName(str,root,tag){
- if(root){
- root = typeof root=="string" ? document.getElementById("root") : root;
- }
- else{
- root = document.body;
- }
- tag = tag || "*";
- var els = root.getElementByTagName(tag), arr=[];
- for(var i=0,n=els.length; i<n ; i++){
- for(var j=0,k=els[i].className.split(" "), l=k.length; j<l; j++){
- if(k[k] == str){
- arr.push(els[i]);
- break;
- }
- }
- }
- return arr;
- }
- //然后我们就可以直接调用啦..
- var aEls = getElementByClassName("a");
- var bEls = getElementByClassName("b");
- // .............
- </script>
2. common 层:
common层本身依赖于base层提供的接口,common层提供的应该是相对更大的组件,供Javascript调用
3. page 层
就是具体的页面特设定啦...
四: 编程的其他一些实用技巧:
1.在遍历数组时对DOM监听事件,索引值将始终等于遍历结束后的值。
如某个监听代码:
- <script type="text/javascript">
- //遍历数组,让tabMenus 监听click事件 (Tab 组件监听选项卡)
- for(var i=0;i<tabMenus.length;i++){
- tabMenus[i].onclick = function(){
- //遍历数组,隐藏所有tabcontent
- for(var j=0;j<tabcontent.length;j++){
- tabcontent[j].style.display = "none";
- }
- //显示被点击的tabMenus 对应的tabcontent
- tabcontent[i].style.display = "block";
- }
- }
- </script>
这样做之后,所有content将会隐藏且 有报错---> tabcontent[i] is undefined !
要怎么改正呢? ----------------------------->
- <script type="text/javascript">
- //方法一: 利用闭包
- for(var i=0;i<tabMenus.length;i++){
- (function(_i){
- tabMenus[_i].onclick = function(){
- //遍历数组,隐藏所有tabcontent
- for(var j=0;j<tabcontent.length;j++){
- tabcontent[j].style.display = "none";
- }
- //显示被点击的tabMenus 对应的tabcontent
- tabcontent[_i].style.display = "block";
- }
- })(i); // 闭包...
- }
- //方法二: 给DOM节点添加 _index属性
- <script type="text/javascript">
- for(var i=0;i<tabMenus.length;i++){
- tabMenus[i]._index = i;
- tabMenus[i].onclick = function(){
- //遍历数组,隐藏所有tabcontent
- for(var j=0;j<tabcontent.length;j++){
- tabcontent[j].style.display = "none";
- }
- //显示被点击的tabMenus 对应的tabcontent
- tabcontent[this._index].style.display = "block";
- }
- }
- </script>
2. 另一方面,我们还需要注意控制好 关键字this 的指向问题:
<1> Javascript伪协议和内联事件对this的指向不同
如
- <script type="text/javascript">
- // 弹出 “A"
- <a href="#" onclick="alert(this.tagName)";>click me</a>
- // 弹出 "undefined"
- <a href="Javascript:alert(this.tagName)">click me</a>
- //弹出 "true"
- <a href="Javascript:alert(this==window)">click me</a>
- </script>
<2> setTimeout 和 setInterval 也会改变this指向
他们都是直接调用函数里的this 指向window
如
- <script type="text/javascript">
- var name="somebody";
- var adang = {
- name : "adang";
- say : funciton(){
- alert("I'm" + this.name);
- }
- };
- adang.say(); // I'm adang
- setTimeout(adang.say,1000); // I'm somebody
- setInterval(adang.say,1000); // I'm somebody
- // 解决办法使用匿名函数 --->
- setTimeout(function(){ adang.say()},1000); // I'm adang
- </script>
<3> DomNode.on XXX 方式也会改变this 指向
它将直接调用函数里面的this 指向DomNode
比如:
- <script type="text/javascript">
- var name="somebody";
- var btn = document.getElementById("btn");
- var adang = {
- name : "adang";
- say : funciton(){
- alert("I'm" + this.name);
- }
- };
- adang.say(); // I'm adang
- btn.onclick = adang.say; // I'm BUTTON
- // 解决办法使用匿名函数 --->
- btn.onclick = funciton(){ adang.say()} ; // I'm adang
- </script>
<4> 对此,另外我们还可以用 call 和 apply 函数来改变处理函数的this指向
比如:
- <script type="text/javascript">
- var name="somebody";
- var btn = document.getElementById("btn");
- var adang = {
- name : "adang";
- say : funciton(){
- alert("I'm" + this.name);
- }
- };
- adang.say.call(btn); // I'm BOTTON ---- 把this指向改成了按钮
- adang.say.apply(btn); // I'm BOTTON ---- 把this指向改成了按钮
- setTimeout(adang.say,1000); // I'm somebody
- setInterval(adang.say,1000); // I'm somebody
- setTimeout(function(){ adang.say.apply(btn)},1000); // I'm BUTTON
- </script>
=============================分割线=====================待续=====================================
编写高质量 JavaScript -- 知识点小记的更多相关文章
- 深入理解JavaScript系列(1):编写高质量JavaScript代码的基本要点
深入理解JavaScript系列(1):编写高质量JavaScript代码的基本要点 2011-12-28 23:00 by 汤姆大叔, 139489 阅读, 119 评论, 收藏, 编辑 才华横溢的 ...
- 编写高质量JavaScript代码绳之以法(The Essentials of Writing High Quality JavaScript)翻译
原文:The Essentials of Writing High Quality JavaScript 才华横溢的Stoyan Stefanov,在他写的由O'Reilly初版的新书<Java ...
- 编写高质量JavaScript代码的基本要点记录
原文:深入理解JavaScript系列(1):编写高质量JavaScript代码的基本要点 1.最小全局变量(Minimizing Globals)的重要性 JavaScript通过函数管理作用域.在 ...
- 编写高质量JavaScript代码的68个有效方法
简介: <Effective JavaScript:编写高质量JavaScript代码的68个有效方法>共分为7章,分别涵盖JavaScript的不同主题.第1章主要讲述最基本的主题,如版 ...
- 编写高质量JavaScript代码的基本要点
全局变量 javaScript通过函数管理变量作用域.在函数内部声明的变量只在这个函数内部可用,函数外面不可用.另一方面,在任何函数外面声明的或是函数内未声明直接使用的变量叫做全局变量. 每个Java ...
- 如何编写高质量JavaScript代码
书写可维护的代码(Writing Maintainable Code ) 软件bug的修复是昂贵的,并且随着时间的推移,这些bug的成本也会增加,尤其当这些bug潜伏并慢慢出现在已经发布的软件中时.当 ...
- <深入理解JavaScript>学习笔记(1)_编写高质量JavaScript代码的基本要点
注:本文是拜读了 深入理解JavaScript 之后深有感悟,故做次笔记方便之后查看. JQuery是一个很强大的JavaScript 类库,在我刚刚接触JavaScript的就开始用了. JQuer ...
- 高质量JavaScript代码书写基本要点
翻译-高质量JavaScript代码书写基本要点 by zhangxinxu from http://www.zhangxinxu.com本文地址:http://www.zhangxinxu.com/ ...
- [转] 翻译-高质量JavaScript代码书写基本要点 ---张鑫旭
by zhangxinxu from http://www.zhangxinxu.com本文地址:http://www.zhangxinxu.com/wordpress/?p=1173 原文作者:St ...
随机推荐
- Python学习-22.Python中的函数——type
type函数可以检测任何值或变量的类型. 例子: def printType(var): print(type(var)) class TestClass: pass printType(1) pri ...
- Python学习-2.安装IDE
Python安装包中已经包含了一个IDE了,叫IDLE,可以在Python的安装目录内找到路径为 ./Lib/idlelib/idle.bat 或者可以在开始菜单中找到. 但是这个IDE功能很弱,缺少 ...
- web api 多版本控制重要的两个类
1.版本路径替换 public class ReplaceVersionWithExactValueInPath : IDocumentFilter { public void ...
- IIS 绑定 HTTPS 域名
HTTPS为SSL安全通道,虽然并不清楚具体有什么用,但至少网站看上去比HTTP上档次,访问速度也没什么影响,所以有条件的话,还是做下,可以做噱头忽悠人. WIN2008系统 因为端口443冲突,只能 ...
- 【slenium专题】Webdriver同步设置
Webdriver同步设置常用等待类主要如下图所示 注:support.ui包内类主要实现显性等待功能,timeouts()内方法主要实现隐性等待功能 一.线程休眠 Thread.sleep(long ...
- TOJ2470
#include <stdio.h> struct node{ int x; int y; int step; }first; int zx[4]={-1,0,1,0}; int zy[4 ...
- OSX - libc++究竟是啥?
libc++是什么? libc++是C++标准库的实现! libc++ is an implementation of the C++ standard library, targeting C++ ...
- “全栈2019”Java多线程第十八章:同步代码块双重判断详解
难度 初级 学习时间 10分钟 适合人群 零基础 开发语言 Java 开发环境 JDK v11 IntelliJ IDEA v2018.3 文章原文链接 "全栈2019"Java多 ...
- ElasticSearch的概念解析
ElasticSearch是面向文档的,它不仅仅可以存储整个对象或则文档(document),还会索引(index)每个文档的内容使它可以被快速的检索.ElasticSearch和关系型数据库的对比如 ...
- (转)C# Enum,Int,String的互相转换 枚举转换--非常实用
Enum为枚举提供基类,其基础类型可以是除 Char 外的任何整型.如果没有显式声明基础类型,则使用 Int32.编程语言通常提供语法来声明由一组已命名的常数和它们的值组成的枚举. 注意:枚举类型的基 ...