JavaScript best practices JS最佳實(shí)踐

#JavaScript best practices JS最佳實(shí)踐## 0 簡介> 最佳實(shí)踐起初比較棘手伟骨,但最終會(huì)讓你發(fā)現(xiàn)這是非常明智之舉。##? 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. 堅(jiān)持嚴(yán)格模式 嚴(yán)格模式:[https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Strict_mode](https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Strict_mode)### 嚴(yán)格模式主要有以下限制首繁。- 變量必須聲明后再使用- 函數(shù)的參數(shù)不能有同名屬性,否則報(bào)錯(cuò)- 不能使用with語句- 不能對(duì)只讀屬性賦值陨囊,否則報(bào)錯(cuò)- 不能使用前綴0表示八進(jìn)制數(shù)弦疮,否則報(bào)錯(cuò)- 不能刪除不可刪除的屬性,否則報(bào)錯(cuò)- 不能刪除變量delete prop蜘醋,會(huì)報(bào)錯(cuò)胁塞,只能刪除屬性delete global[prop]- eval不會(huì)在它的外層作用域引入變量- eval和arguments不能被重新賦值- arguments不會(huì)自動(dòng)反映函數(shù)參數(shù)的變化- 不能使用arguments.callee- 不能使用arguments.caller- 禁止this指向全局對(duì)象- 不能使用fn.caller和fn.arguments獲取函數(shù)調(diào)用的堆棧- 增加了保留字(比如protected、static和interface)> 清潔有效的代碼意味著更少的confusing bugs 去fix压语,更容易地遞交給其他開發(fā)人員和更強(qiáng)的代碼安全性啸罢。## 4. 關(guān)鍵點(diǎn)寫注釋>起初可能看起來是不必要的,但是相信我胎食,你最好盡可能地注釋你的代碼## 5. 避免或減少其它技術(shù)(DOM),優(yōu)化DOM操作> 網(wǎng)頁如何生成的扰才?![http://www.ruanyifeng.com/blogimg/asset/2015/bg2015091501.png](http://www.ruanyifeng.com/blogimg/asset/2015/bg2015091501.png)大致分以下五步:1. HTML代碼轉(zhuǎn)化成DOM;2. CSS代碼轉(zhuǎn)化成CSSOM(CSS Object Model)厕怜;3. 結(jié)合DOM和CSSOM衩匣,生成一棵渲染樹(包含每個(gè)節(jié)點(diǎn)的視覺信息);4. 渲染(render)---耗時(shí):? ? 1. 生成布局(flow)粥航,將所有渲染樹的所有節(jié)點(diǎn)進(jìn)行平面合成(layout)琅捏;? ? 2. 繪制(paint)在屏幕;### 提升頁面性能技巧- 避免交叉DOM的讀寫操作递雀,讀寫分別放在一起柄延;- 若某樣式通過重排獲得,最好緩存一下映之;- 集中改變樣式```el.className += " theclassname";el.style.cssText += "; left: " + left + "px; top: " + top + "px;";```- 盡量使用離線DOM而非網(wǎng)頁里的DOM,改變?cè)貥邮嚼唬鏲loneNode杠输;- 先將元素的display設(shè)為none,再做操作- position屬性為absolute或fixed的元素秕衙,重排的開銷會(huì)比較小蠢甲,因?yàn)椴挥每紤]它對(duì)其他元素的影響。- 只在必要的時(shí)候据忘,才將元素的display屬性為可見鹦牛,因?yàn)椴豢梢姷脑夭挥绊懼嘏藕椭乩L搞糕。另外,visibility : hidden的元素只對(duì)重繪有影響曼追,不影響重排- 使用虛擬DOM的腳本庫窍仰,比如React等。- 使用 window.requestAnimationFrame()礼殊、window.requestIdleCallback() 這兩個(gè)方法調(diào)節(jié)重新渲染### 常見做法#### 1.集中處理樣式讀寫:```var left = div.offsetLeft;var top? = div.offsetTop;div.style.left = left + 10 + "px";div.style.top = top + 10 + "px";```> 樣式表越簡單驹吮,重排和重繪就越快。重排和重繪的DOM元素層級(jí)越高晶伦,成本就越高碟狞。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. 首選使用字面量婚陪,推薦短路運(yùn)算符```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;//動(dòng)態(tài)添加屬性或方法cow.name = 'xx';//避免//推薦,但最佳在定義對(duì)象時(shí)將name屬性值設(shè)為nullObject.defineProperty(cow,'name',{? ? configurable:false,? ? writable:false,? ? enumerable:true,? ? value:'xx'});```## 7. 模塊化去耦合---一個(gè)方法一個(gè)任務(wù)```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對(duì)于那些不熟悉的人泌参,“eval”函數(shù)可以讓我們?cè)L問JavaScript的編譯器脆淹。 事實(shí)上,我們可以通過將字符串作為參數(shù)傳遞給eval,返回其的結(jié)果及舍。這不僅會(huì)大大降低您的腳本的性能未辆,還會(huì)造成巨大的安全風(fēng)險(xiǎn),因?yàn)樗o文本中傳遞的功能太多了锯玛。 避免咐柜!```if(a===b){? //...}```## 9. 避免多重嵌套```function renderProfiles(o){? var out = document.getElementById('profiles');? for(var i=0;i在談?wù)摯a和數(shù)據(jù)安全性時(shí)要牢記的要點(diǎn)之一是不要信任任何數(shù)據(jù)確保進(jìn)入系統(tǒng)的所有數(shù)據(jù)都是干凈的,正是您需要的攘残。 這在后端寫出從URL檢索的參數(shù)時(shí)最重要拙友。 在JavaScript中,測(cè)試發(fā)送到您的函數(shù)的參數(shù)類型(如使用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+最佳實(shí)踐 [http://es6.ruanyifeng.com/#docs/style](http://es6.ruanyifeng.com/#docs/style)

