严格模式

注:有时您会看到默认的非限制模式,即草率模式。这不是一个官方术语,但要注意,以防万一。

JavaScript的严格模式是选择加入到JavaScript的限制变体,从而隐式选择“草率模式“.严格模式不仅仅是一个子集:它故意与普通代码具有不同的语义。不支持strict模式的浏览器将运行严格模式代码,其行为与支持严格模式的浏览器不同,因此,在没有进行功能测试的情况下,不要依赖strict方式来支持strict-mode的相关方面。严格模式代码和非严格模式代码可以共存,因此脚本可以逐步选择严格模式。

严格模式对常规JavaScript语义进行了一些更改:

  1. 通过将一些JavaScript静默错误更改为抛出错误来消除这些错误。
  2. 修复了使JavaScript引擎难以执行优化的错误:有时可以使严格模式代码比非严格模式的相同代码运行得更快。
  3. 禁止ECMAScript的未来版本中可能定义的某些语法。

调用严格模式

严格模式适用于完整的脚本或至单个功能。它不适用于块语句封装在{}支撑;试图将其应用于此类上下文无济于事。评估代码,功能代码,事件处理程序属性,传递给的字符串设置超时(),而相关函数要么是函数体,要么是整个脚本,在其中调用strict模式可以正常工作。

脚本的严格模式

要为整个脚本调用严格模式,请将准确的陈述“使用严格”;(或'使用严格';)在任何其他声明之前。

js型
//全脚本严格模式语法“使用严格”;const v=“嗨!我是一个严格模式脚本!”;

函数的严格模式

同样,要调用函数的严格模式,请将准确的陈述“使用严格”;(或'使用严格';)在函数体中的任何其他语句之前。

