javascript严格模式

稿件来源: 互联网   撰稿作者: 太阳光   发表日期: 2015-06-30   阅读次数: 59   查看权限: 游客查看

ECMAScript 5的严格模式是Javascript中的一种限制性更强的变种方式。

严格模式不是一个子集:它在语义上与正常代码有着明显的差异。不支持严格模式的浏览器与同支持严格模式的浏览器行为上也不一样, 所以不要在未经严格模式特性测试情况下使用严格模式。严格模式可以与非严格模式共存,所以脚本可以逐渐的选择性加入严格模式。

严格模式在语义上与正常的JavaSript有一些变化。 首先,严格模式会将JavaScript陷阱直接变成明显的错误。其次,严格模式修正了一些引擎难以优化的错误:同样的代码有些时候严格模式会比非严格模 式下更快。 第三,严格模式禁用了一些有可能在未来版本中定义的语法。

开启严格模式

严格模式可以应用到整个script标签或某个别函数中。不要在封闭大括弧( {} )内这样做;在这样的上下文中这么做是没有效果的。 例如,eval 代码,Function 代码,事件处理属性,传入 setTimeout方法的字符串 等等包括整个脚本中开启严格模式都会如预期一样工作。

为某个script标签开启严格模式

为整个script标签开启严格模式, 需要在所有语句之前放一个特定语句 "use strict"; (或 'use strict';)

// 整个语句都开启严格模式的语法
"use strict";
var v = "Hi!  I'm a strict mode script!";

这种语法存在陷阱, 不能盲目的拼合非冲突代码。如果串联一个严格模式的脚本和一个非严格模式的脚本:整个拼合后的脚本代码看起来成了严格模式。反之亦然:非严格串联严格看起 来像是非严格的。拼合全为严格模式的脚本或全为非严格模式的都没问题,只有在拼合严格模式与非严格模式有可能有问题。建议按一个个函数去开启严格模式(至 少在学习的过渡期要这样做).

您也可以将整个脚本的内容用包裹在一个函数里面,并在外部函数中使用严格模式。这样做就可以消除拼接的问题,但是这就意味着您必须要在函数作用域外声明一个全局变量。

为某个函数开启严格模式

同样的,各某个函数开启严格模式就放一句 "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."; }

严格模式有哪些不同

严格模式同时改变了语法及运行时行为。变化通常分为这几类:将问题直接转化为错误(如语法错误或运行时错误), 简化了如何为给定名称的特定变量计算,简化了 eval 以及 arguments, 将写"安全“JavaScript的步骤变得更简单,以及改变了预测未来ECMAScript行为的方式。

将拼写错转成异常

在严格模式下, 先前被接受的拼写错误将会被认为是异常. JavaScript被设计为能使新人开发者更易于上手, 所以有时候会给本来错误操作赋予新的不报错误的语义(non-error semantics). 有时候这可以解决当前的问题, 但有时候却会给以后留下更大的问题. 严格模式则把这些失误当成错误, 以便可以发现并立即将其改正.

首先,严格模式下无法再意外创建全局变量。在普通的JavaScript里面给一个拼写错误的变量名赋值会使全局对象新增一个属性并继续“工作”(仅管后面可能出错:在现在的JavaScript中有可能)。严格模式中意外创建全局变量被抛出错误替代:

"use strict";
mistypedVariable = 17; // 抛出 ReferenceError

其次, 严格模式会使引起静默失败(silently fail,注:不报错也没有任何效果)的赋值操作抛出异常. 例如, NaN 是一个不可写的全局变量. 在正常模式下, 给 NaN 赋值不会产生任何作用; 开发者也不会受到任何错误反馈. 但在严格模式下, 给 NaN 赋值会抛出一个异常. 任何在正常模式下引起静默失败的赋值操作 (给不可写属性赋值, 给只读属性(getter-only)赋值赋值, 给不可扩展对象(non-extensible object)的新属性赋值) 都会抛出异常:

"use strict";

// 给不可写属性赋值
var obj1 = {};
Object.defineProperty(obj1, "x", { value: 42, writable: false });
obj1.x = 9; // throws a TypeError

// 给只读属性赋值
var obj2 = { get x() { return 17; } };
obj2.x = 5; // throws a TypeError

// 给不可扩展对象的新属性赋值
var fixed = {};
Object.preventExtensions(fixed);
fixed.newProp = "ohai"; // throws a TypeError

第三, 在严格模式下, 试图删除不可删除的属性时会抛出异常(之前这种操作不会产生任何效果):

"use strict";
delete Object.prototype; // throws a TypeError

