JS中严格格式的使用--'use strict'

一:简单介绍

严格模式是在ES5中引入的,它可以视为JS的一个子集,在严格模式下,限制了JS的标准使用下一些行为。

  • 严格模式消除了一些 JavaScript的静默错误,通过改变它们来抛出错误。
  • 严格的模式修复了 JavaScript引擎难以执行优化的错误:有时候,严格模式代码可以比非严格模式的相同的代码运行得更快。
  • 严格模式禁用了在ECMAScript的未来版本中可能会定义的一些语法。

但是需要注意的是:不支持严格模式的浏览器将会执行与支持严格模式的浏览器不同行为的严格模式代码。所以不要依靠严格模式,而是应当加强自己代码的鲁棒性

二:使用方法

严格模式可以应用于整个脚本或单个函数中。其中的脚本不仅仅包括了单个文件,还包括了dom中事件处理,eval(),Function(),以及window.setTimeOut()中的字符串

1:为某个脚本使用严格模式

需要在所有代码前,声明

"use strict";

PS:注意,必须为"use strict";或者'use strict';,且必须带分号结束。
但这种使用方式在存在代码引用和合并的时候,会无法正常激活严格模式。因为,当一个声明了严格模式的脚本,被引入合并到一个新的未使用严格模式的脚本中的时候,由于声明并没有在所有代码前使用,从而导致严格模式声明失败。
为了保证严格模式的正常使用,一般的做法是用一个外部匿名函数将使用严格模式的脚本封装,然后执行。代码一般如下

(function () {
  'use strict';
  /**** 原有脚本代码 ****/
})()

但需要注意的是,这种方法,会将原有的代码封装到一个全局环境的函数中,原有脚本代码的作用域由全局变为了函数内部作用域,因此在使用的时候,务必注意。
PS:一个变通的方法是,将内部脚本需要全局的变量,直接声明为全局变量,即不用var或者let定义,比如a,不适用var a;或者let a;,而是直接 a = xxx

2:单函数使用严格模式

单函数使用严格模式,只需要在函数代码开头声明

"use strict";

即可

三:严格模式带来的具体差异

1:消除了静默错误,改为抛出错误
  • 无法静默声明全局变量
    全局变量必须显式声明,标准模式下,如果一个变量没有声明就赋值,默认是全局变量。严格模式禁止这种用法,全局变量必须显式声明。
"use strict";
v = 1; // 报错,v未声明
for(i = 0; i < 2; i++) { // 报错,i未声明
}
  • 函数的参数必须命名唯一
    在正常模式中,如果函数的参数中出现重名,则最后出现的重名参数替换之前出现的参数,但之前出现的参数依然可以通过arguments属性读取。在严格模式下,禁止出现这种情况,此时会跳出错误。
function sum(a, a, c){ // !!! 语法错误
  "use strict";
  return a + a + c; // 代码运行到这里会出错
}
  • 对象的属性名必须唯一
    严格模式要求一个对象内的所有属性名在对象内必须唯一。正常模式下重名属性是允许的,最后一个重名的属性决定其属性值。
"use strict";
var o = { p: 1, p: 2 }; // !!! 语法错误
  • 禁止出现八进制数字数据
    在ES标准中,并没有八进制数据,但现在的浏览器都支持以0开头作为八进制数据。严格模式下禁止了这种数据类型,但在ES6中,新引入了'0o'前缀来表示八进制数据
var a = 015; // 错误
var a = 0o10; // ES6: 八进制
  • 任何在正常模式下引起静默失败的赋值操作 (给不可赋值的全局变量赋值,给不可写属性赋值, 给只读属性(getter-only)赋值赋值, 给不可扩展对象(non-extensible object)的新属性赋值) 都会抛出异常
"use strict";
NaN = 1; // 不可复制的全局变量
var o = {
  get v() {
    return this.v
  }
};
Object.defineProperty(o, "v", { value: 1, writable: false }); // 不可写的属性
o.v = 2; // 报错

var o1 = {
  get v() { return 1; }  // 只读属性
};
o1.v = 2; // 报错

