JavaScript编码规范
Posted namehou
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了JavaScript编码规范相关的知识,希望对你有一定的参考价值。
2 代码风格
2.1 文件
[建议] javascript 文件使用无 BOM
的 UTF-8
编码。
解释:
UTF-8 编码具有更广泛的适应性。BOM 在使用程序或工具处理文件时可能造成不必要的干扰。
[建议] 在文件结尾处,保留一个空行。
2.2 结构
2.2.1 缩进
[强制] 使用 4
个空格做为一个缩进层级,不允许使用 2
个空格 或 tab
字符。
[强制] switch
下的 case
和 default
必须增加一个缩进层级。
2.2.2 空格
[强制] 二元运算符两侧必须有一个空格,一元运算符与操作对象之间不允许有空格。
[强制] 用作代码块起始的左花括号 {
前必须有一个空格。
[强制] if / else / for / while / function / switch / do / try / catch / finally
关键字后,必须有一个空格。
[强制] 在对象创建时,属性中的 :
之后必须有空格,:
之前不允许有空格。
[强制] 函数声明、具名函数表达式、函数调用中,函数名和 (
之间不允许有空格。
[强制] ,
和 ;
前不允许有空格。如果不位于行尾,,
和 ;
后必须跟一个空格。
[强制] 在函数调用、函数声明、括号表达式、属性访问、if / for / while / switch / catch
等语句中,()
和 []
内紧贴括号部分不允许有空格。
[强制] 单行声明的数组与对象,如果包含元素,{}
和 []
内紧贴括号部分不允许包含空格。
声明包含元素的数组与对象,只有当内部元素的形式较为简单时,才允许写在一行。元素复杂的情况,还是应该换行书写。
[强制] 行尾不得有多余的空格。
2.2.3 换行
[强制] 每个独立语句结束后必须换行。
[强制] 每行不得超过 120
个字符。
[强制] 运算符处换行时,运算符必须在新行的行首。
// good if (user.isAuthenticated() && user.isInRole(‘admin‘) && user.hasAuthority(‘add-admin‘) || user.hasAuthority(‘delete-admin‘) ) { // Code } var result = number1 + number2 + number3 + number4 + number5; // bad if (user.isAuthenticated() && user.isInRole(‘admin‘) && user.hasAuthority(‘add-admin‘) || user.hasAuthority(‘delete-admin‘)) { // Code } var result = number1 + number2 + number3 + number4 + number5;
[强制] 在函数声明、函数表达式、函数调用、对象创建、数组创建、for
语句等场景中,不允许在 ,
或 ;
前换行。
[建议] 不同行为或逻辑的语句集,使用空行隔开,更易阅读。
[建议] 在语句的行长度超过 120
时,根据逻辑条件合理缩进。
// 较复杂的逻辑条件组合,将每个条件独立一行,逻辑运算符放置在行首进行分隔,或将部分逻辑按逻辑组合进行分隔。 // 建议最终将右括号 ) 与左大括号 { 放在独立一行,保证与 `if` 内语句块能容易视觉辨识。 if (user.isAuthenticated() && user.isInRole(‘admin‘) && user.hasAuthority(‘add-admin‘) || user.hasAuthority(‘delete-admin‘) ) { // Code } // 按一定长度截断字符串,并使用 + 运算符进行连接。 // 分隔字符串尽量按语义进行,如不要在一个完整的名词中间断开。 // 特别的,对于 html 片段的拼接,通过缩进,保持和 HTML 相同的结构。 var html = ‘‘ // 此处用一个空字符串,以便整个 HTML 片段都在新行严格对齐 + ‘<article>‘ + ‘<h1>Title here</h1>‘ + ‘<p>This is a paragraph</p>‘ + ‘<footer>Complete</footer>‘ + ‘</article>‘; // 也可使用数组来进行拼接,相对 `+` 更容易调整缩进。 var html = [ ‘<article>‘, ‘<h1>Title here</h1>‘, ‘<p>This is a paragraph</p>‘, ‘<footer>Complete</footer>‘, ‘</article>‘ ]; html = html.join(‘‘); // 当参数过多时,将每个参数独立写在一行上,并将结束的右括号 ) 独立一行。 // 所有参数必须增加一个缩进。 foo( aVeryVeryLongArgument, anotherVeryLongArgument, callback ); // 也可以按逻辑对参数进行组合。 // 最经典的是 baidu.format 函数,调用时将参数分为“模板”和“数据”两块 baidu.format( dateFormatTemplate, year, month, date, hour, minute, second ); // 当函数调用时,如果有一个或以上参数跨越多行,应当每一个参数独立一行。 // 这通常出现在匿名函数或者对象初始化等作为参数时,如 `setTimeout` 函数等。 setTimeout( function () { alert(‘hello‘); }, 200 ); order.data.read( ‘id=‘ + me.model.id, function (data) { me.attchToModel(data.result); callback(); }, 300 ); // 链式调用较长时采用缩进进行调整。 $(‘#items‘) .find(‘.selected‘) .highlight() .end(); // 三元运算符由3部分组成,因此其换行应当根据每个部分的长度不同,形成不同的情况。 var result = thisIsAVeryVeryLongCondition ? resultA : resultB; var result = condition ? thisIsAVeryVeryLongResult : resultB; // 数组和对象初始化的混用,严格按照每个对象的 `{` 和结束 `}` 在独立一行的风格书写。 var array = [ { // ... }, { // ... } ];
[建议] 对于 if...else...
、try...catch...finally
等语句,推荐使用在 }
号后添加一个换行 的风格,使代码层次结构更清晰,阅读性更好。
2.2.4 语句
[强制] 不得省略语句结束的分号。
[强制] 在 if / else / for / do / while
语句中,即使只有一行,也不得省略块 {...}
。
[强制] 函数定义结束不允许添加分号。
[强制] IIFE
必须在函数表达式外添加 (
,非 IIFE
不得在函数表达式外添加 (
。
IIFE = Immediately-Invoked Function Expression.
额外的 ( 能够让代码在阅读的一开始就能判断函数是否立即被调用,进而明白接下来代码的用途。而不是一直拖到底部才恍然大悟。
// good var task = (function () { // Code return result; })(); var func = function () { };
2.3 命名
[强制] 变量
使用 Camel命名法
。
var loadingModules = {};
[强制] 常量
使用 全部字母大写,单词间下划线分隔
的命名方式。
[强制] 函数
使用 Camel命名法
。
function stringFormat(source) { }
强制] 函数的 参数
使用 Camel命名法
。
function hear(theBells) { }
[强制] 类
使用 Pascal命名法
。
function TextNode(options) { }
[强制] 类的 方法
/ 属性
使用 Camel命名法
。
function TextNode(value, engine) { this.value = value; this.engine = engine; } TextNode.prototype.clone = function () { return this; };
[强制] 枚举变量
使用 Pascal命名法
,枚举的属性
使用 全部字母大写,单词间下划线分隔
的命名方式。
var TargetState = { READING: 1, READED: 2, APPLIED: 3, READY: 4 };
[强制] 命名空间
使用 Camel命名法
。
equipments.heavyWeapons = {};
[强制] 由多个单词组成的缩写词,在命名中,根据当前命名法和出现的位置,所有字母的大小写与首字母的大小写保持一致。
function XMLParser() { } function insertHTML(element, html) { } var httpRequest = new HTTPRequest();
[强制] 类名
使用 名词
。
function Engine(options) { }
[建议] 函数名
使用 动宾短语
。
function getStyle(element) { }
[建议] boolean
类型的变量使用 is
或 has
开头。
var isReady = false; var hasMoreCommands = false;
[建议] Promise对象
用 动宾短语的进行时
表达。
var loadingData = ajax.get(‘url‘); loadingData.then(callback);
2.4 注释
2.4.1 单行注释
[强制] 必须独占一行。//
后跟一个空格,缩进与下一行被注释说明的代码一致。
2.4.2 多行注释
[建议] 避免使用 /*...*/
这样的多行注释。有多行注释内容时,使用多个单行注释。
2.4.3 文档化注释
[强制] 为了便于代码阅读和自文档化,以下内容必须包含以 /**...*/
形式的块注释中。
[强制] 文档注释前必须空一行。
[建议] 自文档化的文档说明 what,而不是 how。
2.4.4 类型定义
[强制] 类型定义都是以 {
开始, 以 }
结束。
解释:
常用类型如:{string}, {number}, {boolean}, {Object}, {Function}, {RegExp}, {Array}, {Date}。
类型不仅局限于内置的类型,也可以是自定义的类型。比如定义了一个类 Developer,就可以使用它来定义一个参数和返回值的类型。
[强制] 对于基本类型 {string}, {number}, {boolean},首字母必须小写。
2.4.5 文件注释
[强制] 文件顶部必须包含文件注释,用 @file
标识文件说明。
/** * @file Describe the file */
[建议] 文件注释中可以用 @author
标识开发者信息。
2.4.6 命名空间注释
[建议] 命名空间使用 @namespace
标识。
/** * @namespace */ var util = {};
2.4.7 类注释
[建议] 使用 @class
标记类或构造函数。
解释:
对于使用对象 constructor
属性来定义的构造函数,可以使用 @constructor
来标记。
/** * 描述 * * @class */ function Developer() { // constructor body }
[建议] 使用 @extends
标记类的继承信息。
/** * 描述 * * @class * @extends Developer */ function Fronteer() { Developer.call(this); // constructor body } util.inherits(Fronteer, Developer);
[强制] 使用包装方式扩展类成员时, 必须通过 @lends
进行重新指向。
解释:
没有 @lends
标记将无法为该类生成包含扩展类成员的文档。
/** * 类描述 * * @class * @extends Developer */ function Fronteer() { Developer.call(this); // constructor body } util.extend( Fronteer.prototype, /** @lends Fronteer.prototype */{ getLevel: function () { // TODO } } );
.....部分省略
3 语言特性
3.1 变量
[强制] 变量、函数在使用前必须先定义。
不通过 var 定义变量将导致变量污染全局环境。
原则上不建议使用全局变量,对于已有的全局变量或第三方框架引入的全局变量,需要根据检查工具的语法标识。
[强制] 每个 var
只能声明一个变量。
一个 var
声明多个变量,容易导致较长的行长度,并且在修改时容易造成逗号和分号的混淆。
// good var hangModules = []; var missModules = []; var visited = {}; // bad var hangModules = [], missModules = [], visited = {};
[强制] 变量必须 即用即声明
,不得在函数或其它形式的代码块起始位置统一声明所有变量。
变量声明与使用的距离越远,出现的跨度越大,代码的阅读与维护成本越高。虽然JavaScript的变量是函数作用域,还是应该根据编程中的意图,缩小变量出现的距离空间。
// good function kv2List(source) { var list = []; for (var key in source) { if (source.hasOwnProperty(key)) { var item = { k: key, v: source[key] }; list.push(item); } } return list; } // bad function kv2List(source) { var list = []; var key; var item; for (key in source) { if (source.hasOwnProperty(key)) { item = { k: key, v: source[key] }; list.push(item); } } return list; }
3.2 条件
[强制] 在 Equality Expression 中使用类型严格的 ===
。仅当判断 null
或 undefined
时,允许使用 == null
。
使用 ===
可以避免等于判断中隐式的类型转换。
// good if (age === 30) { // ...... } // bad if (age == 30) { // ...... }
[建议] 尽可能使用简洁的表达式。
// 字符串为空 // good if (!name) { // ...... } // bad if (name === ‘‘) { // ...... }
[建议] 按执行频率排列分支的顺序。
按执行频率排列分支的顺序好处是:
- 阅读的人容易找到最常见的情况,增加可读性。
- 提高执行效率。
[建议] 对于相同变量或表达式的多值条件,用 switch
代替 if
。
// good switch (typeof variable) { case ‘object‘: // ...... break; case ‘number‘: case ‘boolean‘: case ‘string‘: // ...... break; } // bad var type = typeof variable; if (type === ‘object‘) { // ...... } else if (type === ‘number‘ || type === ‘boolean‘ || type === ‘string‘) { // ...... }
[建议] 如果函数或全局中的 else
块后没有任何语句,可以删除 else
。
// good function getName() { if (name) { return name; } return ‘unnamed‘; } // bad function getName() { if (name) { return name; } else { return ‘unnamed‘; } }
3.3 循环
[建议] 不要在循环体中包含函数表达式,事先将函数提取到循环体外。
解释:
循环体中的函数表达式,运行过程中会生成循环次数个函数对象。
// good function clicker() { // ...... } for (var i = 0, len = elements.length; i < len; i++) { var element = elements[i]; addListener(element, ‘click‘, clicker); } // bad for (var i = 0, len = elements.length; i < len; i++) { var element = elements[i]; addListener(element, ‘click‘, function () {}); }
[建议] 对循环内多次使用的不变值,在循环外用变量缓存。
// good var width = wrap.offsetWidth + ‘px‘; for (var i = 0, len = elements.length; i < len; i++) { var element = elements[i]; element.style.width = width; // ...... } // bad for (var i = 0, len = elements.length; i < len; i++) { var element = elements[i]; element.style.width = wrap.offsetWidth + ‘px‘; // ...... }
[建议] 对有序集合进行遍历时,缓存 length
。
虽然现代浏览器都对数组长度进行了缓存,但对于一些宿主对象和老旧浏览器的数组对象,在每次 length
访问时会动态计算元素个数,此时缓存 length
能有效提高程序性能。
for (var i = 0, len = elements.length; i < len; i++) { var element = elements[i]; // ...... }
[建议] 对有序集合进行顺序无关的遍历时,使用逆序遍历。
解释:
逆序遍历可以节省变量,代码比较优化。
var len = elements.length; while (len--) { var element = elements[len]; // ...... }
3.4 类型
3.4.1 类型检测
[建议] 类型检测优先使用 typeof
。对象类型检测使用 instanceof
。null
或 undefined
的检测使用 == null
。
// string typeof variable === ‘string‘ // number typeof variable === ‘number‘ // boolean typeof variable === ‘boolean‘ // Function typeof variable === ‘function‘ // Object typeof variable === ‘object‘ // RegExp variable instanceof RegExp // Array variable instanceof Array // null variable === null // null or undefined variable == null // undefined typeof variable === ‘undefined‘
3.4.2 类型转换
[建议] 转换成 string
时,使用 + ‘‘
。
// good num + ‘‘; // bad new String(num); num.toString(); String(num);
建议] 转换成 number
时,通常使用 +
。
// good +str; // bad Number(str);
[建议] string
转换成 number
,要转换的字符串结尾包含非数字并期望忽略时,使用 parseInt
。
var width = ‘200px‘; parseInt(width, 10);
[强制] 使用 parseInt
时,必须指定进制。
// good parseInt(str, 10); // bad parseInt(str);
建议] 转换成 boolean
时,使用 !!
。
var num = 3.14; !!num;
[建议] number
去除小数点,使用 Math.floor
/ Math.round
/ Math.ceil
,不使用 parseInt
。
3.5 字符串
[强制] 字符串开头和结束使用单引号 ‘
。
- 输入单引号不需要按住
shift
,方便输入。 - 实际使用中,字符串经常用来拼接 HTML。为方便 HTML 中包含双引号而不需要转义写法。
var str = ‘我是一个字符串‘; var html = ‘<div class="cls">拼接HTML可以省去双引号转义</div>‘;
[建议] 使用 数组
或 +
拼接字符串。
- 使用
+
拼接字符串,如果拼接的全部是 StringLiteral,压缩工具可以对其进行自动合并的优化。所以,静态字符串建议使用+
拼接。 - 在现代浏览器下,使用
+
拼接字符串,性能较数组的方式要高。 - 如需要兼顾老旧浏览器,应尽量使用数组拼接字符串。
/ 使用数组拼接字符串 var str = [ // 推荐换行开始并缩进开始第一个字符串, 对齐代码, 方便阅读. ‘<ul>‘, ‘<li>第一项</li>‘, ‘<li>第二项</li>‘, ‘</ul>‘ ].join(‘‘); // 使用 `+` 拼接字符串 var str2 = ‘‘ // 建议第一个为空字符串, 第二个换行开始并缩进开始, 对齐代码, 方便阅读 + ‘<ul>‘, + ‘<li>第一项</li>‘, + ‘<li>第二项</li>‘, + ‘</ul>‘;
[建议] 使用字符串拼接的方式生成HTML,需要根据语境进行合理的转义。
在 JavaScript
中拼接,并且最终将输出到页面中的字符串,需要进行合理转义,以防止安全漏洞。下面的示例代码为场景说明,不能直接运行。
// HTML 转义 var str = ‘<p>‘ + htmlEncode(content) + ‘</p>‘; // HTML 转义 var str = ‘<input type="text" value="‘ + htmlEncode(value) + ‘">‘; // URL 转义 var str = ‘<a href="/?key=‘ + htmlEncode(urlEncode(value)) + ‘">link</a>‘; // JavaScript字符串 转义 + HTML 转义 var str = ‘<button onclick="check(‘‘ + htmlEncode(strLiteral(name)) + ‘‘)">提交</button>‘;
[建议] 复杂的数据到视图字符串的转换过程,选用一种模板引擎。
使用模板引擎有如下好处:
- 在开发过程中专注于数据,将视图生成的过程由另外一个层级维护,使程序逻辑结构更清晰。
- 优秀的模板引擎,通过模板编译技术和高质量的编译产物,能获得比手工拼接字符串更高的性能。
- 模板引擎能方便的对动态数据进行相应的转义,部分模板引擎默认进行HTML转义,安全性更好。
- artTemplate: 体积较小,在所有环境下性能高,语法灵活。
- dot.js: 体积小,在现代浏览器下性能高,语法灵活。
- etpl: 体积较小,在所有环境下性能高,模板复用性高,语法灵活。
- handlebars: 体积大,在所有环境下性能高,扩展性高。
- hogon: 体积小,在现代浏览器下性能高。
- nunjucks: 体积较大,性能一般,模板复用性高。
3.6 对象
[强制] 使用对象字面量 {}
创建新 Object
。
// good var obj = {}; // bad var obj = new Object();
[建议] 对象创建时,如果一个对象的所有 属性
均可以不添加引号,建议所有 属性
不添加引号。
var info = { name: ‘someone‘, age: 28 };
[建议] 对象创建时,如果任何一个 属性
需要添加引号,则所有 属性
建议添加 ‘
。
如果属性不符合 Identifier 和 NumberLiteral 的形式,就需要以 StringLiteral 的形式提供。
/ good var info = { ‘name‘: ‘someone‘, ‘age‘: 28, ‘more-info‘: ‘...‘ }; // bad var info = { name: ‘someone‘, age: 28, ‘more-info‘: ‘...‘ };
[强制] 不允许修改和扩展任何原生对象和宿主对象的原型。
// 以下行为绝对禁止 String.prototype.trim = function () { };
建议] 属性访问时,尽量使用 .
。
属性名符合 Identifier 的要求,就可以通过 .
来访问,否则就只能通过 [expr]
方式访问。
通常在 JavaScript 中声明的对象,属性命名是使用 Camel 命名法,用 .
来访问更清晰简洁。部分特殊的属性(比如来自后端的 JSON ),可能采用不寻常的命名方式,可以通过 [expr]
方式访问。
[建议] for in
遍历对象时, 使用 hasOwnProperty
过滤掉原型中的属性。
var newInfo = {}; for (var key in info) { if (info.hasOwnProperty(key)) { newInfo[key] = info[key]; } }
3.7 数组
[强制] 使用数组字面量 []
创建新数组,除非想要创建的是指定长度的数组。
// good var arr = []; // bad var arr = new Array();
强制] 遍历数组不使用 for in
。
解释:
数组对象可能存在数字以外的属性, 这种情况下 for in
不会得到正确结果。
var arr = [‘a‘, ‘b‘, ‘c‘]; // 这里仅作演示, 实际中应使用 Object 类型 arr.other = ‘other things‘; // 正确的遍历方式 for (var i = 0, len = arr.length; i < len; i++) { console.log(i); } // 错误的遍历方式 for (var i in arr) { console.log(i); }
[建议] 不因为性能的原因自己实现数组排序功能,尽量使用数组的 sort
方法。
自己实现的常规排序算法,在性能上并不优于数组默认的 sort
方法。以下两种场景可以自己实现排序:
- 需要稳定的排序算法,达到严格一致的排序结果。
- 数据特点鲜明,适合使用桶排。
[建议] 清空数组使用 .length = 0
。
3.8 函数
3.8.1 函数长度
[建议] 一个函数的长度控制在 50
行以内。
将过多的逻辑单元混在一个大函数中,易导致难以维护。一个清晰易懂的函数应该完成单一的逻辑单元。复杂的操作应进一步抽取,通过函数的调用来体现流程。
特定算法等不可分割的逻辑允许例外。
function syncViewStateOnUserAction() { if (x.checked) { y.checked = true; z.value = ‘‘; } else { y.checked = false; } if (a.value) { warning.innerText = ‘‘; submitButton.disabled = false; } else { warning.innerText = ‘Please enter it‘; submitButton.disabled = true; } } // 直接阅读该函数会难以明确其主线逻辑,因此下方是一种更合理的表达方式: function syncViewStateOnUserAction() { syncXStateToView(); checkAAvailability(); } function syncXStateToView() { y.checked = x.checked; if (x.checked) { z.value = ‘‘; } } function checkAAvailability() { if (a.value) { clearWarnignForA(); } else { displayWarningForAMissing(); } }
3.8.2 参数设计
[建议] 一个函数的参数控制在 6
个以内。
解释:
除去不定长参数以外,函数具备不同逻辑意义的参数建议控制在 6
个以内,过多参数会导致维护难度增大。
某些情况下,如使用 AMD Loader 的 require
加载多个模块时,其 callback
可能会存在较多参数,因此对函数参数的个数不做强制限制。
[建议] 通过 options
参数传递非数据输入型参数。
解释:
有些函数的参数并不是作为算法的输入,而是对算法的某些分支条件判断之用,此类参数建议通过一个 options
参数传递。
/** * 移除某个元素 * * @param {Node} element 需要移除的元素 * @param {boolean} removeEventListeners 是否同时将所有注册在元素上的事件移除 */ function removeElement(element, removeEventListeners) { element.parent.removeChild(element); if (removeEventListeners) { element.clearEventListeners(); } }
可以转换为下面的签名:
/** * 移除某个元素 * * @param {Node} element 需要移除的元素 * @param {Object} options 相关的逻辑配置 * @param {boolean} options.removeEventListeners 是否同时将所有注册在元素上的事件移除 */ function removeElement(element, options) { element.parent.removeChild(element); if (options.removeEventListeners) { element.clearEventListeners(); } }
这种模式有几个显著的优势:
boolean
型的配置项具备名称,从调用的代码上更易理解其表达的逻辑意义。- 当配置项有增长时,无需无休止地增加参数个数,不会出现
removeElement(element, true, false, false, 3)
这样难以理解的调用代码。 - 当部分配置参数可选时,多个参数的形式非常难处理重载逻辑,而使用一个 options 对象只需判断属性是否存在,实现得以简化。
3.8.3 闭包
[建议] 在适当的时候将闭包内大对象置为 null
。
解释:
在 JavaScript 中,无需特别的关键词就可以使用闭包,一个函数可以任意访问在其定义的作用域外的变量。需要注意的是,函数的作用域是静态的,即在定义时决定,与调用的时机和方式没有任何关系。
闭包会阻止一些变量的垃圾回收,对于较老旧的 JavaScript 引擎,可能导致外部所有变量均无法回收。
首先一个较为明确的结论是,以下内容会影响到闭包内变量的回收:
- 嵌套的函数中是否有使用该变量。
- 嵌套的函数中是否有 直接调用eval。
- 是否使用了 with 表达式。
Chakra、V8 和 SpiderMonkey 将受以上因素的影响,表现出不尽相同又较为相似的回收策略,而 JScript.dll 和 Carakan 则完全没有这方面的优化,会完整保留整个 LexicalEnvironment 中的所有变量绑定,造成一定的内存消耗。
3.8.4 空函数
[建议] 空函数不使用 new Function()
的形式。
var emptyFunction = function () {};
[建议] 对于性能有高要求的场合,建议存在一个空函数的常量,供多处使用共享。
由于对闭包内变量有回收优化策略的 Chakra、V8 和 SpiderMonkey 引擎的行为较为相似,因此可以总结如下,当返回一个函数 fn 时:
- 如果 fn 的
[[Scope]]
是 ObjectEnvironment(with 表达式生成 ObjectEnvironment,函数和 catch 表达式生成 DeclarativeEnvironment),则:- 如果是 V8 引擎,则退出全过程。
- 如果是 SpiderMonkey,则处理该 ObjectEnvironment 的外层 LexicalEnvironment。
- 获取当前 LexicalEnvironment 下的所有类型为 Function 的对象,对于每一个 Function 对象,分析其 FunctionBody:
- 如果 FunctionBody 中含有 直接调用 eval,则退出全过程。
- 否则得到所有的 Identifier。
- 对于每一个 Identifier,设其为 name,根据查找变量引用的规则,从 LexicalEnvironment 中找出名称为 name 的绑定 binding。
- 对 binding 添加 notSwap 属性,其值为
true
。
- 检查当前 LexicalEnvironment 中的每一个变量绑定,如果该绑定有 notSwap 属性且值为
true
,则:- 如果是 V8 引擎,删除该绑定。
- 如果是 SpiderMonkey,将该绑定的值设为
undefined
,将删除 notSwap 属性。
对于 Chakra 引擎,暂无法得知是按 V8 的模式还是按 SpiderMonkey 的模式进行。
如果有 非常庞大 的对象,且预计会在 老旧的引擎 中执行,则使用闭包时,注意将闭包不需要的对象置为空引用。
以上是关于JavaScript编码规范的主要内容,如果未能解决你的问题,请参考以下文章