JavaScript best practices JS最佳实践
JavaScript best practices JS最佳实践
0 简介
最佳实践起初比较棘手,但最终会让你发现这是非常明智之举。
1.合理命名方法及变量名,简洁且可读
var someItem = 'some string',
anotherItem = 'another string',
oneMoreItem = 'one more string';
let [ , , third] = ["foo", "bar", "baz"];
let [x, y = 'b'] = ['a'];
var {x, y = 5} = {x: 1};
2. 避免全局变量
var myNameSpace = {
current:null,
init:function(){...},
change:function(){...},
verify:function(){...}
}
//or
myNameSpace = function(){
var current = null;
function init(){...}
function change(){...}
function verify(){...}
}();
// 暴露出去
myNameSpace = function(){
var current = null;
function verify(){...}
return{
init:function(){...}
change:function(){...}
}
}();
// 全部私有化
(function(){
var current = null;
function init(){...}
function change(){...}
function verify(){...}
})();
3. 坚持严格模式
严格模式:https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Strict_mode
严格模式主要有以下限制。
- 变量必须声明后再使用
- 函数的参数不能有同名属性,否则报错
- 不能使用with语句
- 不能对只读属性赋值,否则报错
- 不能使用前缀0表示八进制数,否则报错
- 不能删除不可删除的属性,否则报错
- 不能删除变量delete prop,会报错,只能删除属性delete global[prop]
- eval不会在它的外层作用域引入变量
- eval和arguments不能被重新赋值
- arguments不会自动反映函数参数的变化
- 不能使用arguments.callee
- 不能使用arguments.caller
- 禁止this指向全局对象
- 不能使用fn.caller和fn.arguments获取函数调用的堆栈
- 增加了保留字(比如protected、static和interface)
清洁有效的代码意味着更少的confusing bugs 去fix,更容易地递交给其他开发人员和更强的代码安全性。
4. 关键点写注释
起初可能看起来是不必要的,但是相信我,你最好尽可能地注释你的代码
5. 避免或减少其它技术(DOM),优化DOM操作
网页如何生成的?

