JavaScript 严格模式

325 阅读8分钟

什么是严格模式

JavaScript 严格模式(Strict Mode)是 ECMAScript 5 引入的一种改进机制,目的是为了提供更强的错误检查和安全性。使用严格模式,可以减少一些不安全的操作,同时帮助开发人员写出更易于维护的代码。

严格模式已得到所有主流浏览器支持

如何开启严格模式

严格模式可以应用到全局,也可以应用到函数内部作用域

1、使用"use strict"字符串开启

使用字符串开启后,支持严格模式的引擎会启用严格模式,而不支持的引擎则会将这个编译指示当成一个未赋值的字符串字面量,不影响程序正常运行。


function foo() {

    'use strict'

    // 其它逻辑代码,严格模式只在此函数中生效

}

2、ES6 的模块自动采用严格模式,不管你有没有在模块头部加上"use strict";
3、Vue 也支持开启严格模式,Vue3默认开启,Vue2则可以使用以下配置:

const app = new Vue({

    strict: true //通过此配置开启或关闭

})

//或者通过以下全局配置设置

Vue.config.devtools = true

Vue.config.productionTip = false

Vue.config.strict = true

严格模式下有哪些限制

1、变量声明
  • 非严格模式: 可以隐式声明变量,即没有使用 varletconst 关键字

  • 严格模式: 必须使用 varletconst 关键字显式声明变量,否则会导致 ReferenceError


// 非严格模式

undeclaredVar = 10;

// 严格模式

'use strict';

undeclaredVar = 10; // ReferenceError

2、函数参数不能有同名属性,否则会报错
  • 非严格模式: 如果函数的参数重复了,后面 的参数会覆盖前面的参数

  • 严格模式: 这被认为是一个语法错误


// 非严格模式

function nonStrictMode(x, x) {

    console.log(x);

}

nonStrictMode(1, 2); // 输出 2,因为第二个参数覆盖了第一个参数

// 严格模式

'use strict';

function strictMode(x, x) {

    console.log(x); // SyntaxError: Duplicate parameter name not allowed in this context

}

strictMode(1, 2);

3、不能使用with语句
  • 非严格模式: with 语句用于创建一个指定对象的作用域,延伸作用域链。但会导致代码可读性和性能问题,因此不推荐使用

  • 严格模式: 完全禁止使用 with 语句,会导致 SyntaxError


// 非严格模式

var obj = { x: 10, y: 20 };

    with (obj) {

    console.log(x + y); // 输出 30

}

// 严格模式

'use strict';

var obj = { x: 10, y: 20 };

with (obj) {

    console.log(x + y); // SyntaxError

}

4、不能对只读属性赋值,否则会报错
  • 非严格模式: 对只读属性的赋值不会引发错误,但操作将被忽略

  • 严格模式: 对只读属性的赋值会导致 TypeError


// 非严格模式

var obj = {};

Object.defineProperty(obj, 'readOnly', {

    value: 42,

    writable: false

});

obj.readOnly = 10; // 不会报错,但不起作用

// 严格模式

'use strict';

var obj = {};

Object.defineProperty(obj, 'readOnly', {

    value: 42,

    writable: false

});

obj.readOnly = 10; // TypeError

5、禁止使用八进制字面量(前缀0)
  • 非严格模式: 八进制字面量(如 010,以0开头)会被解释为八进制数

  • 严格模式: 禁止使用八进制字面量,会导致 SyntaxError


// 非严格模式

var octalValue = 0123; // 合法

// 严格模式

'use strict';

var octalValue = 0123; // SyntaxError

在实际编码中,使用十进制字面量比八进制更清晰,更容易理解,而且八进制的使用方式可能容易导致一些错误。因此,严格模式下禁止了这种不太常见的特性,以提高代码的可读性和可维护性

如果在严格模式下需要使用8进制,可以使用以下方法


"use strict";

var num = parseInt("0755", 8); // 将字符串解析为八进制数值

console.log(num); // 输出 493

6、禁止删除变量、函数和函数的形参
  • 非严格模式: 可以通过 delete 操作符删除变量、函数和函数的形参,不报错,但没有效果

  • 严格模式: 禁止使用 delete 操作符删除变量、函数和函数的形参,会报 SyntaxError,只能删除属性 delete global[prop]

7、eval中定义的变量和函数不会提升到外层作用域

//非严格模式

eval('var x = 1;')

console.log(x); // 1

//严格模式

'use strict';

eval('var x = 1;');

console.log(x); // ReferenceError: x is not defined

8、evalarguments 不能被重新赋值
  • 非严格模式: 可以被赋值

  • 严格模式: 不能被赋值

9、arguments 不会自动反映函数参数的变化
  • 非严格模式: 当你修改了函数参数的值,arguments 对象中对应的值也会同步更新;反向亦可影响。所以 arguments 可以看做是参数的别名,

  • 严格模式: 当你修改了函数参数的值,arguments 对象中对应的值不会变化;反向也不会变。相当于只保留初始值,后续变化不会互相影响


