代码改变世界

【javascript基础】JavaScript语法支持严格模式:”use strict”

2012-10-30 15:57  sniper007  阅读(3227)  评论(0编辑  收藏  举报
浏览器支持情况:
IE10+
Firefox4+
Chrome11+
Opera11.6+
Safari5.14+
 
 
 

 
4.2.2 ECMAScript的严格变体的概念.(Strict Variant)
     ECMAScript语言认可一些用户所期望的,在语言特性实现上施加的某些限制.之所以他们会期望这样,可能是出于安全方面的考虑,又或者是为了避免一些容易出错的地方,获得更好的错误检查 .又或者是其他什么原因.为了支持这个可能性.ECMAScript,定义了一个语言的严格变体.这个变体排除或修改了,正规的ECMAScript语言的一些特性语法和语义的实现.
     
     ECMAScript的这种严格变体,一般被称为语言的严格模式. 严格模式,作用于ECMAScript的代码单元,并选择和使用其自身特有的严格模式语法和语义.因为严格模式,是选择在一个语法代码单元上应用. 严格模式只在该代码单元的局部产生作用.严格模式,不会跨多个代码单元,去限制或修改ECMAScript的语义.一个完整的ECMAScript程序可能同时由严格模式、以及非严格模式的代码单元所组成.这种情况下,严格模式只应用于,被定义为严格模式的代码单元部分.
     
     一个遵守此标准的ECMAScript语言实现,必须同时具备非严格,和严格模式变体两种,本标准定义的ECMAScript语言实现.另外,也必须支持两种模式的代码单元共存于一个程序中的情况.
 
 
 

 
 
10.1.1 严格模式代码(Strict Mode Code)
     ECMAScript Program语法单元,可以使用非严格模式,或严格模式,两种语法,语义的处理方式.有三种类型的代码,组成了所谓的严格模式的EC     MAScript代码,分别是strict global code, strict eval code,以及strict function code. 分别对应下面三种:
     . 全局代码中,存在一个严格模式指令的情况.就称为 strict global code.(参见14.1章节.)
     . 如果eval所执行的代码中存在严格模式指令,或者.一个直接调用的eval(参见15.1.2.1.1章节)(注1),被包含在严格模式的代码中时.就称为 strict eval code.
     . FunctionDeclaration,FunctionExpression或属性器PropertyAssignment.被包含于一个严格模式代码中,或其内部代码以一个严格模式指令作为起始.这些情况都叫做 strict function code.
     . 还有一种strict function code就是由 内置构造函数Function,创建的函数对象.如果调用Function时,最后一个参数代表的FunctionBody.以严格模式指令开头.那么这部分也是一个strict function code.
 
 
 

 
各类严格模式限制:
 
7.6.1.2 严格模式的预保留字限制(注3)
     常规预保留字 : class, enum, extends, super, const, export, import
     严格模式增加预保留字: implements, let, private, public, yield, interface, package, protected,static
   以上两种预保留字,在严格模式代码中,在语法分析期,作为token出现时.都要抛出一个相关(SyntaxError)异常.
 
     PS: 要区分IdentifierIdentifierName,前者不能包含保留字(前者即后者排除保留字的集合).而后者可以,而后者才是propertyName所使用的产生式对应的非终结符.
          1. Firefox的严格模式对 const 网开一面. 仍然允许使用 const在严格模式下作为一个关键字, 来声明一个常量.但仍然不允许使用const 作为标识符出现.而chrome,和IE10则无此问题.
          2. firefox4-(5+已修复)在实现严格模式,保留字相关限制上的一个bug是,对常规预保留字,非严格模式中.作为标识符,居然不会抛出异常.这是一个明显的疏漏.IE10则无此问题.
          3. firefox2+(1.5-ok),chrome1+,ie10+.同时存在的一个问题在于,obj.enum ,居然没有抛出异常.没有遵守任何标准(enum作为token出现在上下文中.应该抛出异常 ).
    ps: 这里其实标准说的不太清楚,或者我没看到. ES5,和3的一个重大改进是, obj.xxx 的这种形式的property accessors 的语法。 允许xxx部分出现保留字.  我们姑且认为这个规则和 上面所说的作为token 出现在任何上下文中应该抛出异常是有些冲突吧?
     
 
7.8.3 严格模式的字符串直接量限制.
      严格模式,取消了ES3对EscapeSequence非终结符产生式OctalEscapeSequence扩展.也就是说,取消了字符串直接量中,八进制的转义序列(注4)(也就是说'\01'是非法的).
 
 
7.8.4 严格模式的数字直接量限制.
      严格模式,取消了ES3对NumericLiteral非终结符产生式OctalIntegerLiteral扩展.也就是说,取消了八进制的数字直接量(也就是说011是非法的).
 
10.4.2.1 严格模式的eval调用的相关限制.
      严格模式下,eval代码中的变量初始化..其外部不再可访问,也就是说eval有了一个独立的变量环境(参考ES3的variable object).
 
 
10.6 严格模式的arguments 对象的相关限制.
      1.严格模式下,不允许访问arguments的callee和callee.caller(注5)属性了. 主要体现在arguments.[[Get]]内部方法中.
      2.严格模式下,arguments的索引器对应的那些属性,仅仅是传递的参数值的拷贝.并不存在与形参的动态关联性(注6)
      3.严格模式下,arguments的callee和caller的特性被设置为[[Configurable:false]].所以,使用Object.defineProperty等可接口去设置或修改.都将抛出异常.
      4.严格模式洗啊,arguments,以及arguments.callee,arguments.caller,arguments.callee.caller也不允许被重新赋值.
 
 
 
11.1.5 严格模式对对象初始化中PropertyAssignment产生式中set get PropertyName ( PropertySetParameterList ) { FunctionBody }语法的限制
     在严格模式中,set get属性器语法中,不允许以arguments和eval作为参数.(注7)否则,将抛出一个SyntaxError异常.
 
 
11.4.1 严格模式的delete运算符限制.
     当进入严格模式时,对变量、函数参数、函数名,又或者是对某个特性[[Configurable]]为false的属性进行delete运算,则抛出一个TypeError异常.
   另外则是,在进入严格模式后, delete 一个未声明变量. 则同样会抛出一个异常.
 
     其实对于delete,一些奇怪问题,我始终无法理解.尤其是对于宿主对象的操作.
     比如IE9,Firefox8-,Chrome等浏览器下面的代码就会比较难以解释:
     delete window.alert;//true ,
     typeof window.alert;// 'function'
     显然,delete运算的结果应该是false....这个bug.至今存在!那么我们的结论就是.不要信任delete运算符 操作宿主对象的结果.

 

 

 

 
11.13.1 严格模式的赋值运算相关限制.
     当进入严格模式时:
          1.赋值运算符的左边,是一个未定义的变量标识符,则会导致一个无法解析的引用异常(也就是严格模式,不再自动给把未定义的变量标识符,作为属性添加给global了).就要抛出一个TypeError异常.
          2.如果左值表达式执行后获得的引用,是一个特性[[Writable]]为false的数据属性,或一个特性[[Set]]为undefined的访问器属性,又或者是一个[[Extensible]]为false的某个对象的不存在的属性,赋值运算的结果是抛出一个TypeError异常.
     补充:
          3.赋值语句中,左值表达式如果是一个标识符,则不能是eval 或 arguments. 
          4.能产生类似赋值效果的运算,都参考第3条. 如 eval++, --arguments. 参考11.4.4和11.4.5章节
 
 
 
12.2.1 严格模式的变量声明限制.
     当进入严格模式时,变量声明时,如果标识符为 arguments 或 eval.就要抛出一个SyntaxError异常.
 
 
12.10.1 严格模式的with语句(withStatement)限制.
     当进入严格模式时,代码中不能出现 with语句(WithStatement).如果出现,就应该抛出一个SyntaxError异常.
 
12.14.1 严格模式的try{}catch(identifer)限制
     当进入严格模式时,TryStatement后面的catch所使用的标识符如果是eval或arguments,则要抛出一个SyntaxError异常.(try{}catch(eval){}.抛出语法错误异常.)
 
 
13.1 严格模式的函数对象相关限制.
     1. 当进入严格模式时,无论是一个函数声明,还是一个函数表达式,其形参列表中,出现重复的标识符,就要抛出一个SyntaxError异常.
     2. 当进入严格模式时,无论是一个函数声明,还是一个函数表达式,其形参列表中出现 eval或arguments 就要抛出一个SyntaxError异常.
     3. 当进入严格模式或一个函数对象内部的代码为strict function code.那么无论是一个函数声明,还是一个函数表达式,其标识符(函数名),如果是eval,或arguments.则抛出一个SyntaxError异常.(注2)
 
 
 
15.3.5.4 严格模式的函数对象的内部方法[[Get]]的限制.
     在严格模式下,如果属性名是caller那么就要抛出一个TypeError异常.(即fn.caller,访问即抛出类型错误异常)
 
 
16. 严格模式的对象直接量相关限制
     在严格模式中,对象直接量中,出现两个相同的属性名,则抛出SyntaxError异常.
 
(10.4.3, 11.1.1, 15.3.4.3, 15.3.4.4).严格模式对this的相关限制.
     在严格模式中,this可以是undefined,或null,也可以是一个原始类型(123,'abc',true),也就是说,严格模式中,this初始化的时候,不会再有自动初始化为global以及装箱的包裹机制.
 
PS:IE10,预览第二版有一个奇怪的问题,参考下面的代码:
     (function(){ 'use strict'; return this })()//IE10 -> global. 
     当直接调用某函数,非call,apply强制指定this为undefined等情况. 并且,严格模式声明的指令序言,在函数内时. 其this居然仍然自动指向global. 而该函数,确确实实进入了严格模式.这简直就是莫名其妙.
 

 
 

14.1 指令序言(Directive Prologues)和严格模式指令

    
   
一个指令序言,是那些从ProgramFunctionBody的首个SourceElement开始,到那些完全由一个字符串字面量后面跟一个分号,所构成的最长的.那一组ExpressionStatement序列中的每一个.字符串字面量后面的分号,可以显式的插入,或者借助分号自动插入机制来插入.一个指令序言,也可以是一个空的序列.(也就是说,一个指令序言,可以包含n个指令.)
 
     严格模式指令是一个"use strict"或'use strict'的字符串直接量.一个严格模式指令中,不应该包含转意序列或行终结符(如果有,即不算是一个严格模式指令了,也就无法进入严格模式).
 
     一个指令序言,可以不仅仅包含一个严格模式指令.然而,当这种情况出现的时候,实现者可以发出一个相关警告.
 
     
   注     指令序言的包含的ExpressionStatement 产生式们,会在解释执行包含他们的SourceElements 产生式期间,被正常的解析执行. ECMAScript实现,可以在一个指令序言中定义其他非严格模式指令. 当一个指令序言中的某个ExpressionStatement 并不是一个严格模式指令,也不是一个被ECMAScript实现所定义的指令.且存在某种通知机制的话.就要借助该机制,发出一个警告.
 
PS: 在各个支持严格模式的浏览器实现中,指令序言很明确, 所有的指令,必须出现在其他非指令序言指令的语句前面,参考下面的代码:
<script>
'abc';
'use strict';
</script>
这就是合法的指令序言.
 
<script>
var abc;
'use strict';
</script>
这里的严格模式指令,就会失效.
 
 
 
最终我们参考看看什么样的是合法的严格模式指令:
 
以上都是合法的.
 
 
 
那么我们看看不合法的:
 
 
我们有看到IE10预览第二版.某些特殊情况下,没有遵守标准.而且转义序列\use 的使用,其实也存在兼容性问题. 记得不要这样用即可.
 
 
 

注1: 所谓直接调用的eval 是指  eval('xxx') 这样的方式.而  (1,eval)('xxx') 或 var fn = eval; fn('xxx');这样的就都不算做直接调用. 也就是说,只有直接调用.如果eval('xxx');是严格模式中的一个语句.那么 这里面的代码执行,也在严格模式中.其实这个原因很好理解. 因为关于eval的非直接调用来说,其作用域,以及this,都是global.而不是其所在的执行环境中的variable object 以及 execution context了.
 
 
 
注2: 也就是说这样的代码在严格模式中是非法的:
     
<script>
function arguments(){
    'use strict';
}
</script>

但是有一点是ES5无能为力的.参考下面的代码:

var arguments = function(){
    'use strict';
};

 
 
 
 
注3: 保留字的变化:
          Keywords(关键字):
                         下列标记(tokens) 即就是ECMAScript-edition 3的25个关键字.以及edtion5 额外追加的 debugger 关键字.共26个关键字.
                              break else new var case finally return void catch for switch while continue function this with default if throw delete in try do instanceof typeof [debugger]
 
                    Future Reserved Words(预保留字):
     
     
     
     
     预保留字是,用作将来的扩展而预留的.也作为关键字使用的一些单词.
                         下列单词为edtion 3 31个预保留字:
                              abstract enum int short boolean export interface static byte extends long super char final native synchronized class float package throws const goto private 
                              transient debugger implements protected volatile double import public
                         下列单词为edition5 7个预保留字:
                              class enum extends super const export import
                         下列标记(tokens)为edition5中 严格模式下,任何上下文中不允许出现的9个预保留字,一但出现,必须抛出异常:
                              implements let private public yield interface package protected static
 
 
 
注4:
    OctalEscapeSequence
          \0 视觉上转意成一个空字符. \0 等价于 \u0000 ,'\0'.length == 1.
          \0DecimalDigit 其中0 将被忽略.直接去看\DecimailDigit, \00Decimal也是如此.\000Decimal则不是. 因为255的8进制表示,377 最多3位.
          那么\DecimalDigit 实际上是 8进制 ASCII转码. 所以 不超过十进制255的,有效的8进制表示 前面加\ 都会被转换成对应的ASCII字符
          即: '\u00ff' == '\377' //true.
 
 
 
注5: 此处ES5有bug.因为arguments.caller根本不是ES3支持的东西,它仅仅是IE8-自己的实现.所以完全没必要特别提出来禁止arguments.caller.而悲剧的Chrome和IE10居然还实现了这个错误. 
        ES5搞笑的实现在于.非严格模式,不会为arguments设置一个caller属性.而严格模式,反倒设置一个属性,get set 都是trhow.即抛出异常.
 
 
 
 
注6: 参考下面的代码:
     
 'use strict';
          void function fn(a) {
                alert(arguments[0]);
                a = 2;
                alert(arguments[0]);
          }(1);
两次都是1.
 
 
 
 
 
注7: 参考下面的代码:
<script>
'use strict';
var obj = {
    get arguments() {},
    get eval(){}
};
</script>
这两种都要抛出异常. 不过目前只有chrome浏览器实现了这个语法错误异常的抛出. firefox4+,ie10.暂时都没实现.
 
 
 
 

如果给JavaScript代码标志为“严格模式”,则其中运行的所有代码都必然是严格模式下的。
其一:如果在语法检测时发现语法问题,则整个代码块失效,并导致一个语法异常。
其二:如果在运行期出现了违反严格模式的代码,则抛出执行异常。

注:经过测试IE6,7,8,9均不支持严格模式。

JavaScript代码“严格模式”使用方法:
严格模式需要使用字符串序列:

"use strict"

在如下位置加入可以开启相应代码块中的严格模式:
1.必须在全局代码的开始处加入。
2.在eval代码开始处加入。
3.在函数声明代码开始处加入。
4.在new Function()所传入的body参数块开始加入。

例1:

var num =012;alert(num);

在非严格模式下,可以使用0(零)开头前缀声明8进制。显示10。

但是在严格模式下,会产生错误。

"use strict";

var num =012;

alert(num);

测试结果:
IE6,7,8,9均显示10。
FF报错:octal literals and octal escape sequences are deprecated
Chrome报错:Uncaught SyntaxError: Octal literals are not allowed in strict mode.
Opera报错:Syntax error at line 3 while loading: Invalid character var num = 012;

如果使用严格模式,除了0(零)开头前缀8进制以外还有:
1.在代码中不能使用一些扩展的保留字:
implements,interface,let,package,private,public,static,yield
2.with语句也不能使用。
3.不能声明或重写eval和arguments两个标识符。
4.不能用delete 删除显式声明的标识符,名称或具名函数。