js型
函数myStrictFunction(){//功能级严格模式语法“使用严格”;函数nested(){return“我也是!”;}return`嗨!我是一个严格的模式函数${嵌套()}`;}函数myNotStrictFunction(){return“我不严格。”;}

这个“使用严格”指令只能应用于具有简单参数的函数体。使用“使用严格”在具有的函数中休息,违约,或被破坏的参数是一个语法错误.

js型
函数和(a=1,b=2){//语法错误:在带有默认参数的函数中不允许使用“use strict”“使用严格”;返回a+b;}

模块的严格模式

的全部内容JavaScript模块自动处于严格模式,不需要任何语句来启动它。

js型
函数myStrictFunction(){//因为这是一个模块,所以默认情况下我是严格的}导出默认myStrictFunction;

类的严格模式

a的所有部分的主体是严格的模式代码,包括类声明类表达式.

js型
C1级{//这里的所有代码都是在严格模式下计算的测试(){删除Object.protype;}}新C1().test();//TypeError,因为test()处于严格模式const C2=类别{//这里的所有代码都是在严格模式下计算的};//此处的代码可能不是严格模式删除Object.protype;//不会抛出错误

严格模式中的更改

严格模式会更改语法和运行时行为。变更通常分为以下几类:

  • 更改将错误转换为错误(作为语法错误或在运行时)
  • 简化变量引用解析方式的更改
  • 更改简化评估论据
  • 更容易编写“安全”JavaScript的更改
  • 预测未来ECMAScript演变的变化。

将错误转化为错误

严格模式将以前接受的一些错误更改为错误。JavaScript的设计初学者很容易理解,有时它提供的操作应该是错误非错误语义。有时这可以解决眼前的问题,但有时这会在未来造成更严重的问题。严格模式将这些错误视为错误,以便发现并及时修复。

分配给未声明的变量

严格模式使得不可能意外创建全局变量。在sloppy模式下,在赋值中错误地键入变量会在全局对象上创建一个新属性,并继续“工作”。会意外创建全局变量的赋值在严格模式下引发错误:

js型
“使用严格”;let mistypeVariable;//假设不存在全局变量mistypeVariable//由于//“mistypeVariable”拼写错误(缺少“a”)mistypeVariable=17;

未能分配给对象属性

严格模式会进行赋值,否则这些赋值将无法抛出异常。有三种方法可以使属性分配失败:

  • 分配给不可写数据属性
  • 对仅getter访问器属性的赋值
  • 上新属性的赋值不可扩展的对象

例如,NaN公司是不可写的全局变量。在sloppy模式下,分配给NaN公司什么都不做;开发人员没有收到失败反馈。在严格模式下,分配给NaN公司引发异常。

js型
“使用严格”;//分配给不可写全局未定义=5;//类型错误无限=5;//类型错误//不可写属性的赋值常量obj1={};Object.defineProperty(obj1,“x”,{value:42,可写:false});obj1.x=9;//类型错误//对getter-only属性的赋值常量对象2={获取x(){返回17;},};对象2.x=5;//类型错误//分配给不可扩展对象上的新属性常量固定={};Object.proventExtensions(固定);fixed.newProp=“ohai”;//类型错误

无法删除对象属性

尝试删除不可配置或不可删除的(例如,被代理拦截删除属性返回的处理程序)属性在严格模式下抛出(在此之前尝试无效):

js型
“使用严格”;删除Object.protype;//类型错误删除[].length;//类型错误

严格模式还禁止删除纯名称。删除名称在strict模式下是一个语法错误:

js型
“使用严格”;变量x;删除x;//语法错误

如果名称是可配置的全局属性,请在其前面加上全局此删除它。

js型
“使用严格”;删除globalThis.x;

参数名称重复

严格模式要求函数参数名称是唯一的。在sloppy模式下,最后一个重复的参数会隐藏以前同名的参数。之前的参数通过以下方式保持可用论据,所以它们不是完全无法访问的。然而,这种隐藏毫无意义,可能是不可取的(例如,它可能隐藏了一个输入错误),因此在严格模式下,重复的参数名称是一种语法错误:

js型
函数和(a,a,c){//语法错误“使用严格”;返回a+a+c;//如果运行此代码,则返回错误}

如果函数具有默认参数、rest参数或析构化参数,那么在非限定模式下,参数名称重复也是一个语法错误。

旧八进制文字

严格模式禁止0-带前缀的八进制文字。在sloppy模式下,以0,例如0644,被解释为八进制数(0644 === 420),如果所有数字都小于8。新手开发人员有时认为前导零前缀没有语义意义,因此他们可能会将其用作对齐设备,但这会改变数字的含义!八进制的前导零语法很少有用,可能会被错误使用,因此严格模式会导致语法错误:

js型
“使用严格”;常数和=015+//语法错误197 +142;

表示八进制文字的标准化方法是通过0个前缀。例如:

js型
const sum With Octal=0o10+8;console.log(sumWithOctal);//16

八进制转义序列,例如"\45",等于"%",可用于通过扩展-ASCII码八进制字符代码。在严格模式下,这是一个语法错误。更正式地说,不允许\后跟除0,或\0后跟一个十进制数字;例如\9\07.

设置基元值的属性

严格模式禁止在上设置属性原始的值。访问基元上的属性会隐式创建不可观察的包装器对象,因此在sloppy模式下,设置属性会被忽略(no-op)。在严格模式下类型错误被抛出。

js型
“使用严格”;false.true=“”;//类型错误(14) .sailing=“home”;//类型错误“with”.you=“fart away”;//类型错误

重复的属性名称

重复的属性名称过去被视为语法错误严格模式下。随着引入计算的特性名称由于可以在运行时进行复制,因此在ES2015中取消了此限制。

js型
“使用严格”;常数o={p:1,p:2};//ECMAScript 2015之前的语法错误

注:使用于出错的代码成为非错误代码始终被认为是向后兼容的。这是语言严格要求抛出错误的一个很好的方面:它为将来的语义更改留出了空间。

简化范围管理

严格模式简化了变量名如何映射到代码中的特定变量定义。许多编译器优化依赖于说出该变量的能力X(X)存储在那个位置:这对充分优化JavaScript代码至关重要。JavaScript有时会使代码中的名称到变量定义的基本映射在运行时之前无法执行。严格模式删除了发生这种情况的大多数情况,因此编译器可以更好地优化严格模式代码。

删除with语句

严格模式禁止具有。的问题具有块内的任何名称都可能在运行时映射到传递给它的对象的属性,或者映射到周围(甚至全局)范围内的变量;事先不可能知道是哪一个。严格模式使具有语法错误,因此不可能在具有要在运行时引用未知位置:

js型
“使用严格”;常数x=17;带有(obj){//语法错误//如果这不是严格模式,这是常量x还是//它会是obj.x吗?一般来说这是不可能的//也就是说,不运行代码,所以名称不能是//优化。x;}

简单的替代方法是将对象分配给一个短名称变量,然后访问该变量的相应属性具有.

无泄漏评估

在严格模式下,评估不会将新变量引入周围的范围.在sloppy模式下,eval(“var x;”)引入变量x个进入周围的函数或全局范围。这意味着,一般来说,在包含对评估,每个未引用参数或局部变量的名称都必须在运行时映射到特定定义(因为评估可能引入了一个隐藏外部变量的新变量)。在严格模式下,评估只为正在计算的代码创建变量,因此评估不能影响名称是指外部变量还是指某些局部变量:

js型
var x=17;var evalX=eval(“使用严格”;var x=42;x;”);console.assert(x===17);console.assert(evalX===42);

字符串是否传递给评估()在严格模式下求值取决于评估()被调用(直接评估或间接评估).

块范围函数声明

JavaScript语言规范从一开始就不允许在块语句中嵌套函数声明。然而,它是如此直观,以至于大多数浏览器都将其作为扩展语法来实现。不幸的是,实现的语义出现了分歧,语言规范不可能协调所有实现。因此,块范围函数声明仅在严格模式下显式指定(而在严格模式中曾经不允许),而在浏览器之间,草率模式行为仍然存在差异。

简化eval和arguments

严格模式使论据评估没有那么神奇。这两种方式都涉及大量马虎模式下的魔法行为:评估添加或删除绑定以及更改绑定值,以及论据将命名参数与其索引属性同步。严格模式在治疗方面取得了长足进步评估论据作为关键字。

防止绑定或分配eval和参数

名字评估论据无法在语言语法中绑定或分配。所有这些尝试都是语法错误:

js型
“使用严格”;eval=17;参数++;++评价;const-obj={set p(参数){}};让评估;尝试{}catch(参数){}函数x(eval){}函数参数(){}consty=函数eval(){};const f=new函数(“arguments”,“'use strict';return 17;”);

参数和参数索引之间没有同步

严格模式代码不同步论据对象与每个参数绑定。在第一个参数为参数,设置参数也设置参数[0]反之亦然(除非未提供参数或参数[0]已删除)。论据严格模式函数的对象存储调用函数时的原始参数。参数[i]不跟踪相应命名参数的值,命名参数也不跟踪相应参数[i].

js型
函数f(a){“使用严格”;a=42;return[a,arguments[0]];}常数对=f(17);console.assert(对[0]===42);console.assert(对[1]===17);

“保护”JavaScript

严格模式使编写“安全”JavaScript更容易。一些网站现在为用户提供了编写JavaScript的方法,这些JavaScript将由网站运行代表其他用户浏览器中的.JavaScript可以访问用户的私有信息,因此在运行此类JavaScript之前必须对其进行部分转换,以检查对禁用功能的访问。JavaScript的灵活性使它在没有很多运行时检查的情况下实际上不可能做到这一点。某些语言函数是如此普遍,以至于执行运行时检查要付出相当大的性能代价。一些严格的模式调整,再加上要求用户提交的JavaScript是严格的模式代码并以某种方式调用,大大减少了这些运行时检查的需要。

无此替换

传递的值为严格模式下的函数不会强制成为对象(也称为“装箱”)。对于松散模式函数,始终是对象:如果使用对象值调用,则为提供的对象; 或的装箱值,如果使用原语作为调用; 或全局对象(如果使用调用)未定义无效的作为.(使用呼叫,应用,或绑定指定特定的.)自动装箱不仅会带来性能成本,而且在浏览器中公开全局对象也会带来安全隐患,因为全局对象提供对“安全”JavaScript环境必须限制的功能的访问。因此,对于严格模式函数,指定未装箱到对象中,如果未指定,未定义而不是全局此:

js型
“使用严格”;函数fun(){返回此;}console.assert(fun()===未定义);console.assert(fun.call(2)===2);console.assert(fun.apply(null)===null);console.assert(fun.call(undefined)===未定义);console.assert(fun.bind(true)()===true);

移除堆垛行走属性

在严格模式下,不再可能“遍历”JavaScript堆栈。许多实现用于实现一些扩展功能,这些功能使检测函数的上游调用方成为可能。当函数乐趣正在被调用,滑稽演员是最近调用的函数乐趣、和函数参数论据为了那个召唤乐趣。这两个扩展对于“安全”JavaScript都有问题,因为它们允许“安全”代码访问“特权”函数及其(可能不安全的)参数。如果乐趣都处于严格模式滑稽演员fun.论据是在设置或检索时引发的不可删除属性:

js型
函数restricted(){“使用严格”;受限制的调用者;//引发TypeError受限参数;//引发TypeError}函数特权invoker(){return restricted();}privilegedInvoker();

同样,参数.被叫不再支持。在草率模式下,参数.被叫引用封闭函数。这个用例很弱:命名封闭函数!此外,参数.被叫极大地阻碍了像内联函数这样的优化,因为如果参数.被叫已访问。参数.被叫对于严格模式函数,是一个不可删除的属性,在设置或检索时会引发错误:

js型
“使用严格”;常数f=函数(){返回arguments.callee;};f();//引发TypeError

防未来JavaScript

额外保留字

保留字是不能用作变量名的标识符。与sloppy模式相比,Strict模式保留了更多的名称,其中一些名称已经在语言中使用,而其中一些是为将来保留的,以使将来的语法扩展更容易实现。

转换为严格模式

设计了严格模式,以便逐步过渡到该模式。可以单独更改每个文件,甚至可以将代码转换为严格模式,直至达到函数粒度。

您可以通过首先添加“使用严格”然后修复所有执行错误,同时注意语义差异。

语法错误

添加时'使用严格';,以下情况将抛出语法错误在执行脚本之前:

  • 八进制语法常数n=023;
  • 具有陈述
  • 使用删除关于变量名删除myVariable;
  • 使用评估论据作为变量或函数参数名
  • 使用一个新的保留关键字(预告未来的语言功能):实施,接口,,包裹,私有的,受保护的,公众的,静止的、和产量
  • 声明两个同名函数参数函数f(a,b,b){}
  • 在对象文字中两次声明相同的属性名称{a:1,b:3,a:7}。此约束后来被删除(错误1041128).

这些错误是好的,因为它们揭示了明显的错误或不良实践。它们发生在代码运行之前,因此只要代码被运行时解析,它们就很容易被发现。

新的运行时错误

JavaScript通常会在发生错误的情况下自动失败。在这种情况下会抛出严格模式。如果您的代码库包含此类情况,则需要进行测试以确保没有任何损坏。您可以在函数粒度级别筛选此类错误。

  • 为未声明的变量赋值会引发引用错误。这用于设置全局对象的属性,这很少是预期的效果。如果确实要为全局对象设置值,请将其显式指定为上的属性全局此.
  • 未能分配给对象的属性(例如,它是只读的)会引发类型错误。在草率模式下,这将自动失败。
  • 删除不可删除的属性会引发类型错误。在草率模式下,这将自动失败。
  • 正在访问参数.被叫,strictFunction.调用程序,或strictFunction.参数抛出一个类型错误如果函数处于严格模式。如果您正在使用参数.被叫要递归调用函数,可以使用命名函数表达式。

语义差异

这些差异是非常细微的差异。测试套件可能无法捕捉到这种细微的差异。仔细检查代码库可能是必要的,以确保这些差异不会影响代码的语义。幸运的是,这种仔细的审查可以在函数粒度上逐步完成。

在sloppy模式下,函数调用如下f()将全局对象作为值。在严格模式下,现在是未定义。使用调用函数时呼叫应用,如果该值是原语值,则将此值装箱到对象(或的全局对象)中未定义无效的). 在严格模式下,直接传递值,无需转换或替换。

论据

在sloppy模式下,修改论据对象修改相应的命名参数。这使得JavaScript引擎的优化变得复杂,并且使代码更难阅读/理解。在严格模式下论据对象的值与命名参数的值相同,但会更改为论据对象或命名参数不会相互反映。

评估

在严格模式代码中,评估不会在调用它的作用域中创建新的变量。当然,在strict模式下,字符串是用strict方式规则计算的。需要进行彻底的测试,以确保没有任何损坏。如果你真的不需要eval,那么不使用eval可能是另一个实用的解决方案。

块范围函数声明

在sloppy模式下,块内的函数声明可能在块外可见,甚至可以调用。在严格模式下,块内的函数声明仅在块内可见。

规格

规范
ECMAScript语言规范

另请参见