// 非严格模式

'use strict';

function foo(a, b) {

    console.log(arguments[0], arguments[1]); // 1, 2

    a = 10;

    b = 20;

    console.log(arguments[0], arguments[1]); // 1, 2 严格模式, 10, 20 in 非严格模式

}

foo(1, 2);

function bar(a, b) {

    console.log(a, b); // 1, 2

    console.log(arguments[0], arguments[1]); // 1, 2

    arguments[0] = 10;

    arguments[1] = 20;

    console.log(a, b); // 1, 2 严格模式, 10, 20 非严格模式

}

bar(1, 2);

10、不能使用 arguments.calleearguments.caller
  • arguments.callee 是一个指向当前正在执行的函数的指针。它允许在匿名函数内部引用函数自身,这在递归调用时可能会用到。

  • 在严格模式下,访问 arguments.callee 会导致 TypeError


// 非严格模式

function factorial(n) {

    if (n <= 1) {

        return 1;

    } else {

        return n * arguments.callee(n - 1);

    }

}

console.log(factorial(5)); // 输出 120

  • arguments.caller 是一个指向调用当前函数的函数的引用。它提供了对调用栈的访问。

  • 由于涉及到隐式的调用链,使用 arguments.caller 在现代 JavaScript 中被废弃,并在严格模式下会导致 TypeError


// 非严格模式

function outer() {

    inner();

}

function inner() {

    console.log(arguments.caller); // 在非严格模式下输出 outer 函数的引用

}

outer();

  • 总结:由于这两个特性存在一些问题和潜在的安全风险,它们已经被 ECMAScript 5 引入的严格模式中移除。在现代 JavaScript 中,建议使用更可靠和清晰的方式,如函数引用或者命名函数表达式来实现相同的功能。
11、不允许使用 fn.callerfn.arguments 来获取函数调用的堆栈信息
  • 非严格模式: fn.caller 返回一个对当前函数进行调用的函数的引用

  • 严格模式: 访问 fn.caller 会导致 TypeError


function outer() {

    inner();

}

function inner() {

    console.log(inner.caller); // 在非严格模式下输出 outer 函数的引用

}

outer();

  • 非严格模式: fn.arguments 返回一个包含传递给函数的参数的类数组对象

  • 严格模式: 访问 fn.arguments 会导致 TypeError


function example() {

    console.log(arguments[0]);

    console.log(example.arguments[0]); // 在非严格模式下输出相同的值

}

example(42);

  • 总结: 这些限制是为了提高代码的可靠性和安全性。在现代 JavaScript 中,推荐使用其他方式来实现函数堆栈的相关需求,比如使用 Error 对象,new Error().stack 可以在任何模式下获取调用堆栈信息。
12、禁止 this 指向全局对象
  • 非严格模式: 如果函数没有明确指定执行上下文(通过 call、apply 或者作为对象的方法调用),那么 this 会指向全局对象

  • 严格模式: 而在严格模式下,函数的this值始终是指定的值,无论指定的是什么值


var color = "red";

function displayColor() {

    console.log(this.color);

}

displayColor.call(null); //非严格模式下输出red,严格模式下报错

13、保留字

严格模式下,不能使用 implementsinterfaceletpackageprivateprotectedpublicstaticyield 等保留字作为函数名,变量名

14、严格模式函数声具有块级作用域

严格模式中声明的函数,在块级作用域外不能访问

严格模式中使用var 声明的变量,在块级作用域外不能访问

15、严格模式对变量声明的影响

全局变量需显式声明:严格模式下不能通过未声明的变量隐式创建全局变量。

"use strict";
function test() {
  x = 10; // 报错:x 未声明
}
test();

其它

如何使用 Error 获取堆栈信息

function getStackTrace() {

    try {

        throw new Error('Getting stack trace');

    } catch (error) {

        return error.stack;

    }

}

// 使用

const stackTrace = getStackTrace();

console.log(stackTrace);

在这个例子中,getStackTrace 函数通过 throw new Error 来创建一个错误,并在 catch 语句中返回错误的堆栈信息。你可以将这个函数嵌套在你的代码中的任何地方,以获取相应的调用堆栈。

注意:

  • 这种方法在任何 JavaScript 环境中都有效,无论是否处于严格模式。

  • 堆栈信息可能会因 JavaScript 引擎和环境的不同而有所差异。

  • 这仅在需要获取调用堆栈信息时使用,因为创建 Error 对象可能会对性能产生一些微小的影响。

其它方式:

  • 另外可以使用三方库获取更详细的信息,例如:stacktrace-js

  • 使用浏览器开发者工具: 大多数现代浏览器的开发者工具提供了强大的调试功能,包括异步调用的堆栈信息。你可以在调试器中设置断点并检查堆栈。