从 “BUG 重灾区” 到规范开发,全靠 JavaScript 严格模式

0 阅读6分钟

家人们谁懂啊!刚学前端写 JavaScript 代码的时候,真的是状况百出。好几次代码运行结果和我想的完全不一样,找了好久才发现是因为 JavaScript 太 “惯着” 我了,有些明明写错的地方,它居然不报错!直到遇到严格模式,我才感觉自己的代码世界终于走上正轨了。

就拿变量声明来说,以前不声明变量直接用,JavaScript 就帮我 “擦屁股” 创建全局变量。像这样:

function test() {
  message = "Hello, world!";
}
test();

console.log(message);  // Hello, world!

但开了严格模式,在脚本或者函数开头加上'use strict';,再这么干就直接报错了。

'use strict';
function test() {
    message = "Hello, world!";
}
test();
// 报错: Uncaught ReferenceError: message is not defined

这一下就治好了我不声明变量的臭毛病,也不用担心变量名冲突了。

对象属性这块也是,普通模式下随便加属性都没问题。

const person = { name: "Alice" };
person.age = 30;
console.log(person); // { name: "Alice", age: 30 }

但严格模式下,对象没这个属性还硬加,直接就报错,能帮我及时发现是不是属性名写错了。

'use strict';
const person = { name: "Alice" };
person.age = 30;
console.log(person)// 报错: Uncaught TypeError: Cannot create property 'age' on object 

还有this指向的问题,我之前一直晕乎乎的。普通模式下,函数里的this在不同地方调用,指向都不一样,搞得我头大。严格模式就清爽多了,this不会默认指向全局对象(浏览器里是window),没绑定的话就是undefined,理解起来轻松多了。

function normalModeThis() {
    console.log(this);
}
function strictModeThis() {
    'use strict';
    console.log(this);
}
normalModeThis(); //  Window 对象

strictModeThis(); //  undefined

上面就是严格模式的部分使用,它的作用可不是可以忽略的,它可以让代码更加规范。

下面让我将仔细说说严格模式的变化

1. 消除静默错误,让问题无所遁形

以前写代码最头疼的就是遇到那种不报错,但结果不对的情况。在非严格模式下,给未声明的变量赋值,JavaScript 会悄咪咪地创建一个全局变量,就像这样:

function test() {
    message = "Hello, world!";
}
test();
console.log(message); // 输出: Hello, world!

当时我还纳闷为啥突然多出来个全局变量,找了半天才发现是变量声明写错了。但在严格模式下,这种操作直接就会抛出错误:

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

这样一来,代码里的小错误一下子就暴露出来,再也不用像个无头苍蝇一样到处找问题了。

2. 防止意外全局变量,规范变量声明

以前总偷懒,经常忘记用let、const或var声明变量,JavaScript 也不拦着我。但严格模式下,必须老老实实声明变量,不然就会报错。这虽然刚开始让我有点不适应,但慢慢养成习惯后,代码的作用域清晰多了,也很少出现变量命名冲突的问题。

3. 禁止删除不可删除的属性,避免 “手滑误操作”

有一回,我手贱想试试能不能删掉Object.prototype,在非严格模式下,虽然没成功但也没报错,给我一种 “好像能操作” 的错觉。但在严格模式下,直接给我报了个TypeError:

'use strict';
delete Object.prototype; 
// TypeError

这就相当于给重要的属性上了把锁,再也不用担心不小心误删关键内容了。

4. 函数参数必须唯一,告别 “糊涂账”

写函数的时候,有时候脑子一抽,给函数定义了两个相同名字的参数,在非严格模式下,虽然能用但逻辑特别混乱。严格模式直接杜绝了这种情况,只要参数名重复,就会报SyntaxError:

'use strict';
function sum(a, a, c) { 
// SyntaxError
    return a + a + c;
}

这样函数的逻辑就清晰多了,再也不会自己把自己绕晕。

5. 禁止使用 with 语句,减少代码歧义

with语句我之前觉得挺方便的,但其实它特别容易让代码产生歧义,而且性能也不好。严格模式直接把它禁用了,一旦使用就会报SyntaxError:

'use strict';
with (obj) {} 
// SyntaxError

虽然刚开始有点不习惯,但后来发现,不用with语句,代码反而更加简洁明了,可读性也提高了。

6. eval 行为变化,让作用域更可控

eval函数之前我用的时候总是小心翼翼,因为在非严格模式下,eval里声明的变量会泄漏到外部作用域,特别容易造成变量污染。但在严格模式下,eval中声明的变量只在eval内部有效,再也不用担心 “变量乱窜” 的问题了:

'use strict';
eval("var x = 10;");
console.log(x); 
// ReferenceError

7. this 行为变化,理清作用域关系

this的指向问题,我之前简直被搞到怀疑人生,在不同场景下它的指向完全不一样。在非严格模式下,全局作用域中的this指向window,但在严格模式下,this的值变成了undefined,函数里的this也不会随便乱指了:

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

这样一来,我能更清楚地理解函数调用时this的作用域关系,写代码也更有底气了。

ES6 之后的async函数,在严格模式下this的处理也优化了。以前async函数的this指向很迷,现在终于稳定了,始终绑定在定义时的对象上。

'use strict';

const myObj = {
    async asyncFunc() {
        console.log(this);
    }
};

myObj.asyncFunc(); // myObj 对象

8. 禁止八进制语法,统一代码规范

之前看到以0开头的数字,还以为是普通数字,后来才知道是八进制语法,而且不同环境下解析还可能有差异。严格模式直接禁止了这种写法,一旦出现就会报错:

'use strict';
var num = 010; 
// SyntaxError

这样代码的规范就统一了,也避免了因为八进制语法带来的潜在问题。

9. 禁止对只读属性赋值,保护数据安全

有时候给对象设置了只读属性,结果不小心又去修改它的值,在非严格模式下不会报错,但其实数据已经被破坏了。严格模式下,对只读属性赋值会直接抛出TypeError:

'use strict';
var obj = {};
Object.defineProperty(obj, "x", { value: 42, writable: false });
obj.x = 9; 
// TypeError

这就像是给数据加上了一层保护罩,保证数据的安全性和一致性。

刚开始用严格模式的时候,我老是被各种报错打击到,一度想放弃。但慢慢发现,这些报错其实是在帮我查漏补缺,逼着我养成好的编程习惯。