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](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操作> 网页如何生成的?![http://www.ruanyifeng.com/blogimg/asset/2015/bg2015091501.png](http://www.ruanyifeng.com/blogimg/asset/2015/bg2015091501.png)大致分以下五步:1. HTML代码转化成DOM;2. CSS代码转化成CSSOM(CSS Object Model);3. 结合DOM和CSSOM,生成一棵渲染树(包含每个节点的视觉信息);4. 渲染(render)---耗时:    1. 生成布局(flow),将所有渲染树的所有节点进行平面合成(layout);    2. 绘制(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${basket.count}items  in your basket,${basket.onSale}are on sale!`);const tmpl = addrs => `    ${addrs.map(addr => `      `).join('')}

${addr.first}

${addr.last}

`;```## 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属性值设为nullObject.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对于那些不熟悉的人,“eval”函数可以让我们访问JavaScript的编译器。 事实上,我们可以通过将字符串作为参数传递给eval,返回其的结果。这不仅会大大降低您的脚本的性能,还会造成巨大的安全风险,因为它给文本中传递的功能太多了。 避免!```if(a===b){  //...}```## 9. 避免多重嵌套```function renderProfiles(o){  var out = document.getElementById('profiles');  for(var i=0;i在谈论代码和数据安全性时要牢记的要点之一是不要信任任何数据确保进入系统的所有数据都是干净的,正是您需要的。 这在后端写出从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;iJSLint 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](http://es6.ruanyifeng.com/#docs/style)

## 16. 站在巨人肩上,阅读优秀源码

- bootstrap: [https://github.com/twbs/bootstrap/blob/v4-dev/js/src/carousel.js](https://github.com/twbs/bootstrap/blob/v4-dev/js/src/carousel.js)

参考点: 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) {

```

- jquery:[https://github.com/jquery/jquery/blob/master/src/core/ready.js](https://github.com/jquery/jquery/blob/master/src/core/ready.js)

```

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;

};

```

- vue.js [https://github.com/vuejs/vue/blob/dev/src/core/config.js](https://github.com/vuejs/vue/blob/dev/src/core/config.js)

参考点: 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]

}

}

```

- react.js [https://github.com/facebook/react/blob/master/src/shared/utils/getComponentName.js](https://github.com/facebook/react/blob/master/src/shared/utils/getComponentName.js)

参考点: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;

```

### 参考链接:

1. [https://www.w3.org/wiki/JavaScript_best_practices][1]

2. [https://code.tutsplus.com/tutorials/24-javascript-best-practices-for-beginners--net-5399][2]

3. [http://www.ruanyifeng.com/blog/2015/09/web-page-performance-in-depth.html](http://www.ruanyifeng.com/blog/2015/09/web-page-performance-in-depth.html)

4. [http://domenicodefelice.blogspot.sg/2015/08/how-browsers-work.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

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 158,736评论 4 362
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 67,167评论 1 291
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 108,442评论 0 243
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 43,902评论 0 204
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 52,302评论 3 287
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,573评论 1 216
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,847评论 2 312
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,562评论 0 197
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,260评论 1 241
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,531评论 2 245
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 32,021评论 1 258
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,367评论 2 253
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 33,016评论 3 235
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,068评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,827评论 0 194
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,610评论 2 274
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,514评论 2 269

推荐阅读更多精彩内容