// 给不可扩展对象的新属性赋值
var fixed = {};
Object.preventExtensions(fixed);
fixed.newProp = "ohai"; // 抛出TypeError错误
  • 试图删除不可删除的属性时会抛出异常
"use strict";
delete Object.prototype; // 抛出TypeError错误
  • ECMAScript 6中的严格模式禁止设置primitive值的属性.
2:修复了JS的随意性,提升了优化能力

在标准模式下,JS的随意性,或者灵活性,是的很多变量,只有在运行时才能确切知道具体指向,这就使得变量名到内存的映射也只有到运行时才能完成。严格模式修复了大部分这种行为,使得所有的变量名在编译的时候,就已经可以一起进行优化,从而提升了执行速度。

  • 禁止使用with
    在标准模式中,使用with的时候,with代码块内的变量,只有在运行时,才能根据with引入的Obj是否存在相应的属性,来确定具体的指向。因此,严格模式禁止使用with
"use strict";
var x = 17;
with (obj) // !!! 语法错误
{
  // 如果没有开启严格模式,with中的这个x会指向with上面的那个x,还是obj.x?
  // 如果不运行代码,我们无法知道,因此,这种代码让引擎无法进行优化,速度也就会变慢。
  x;
}
  • eval不在为上层作用域引入新变量
    在标准模式下,如果某个函数内部引入了eval代码。那么在函数内部,所有出现的名称(也就是所有的变量名)应当映射到的变量,除了引用到函数的参数,以及函数的内部变量,可以在编译的时候确定外,其他所有的名称只有在运行的时候,执行完eval代码后才能映射到相应的变量。因为有些变量可能是由eval代码引入到函数作用域的。
var x = 17;
var evalX = eval("'use strict'; var x = 42; x");
console.log(x === 17); // true  未能引入x,所以还是原来的x,但如果去掉 use strict,那么为false
console.log(evalX === 42); // true 

但在严格格式下,eval不在为上层作用域引入新的局部变量和全局变量。所有的eval中出现的变量,只在eval的字符串代码块中有效。
另外如果判定eval是否为严格模式,也存在各种复杂的情况。
-- 如果直接调用eval(...)代码的代码块显式使用了严格格式,那么eval()也执行严格格式。如下面代码

function test () {
  'use strict';
  eval(...)  // 无论eval中的字符串是否包含 use strict; 都会进入严格格式
}

-- 如果直接调用eval的代码块未使用严格模式,而是更上级别的代码块使用了严格模式,则eval代码按照标准模式执行,如下面代码

function test () {
  'use strict';
  f(){} // 无论eval中的字符串是否包含 use strict; 都会进入严格格式
}

-- 如果eval(...)的字符串中显式使用了'use strict'; 则必然进入严格模式

  • 严格模式禁止删除声明变量
"use strict";
var x;
delete x; // !!! 语法错误
eval("var y; delete y;"); // !!! 语法错误
3:让eval和arguments变的更加简单
  • 严格模式下,eval和arguments作为保留字
    在严格模式下,不允许对eval和arguments进行赋值或者绑定,以下代码全部为错误语法
"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的复制,指的是arguments这个变量,而不是指的其中的元素,比如

function test (a, b) {
  'use strict';
  a = 42;
  arguments[1] = 17;  // 可以通过语法检测,但并不会修改b的值
}

依然是允许的,也可以通过语法检测。

  • 严格模式下,参数的值,不会随arguments对象的值的变化而修改。arguments对象的值的修改也不会影响同名函数。
    在标准模式下,比如一个函数第一个参数为arg,那么在函数内部,修改arg的时候,也会同步修改arguments[0],反之亦然。但在严格模式下,两者进行了隔离。函数的 arguments 对象会保存函数被调用时的原始参数。arguments[i] 的值不会随与之相应的参数的值的改变而变化,同名参数的值也不会随与之相应的 arguments[i] 的值的改变而变化。
  • 不再支持arguments. callee
4:更加安全的JS
  • 对this的严格限制
    在普通模式下,无论任何情况下,this都是一个对象。需要注意:this的指向不在函数定义的时候是确定不了的,只有函数执行的时候才能确定this到底指向谁,在标准模式下,只想调用函数的对象。但如果是箭头函数,则this为定义时上下文的this。
    -使用new新建对象
