写点什么

为什么需要在 JavaScript 中使用严格模式?

  • 2019-11-27
  • 本文字数:4077 字

    阅读完需:约 13 分钟

为什么需要在JavaScript中使用严格模式?

严格模式是什么意思?有什么用途?为什么我们应该使用它?本文将主要从这几个问题入手,讲述在 JavaScript 中使用严格模式的必要性。


严格模式是现代 JavaScript 的重要组成部分。通过这种模式,我们可以选择使用更为严格的 JavaScript 语法。


严格模式的语义不同于以前的 JavaScript“稀松模式”(sloppy mode),后者的语法更宽松,并且会静默代码中的错误。这意味着错误会被忽略,并且运行代码可能会产生意外的结果。


严格模式对 JavaScript 语义进行了一些更改。它不会静默错误,而是会抛出错误,阻止出错的代码继续运行。


它还能指出会阻碍 JavaScript 引擎优化工作的过错。另外,它禁止使用可能在未来的 JavaScript 版本中定义的功能。


严格模式可以应用于单个函数或整个脚本。它不能仅应用于大括号内的语句等块。要为某个脚本启用严格模式,我们要在脚本顶部所有语句之前添加"use strict"或’use strict’语句。


如果我们让一些脚本使用严格模式,而其他脚本不使用严格模式,那么使用严格模式的脚本可能会与其他不使用严格模式的脚本串联在一起。


串联起来后,不使用严格模式的代码可能会被设置为严格模式,反之亦然。因此,最好不要将它们混合在一起。


我们也可以将其应用于函数。为此,我们在函数主体顶部开头添加"use strict"或’use strict’语句,后面接其他语句。它会应用到函数内部的所有内容上,包括嵌套在使用严格模式的函数中的函数。


例如:


