网站建设时间推进表模板,wordpress验证码失效,源码网站php,网站设计代码持续更新地址 https://wdd.js.org/js-best-pr... 1. 风格 一千个读者有一千个哈姆雷特#xff0c;每个人都有自己的code style。我也曾为了要不要加分号给同事闹个脸红脖子粗#xff0c;实际上有必要吗#xff1f; 其实JavaScript已经有了比较流行的几个风格 JavaScript Sta… 持续更新地址 https://wdd.js.org/js-best-pr... 1. 风格 一千个读者有一千个哈姆雷特每个人都有自己的code style。我也曾为了要不要加分号给同事闹个脸红脖子粗实际上有必要吗 其实JavaScript已经有了比较流行的几个风格 JavaScript Standard Style Google JavaScript Style GuideAirbnb JavaScript Style Guide我自己使用的是JavaScript Standard Style, 我之所以使用这个是因为它有一些工具。可以让你写完代码后一旦保存就自动帮你把你的风格的代码修正成标准分割而不是死记硬背应该怎么写。看完这个页面你就应该立马爱上JavaScript Standard Style , 如果你用vscode, 恰好你有写vue, 你想在.vue文件中使用standard风格那么你需要看看这篇文章 2. 可维护性 很多时候我们不是从零开始开发新代码。而是去维护别人的代码以他人的工作成果为基础。确保自己的代码可维护是赠人玫瑰手留余香的好事。一方面让别人看的舒服另一方面也防止自己长时间没看过自己的代码自己都难以理解。 2.1. 什么是可维护代码 可维护的代码的一些特征 可理解易于理解代码的用途 可适应数据的变化不需要完全重写代码 可扩展要考虑未来对核心功能的扩展 可调试给出足够的信息让调试的时候确定问题所在 不可分割函数的功能要单一功能粒度不可分割可复用性增强2.2. 代码约定 2.2.1. 可读性 统一的缩进方式注释空白行2.2.1.1. 缩进 一般使用4个空格不用制表符的原因是它在不同编辑器里显示效果不同2.2.1.2. 注释哪些地方需要注释 函数和方法大段代码复杂的算法hack2.2.1.3. 空白行哪些地方需要空白行 方法之间方法里的局部变量和第一个语句之间单行或者多行注释方法内衣个逻辑单元之间// Good
if (wl wl.length) {for (i 0, l wl.length; i l; i) {p wl[i];type Y.Lang.type(r[p]);if (s.hasOwnProperty(p)) {if (merge type object) {Y.mix(r[p], s[p]);} else if (ov || !(p in r)) {r[p] s[p];}}}
} 2.2.2. 变量名和函数名 There are only two hard problem in Computer Science cache invalidation and naming things.---Phil Karlton 驼峰式命名变量名以名词开头方法名以动词开头常量全部大写构造函数以大写字母开头jQuery对象以$符号开头自定义事件处理函数以“on”开头// Good
var count 10;
var myName wdd;
var found true;// Bad: Easily confused with functions
var getCount 10;
var isFound true;// Good
function getName() {return myName;
}// Bad: Easily confused with variable
function theName() {return myName;
}// Bad:
var btnOfSubmit $(#submit);// Good:
var $btnOfSubmit $(#submit);// Bad:给App添加一个处理聊天事件的函数一般都是和websocket服务端推送消息相关
App.addMethod(createChat,function(res){App.log(res);
});
// Bad: 此处调用,这里很容易误以为这个函数是处理创建聊天的逻辑函数
App.createChat();// Good:
App.addMethod(onCreateChat,function(res){App.log(res);
});
// Good此处调用
App.onCreateChat(); 变量命名不仅仅是一种科学更是一种艺术。总之要短小精悍见名知意。有些名词可以反应出变量的类型。 2.2.2.1. 变量名 名词数据类型含义count, length,size数值name, title,message字符串i, j, k用来循环car,person,student,user对象success,fail布尔值payloadpost数据的请求体method请求方式2.2.2.2. 函数名 动词含义canFunction returns a booleanhasFunction returns a booleanisFunction returns a booleangetFunction returns a nonbooleanset Function is used to save a value2.2.2.3. 一些与函数名搭配的常用动词 动词用法send发送resend重发validate验证query查询create创建add添加delete删除remove移除insert插入update更新编辑copy复制render渲染close关闭open开启clear清除edit编辑query查询on当事件发生list渲染一个列表如用户列表renderUsersList()content渲染内容如用户详情的页面 renderUserContent()2.2.2.4. 接口常用的动词 对于http请求的最常用的四种方法get,post,put,delete有一些常用的名词与其对应 含义请求方法词语栗子增加postcreatecreateUser,createCall删除deletedeletedeleteUser修改putupdateupdateUser,updateProfile查询getget,querygetUser,queryUser(无条件查询使用get有条件查询使用query)2.2.2.5. 学会使用单复数命名函数 函数名含义getUser()获取一个用户一般是通过唯一的id来获取getUsers()获取一组用户一般是通过一些条件来获取createUser()创建一个用户createUsers()创建一组用户2.2.2.6. 常量 var MAX_COUNT 10;
var URL http://www.nczonline.net/; 2.2.2.7. 构造函数 // Good
function Person(name) {this.name name;
}
Person.prototype.sayName function() {alert(this.name);
};
var me new Person(wdd); 2.2.2.8. 底层http请求接口函数 建议使用“_”开头例如App._getUsers();而对于接口函数的封装例如App.getUsers(),内部逻辑调用App._getUsers();2.2.3. 文件名 全部使用小写字母单词之间的间隔使用“-”eg app-main.js
app-event.js
app-user-manger.js 2.2.4. 文件归类 自己写的js文件最好和引用的一些第三方js分别放置在不同的文件夹下。 2.2.5. 千万别用alert alert的缺点 如果你用alert来显示提醒消息那么用户除了点击alert上的的确定按钮外就只能点击上面的关闭或者选择禁止再选择对话框除此以外什么都不能操作。有些浏览器如果禁止了alert的选项那么你的alert是不会显示的如果你在try catch语句里使用alert那么console里将不会输出错误信息你都没办法查看错误的详细原因以及储出错的位置。更优雅的提醒方式 console.log() 普通提示消息console.error() 错误提示消息console.info() 信息提示消息console.warn() 警告提示消息2.3. 松散耦合 html文件中尽可能避免写js语句尽量避免在js更改某个css类的属性而使用更改类的方法不要在css中写js的表达式解耦应用逻辑和事件处理程序2.3.1. 将应用逻辑和事件处理程序的解耦 //一般事件订阅的写法以jQuery的写法为栗子
$(document).on(click,#btn-get-users,function(event){event.stopPropagation();//下面的省略号表示执行获取所有用于并显示在页面上的逻辑// Bad.........//
}); 如果增加了需求当点击另外一个按钮的时候也要执行获取所有用户并显示在页面上那么上面省略的代码又要复制一份。如果接口有改动那么需要在两个不同的地方都要修改。所以应该这样。 $(document).on(click,#btn-get-users,function(event){event.stopPropagation();//将应用逻辑分离在其他个函数中// GoodApp.getUsers();App.renderUsers();
}); 2.3.2. 松散解耦规则 不要将event对象传给其他方法只传递来自event对象中的某些数据任何事件处理程序都应该只处理事件然后把处理转交给应用逻辑。2.3.3. 将异步请求和数据处理解耦 // Bad
ReqApi.tenant.queryUsers({},function(res){if(!res.success){console.error(res);return;}//对数据的处理.........
}); 上面代码对数据的处理直接写死在异步请求里面如果换了一个请求但是数据处理方式是一样的那么又要复制一遍数据处理的代码。最好的方式是将数据处理模块化成为一个函数。 // Good
ReqApi.tenant.queryUsers({},function(res){if(!res.success){console.error(res);return;}//对数据的处理App.renderUsers(res.data);
}); 异步请求只处理请求不处理数据。函数的功能要专一功能粒度不可分割。 2.3.4. 不要将某个变量写死在函数中尽量使用参数传递进来 如果你需要一个函数去验证输入框是否是空如下。这种方式就会绑定死了这个只能验证id为test的输入框换成其他的就不行 // bad
function checkInputIsEmpty(){var value $(#test).val();if(value){return true;}else{return false;}
}// good
function isEmptyInput(id){var value $(#id).val();if(value){return true;}else{return false;}
} 2.4. 编程实践 2.4.1. 尊总对象所有权 javascript动态性质是的几乎任何东西在任何时间都能更改这样就很容易覆写了一些默认的方法。导致一些灾难性的后果。如果你不负责或者维护某个对象那么你就不能对它进行修改。 不要为实例或原型添加属性不要为实例或者原型添加方法不要重定义存已存在的方法2.4.2. 避免全局变量 // Bad 两个全局变量
var name wdd;
funtion getName(){console.log(name);
}// Good 一个全局变量
var App {name:wdd,sayName:funtion(){console.log(this.name);//如果这个函数当做回调数使用这个this可能指向window,}
}; 单一的全局变量便是命名空间的概念例如雅虎的YUI,jQuery的$等。 2.4.3. 避免与null进行比较 funtion sortArray(values){// 避免if(values ! null){values.sort(comparator);}
} function sortArray(values){// 推荐if(values instanceof Array){values.sort(compartor);}
} 2.4.3.1. 与null进行比较的代码可以用以下技术进行替换 如果值是一个应用类型使用instanceof操作符检查其构造函数如果值是基本类型使用typeof检查其类型如果是希望对象包含某个特定的方法名则只用typeof操作符确保指定名字的方法存在于对象上。代码中与null比较越少就越容易确定代码的目的消除不必要的错误。 2.4.4. 从代码中分离配置文件 配置数据是一些硬代码(hardcoded)看下面的栗子 function validate(value){if(!value){alert(Invalid value);location.href /errors/invalid.php;}
} 上面代码里有两个配置数据一个是UI字符串(Invalid value),另一个是一个Url(/error/invalid.php)。如果你把他们写死在代码里那么如果当你需要修改这些地方的时候那么你必须一处一处的检查并修改而且还可能会遗漏。 2.4.4.1. 所以第一步是要区分哪些代码应该写成配置文件的形式 显示在UI元素中的字符串URL一些重复的唯一值一些设置变量任何可能改变的值2.4.4.2. 一些例子 var Config {MSG_INVALID_VALUE:Invalid value,URL_INVALID:/errors/invalid.php
} 2.4.5. 调试信息开关 在开发过程中可能随处留下几个console.log,或者alert语句这些语句在开发过程中是很有价值的。但是项目一旦进入生产环境过多的console.log可能影响到浏览器的运行效率过多的alert会降低程序的用户体验。而我们最好不要在进入生产环境前一处一处像扫雷一样删除或者注释掉这些调试语句。 最好的方式是设置一个开关。 //全局命令空间
var App {debug:true,log:function(msg){if(debug){console.log(msg);}},alert:function(msg){if(debug){alert(msg);}}
};//使用
App.log(获取用户信息成功);
App.alert(密码不匹配);//关闭日志输出与alert
App.debug false; 2.4.6. 使用jQuery Promise 没使用promise之前的回调函数写法 // bad没使用promise之前的回调函数写法
function sendRequest(req,successCallback,errorCallback){var inputData req.data || {};inputData JSON.stringify(inputData);$.ajax({url:req.basereq.destination,type:req.type || get,headers:{sessionId:session.id},data:inputData,dataType:json,contentType : application/json; charsetUTF-8,success:function(data){successCallback(data);},error:function(data){console.error(data);errorCallback(data);}});
}//调用
sendRequest(req,function(res){...
},function(res){...
}); 使用promise之后 function sendRequest(req){var dfd $.Deferred();var inputData req.data || {};inputData JSON.stringify(inputData);$.ajax({url:req.basereq.destination,type:req.type || get,headers:{sessionId:session.id},data:inputData,dataType:json,contentType : application/json; charsetUTF-8,success:function(data){dfd.resolve(data);},error:function(data){dfd.reject(data);}});return dfd.promise();
}//调用
sendRequest(req)
.done(function(){//请求成功...
})
.fail(function(){//请求失败...
}); 2.4.7. 显示错误提醒不要给后端接口背锅 假如前端要去接口获取用户信息并显示出来如果你的请求格式是正确的但是接口返回400以上的错误你必须通过提醒来告知测试这个错误是接口的返回错误而不是前端的逻辑错误。 2.4.8. REST化接口请求 对资源的操作包括获取、创建、修改和删除资源这些操作正好对应HTTP协议提供的GET、POST、PUT和DELETE方法。 对应方式 请求类型接口前缀GET.get,POST.create 或者 .getPUT.updateDELETE.delete说明 有些接口虽然是获取某一个资源但是它使用的却是POST请求所以建议使用.get比较好示例 // 与用户相关的接口
App.api.user {};// 获取一个用户: 一般来说是一个指定的Id例如userId
App.api.user.getUser function(){...
};// 获取一组用户: 一般来说是一些条件获取条件下的用户筛选符合条件的用户
App.api.user.getUsers function(){...
};// 创建一个用户
App.api.user.createUser function(){};// 创建一组用户
App.api.user.createUsers function(){};// 更新一个用户
App.api.user.updateUser function(){};// 更新一组用户
App.api.user.updateUsers function(){};// 更新一个用户
App.api.user.updateUser function(){};// 更新一组用户
App.api.user.updateUsers function(){};// 删除一个用户
App.api.user.deleteUser function(){};// 删除一组用户
App.api.user.deleteUsers function(){}; 3. 性能 3.1. 注意作用域 避免全局查找避免with语句3.2. 选择正确的方法 优化循环 减值迭代从最大值开始在循环中不断减值的迭代器更加高效 简化终止条件由于每次循环过程都会计算终止条件所以必须保证它尽可能快。也就是避免其他属性查找 简化循环体由于循环体是执行最多的所以要确保其最大限度地优化。展开循环避免双重解释// **Bad** 某些代码求值
eval(alert(hello));// **Bad** 创建新函数
var sayHi new Function(alert(hello));// **Bad** 设置超时
setTimeout(alert(hello)); 性能的其他注意事项 原生方法较快switch语句较快可以适当的替换ifelse语句case 的分支不要超过128条 位运算符较快3.3. 最小化语句数 3.3.1. 多个变量声明(废弃) // 方式1Bad
var count 5;
var name wdd;
var sex male;
var age 10;// 方式2Good
var count 5,name wdd,sex male,age 10; 2017-03-07 理论上方式2可能要比方式1性能高一点。但是我在实际使用中这个快一点几乎是没什么感受的。就像你无法感受到小草的生长一样。反而可读性更为重要。所以每行最好只定义一个变量并且每行都有一个var,并用分号结尾。 3.3.2. 插入迭代值 // Good
var name values[i]; 3.3.3. 使用数组和对象字面量 // Good
var values [a,b,c];var person {name:wdd,age:10
}; 只要有可能尽量使用数组和对象字面量的表达式来消除不必要的语句 3.4. 优化DOM交互 在JavaScript各个方面中DOM无疑是最慢的一部分。DOM操作与交互要消耗大量的时间。因为他们往往需要重新渲染整个页面或者某一部分。进一步说看似细微的操作也可能花很久来执行。因为DOM要处理非常多的信息。理解如何优化与DOM的交互可以极大的提高脚本完成的速度。 使用dom缓存技术最小化现场更新使用innerHTML插入大段html使用事件代理3.4.1. Dom缓存技术 调用频率非常高的dom查找可以将DOM缓存在于一个变量中 // 最简单的dom缓存var domCache {};function myGetElement(tag){return domCache[tag] domCache[tag] || $(tag);
} 3.5. 避免过长的属性查找设置一个快捷方式 // 先看下面的极端情况
app.user.mother.parent.home.name wdd
app.user.mother.parent.home.adderess 上海
app.user.mother.parent.home.weather 晴天// 更优雅的方式
var home app.user.mother.parent.home;
home.name wdd;
home.address 上海,
home.weather 晴天 注意使用上面的方式是有前提的必须保证app.user.mather.parent.home是一个对象因为对象是传递的引用。如果他的类型是一个基本类型例如number,string,boolean那么复制操作仅仅是值传递新定义的home的改变并不会影响到app.user.mather.parent.home的改变。 4. 快捷方式 4.1. 字符串转数字 4.1 4.1 4.2. 数字转字符 4.1 4.1 4.3. 字符串取整 4.99 | 0 4 5. 通用编码原则 建议读者自行扩展 DRY(dontt repeat yoursele: 不要重复你自己)高内聚低耦合开放闭合最小意外单一职责(single responsibility)6. 高级技巧 6.1. 安全类型检测 javascript内置类型检测并不可靠safari某些版本4typeof正则表达式返回为function建议使用Object.prototype.toString.call()方法检测数据类型 function isArray(value){return Object.prototype.toString.call(value) [object Array];
}function isFunction(value){return Object.prototype.toString.call(value) [object Function];
}function isRegExp(value){return Object.prototype.toString.call(value) [object RegExp];
}function isNativeJSON(){return window.JSON Object.prototype.toString.call(JSON) [object JSON];
} 对于ie中一COM对象形式实现的任何函数isFunction都返回false因为他们并非原生的javascript函数。 在web开发中能够区分原生与非原生的对象非常重要。只有这样才能确切知道某个对象是否有哪些功能 以上所有的正确性的前提是Object.prototype.toString没有被修改过 6.2. 作用域安全的构造函数 function Person(name){this.name name;
}//使用new来创建一个对象
var one new Person(wdd);//直接调用构造函数
Person(); 由于this是运行时分配的如果你使用new来操作this指向的就是one。如果直接调用构造函数那么this会指向全局对象window,然后你的代码就会覆盖window的原生name。如果有其他地方使用过window.name, 那么你的函数将会埋下一个深藏的bug。 那么如何才能创建一个作用域安全的构造函数 function Person(name){if(this instanceof Person){this.name name;}else{return new Person(name);}
} 6.3. 惰性载入函数 假设有一个方法X在A类浏览器里叫A,在b类浏览器里叫B,有些浏览器并没有这个方法,你想实现一个跨浏览器的方法。 惰性载入函数的思想是在函数内部改变函数自身的执行逻辑 function X(){if(A){return new A();}else{if(B){return new B();}else{throw new Error(no A or B);}}
} 换一种写法 function X(){if(A){X function(){return new A();};}else{if(B){X function(){return new B();};}else{throw new Error(no A or B);}}return new X();
} 6.4. 防篡改对象 6.4.1. 不可扩展对象 Object.preventExtensions // 下面代码在谷歌浏览器中执行var person {name: wdd};
undefinedObject.preventExtensions(person);
Object {name: wdd}person.age 10
10person
Object {name: wdd}Object.isExtensible(person)
false 6.4.2. 密封对象Object.seal 密封对象不可扩展并且不能删除对象的属性或者方法。但是属性值可以修改。 var one {name: hihi}
undefinedObject.seal(one)
Object {name: hihi}one.age 12
12one
Object {name: hihi}delete one.name
falseone
Object {name: hihi} 6.4.3. 冻结对象 Object.freeze 最严格的防篡改就是冻结对象。对象不可扩展而且密封不能修改。只能访问。 6.5. 高级定时器 6.5.1. 函数节流 函数节流的思想是某些代码不可以没有间断的连续重复执行 var processor {timeoutId: null,// 实际进行处理的方法performProcessing: function(){...},// 初始化调用方法process: function(){clearTimeout(this.timeoutId);var that this;this.timeoutId setTimeout(function(){that.performProcessing();}, 100);}
}// 尝试开始执行
processor.process(); 6.5.2. 中央定时器 页面如果有十个区域要动态显示当前时间一般来说可以用10个定时来实现。其实一个中央定时器就可以搞定。 中央定时器动画 demo地址http://wangduanduan.coding.me... var timers {timerId: 0,timers: [],add: function(fn){this.timers.push(fn);},start: function(){if(this.timerId){return;}(function runNext(){if(timers.timers.length 0){for(var i0; i timers.timers.length ; i){if(timers.timers[i]() false){timers.timers.splice(i, 1);i--;}}timers.timerId setTimeout(runNext, 16);}})();},stop: function(){clearTimeout(timers.timerId);this.timerId 0;}}; 7. 函数式编程 推荐阅读JS函数式编程中文版 8. HTML的告诫 使用input的时候一定要加上maxlength属性。你以为只需要输入一个名字的地方用户可能复制一篇文章放进去。从input取值的时候最好去除一下首尾空格9. ajax的告诫 ajax在使用的时候例如点击按钮获取某个列表。需要注意以下方面 ajax请求还没有结束时按钮一定要disabled防止多次点击。请求结束时才去掉按钮的disabled属性。请求没结束的时候一定要显示一个gif的动画告诉用户请求还在loading。不要让用户以为这垃圾程序又卡死了。请求的结果如果是空的一定要告诉用户: 很抱歉暂时没有查询到相关记录之类的话语。不要给一个空白页面给用户。最好考虑到请求报错的情况给出友好的错误提醒。10. 代码整洁之道 10.1. 函数整洁 尽量将所有代码封装在函数中不要暴露全局变量每个函数的函数体中代码行越少越好最好一个函数中就一句代码11. 工程化与模块化 11.1. 前端构建工具必不可少 11.1.1. webpack 11.1.2. rollup 11.1.3. parcel 12. 协议 TCP IP HTTP 如果你认为前端不需要关于协议的知识那么你就是大错特错了。其实不仅仅是前端所有的开发者都应该学习底层的协议。因为他们是互联网通信的基石。 推荐三本必读的书籍 HTTP权威指南图解TCP/IP : 第5版图解HTTP或者你一也可以看看关于协议方面的一些问题以及如果你遇到过你是否知道如何解决 可能被遗漏的https与http的知识点哑代理 - TCP链接高Recv-Q内存泄露的罪魁祸首13. 推荐深度阅读 13.1. 推荐阅读技术书籍 编写可读代码的艺术编写可维护的JavaScriptJavaScript忍者秘籍第2版JavaScript语言精粹HTTP权威指南图解TCP/IP : 第5版图解HTTP代码整洁之道13.2. 推荐阅读在线文章 Writing Fast, Memory-Efficient JavaScriptJavaScript 秘密花园You-Dont-Know-JS关于缓存你应该链接的一切JS函数式编程中文版13.3. 技术之外 筑巢引凤-高黏度社会化网站设计秘诀黑客与画家14. 参考文献 JavaScript高级程序设计(第3版) 【美】尼古拉斯·泽卡斯Maintainable JavaScript (英文版) Nicholas C. Zakas(其实和上边那本书应该是同一个人)JavaScript忍者秘籍 John Resig / Bear Bibeault John Resig 大名鼎鼎jQuery的创造者百度前端研发部 文档与源码编写风格js函数式编程指南JavaScript SDK Design Guide: JavaScript-sdk设计指南