function test () {
  console.log(typeof this);
  return this;
}
var t = new test()
console.log(t)  // this 为新建的对象

-使用call,apply,bind传入this,如果传入的是数字,字符串,布尔值等,那么就会将这些基本数据的this转换为Number,String,Boolean对象类型。如果传入的是null和undefined,则为全局变量window,默认调用下,this也为window

function fun() {
  console.log(typeof this)
  return this; 
}
console.log(fun());  // window  需要注意,单独的此类 fun(),等价于window.fun()。所以为window
console.log(fun.call(2)); // Number
console.log(fun.call('2018-01-01 10:00:00')); // String
console.log(fun.apply(null)); // window
console.log(fun.call(undefined)); // window
console.log(fun.bind(true)()); // Boolean

上面的这种模式既增加了转换为对象的对象的开销,又因为将全局对象window暴露出来造成安全性问题。
因此在严格模式下指定的this不再被封装为对象,而且如果没有指定this的话它值是undefined,上面的结果如下:

'use strict';
function fun() {
  console.log(typeof this)
  return this; 
}
console.log(fun());  // undefined
console.log(fun.call(2)); // 2
console.log(fun.call('2018-01-01 10:00:00')); // '2018-01-01 10:00:00'
console.log(fun.apply(null)); // null
console.log(fun.call(undefined)); // undefined
console.log(fun.bind(true)()); // true
  • 禁止对函数扩展,fun.caller和fun.arguments进行读取和复制
    在普通模式下用这些扩展的话,当一个叫fun的函数正在被调用的时候,fun.caller是最后一个调用fun的函数,而且fun.arguments包含调用fun时用的形参。通过这些扩展,可以让不安全的用户操作到危险的属性。
    因此在严格模式下,fun.caller和fun.arguments都是不可删除的属性而且在存值、取值时都会报错
5:对未来的兼容
  • 增加了一些保留字
    在严格模式中一部分字符变成了保留的关键字。这些字符包括implements, interface, let, package, private, protected, public, static和yield
  • 禁止不在脚本或者函数层面声明函数
    所谓的脚本层面,指的是文件的全局作用域。而函数层面,指的是函数的直接作用域,这里并不包括了脚本中的块作用域,以及函数中嵌套的块作用域
"use strict";
if (true){
  function f() { } // !!! 语法错误
  f();
}
for (var i = 0; i < 5; i++){
  function f2() { } // !!! 语法错误
  f2();
}
function baz() { // 合法
  function eit() { } // 同样合法
}
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 206,839评论 6 482
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 88,543评论 2 382
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 153,116评论 0 344
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 55,371评论 1 279
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 64,384评论 5 374
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,111评论 1 285
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,416评论 3 400
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,053评论 0 259
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 43,558评论 1 300
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,007评论 2 325
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,117评论 1 334
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,756评论 4 324
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,324评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,315评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,539评论 1 262
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,578评论 2 355
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,877评论 2 345

推荐阅读更多精彩内容

  • 第2章 基本语法 2.1 概述 基本句法和变量 语句 JavaScript程序的执行单位为行(line),也就是一...
    悟名先生阅读 4,118评论 0 13
  • 第3章 基本概念 3.1 语法 3.2 关键字和保留字 3.3 变量 3.4 数据类型 5种简单数据类型:Unde...
    RickCole阅读 5,099评论 0 21
  • 函数和对象 1、函数 1.1 函数概述 函数对于任何一门语言来说都是核心的概念。通过函数可以封装任意多条语句,而且...
    道无虚阅读 4,527评论 0 5
  • 第5章 引用类型(返回首页) 本章内容 使用对象 创建并操作数组 理解基本的JavaScript类型 使用基本类型...
    大学一百阅读 3,212评论 0 4
  • 让我怎样感谢你 当我走向你的时候 我原想收获几朵清香 你却给了我整个花园 让我怎样感谢你 当我走向你的时候 我原想...
    小寇同学阅读 159评论 0 2