const strictFunction = ()=>{  'use strict';  const nestedFunction = ()=>{    // 这个函数也使用严格模式  }}
复制代码


ES2015 中引入的 JavaScript 模块自动启用了严格模式,因此无需声明即可启用它。

严格模式下的变化

严格模式同时改变了语法及运行时行为。变化分为这几类:将过错(mistake)转化为运行时抛出的语法错误;简化了特定变量的计算方式;简化了 eval 函数以及 arguments 对象;还改变了可能在未来 ECMAScript 规范中实现的功能的应对方式。

将过失转化为错误

过失会转化为错误。以前它们在稀松模式中会被接受。严格模式限制了错误语法的使用,并且不会让代码在有错误的位置继续运行。


由于这种模式不允许我们使用 var、let 或 const 声明变量,因此很难创建全局变量;所以创建变量时,不使用这些关键字声明这些变量是不行的。例如,以下代码将抛出一个 ReferenceError:


'use strict';badVariable = 1;
复制代码


我们无法在严格模式下运行上述代码,因为如果关闭了严格模式,此代码将创建一个全局变量 badVariable。严格模式可以防止这种情况,以防止意外创建全局变量。


现在,任何以静默方式失败的代码都将抛出异常。这包括以前被静默忽略的所有无效语法。


例如,我们启用了严格模式后,不能为只读变量(如 arguments、NaN 或 eval)赋值。


对只读属性(如不可写的全局属性)的赋值,对 getter-only 属性的赋值以及对不可扩展对象上的属性的赋值都将在严格模式下抛出异常。


以下是一些语法示例,这些语法在启用严格模式后将失败:


'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 是不可写的属性。obj2 的 foo 属性是 getter-only 的属性,因此无法设置。使用 Object.preventExtensions 方法阻止了 fixedObj 向其添加更多属性。


此外,如果有代码尝试删除不可删除的属性,则会抛出一个 TypeError 。例如:


'use strict';delete Array.prototype
复制代码


这将抛出一个 TypeError。


严格模式还不允许在引入 E​​S6 之前,在对象中复制属性名称,因此以下示例将抛出语法错误:


'use strict';var o = { a: 1, a: 2 };
复制代码


严格模式要求函数参数名称唯一。不使用严格模式时,如果两个形参(parameter)的名称均为 1,则传入实参(argument)时,后定义的那个 1 将被接受为形参的值。


在严格模式下,不再允许具有相同名称的多个函数参数,因此以下示例将因语法错误而无法运行:


const multiply = (x, x, y) => x*x*y;
复制代码


在严格模式下也不允许八进制语法。它不是规范的一部分,但是在浏览器中可以通过为八进制数字加上 0 前缀来支持这种语法。


这使开发人员感到困惑,因为有些人可能认为数字前面的 0 是没有意义的。因此,严格模式不允许使用此语法,并且会抛出语法错误。


严格模式还阻止使用阻碍优化的语法。在优化执行之前,程序需要知道一个变量实际上存储在了预期的位置,因此我们必须避免那种阻碍优化的语法。


一个示例是 with 语句。如果我们使用它,它会阻止 JavaScript 解释器了解你要引用的变量或属性,因为可能在 with 语句的内部或外部具有相同名称的变量。


如果我们有类似以下代码的内容:


let x = 1;with (obj) {  x;}
复制代码


JavaScript 就不会知道 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 和 argument 的无效语法。这意味着不允许对它们执行任何操作,例如为它们分配新值或将它们用作变量、函数或函数中参数的名称。


以下是 eval 的无效用法和不允许的 argument 对象的示例:


'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 对象创建别名,并不允许通过别名设置新值。非严格模式下,如果函数的第一个参数是 a,则设置 a 还将设置 arguments[0]。在严格模式下,arguments 对象将始终具有调用该函数所使用的参数列表。


例如,如果我们有:


const fn = function(a) {  'use strict';  a = 2;  return [a, arguments[0]];}console.log(fn(1))
复制代码


那么我们应该看到[2,1]已记录。这是因为将 a 设置为 2 也会同时将 arguments[0]设置为 2。

性能优化

此外,严格模式不再支持 arguments.callee。非严格模式下,它所做的就是返回 arguments.callee 所在的,被调用函数的名称。


它阻止了诸如内联函数之类的优化,因为 arguments.callee 要求,如果访问 arguments.callee,则对未内联函数的引用可用。因此在严格模式下,arguments.callee 现在将抛出 TypeError。


使用严格模式时,this 不会强制始终成为对象。如果函数的 this 是用 call、apply 或 bind 绑定到任何非对象类型(例如 undefined、null、number、boolean 等原始类型)的,则必须强制它们成为对象。


如果 this 的上下文切换为非对象类型,则全局 window 对象将取代其位置。这意味着全局对象公开了正在被调用的函数,并且 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);
复制代码


所有控制台日志都会是 true,因为当 this 更改为具有非对象类型的对象时,该函数内部的 this 不会自动转换为 window 全局对象。

安全修复

在严格模式下,我们也不允许公开函数的 caller 和 arguments,因为函数的 caller 属性访问的函数被一个函数调用时,caller 可能会暴露后者。


arguments 具有在调用函数时传递的参数。例如,如果我们有一个名为 fn 的函数,则可以通过 fn.caller 查看调用 fn 的函数,并通过 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 这样的旧浏览器才可能出问题。


其他浏览器使用严格模式应该不会有问题。因此,应使用它来防止错误并避免安全隐患,例如暴露调用栈或在 eval 中声明新变量。


此外,它还消除了静默错误,现在会抛出错误,从而使代码不会在出现错误的情况下运行。它还会指出阻止 JavaScript 引擎进行优化的过错。


另外,它禁用了可能在将来的 JavaScript 版本中定义的功能。


原文链接


https://medium.com/better-programming/why-do-we-need-strict-mode-in-javascript-df34771eb950


相关文章:


JavaScript 到底是面向对象还是基于对象?


框架的游戏:2019 年 JavaScript 流行趋势


逃离 JavaScript,TypeScript 成新宠


谁将取代 JavaScript?


2019-11-27 17:003297
用户头像
王文婧 InfoQ编辑

发布了 126 篇内容, 共 73.5 次阅读, 收获喜欢 275 次。

关注

评论

发布
暂无评论
发现更多内容

《数字经济全景白皮书》金融数字用户篇 重磅发布!

易观分析

金融 数字用户

近邻搜索算法浅析

得物技术

深度学习 算法 Faiss 近邻搜索 乘积量化

web前端面授培训课程

小谷哥

Tinymce plugins [Tinymce扩展插件集合]

Five

插件开发 tinymce 富文本编辑器 8月月更

Webpack5新特性:使用 Assets Module 处理图片和字体资源

昆吾kw

前端 前端工程化 webpack

mysql进阶(二十四)防御SQL注入的方法总结

No Silver Bullet

MySQL SQL注入 8月月更

浅谈程序员的职业操守

阿里技术

Mysql重启后innodb和myisam插入的主键id变化总结

知识浅谈

8月月更

leetcode 448. Find All Numbers Disappeared in an Array 找到所有数组中消失的数字(简单)

okokabcd

数组 LeetCode 数据结构与算法

通过Gitea实现内外网repo管理

吴脑的键客

git

树莓派 USB摄像头 实现网络监控( MJPG-Streamer)

Five

树莓派 网络监控 #web 8月月更

fastposter v2.9.0 程序员必备海报生成器

物有本末

fastposter 海报生成器 海报编辑器 海报生成 图片生成

TiFlash 计算层概览

TiDB 社区干货传送门

数据库 分布式数据库 TiDB

如何在 UE4 中制作一扇自动开启的大门

HelloWorld杰少

8月月更

开源一夏 | 分享一款实用的太阳能充电电路(室内光照可用)

矜辰所致

开源 电路设计 8月月更 太阳能

Python爬虫抢购某宝秒杀商品

弑着去忘记う

后端

阿里巴巴2022最新Java架构师进阶宝典!助力程序员金九银十面试跳槽涨薪

程序员小毕

Java 程序员 架构 面试 算法

源码阅读其实很简单!阿里业务架构师手写JDK源码笔记(2022版)真香

Java永远的神

Java 程序员 面试 程序人生 jdk源码

七夕专属限定:1 分钟 Serverless 极速抽盲盒

hum建应用专家

云计算 Serverless

什么是研发效能的「黄金三角」?如何助力企业提升效能?

万事ONES

Android技术分享| 一对一音视频呼叫邀请开发流程(一)

anyRTC开发者

android 音视频 移动开发 视频通话 呼叫邀请

深入理解MySQL事务MVCC的核心概念以及底层原理

jiangxl

MySQL 事务 MVCC dba 签约计划第三季

超多精美礼品等你来拿!2022年中国混沌工程调查启动

混沌工程实验室

混沌工程

java培训机构与自学的优缺点有什么

小谷哥

易观分析:2022年Q2中国网络零售B2C市场交易规模达23444.7亿元

易观分析

B2C 网络零售

大型连锁百货运维审计用什么软件好?有哪些功能?

行云管家

IT运维 运维审计 运维软件 百货

安全狗《云原生安全威胁分析报告》首次提出双检测模型

Geek_2d6073

字节算法大神手写算法笔记,已连续多次霸榜 GitHub Trending 首页

冉然学Java

GitHub 算法 Java 分布式 字节

前端高频面试题

helloworld1024fd

tinymce 如何实现动态国际化

Five

插件开发 tinymce 富文本编辑器 8月月更

SpringBoot如何优雅地进行响应数据封装、异常处理?

程序员小毕

Java 源码 程序员 面试 spring-boot

为什么需要在JavaScript中使用严格模式?_大前端_John Au-Yeung_InfoQ精选文章