## 16. 站在巨人肩上遗契,閱讀優(yōu)秀源碼

- 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)

參考點(diǎn): ES6,模塊化代碼封裝病曾,可配置性牍蜂,可擴(kuò)展性,變量命名方式泰涂,核心注釋

```

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)

參考點(diǎn): ES6鲫竞,模塊化,可配置可擴(kuò)展,MVVM核心思想逼蒙,Viral DOM, 數(shù)據(jù)類型判斷

```

/* @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)

參考點(diǎn):ES6从绘, 數(shù)據(jù)類型判斷,模塊化

```

'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

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市僵井,隨后出現(xiàn)的幾起案子陕截,更是在濱河造成了極大的恐慌,老刑警劉巖批什,帶你破解...
    沈念sama閱讀 206,378評(píng)論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件农曲,死亡現(xiàn)場離奇詭異,居然都是意外死亡渊季,警方通過查閱死者的電腦和手機(jī)朋蔫,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,356評(píng)論 2 382
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來却汉,“玉大人驯妄,你說我怎么就攤上這事『仙埃” “怎么了青扔?”我有些...
    開封第一講書人閱讀 152,702評(píng)論 0 342
  • 文/不壞的土叔 我叫張陵,是天一觀的道長翩伪。 經(jīng)常有香客問我微猖,道長,這世上最難降的妖魔是什么缘屹? 我笑而不...
    開封第一講書人閱讀 55,259評(píng)論 1 279
  • 正文 為了忘掉前任凛剥,我火速辦了婚禮,結(jié)果婚禮上轻姿,老公的妹妹穿的比我還像新娘犁珠。我一直安慰自己,他們只是感情好犁享,可當(dāng)我...
    茶點(diǎn)故事閱讀 64,263評(píng)論 5 371
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著豹休,像睡著了一般。 火紅的嫁衣襯著肌膚如雪威根。 梳的紋絲不亂的頭發(fā)上凤巨,一...
    開封第一講書人閱讀 49,036評(píng)論 1 285
  • 那天洛搀,我揣著相機(jī)與錄音敢茁,去河邊找鬼。 笑死姥卢,一個(gè)胖子當(dāng)著我的面吹牛卷要,可吹牛的內(nèi)容都是我干的独榴。 我是一名探鬼主播僧叉,決...
    沈念sama閱讀 38,349評(píng)論 3 400
  • 文/蒼蘭香墨 我猛地睜開眼棺榔,長吁一口氣:“原來是場噩夢(mèng)啊……” “哼瓶堕!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起症歇,我...
    開封第一講書人閱讀 36,979評(píng)論 0 259
  • 序言:老撾萬榮一對(duì)情侶失蹤郎笆,失蹤者是張志新(化名)和其女友劉穎,沒想到半個(gè)月后忘晤,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 43,469評(píng)論 1 300
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡设塔,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 35,938評(píng)論 2 323
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了闰蛔。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 38,059評(píng)論 1 333
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡序六,死狀恐怖任连,靈堂內(nèi)的尸體忽然破棺而出例诀,到底是詐尸還是另有隱情随抠,我是刑警寧澤余佃,帶...
    沈念sama閱讀 33,703評(píng)論 4 323
  • 正文 年R本政府宣布暮刃,位于F島的核電站,受9級(jí)特大地震影響椭懊,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜步势,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,257評(píng)論 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望坏瘩。 院中可真熱鬧盅抚,春花似錦倔矾、人聲如沸妄均。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,262評(píng)論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽禁熏。三九已至邑彪,卻和暖如春瞧毙,著一層夾襖步出監(jiān)牢的瞬間寄症,已是汗流浹背宙彪。 一陣腳步聲響...
    開封第一講書人閱讀 31,485評(píng)論 1 262
  • 我被黑心中介騙來泰國打工有巧, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留释漆,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 45,501評(píng)論 2 354
  • 正文 我出身青樓灵汪,卻偏偏與公主長得像,于是被迫代替她去往敵國和親柑潦。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 42,792評(píng)論 2 345

推薦閱讀更多精彩內(nèi)容