大致分以下五步:
- HTML代码转化成DOM;
- CSS代码转化成CSSOM(CSS Object Model);
- 结合DOM和CSSOM,生成一棵渲染树(包含每个节点的视觉信息);
- 渲染(render)---耗时:
- 生成布局(flow),将所有渲染树的所有节点进行平面合成(layout);
- 绘制(paint)在屏幕;
提升页面性能技巧
- 避免交叉DOM的读写操作,读写分别放在一起;
- 若某样式通过重排获得,最好缓存一下;
- 集中改变样式
el.className += " theclassname";
el.style.cssText += "; left: " + left + "px; top: " + top + "px;";
- 尽量使用离线DOM而非网页里的DOM,改变元素样式,如cloneNode;
- 先将元素的display设为none,再做操作
- position属性为absolute或fixed的元素,重排的开销会比较小,因为不用考虑它对其他元素的影响。
- 只在必要的时候,才将元素的display属性为可见,因为不可见的元素不影响重排和重绘。另外,visibility : hidden的元素只对重绘有影响,不影响重排
- 使用虚拟DOM的脚本库,比如React等。
- 使用 window.requestAnimationFrame()、window.requestIdleCallback() 这两个方法调节重新渲染
常见做法
1.集中处理样式读写:
var left = div.offsetLeft;
var top = div.offsetTop;
div.style.left = left + 10 + "px";
div.style.top = top + 10 + "px";
样式表越简单,重排和重绘就越快。重排和重绘的DOM元素层级越高,成本就越高。table元素的重排和重绘成本,要高于div元素
2.使用模版字符串:
$('#result').append(`
There are <b>${basket.count}</b> items
in your basket, <em>${basket.onSale}</em>
are on sale!
`);
const tmpl = addrs => `
<table>
${addrs.map(addr => `
<tr><td>${addr.first}</td></tr>
<tr><td>${addr.last}</td></tr>
`).join('')}
</table>
`;
6. 首选使用字面量,推荐短路运算符
var cow = {
colour:'brown',
commonQuestion:'What now?',
moo:function(){
console.log('moo);
},
feet:4,
accordingToLarson:'will take over the world',
name:null,
};
var awesomeBands = [
'Bad Religion',
'Dropkick Murphys',
'Flogging Molly',
'Red Hot Chili Peppers',
'Pornophonique'
];
var x = v || 10;
//动态添加属性或方法
cow.name = 'xx';//避免
//推荐,但最佳在定义对象时将name属性值设为null
Object.defineProperty(cow,'name',{
configurable:false,
writable:false,
enumerable:true,
value:'xx'
});
7. 模块化去耦合---一个方法一个任务
function createLink(text,url){
var newLink = document.createElement('a');
newLink.setAttribute('href',url);
newLink.appendChild(document.createTextNode(text));
return newLink;
}
function createMenu(){
var menu = document.getElementById('menu');
var items = [
{t:'Home',u:'index.html'},
{t:'Sales',u:'sales.html'},
{t:'Contact',u:'contact.html'}
];
for(var i=0;i<items.length;i++){
var item = createLink(items.t,items.u);
item.className = 'menu-item';
menu.appendChild(item);
}
}
ES6 Module语法 http://es6.ruanyifeng.com/#docs/module
8.避免eval,避免'=='
对于那些不熟悉的人,“eval”函数可以让我们访问JavaScript的编译器。 事实上,我们可以通过将字符串作为参数传递给eval,返回其的结果。
这不仅会大大降低您的脚本的性能,还会造成巨大的安全风险,因为它给文本中传递的功能太多了。 避免!
if(a===b){
//...
}
9. 避免多重嵌套
function renderProfiles(o){
var out = document.getElementById('profiles');
for(var i=0;i<o.members.length;i++){
var ul = document.createElement('ul');
var li = document.createElement('li');
li.appendChild(document.createTextNode(data.members[i].name));
li.appendChild(addMemberData(o.members[i]));
}
out.appendChild(ul);
}
function addMemberData(member){
var ul = document.createElement('ul');
for(var i=0;i<member.data.length;i++){
var li = document.createElement('li');
li.appendChild(
document.createTextNode(
member.data[i].label + ''+
member.data[i].value
)
);
}
ul.appendChild(li);
return ul;
}
10. 优化循环体
// Declare Variables Outside of the For Statement
var i,
len = someArray.length,
container = document.getElementById('container');
for(i = 0; i < len; i++) {
container.innerHtml += 'my number: ' + i;
console.log(i);
}
11. 不要相信任何数据
在谈论代码和数据安全性时要牢记的要点之一是不要信任任何数据
确保进入系统的所有数据都是干净的,正是您需要的。 这在后端写出从URL检索的参数时最重要。 在JavaScript中,测试发送到您的函数的参数类型(如使用typeof 或instanceof)非常重要。
function buildMemberList(members){
if(typeof members === 'object' &&
typeof members.slice === 'function'){
var all = members.length;
var ul = document.createElement('ul');
for(var i=0;i<all;i++){
var li = document.createElement('li');
li.appendChild(document.createTextNode(members[i].name));
ul.appendChild(li);
}
return ul;
}
}
//循环访问对象中的元素时,for-in将遍历出对象原型的属性,为了解决这个问题,请务必将代码封装在过滤信息的if语句中
for(key in object) {
if(object.hasOwnProperty(key) {
//...then do something...
}
}
12. 不用简写
避免:
if(someVariableExists)
x = false
推荐:
if(someVariableExists) {
x = false;
anotherFunctionCall();
}
13. 使用JS Lint http://www.jslint.com/
JSLint takes a JavaScript source and scans it. If it finds a problem, it returns a message describing the problem and an approximate location within the source. The problem is not necessarily a syntax error, although it often is. JSLint looks at some style conventions as well as structural problems. It does not prove that your program is correct. It just provides another set of eyes to help spot problems."
- JSLint Documentation
14. 将脚本置底 ---使用户尽可能快地加载页面
15. 拥抱ES6+最佳实践 http://es6.ruanyifeng.com/#docs/style
16. 站在巨人肩上,阅读优秀源码
参考点: ES6,模块化代码封装,可配置性,可扩展性,变量命名方式,核心注释
import Util from './util'
/**
* --------------------------------------------------------------------------
* Bootstrap (v4.0.0-beta): carousel.js
* Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE)
* --------------------------------------------------------------------------
*/
const Carousel = (($) => {
/**
* ------------------------------------------------------------------------
* Constants
* ------------------------------------------------------------------------
*/
const NAME = 'carousel'
const VERSION = '4.0.0-beta'
const DATA_KEY = 'bs.carousel'
const EVENT_KEY = `.${DATA_KEY}`
const DATA_API_KEY = '.data-api'
const JQUERY_NO_CONFLICT = $.fn[NAME]
const TRANSITION_DURATION = 600
const ARROW_LEFT_KEYCODE = 37 // KeyboardEvent.which value for left arrow key
const ARROW_RIGHT_KEYCODE = 39 // KeyboardEvent.which value for right arrow key
const TOUCHEVENT_COMPAT_WAIT = 500 // Time for mouse compat events to fire after touch
const Default = {
interval : 5000,
keyboard : true,
slide : false,
pause : 'hover',
wrap : true
}
const DefaultType = {
interval : '(number|boolean)',
keyboard : 'boolean',
slide : '(boolean|string)',
pause : '(string|boolean)',
wrap : 'boolean'
}
const Direction = {
NEXT : 'next',
PREV : 'prev',
LEFT : 'left',
RIGHT : 'right'
}
const Event = {
SLIDE : `slide${EVENT_KEY}`,
SLID : `slid${EVENT_KEY}`,
KEYDOWN : `keydown${EVENT_KEY}`,
MOUSEENTER : `mouseenter${EVENT_KEY}`,
MOUSELEAVE : `mouseleave${EVENT_KEY}`,
TOUCHEND : `touchend${EVENT_KEY}`,
LOAD_DATA_API : `load${EVENT_KEY}${DATA_API_KEY}`,
CLICK_DATA_API : `click${EVENT_KEY}${DATA_API_KEY}`
}
const ClassName = {
CAROUSEL : 'carousel',
ACTIVE : 'active',
SLIDE : 'slide',
RIGHT : 'carousel-item-right',
LEFT : 'carousel-item-left',
NEXT : 'carousel-item-next',
PREV : 'carousel-item-prev',
ITEM : 'carousel-item'
}
const Selector = {
ACTIVE : '.active',
ACTIVE_ITEM : '.active.carousel-item',
ITEM : '.carousel-item',
NEXT_PREV : '.carousel-item-next, .carousel-item-prev',
INDICATORS : '.carousel-indicators',
DATA_SLIDE : '[data-slide], [data-slide-to]',
DATA_RIDE : '[data-ride="carousel"]'
}
/**
* ------------------------------------------------------------------------
* Class Definition
* ------------------------------------------------------------------------
*/
class Carousel {
constructor(element, config) {
define( [
"../core",
"../var/document",
"../core/readyException",
"../deferred"
], function( jQuery, document ) {
"use strict";
// The deferred used on DOM ready
var readyList = jQuery.Deferred();
jQuery.fn.ready = function( fn ) {
readyList
.then( fn )
// Wrap jQuery.readyException in a function so that the lookup
// happens at the time of error handling instead of callback
// registration.
.catch( function( error ) {
jQuery.readyException( error );
} );
return this;
};
参考点: ES6,模块化,可配置可扩展,MVVM核心思想,Viral DOM, 数据类型判断
/* @flow */
import {
no,
noop,
identity
} from 'shared/util'
import { LIFECYCLE_HOOKS } from 'shared/constants'
export type Config = {
// user
optionMergeStrategies: { [key: string]: Function };
silent: boolean;
productionTip: boolean;
performance: boolean;
devtools: boolean;
errorHandler: ?(err: Error, vm: Component
//...
//index.js
import Vue from './instance/index'
import { initGlobalAPI } from './global-api/index'
import { isServerRendering } from 'core/util/env'
initGlobalAPI(Vue)
Object.defineProperty(Vue.prototype, '$isServer', {
get: isServerRendering
})
Object.defineProperty(Vue.prototype, '$ssrContext', {
get () {
/* istanbul ignore next */
return this.$vnode && this.$vnode.ssrContext
}
})
Vue.version = '__VERSION__'
export default Vue
export const hasSymbol =
typeof Symbol !== 'undefined' && isNative(Symbol) &&
typeof Reflect !== 'undefined' && isNative(Reflect.ownKeys)
/**
* Defer a task to execute it asynchronously.
*/
export const nextTick = (function () {
const callbacks = []
let pending = false
let timerFunc
function nextTickHandler () {
pending = false
const copies = callbacks.slice(0)
callbacks.length = 0
for (let i = 0; i < copies.length; i++) {
copies[i]()
}
}
// the nextTick behavior leverages the microtask queue, which can be accessed
// via either native Promise.then or MutationObserver.
// MutationObserver has wider support, however it is seriously bugged in
// UIWebView in iOS >= 9.3.3 when triggered in touch event handlers. It
// completely stops working after triggering a few times... so, if native
// Promise is available, we will use it:
/* istanbul ignore if */
if (typeof Promise !== 'undefined' && isNative(Promise)) {
var p = Promise.resolve()
var logError = err => { console.error(err) }
timerFunc = () => {
p.then(nextTickHandler).catch(logError)
// in problematic UIWebViews, Promise.then doesn't completely break, but
// it can get stuck in a weird state where callbacks are pushed into the
// microtask queue but the queue isn't being flushed, until the browser
// needs to do some other work, e.g. handle a timer. Therefore we can
// "force" the microtask queue to be flushed by adding an empty timer.
if (isIOS) setTimeout(noop)
}
} else if (typeof MutationObserver !== 'undefined' && (
isNative(MutationObserver) ||
// PhantomJS and iOS 7.x
MutationObserver.toString() === '[object MutationObserverConstructor]'
)) {
// use MutationObserver where native Promise is not available,
// e.g. PhantomJS IE11, iOS7, Android 4.4
var counter = 1
var observer = new MutationObserver(nextTickHandler)
var textNode = document.createTextNode(String(counter))
observer.observe(textNode, {
characterData: true
})
timerFunc = () => {
counter = (counter + 1) % 2
textNode.data = String(counter)
}
function mergeData (to: Object, from: ?Object): Object {
if (!from) return to
let key, toVal, fromVal
const keys = Object.keys(from)
for (let i = 0; i < keys.length; i++) {
key = keys[i]
toVal = to[key]
fromVal = from[key]
if (!hasOwn(to, key)) {
set(to, key, fromVal)
} else if (isPlainObject(toVal) && isPlainObject(fromVal)) {
mergeData(toVal, fromVal)
}
}
return to
}
/**
* Normalize all injections into Object-based format
*/
function normalizeInject (options: Object) {
const inject = options.inject
if (Array.isArray(inject)) {
const normalized = options.inject = {}
for (let i = 0; i < inject.length; i++) {
normalized[inject[i]] = inject[i]
}
}
参考点:ES6, 数据类型判断,模块化
'use strict';
import type {ReactInstance} from 'ReactInstanceType';
import type {Fiber} from 'ReactFiber';
function getComponentName(
instanceOrFiber: ReactInstance | Fiber,
): string | null {
if (typeof instanceOrFiber.getName === 'function') {
// Stack reconciler
const instance = ((instanceOrFiber: any): ReactInstance);
return instance.getName();
}
if (typeof instanceOrFiber.tag === 'number') {
// Fiber reconciler
const fiber = ((instanceOrFiber: any): Fiber);
const {type} = fiber;
if (typeof type === 'string') {
return type;
}
if (typeof type === 'function') {
return type.displayName || type.name;
}
}
return null;
}
module.exports = getComponentName;
参考链接:
- [https://www.w3.org/wiki/JavaScript_best_practices][1]
- [https://code.tutsplus.com/tutorials/24-javascript-best-practices-for-beginners--net-5399][2]
- http://www.ruanyifeng.com/blog/2015/09/web-page-performance-in-depth.html
- http://domenicodefelice.blogspot.sg/2015/08/how-browsers-work.html
[1]: https://www.w3.org/wiki/JavaScript_best_practices
[2]: https://code.tutsplus.com/tutorials/24-javascript-best-practices-for-beginners--net-5399
JavaScript best practices JS最佳实践的更多相关文章
- require.js 最佳实践【转】
https://www.cnblogs.com/digdeep/p/4607131.html require.js是一个js库,相关的基础知识,前面转载了两篇博文:Javascript模块化编程(re ...
- require.js 最佳实践
require.js是一个js库,相关的基础知识,前面转载了两篇博文:Javascript模块化编程(require.js), Javascript模块化工具require.js教程,RequireJ ...
- Vue.js最佳实践
Vue.js最佳实践 第一招:化繁为简的Watchers 场景还原: created(){ this.fetchPostList() }, watch: { searchInputValue(){ t ...
- Vue.js最佳实践--给大量子孙组件传值(provide/inject)
开发中有个需求,有个Parent组件(例如div)下,输入框,下拉框,radiobutton等可编辑的子孙组件几百个,根据某个值统一控制Parent下面的所有控件的disabled状态 类似于这样,给 ...
- JavaScrtip之JS最佳实践
一.JavaScript之平稳退化 这边使用一个当用户点击某个页面内某个链接弹出一个新窗口的案例: JavaScript使用window对象的open()方法来创建新的浏览器窗口; window.op ...
- JavaScript的技巧和最佳实践
JavaScript是一个绝冠全球的编程语言,可用于Web开发.移动应用开发(PhoneGap.Appcelerator).服务器端开发 (Node.js和Wakanda)等等.JavaScript还 ...
- js最佳实践
JavaScript使用windows对象的open()方法来创建新的浏览器窗口,这个方法有三个参数:windows.open(url,name,features) 参数一:url:是想在新窗口里打开 ...
- [笔记]《JavaScript高级程序设计》- 最佳实践
一.可维护性 1 什么是可维护的代码 可理解性--其他人可以接受代码并理解它的意图和一般途径,而无需原开发人员的完整解释. 直观性--代码中的东西一看就能明白,不管其操作过程多么复杂. 可适应性--代 ...
- Vue.js最佳实践(五招让你成为Vue.js大师)
对大部分人来说,掌握Vue.js基本的几个API后就已经能够正常地开发前端网站.但如果你想更加高效地使用Vue来开发,成为Vue.js大师,那下面我要传授的这五招你一定得认真学习一下了. 第一招:化繁 ...
随机推荐
- 南阳ACM 题目517:最小公倍数 Java版
最小公倍数 时间限制:1000 ms | 内存限制:65535 KB 难度:3 描述 为什么1小时有60分钟,而不是100分钟呢?这是历史上的习惯导致. 但也并非纯粹的偶然:60是个优秀的数字,它 ...
- codevs 1332 上白泽慧音
1332 上白泽慧音 时间限制: 1 s 空间限制: 128000 KB 题目描述 Description 在幻想乡,上白泽慧音是以知识渊博闻名的老师.春雪异变导致人间之里的很多道路都被大 ...
- 洛谷P2860 [USACO06JAN]冗余路径Redundant Paths
题目描述 In order to get from one of the F (1 <= F <= 5,000) grazing fields (which are numbered 1. ...
- 【设计模式】 模式PK:命令模式VS策略模式
1.概述 命令模式和策略模式的类图确实很相似,只是命令模式多了一个接收者(Receiver)角色.它们虽然同为行为类模式,但是两者的区别还是很明显的.策略模式的意图是封装算法,它认为“算法”已经是一个 ...
- 计数排序Counting sort
注意与基数排序区分,这是两个不同的排序 计数排序的过程类似小学选班干部的过程,如某某人10票,作者9票,那某某人是班长,作者是副班长 大体分两部分,第一部分是拉选票和投票,第二部分是根据你的票数入桶 ...
- java与C#的基础语法区别--持续更新
1.判断字符串是否相等 java : equals()比较的是对象的内容(区分字母的大小写格式),但是如果使用“==”比较两个对象时,比较的是两个对象的内存地址,所以不相等.即使它们内容相等,但是不同 ...
- bzoj 2200: [Usaco2011 Jan]道路和航线——拓扑+dijkstra
Description Farmer John正在一个新的销售区域对他的牛奶销售方案进行调查.他想把牛奶送到T个城镇 (1 <= T <= 25,000),编号为1T.这些城镇之间通过R条 ...
- 贿赂囚犯 Bribe the prisoners ( 动态规划+剪枝)
一个监狱里有P个并排着的牢房,从左往右一次编号为1,2,-,P.最初所有牢房里面都住着一个囚犯.现在要释放一些囚犯.如果释放某个牢房里的囚犯,必须要贿赂两边所有的囚犯一个金币,直到监狱的两端或者空牢房 ...
- Java 中的静态内部类
静态内部类是 static 修饰的内部类,这种内部类的特点是: 1. 静态内部类不能直接访问外部类的非静态成员,但可以通过 new 外部类().成员 的方式访问 2. 如果外部类的静态成员与内部类的成 ...
- beego 相关
bee api bapi bee run -downdoc=true -docgen=true