第四, 严格模式要求一个对象内的所有属性名在对象内必须唯一. 正常模式下重名属性是允许的, 重名的最后一个属性决定其属性值. 因为只有最后一个属性有效, 当修改代码要改变属性值而却不是修改的最后一个重名属性的时候, 这种重复就成了bug的温床. 在严格模式下, 重名属性被认为是语法错误:

"use strict";
var o = { p: 1, p: 2 }; // !!! 语法错误

第五, 严格模式要求函数的参数名唯一. 在正常模式下, 最后一个重名参数名会掩盖之前的重名参数. 之前的参数仍然可以通过 arguments[i] 来访问, 还不是完全无法访问. 然而, 这种隐藏毫无意义或者并不是故意为之 (比如, 就是一个打字错误), 所以在严格模式下重名参数被认为是语法错误:

function sum(a, a, c){
  "use strict"; // !!! 语法错误
  return a + b + c; // 如果代码运行到这里,将会有一个bug
}

第六, 严格模式禁止八进制数字语法. ECMAScript并不包含八进制语法, 但所有的浏览器都支持这种以零(0)开头的八进制语法: 0644 === 420 还有 "\045" === "%". 有些新手开发者认为数字的前导零没有语法意义, 所以他们会用作对齐措施 — 但其实这会改变数字的意义! 八进制语法很少有用并且可能会错误使用, 所以严格模式下八进制语法会引起语法错误:

"use strict";
var sum = 015 + // !!! 语法错误
          197 +
          142;

简化变量的使用

严格模式简化了代码中变量名字映射到变量定义的方式. 很多编译器的优化是依赖存储变量X位置的能力:这对全面优化JavaScript代码至关重要. JavaScript有些情况会使得代码中名字到变量定义的基本映射只在运行时才产生. 严格模式移除了大多数这种情况的发生, 所以编译器可以更好的优化严格模式的代码.

首先, 严格模式禁用 withwith 所引起的问题是块内的任何名称可以映射(map)到with传进来的对象的属性, 也可以映射到包围这个块的作用域内的变量(甚至是全局变量), 这一切都是在运行时决定的: 在代码运行之前是无法得知的. 严格模式下, 使用 with 会引起语法错误, 所以就不会存在 with 块内的变量在运行是才决定引用到哪里的情况了:

"use strict";
var x = 17;
with (obj) // !!! 语法错误
{
  // 如果没有开启严格模式,with中的这个x会指向with上面的那个x,还是obj.x?如果不运行代码,我们无法知道,因此,这种代码让引擎无法进行优化,速度也就会很慢了.
  x;
}

一种取代 with 的简单方法是,将目标对象赋给一个短命名变量,然后访问这个变量上的相应属性.

第二, 严格模式下的 eval 不在为上层范围(surrounding scope,注:包围eval代码块的范围)引入新变量. 在正常模式下,  代码 eval("var x;") 会给上层函数(surrounding function)或者全局引入一个新的变量 x . 这意味着, 一般情况下,  在一个包含 eval 调用的函数内所有没有引用到参数或者局部变量的名称都必须在运行时才能被映射到特定的定义 (因为 eval 可能引入的新变量会覆盖它的外层变量). 在严格模式下 eval 仅仅为被运行的代码创建变量, 所以 eval 不会影响到名称映射到外部变量或者其他局部变量:

var x = 17;
var evalX = eval("'use strict'; var x = 42; x");
assert(x === 17);
assert(evalX === 42);

相应的, 如果函数 eval 在被严格模式下的eval(...)以表达式的形式调用时, 其代码会被当做严格模式下的代码执行. 当然也可以在代码中显式开启严格模式, 但这样做并不是必须的.

function strict1(str){
  "use strict";
  return eval(str); // str中包含的代码肯定会在严格模式下运行
}
function strict2(f, str){
  "use strict";
  return f(str); // f是个处于严格模式下的函数,或者f为eval且str中的适当位置处添加了"use strict"的情况下,str中的代码才会在严格模式下运行
}
function nonstrict(str){
  return eval(str); // 只有在str中的适当位置处添加了"use strict",str中的代码才会在严格模式下运行
}
strict1("'Strict mode code!'");
strict1("'use strict'; 'Strict mode code!'");
strict2(eval, "'Non-strict code.'");
strict2(eval, "'use strict'; 'Strict mode code!'");
nonstrict("'Non-strict code.'");
nonstrict("'use strict'; 'Strict mode code!'");

因此在严格模式下eval执行的内容跟在非严格模式下eval执行的内容的结果是等同的。

第三, 严格模式禁止删除plain names. delete name 在严格模式下会引起语法错误:

"use strict";
eval("var x; delete x;"); // !!! 语法错误

eval和arguments变的简单

