Skip to content

JavaScript 严格模式

什么是严格模式

严格模式(Strict Mode)是 JavaScript 中的一种特殊执行模式,它通过抛出错误来消除一些不合理的语法,防止一些可能导致错误的行为,并提高代码的安全性和性能。

启用严格模式

1. 全局严格模式

在脚本的顶部添加 'use strict';"use strict"; 来启用全局严格模式:

javascript
// 启用全局严格模式
'use strict';

console.log('This code runs in strict mode');

// 严格模式下的代码
function test() {
  console.log('This function also runs in strict mode');
}

test();

2. 函数严格模式

在函数内部的顶部添加 'use strict';"use strict"; 来启用函数级严格模式:

javascript
// 非严格模式
console.log('This code runs in non-strict mode');

function strictFunction() {
  // 启用函数级严格模式
  'use strict';
  
  console.log('This function runs in strict mode');
}

function nonStrictFunction() {
  console.log('This function runs in non-strict mode');
}

strictFunction();
nonStrictFunction();

3. 模块严格模式

在 ES6 模块中,默认就是严格模式,不需要显式添加 'use strict';

javascript
// ES6 模块(默认严格模式)
export function test() {
  console.log('This module runs in strict mode');
}

严格模式的特点

1. 变量必须声明

在严格模式下,变量必须先声明后使用,否则会抛出 ReferenceError。

javascript
// 非严格模式
x = 10; // 隐式声明为全局变量
console.log(x); // 10

// 严格模式
'use strict';
y = 20; // ReferenceError: y is not defined

2. 禁止删除变量、函数和函数参数

在严格模式下,禁止使用 delete 操作符删除变量、函数和函数参数。

javascript
// 非严格模式
var x = 10;
delete x; // 无效果,但不报错
console.log(x); // 10

// 严格模式
'use strict';
var y = 20;
delete y; // SyntaxError: Delete of an unqualified identifier in strict mode.

3. 禁止重复声明变量

在严格模式下,禁止在同一个作用域内重复声明变量。

javascript
// 非严格模式
var x = 10;
var x = 20; // 覆盖之前的声明
console.log(x); // 20

// 严格模式
'use strict';
var y = 10;
var y = 20; // SyntaxError: Identifier 'y' has already been declared

4. 禁止使用八进制字面量

在严格模式下,禁止使用以 0 开头的八进制字面量。

javascript
// 非严格模式
var x = 010; // 八进制字面量,值为 8
console.log(x); // 8

// 严格模式
'use strict';
var y = 010; // SyntaxError: Octal literals are not allowed in strict mode.

// 严格模式下使用八进制的正确方式
var z = 0o10; // ES6 语法,值为 8
console.log(z); // 8

5. 禁止使用 with 语句

在严格模式下,禁止使用 with 语句。

javascript
// 非严格模式
var obj = { x: 10 };
with (obj) {
  console.log(x); // 10
}

// 严格模式
'use strict';
var obj = { x: 10 };
with (obj) { // SyntaxError: Strict mode code may not include a with statement
  console.log(x);
}

6. 函数参数必须唯一

在严格模式下,函数参数名必须唯一。

javascript
// 非严格模式
function test(a, a) {
  console.log(a); // 第二个参数覆盖第一个参数
}
test(1, 2); // 2

// 严格模式
'use strict';
function test(a, a) { // SyntaxError: Duplicate parameter name not allowed in this context
  console.log(a);
}
test(1, 2);

7. 禁止使用 arguments.callee 和 arguments.caller

在严格模式下,禁止使用 arguments.calleearguments.caller,它们会抛出 TypeError。

javascript
// 非严格模式
function test() {
  console.log(arguments.callee); // 指向当前函数
}
test();

// 严格模式
'use strict';
function test() {
  console.log(arguments.callee); // TypeError: 'caller', 'callee', and 'arguments' properties may not be accessed on strict mode functions or the arguments objects for calls to them
}
test();

8. this 的绑定

