企业🤖AI智能体构建引擎,智能编排和调试,一键部署,支持知识库和私有化部署方案 广告
[TOC] # 严格模式 ECMAScript 5的严格模式是采用具有限制性JavaScript变体的一种方式,从而使代码显示地 脱离“马虎模式/稀松模式/懒散模式“(sloppy)模式。 * 严格模式通过抛出错误来消除了一些原有静默错误。 * 严格模式修复了一些导致 JavaScript引擎难以执行优化的缺陷:有时候,相同的代码,严格模式可以比非严格模式下运行得更快。 * 严格模式禁用了在ECMAScript的未来版本中可能会定义的一些语法。 ## 调用严格模式 ### 为脚本开启严格模式 为整个脚本文件开启严格模式,需要在所有语句之前放一个特定语句 `"use strict"`; (或 'use strict';) ``` // 整个脚本都开启严格模式的语法 "use strict"; var v = "Hi! I'm a strict mode script!"; ``` <br> ### 为函数开启严格模式 同样的,要给某个函数开启严格模式,得把 `"use strict"; (或 'use strict'; )`声明一字不漏地放在函数体所有语句之前。 ``` function strict(){ // 函数级别严格模式语法 'use strict'; function nested() { return "And so am I!"; } return "Hi! I'm a strict mode function! " + nested(); } function notStrict() { return "I'm not strict."; } ``` <br> ## 无法再意外创建全局变量 ``` // 无法再意外创建全局变量 // Uncaught ReferenceError: mistypedVaraible is not defined mistypedVaraible = 17; ``` <br> ## 会使引起静默失败 即不报错也没有任何效果。任何在正常模式下引起静默失败的赋值操作 (给不可写属性赋值, 给只读属性(getter-only)赋值赋值, 给不可扩展对象(non-extensible object)的新属性赋值) 都会抛出异常。 ``` // 给不可写的全局变量赋值 var NaN = 1 // TypeError: Cannot assign to read only property 'NaN' of object '#<Window>' // 给不可写属性赋值 var obj1 = {}; Object.defineProperty(obj1, "x", { value: 42, writable: false }); obj1.x = 9; // TypeError: Cannot assign to read only property 'x' of object '#<Object>' // 给只读属性赋值 var obj2 = { get x() { return 17; } }; obj2.x = 5; // TypeError: Cannot set property x of #<Object> which has only a getter // 给不可扩展对象的新属性赋值 var fixed = {}; Object.preventExtensions(fixed); fixed.newProp = "ohai"; // TypeError: Cannot add property newProp, object is not extensible ``` <br> ## 删除不可删除的属性 ``` delete Object.prototype // TypeError: Cannot delete property 'prototype' of function Object() { [native code] } ``` ## 一个对象内的所有属性名在对象内必须唯一(Chrome无效) ``` "use strict"; var o = { p: 1, p: 2 }; // !!! 语法错误 ``` <br> ## 函数的参数名唯一 在正常模式下, 最后一个重名参数名会掩盖之前的重名参数. 之前的参数仍然可以通过 `arguments[i]` 来访问, 还不是完全无法访问. 然而, 这种隐藏毫无意义而且可能是意料之外的 ``` function sum(a, a, c){ // !!! 语法错误 "use strict"; return a + a + c; // SyntaxError: Duplicate parameter name not allowed in this context } ``` <br> ## 禁止八进制数字语法 ECMAScript并不包含八进制语法, 但所有的浏览器都支持这种以零(0)开头的八进制语法: `0644 === 420` 还有 `"\045" === "%"`.在ECMAScript 6中支持为一个数字加"0o"的前缀来表示八进制数 ``` var a = 0o10; // ES6: 八进制 ``` <br> 有些新手开发者认为数字的前导零没有语法意义, 所以他们会用作对齐措施 — 但其实这会改变数字的意义! 八进制语法很少有用并且可能会错误使用, 所以严格模式下八进制语法会引起语法错误: ``` "use strict"; var sum = 015 + 197 + 142; // SyntaxError: Octal literals are not allowed in strict mode. ``` <br> ## 禁止设置基本类型的属性 ``` false.true = ""; // TypeError: Cannot create property 'true' on boolean 'false' (14).sailing = "home"; // TypeError: Cannot create property 'sailing' on number '14' "with".you = "far away"; // TypeError: Cannot create property 'you' on string 'with' ``` <br> ## 禁用 with with所引起的问题是块内的任何名称可以映射(map)到with传进来的对象的属性, 也可以映射到包围这个块的作用域内的变量(甚至是全局变量), 这一切都是在运行时决定的: 在代码运行之前是无法得知的。 <br> 一种取代 with的简单方法是,将目标对象赋给一个短命名变量,然后访问这个变量上的相应属性 <br> ## eval 不再为上层范围引入新变量 在正常模式下, 代码 eval("var x;") 会给上层函数(surrounding function)或者全局引入一个新的变量 x . 这意味着, 一般情况下, 在一个包含 eval 调用的函数内所有没有引用到参数或者局部变量的名称都必须在运行时才能被映射到特定的定义 (因为 eval 可能引入的新变量会覆盖它的外层变量). 在严格模式下 eval 仅仅为被运行的代码创建变量, 所以 eval 不会使得名称映射到外部变量或者其他局部变量 ``` var x = 17; var evalX = eval("'use strict'; var x = 42; x"); console.assert(x === 17); console.assert(evalX === 42); ``` <br> ## 禁止删除声明变量 ``` var x; delete x; // SyntaxError: Delete of an unqualified identifier in strict mode ``` <br> ## 名称 eval 和 arguments 不能通过程序语法被绑定或赋值 以下的所有尝试将引起语法错误 ``` eval = 17; arguments++; ++eval; var obj = { set p(arguments) { } }; var eval; try { } catch (arguments) { } function x(eval) { } function arguments() { } var y = function eval() { }; var f = new Function("arguments", "'use strict'; return 17;"); ``` ## 参数的值不会随 arguments 对象的值的改变而变化 正常模式下,对于第一个参数是 arg 的函数,对 arg 赋值时会同时赋值给 `arguments[0]`,反之亦然(除非没有参数,或者 `arguments[0]` 被删除)。 严格模式下,函数的 arguments 对象会保存函数被调用时的原始参数。`arguments[i]` 的值不会随与之相应的参数的值的改变而变化,同名参数的值也不会随与之相应的 `arguments[i]` 的值的改变而变化。 ``` function f(a){ "use strict"; a = 42; return [a, arguments[0]]; } f(17); // [42, 17] ``` <br> ``` function f(a){ a = 42; return [a, arguments[0]]; } f(17); // [42, 42] ``` <br> ## 不再支持 arguments.callee 正常模式下,`arguments.callee` 指向当前正在执行的函数。这个作用很小:直接给执行函数命名就可以了!此外,`arguments.callee` 十分不利于优化,例如内联函数,因为 `arguments.callee` 会依赖对非内联函数的引用。 在严格模式下,`arguments.callee` 是一个不可删除属性,而且赋值和读取时都会抛出异常 ``` var f2 = function() { return arguments.callee; }; f2(); // 抛出类型错误 ``` ## 通过this传递给一个函数的值不会被强制转换为一个对象 对于一个开启严格模式的函数,指定的this不再被封装为对象,而且如果没有指定this的话它值是undefined: ``` function fun() { return this; } fun() === undefined // false fun.call(2) === 2 // false fun.apply(null) === null // false fun.call(undefined) === undefined // false fun.bind(true)() === true // false ``` <br> ``` 'use strict' function fun() { return this; } fun() === undefined // true fun.call(2) === 2 // true fun.apply(null) === null // true fun.call(undefined) === undefined // true fun.bind(true)() === true // true ``` <br> ## 不能通过广泛实现的ECMAScript扩展“游走于”JavaScript的栈中 在普通模式下用这些扩展的话,当一个叫fun的函数正在被调用的时候,fun.caller是最后一个调用fun的函数,而且fun.arguments包含调用fun时用的形参。这两个扩展接口对于“安全”JavaScript而言都是有问题的,因为他们允许“安全的”代码访问"专有"函数和他们的(通常是没有经过保护的)形参。 <br> 如果fun在严格模式下,那么fun.caller和fun.arguments都是不可删除的属性而且在存值、取值时都会报错: ``` function restricted() { "use strict"; restricted.caller; // 抛出类型错误 restricted.arguments; // 抛出类型错误 } function privilegedInvoker() { return restricted(); } privilegedInvoker(); ``` <br> ## arguments不会再提供访问与调用这个函数相关的变量的途径 严格模式下的arguments不会再提供访问与调用这个函数相关的变量的途径。在一些旧时的ECMAScript实现中arguments.caller曾经是一个对象,里面存储的属性指向那个函数的变量。这是一个安全隐患,因为它通过函数抽象打破了本来被隐藏起来的保留值;它同时也是引起大量优化工作的原因。出于这些原因,现在的浏览器没有实现它。但是因为它这种历史遗留的功能,arguments.caller在严格模式下同样是一个不可被删除的属性,在赋值或者取值时会报错: ``` function fun(a, b) { "use strict"; var v = 12; return arguments.caller; // 抛出类型错误 } fun(1, 2); // 不会暴露v(或者a,或者b) ``` <br> ## 一部分字符变成了保留的关键字 这些字符包括implements, interface, let, package, private, protected, public, static和yield。在严格模式下,你不能再用这些名字作为变量名或者形参名。 ``` function package(protected){ // !!! "use strict"; var implements; // !!! interface: // !!! while (true) { break interface; // !!! } function private() { } // !!! } function fun(static) { 'use strict'; } // !!! ``` <br> ## 禁止了不在脚本或者函数层面上的函数声明(chrome未实现) ``` "use strict"; if (true){ function f() { } // !!! 语法错误 f(); } for (var i = 0; i < 5; i++){ function f2() { } // !!! 语法错误 f2(); } function baz() { // 合法 function eit() { } // 同样合法 } ```