严格模式让argumentseval少了一些奇怪的行为。两者在通常的代码中都包含了很多奇怪的行为: eval to add or remove bindings and to change binding values, and arguments by its indexed properties aliasing named arguments. 虽然在未来的ECMAScript版本解决这个问题之前,是不会有补丁来完全修复这个问题,但严格模式下将eval和arguments作为关键字对于此 问题的解决是很有帮助的。

首先, 名称 eval 和 arguments 不能被绑定(be bound)或赋值 in language syntax. 以下的所有尝试将一起语法错误:

"use strict";
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]];
}
var pair = f(17);
assert(pair[0] === 42);
assert(pair[1] === 17);

第三,不再支持 arguments.callee。正常模式下,arguments.callee 指向当前正在执行的函数。这个作用很小:直接给执行函数命名就可以了!此外,arguments.callee 十分不利于优化,例如内联函数,因为 arguments.callee 会依赖对非内联函数的引用。在严格模式下,arguments.callee 是一个不可删除属性,而且赋值和读取时都会抛出异常:

"use strict";
var f = function() { return arguments.callee; };
f(); // throws a TypeError

严格模式下更容易写出“安全”的Javascript。现在有些网站提供了方式给用户编写能够被网站其他用户执行的javascript代码。在浏览器环 境下,Javascript能够获取用户的隐私信息,因此这类Javascript必须在运行前部分被转换成需要申请访问禁用功能的权限。没有很多的执行 时检查的情况,Javascript的灵活性让它无法有效率地做这个事。

 

以下相关错误你明白多少呢?

eval('"\010"') //is a SyntaxError
eval('010') //is a SyntaxError
__i_dont_exist = 1; //is a ReferenceError
eval = 1; //is a SyntaxError
arguments = 1; //is a SyntaxError
eval++; //is a SyntaxError
arguments++; //is a SyntaxError
arguments.caller; //is a TypeError
arguments.callee; //is a TypeError
(function(x){ x = 2; return arguments[0] === 1; })(1);
(function(x){ arguments[0] = 2; return x === 1; })(1);
({ x: 1, x: 1 }); //is a SyntaxErrorNo
({ set x(eval){ } }); //is a SyntaxError
({ set x(arguments){ } }); //is a SyntaxError
eval('var x'); x; //is a ReferenceError
(function(){ return this === undefined; })();
(function(){ return this === undefined; }).call();
var x; delete x; //is a SyntaxError
delete (function(){}).prototype; //is a TypeError
(function f() { f = 123; })() //is a TypeError
Object.defineProperty({ }, "x", { writable: false }).x = 1 //is a TypeError
({ get x(){ } }).x = 1; //is a TypeError
var eval; //is a SyntaxError
var arguments; //is a SyntaxError
with({}){ } //is a SyntaxError
try { } catch (eval) { } //is a SyntaxError
try { } catch (arguments) { } //is a SyntaxError
function f(eval) { } //is a SyntaxError
function f(arguments) { } //is a SyntaxError
function f(x, x) { } //is a SyntaxError
(function(){}).caller; //is a TypeError
(function(){}).arguments; //is a TypeError
function eval(){ } //is a SyntaxError
function arguments(){ } //is a SyntaxError

扩展阅读:https://msdn.microsoft.com/en-us/library/br230269%28v=vs.94%29.aspx

关键词: javascript,严格模式,user strict   编辑时间: 2015-12-03 11:05:37

  • 感到高兴

    7

    高兴
  • 感到支持

    7

    支持
  • 感到搞笑

    7

    搞笑
  • 感到不解

    7

    不解
  • 感到谎言

    7

    谎言
  • 感到枪稿

    7

    枪稿
  • 感到震惊

    7

    震惊
  • 感到无奈

    7

    无奈
  • 感到无聊

    7

    无聊
  • 感到反对

    7

    反对
  • 感到愤怒

    7

    愤怒
50%(7)
50%(7)
共有0 条评论 发言请遵守【相关规定

网友评论

会员头像
发 表同步腾讯微博    验证码:  点击更新请先登陆
  • 暂无评论
关闭模块文章图片 article Pictrue
  • 我的妈妈爸爸
  • 基于koa2+mysql+vue2.0+Element阳光内容管理系统
  • 代码覆盖率工具 Istanbul 入门教程
  • 全栈工程师的武器——MEAN
  • 9款超炫的 CSS3 复选框(Checkbox)
  • 微信开发在线翻译功能
  • CSS3那些不为人知的高级属性
  • 给easyui的datebox添加清空事件
  • flash写字效果
  • kendoUI系列教程之DropDownList下拉菜单
  • kendoUI系列教程之datetimepicker日期时间选择
  • kendoUI系列教程之datepicker日期选择
  • kendoUI系列教程之combobox下拉列表框
  • kendoUI系列教程之colorpicker
  • kendoUI系列教程之calendar日历表
  • kendoUI系列教程之autocomplete自动补齐