在严格模式下,this 的绑定规则有所不同:

  • 在全局函数中,thisundefined,而不是全局对象
  • 在构造函数中,如果没有使用 new 调用,thisundefined,而不是全局对象
  • 在事件处理函数中,this 仍然指向触发事件的元素
javascript
// 非严格模式
function test() {
  console.log(this); // 全局对象(window)
}
test();

// 严格模式
'use strict';
function test() {
  console.log(this); // undefined
}
test();

// 构造函数
function Person(name) {
  this.name = name;
}

// 非严格模式
var person1 = Person('John'); // 没有使用 new
console.log(person1); // undefined
console.log(window.name); // 'John'(意外污染全局对象)

// 严格模式
'use strict';
var person2 = Person('John'); // 没有使用 new
console.log(person2); // undefined
console.log(window.name); // ''(不会污染全局对象,因为 this 是 undefined)

9. 禁止修改只读属性

在严格模式下,修改只读属性会抛出 TypeError。

javascript
// 非严格模式
var obj = {};
Object.defineProperty(obj, 'name', {
  value: 'John',
  writable: false // 只读属性
});

obj.name = 'Jane'; // 无效果,但不报错
console.log(obj.name); // 'John'

// 严格模式
'use strict';
var obj = {};
Object.defineProperty(obj, 'name', {
  value: 'John',
  writable: false // 只读属性
});

obj.name = 'Jane'; // TypeError: Cannot assign to read only property 'name' of object '#<Object>'

10. 禁止扩展不可扩展的对象

在严格模式下,扩展不可扩展的对象会抛出 TypeError。

javascript
// 非严格模式
var obj = {};
Object.preventExtensions(obj); // 禁止扩展
obj.age = 30; // 无效果,但不报错
console.log(obj.age); // undefined

// 严格模式
'use strict';
var obj = {};
Object.preventExtensions(obj); // 禁止扩展
obj.age = 30; // TypeError: Cannot add property age, object is not extensible

11. 禁止使用 eval 创建的变量影响外部作用域

在严格模式下,eval 语句不会创建新的变量到外部作用域。

javascript
// 非严格模式
eval('var x = 10');
console.log(x); // 10(eval 创建的变量泄露到外部作用域)

// 严格模式
'use strict';
eval('var y = 20');
console.log(y); // ReferenceError: y is not defined(eval 创建的变量不会泄露到外部作用域)

12. 禁止使用保留字作为变量名

在严格模式下,禁止使用某些保留字作为变量名。

javascript
// 非严格模式
var let = 10; // 允许
console.log(let); // 10

// 严格模式
'use strict';
var let = 20; // SyntaxError: Unexpected strict mode reserved word

13. 禁止使用未声明的对象字面量属性

在严格模式下,禁止在对象字面量中使用重复的属性名。

javascript
// 非严格模式
var obj = { x: 10, x: 20 }; // 第二个属性覆盖第一个
console.log(obj.x); // 20

// 严格模式
'use strict';
var obj = { x: 10, x: 20 }; // SyntaxError: Duplicate data property in object literal not allowed in strict mode

严格模式的优点

1. 提高代码质量

严格模式通过抛出错误来捕获一些常见的编码错误,如未声明的变量、重复的参数名等,有助于提高代码质量。

2. 提高安全性

严格模式禁止一些可能导致安全问题的操作,如修改只读属性、扩展不可扩展的对象等,有助于提高代码的安全性。

3. 提高性能

严格模式下,JavaScript 引擎可以进行更多的优化,因为它不需要处理一些非严格模式下的特殊情况,如 arguments.callee 的使用。

4. 为未来的 JavaScript 版本做准备

严格模式禁止使用一些在未来 JavaScript 版本中可能被废弃的特性,有助于代码的向前兼容性。

严格模式的缺点

1. 兼容性问题

严格模式与非严格模式的行为有所不同,可能会导致一些依赖非严格模式行为的代码在严格模式下运行失败。

2. 学习成本

开发者需要了解严格模式的规则,否则可能会遇到一些意外的错误。

3. 调试难度

严格模式下的错误信息可能比非严格模式更详细,但也可能更难以理解。

如何迁移到严格模式

1. 渐进式迁移

