严格模式是现代JavaScript的重要组成部分。 正是这种模式允许开发人员使用比标准语法更多的限制。
严格模式语义与传统的非严格模式(有时称为“草率模式”)不同。 在这种模式下,语言的语法规则不是很严格,并且当发生一些错误时,系统不会将它们通知给用户。 即,可以忽略错误,并且可以进一步执行错误所在的代码。 这可能会导致意外的代码执行结果。

严格模式对JavaScript的语义进行了一些更改。 它通过抛出适当的异常来防止系统对错误视而不见。 这将导致程序执行停止。
此外,严格模式有助于编写程序,而程序中没有缺点,这些缺点会阻止JS引擎优化代码。 此外,在这种模式下,禁止使用可能在该语言的未来版本中具有特殊含义的语法元素。
使用严格模式的功能
严格模式可以应用于单个功能或整个脚本。 它不能仅应用于单独的指令或括号中的代码块。 为了在整个脚本级别,在文件的开头,在任何其他命令之前使用严格模式,您需要先放置
"use strict"
或
'use strict'
结构。
如果项目中的某些脚本不使用严格模式,而其他脚本使用此模式,则可能会合并这些脚本。
这将导致以下事实:当系统尝试以严格模式执行代码时,原本不打算在严格模式下执行的代码将处于这种状态。 反之亦然-为严格模式编写的代码将进入非严格模式。 因此,最好不要混合使用“严格”和“非严格”脚本。
如前所述,严格模式可以应用于各个功能。 为此,必须将
"use strict"
或
"use strict"
的构造放在函数主体的顶部,然后再执行其他任何命令。 这种方法的严格模式适用于放置在函数主体中的所有内容,包括嵌套函数。
例如:
const strictFunction = ()=>{ 'use strict'; const nestedFunction = ()=>{
在ES2015标准中出现的JavaScript模块中,默认情况下启用严格模式。 因此,在与他们合作时,您无需显式包括它。
严格模式对JS代码引入的更改
严格模式会影响代码的语法以及程序执行期间代码的行为方式。 代码中的错误将转换为异常。 在安静模式下在严格模式下安静崩溃的事实会导致错误消息。 这类似于系统以宽松模式响应语法错误的方式。 在严格模式下,简化了变量的使用,严格控制了
eval
函数和
arguments
对象的使用,并简化了可在该语言的将来版本中实现的构造的使用。
▍将静默错误转换为异常
静默错误会在严格模式下转换为异常。 在宽松模式下,系统不会明确响应此类错误。 在严格模式下,此类错误的存在会导致代码无法操作。
因此,由于这个原因,很难犯一个意外声明全局变量的错误,因为如果不使用
var
,
let
或
const
指令就无法在严格模式下声明变量和常量。 结果,在没有这些指令的情况下创建变量将导致程序无法操作。 例如,尝试执行以下代码将引发
ReferenceError
异常:
'use strict'; badVariable = 1;
此类代码不能在严格模式下运行,因为如果严格模式关闭,则会创建全局变量
badVariable
。 严格模式可以保护程序员避免意外创建全局变量。
现在,尝试执行在正常模式下根本不起作用的任何代码的尝试将引发异常。 错误被认为是在松散模式下被忽略的任何不正确的语法构造。
因此,例如,在严格模式下,您不能对诸如
arguments
,
NaN
或
eval
类的只读实体执行值分配操作。
在严格模式下,例如,在以下情况下将引发异常:
- 尝试将值分配给只读属性,例如某种可重写的全局属性;
- 试图将值写入仅具有getter的属性的尝试;
- 尝试向不可扩展对象的属性写入内容。
以下是导致严格模式异常的语法构造示例:
'use strict'; let undefined = 5; let Infinity = 5; let obj = {}; Object.defineProperty(obj, 'foo', { value: 1, writable: false }); obj.foo = 1 let obj2 = { get foo() { return 17; } }; obj2.foo = 2 let fixedObj = {}; Object.preventExtensions(fixedObj); fixed.bar= 1;
尝试在严格模式下执行此类代码片段将引发
TypeError
异常。 例如,
undefined
和
Infinity
是其值无法覆盖的全局实体,并且
obj
对象的
foo
属性不支持重写。
obj2
的
foo
属性只有一个吸气剂。 使用
Object.preventExtensions
方法使
fixedObj
对象不可扩展。
尝试删除
TypeError
删除的
TypeError
也会导致
TypeError
:
'use strict'; delete Array.prototype
严格模式禁止将相同名称的属性分配给对象。 结果,尝试执行以下代码将导致语法错误:
'use strict'; let o = { a: 1, a: 2 };
严格模式要求函数参数名称唯一。 在非严格模式下,例如,如果两个函数参数具有相同的名称
one
,则在传递参数函数时,参数值将是最后声明的参数中的值。
在严格模式下,禁止使用具有相同名称的功能参数。 结果,尝试执行以下代码将导致语法错误:
'use strict'; const multiply = (x, x, y) => x*x*y;
在严格模式下,不能使用数字的八进制表示法,在数字之前加零。 这不在规范中,但浏览器支持此功能。
这种状况使开发人员感到困惑,迫使他们认为在数字前面的0只是被忽略,没有太多意义。 在严格模式下,尝试使用以0开头的数字将导致语法错误。
严格模式还禁止使用妨碍优化的构造。 解释器在执行代码优化之前,需要知道变量存储在准确的位置,根据解释器,该变量存储在哪里。 在严格模式下,禁止干扰优化的内容。
这种禁令的一个例子是
with
语句。 如果使用此指令,这将阻止JS解释器找出我们要引用的变量或属性,因为同名实体可能存在于
with
语句块的内部和外部。
假设有这样的代码:
let x = 1; with (obj) { x; }
解释器将无法发现位于
with
块内部的变量
x
是引用外部变量
x
还是
obj
对象的
obj.x
属性。
结果,不清楚
x
值在内存中的确切位置。 为了消除此类歧义,在严格模式下,禁止使用
with
语句。 让我们看看如果尝试在严格模式下执行以下代码会发生什么:
'use strict'; let x = 1; with (obj) { x; }
尝试的结果将是语法错误。
即使在严格模式下,也禁止在传递给
eval
方法的代码中声明变量。
例如,在正常模式下,格式为
eval('let x')
将导致变量
x
的声明。 这允许程序员在字符串中隐藏变量声明,这可能导致覆盖
eval
之外的相同变量的定义。
为了防止这种情况,在严格模式下,禁止在以字符串形式传递给
eval
方法的代码中声明变量。
严格模式还禁止删除常规变量。 结果,尝试执行以下代码将导致语法错误:
'use strict'; let x; delete x;
▍禁止错误的语法构造
在严格模式下,禁止错误使用
eval
和
arguments
。 这是禁止使用它们进行各种操作的禁令。 例如,这类似于为它们分配新值,将它们的名称用作变量名,函数,函数参数。
以下是滥用
eval
和
arguments
示例:
'use strict'; eval = 1; arguments++; arguments--; ++eval; eval--; let obj = { set p(arguments) { } }; let eval; try { } catch (arguments) { } try { } catch (eval) { } function x(eval) { } function arguments() { } let y = function eval() { }; let eval = ()=>{ }; let f = new Function('arguments', "'use strict'; return 1;");
在严格模式下,不能为
arguments
对象创建别名,也不能通过这些别名设置新的
arguments
值。
在正常模式下,如果函数的第一个参数是
a
,那么在函数代码中设置a的值也会导致
arguments[0]
中的值发生变化。 在严格模式下,
arguments
将始终包含调用该函数的参数列表。
假设您有以下代码:
const fn = function(a) { 'use strict'; a = 2; return [a, arguments[0]]; } console.log(fn(1))
控制台将获得
[2,1]
。 这是因为将2写入值不会将2写入
arguments[0]
。
performance优化性能
在严格模式下,不支持
arguments.callee
属性。 在普通模式下,它返回该函数的父函数的名称,该函数的父对象的
arguments
我们正在检查。
对此属性的支持会干扰优化(例如内联函数),因为使用
arguments.callee
要求在访问此属性时提供对非嵌入式函数的引用的可用性。 在严格模式下,使用
arguments.callee
会引发
TypeError
异常。
在严格模式下,
this
不必始终是一个对象。 在正常情况下,如果
this
函数使用
call
,
apply
或
bind
到非对象上,则
bind
到原始类型的值(如
undefined
,
null
,
number
或
boolean
,则该值应该是一个对象。
如果此上下文的内容变为非对象,则将使用全局对象。 例如,
window
。 这意味着,如果通过将函数
this
设置为不是对象的值而不是此值来调用函数,则对全局对象的引用将属于
this
。
考虑一个例子:
'use strict'; function fn() { return this; } console.log(fn() === undefined); console.log(fn.call(2) === 2); console.log(fn.apply(null) === null); console.log(fn.call(undefined) === undefined); console.log(fn.bind(true)() === true);
所有
console.log
命令都将输出
true
,因为在严格模式下,如果
this
的值设置为不是对象的值,则不会自动将其替换为对全局对象的引用。
▍与安全相关的变更
在严格模式下,您不能将
caller
和
arguments
函数属性公开。 事实是,例如,
caller
可以访问调用了我们正在访问其
caller
属性的函数的函数。
arguments
对象存储调用时传递给函数的参数。 例如,如果我们有一个函数
fn
,这意味着可以通过
fn.caller
访问调用该函数的函数,并使用
fn.arguments
可以看到在调用
fn
时传递给
fn
的参数。
这些功能构成潜在的安全风险。 因此,在严格模式下禁止访问这些属性。
function secretFunction() { 'use strict'; secretFunction.caller; secretFunction.arguments; } function restrictedRunner() { return secretFunction(); } restrictedRunner();
在前面的示例中,我们不能在严格模式下访问
secretFunction.caller
和
secretFunction.arguments
。 事实是,这些属性可用于获取函数调用堆栈。 如果尝试运行此代码,将
TypeError
异常。
在严格模式下,将来的JavaScript版本中可能使用的标识符不能用于命名变量或对象的属性。 例如,我们正在讨论以下标识符:
implements
,
interface
,
let
,
package
,
private
,
protected
,
public
,
static
和
yield
。
在ES2015和更高版本的标准中,这些标识符成为保留字。 而且它们不能用于在严格模式下命名变量或属性。
总结
严格模式是已经存在多年的标准。 它享有极其广泛的浏览器支持。 严格模式代码的问题只能在较旧的浏览器(例如Internet Explorer)中发生。
现代浏览器在严格的JavaScript模式下应该不会遇到困难。 因此,可以说应该使用此模式来防止“无提示”错误并提高应用程序安全性。 静默错误会转换为阻碍程序执行的异常,并且在提高安全性方面,例如,可以注意到严格的机制,这些机制会限制
eval
并阻止对函数调用堆栈的访问。 另外,严格模式的使用有助于JS引擎代码优化,并迫使程序员谨慎处理可能在将来的JavaScript版本中使用的保留字。
亲爱的读者们! 在为项目编写JS代码时是否使用严格模式?