可以先在一些新的代码或模块中使用严格模式,然后逐步迁移旧代码。

2. 测试

在迁移到严格模式后,需要进行充分的测试,确保代码能够正常运行。

3. 工具支持

可以使用 ESLint 等工具来帮助检测代码中的严格模式问题。

严格模式的最佳实践

1. 在所有代码中使用严格模式

建议在所有 JavaScript 代码中使用严格模式,以提高代码质量和安全性。

2. 使用函数级严格模式进行过渡

如果直接在全局启用严格模式可能会影响到其他代码,可以先在函数级使用严格模式进行过渡。

3. 了解严格模式的规则

开发者需要了解严格模式的规则,以避免在使用严格模式时遇到意外的错误。

4. 结合其他最佳实践

严格模式应该与其他 JavaScript 最佳实践结合使用,如模块化编程、使用 let 和 const 代替 var 等。

严格模式的常见问题

1. 变量未声明

问题:在严格模式下,使用未声明的变量会抛出 ReferenceError。

解决方案:确保所有变量都先声明后使用。

javascript
// 错误
'use strict';
x = 10; // ReferenceError: x is not defined

// 正确
'use strict';
var x = 10;
console.log(x); // 10

2. this 为 undefined

问题:在严格模式下,全局函数中的 this 是 undefined,可能会导致依赖 this 的代码失败。

解决方案:使用 call、apply 或 bind 来显式绑定 this,或者使用箭头函数。

javascript
// 错误
'use strict';
function test() {
  console.log(this.name); // TypeError: Cannot read property 'name' of undefined
}

test();

// 正确
'use strict';
function test() {
  console.log(this.name);
}

var obj = { name: 'John' };
test.call(obj); // 'John'

// 使用箭头函数
'use strict';
const test = () => {
  console.log(this); // 取决于外部作用域的 this
};

3. arguments.callee 不可用

问题:在严格模式下,arguments.callee 不可用,可能会导致依赖它的代码失败。

解决方案:使用命名函数表达式代替。

javascript
// 错误
'use strict';
var factorial = function(n) {
  if (n <= 1) return 1;
  return n * arguments.callee(n - 1); // TypeError
};

// 正确
'use strict';
var factorial = function fact(n) {
  if (n <= 1) return 1;
  return n * fact(n - 1); // 使用命名函数表达式
};

4. 构造函数忘记使用 new

问题:在严格模式下,如果忘记使用 new 调用构造函数,this 是 undefined,可能会导致代码失败。

解决方案:确保使用 new 调用构造函数,或者在构造函数中添加检查。

javascript
// 错误
'use strict';
function Person(name) {
  this.name = name; // TypeError: Cannot set property 'name' of undefined
}

var person = Person('John'); // 忘记使用 new

// 正确
'use strict';
function Person(name) {
  if (!(this instanceof Person)) {
    return new Person(name); // 检查并自动使用 new
  }
  this.name = name;
}

var person = Person('John'); // 即使忘记使用 new,也能正常工作

总结

严格模式是 JavaScript 中的一种特殊执行模式,它通过抛出错误来消除一些不合理的语法,防止一些可能导致错误的行为,并提高代码的安全性和性能。

严格模式的主要特点包括:

  • 变量必须声明后使用
  • 禁止删除变量、函数和函数参数
  • 禁止重复声明变量
  • 禁止使用八进制字面量
  • 禁止使用 with 语句
  • 函数参数必须唯一
  • 禁止使用 arguments.callee 和 arguments.caller
  • this 的绑定规则有所不同
  • 禁止修改只读属性
  • 禁止扩展不可扩展的对象
  • 禁止使用 eval 创建的变量影响外部作用域
  • 禁止使用保留字作为变量名
  • 禁止使用未声明的对象字面量属性

严格模式的优点包括提高代码质量、安全性和性能,为未来的 JavaScript 版本做准备。缺点包括兼容性问题、学习成本和调试难度。

建议在所有 JavaScript 代码中使用严格模式,以提高代码质量和安全性。在迁移到严格模式时,需要进行充分的测试,确保代码能够正常运行。