代码中经常看到使用这样的声明,却一直不知道有什么作用,直到有一天。。。
1. 历史
ES5开始使用Directive Prolog,并据说直到现在也只有'use strict'
一个实现,至于引入Strict Mode的原因,总的来还是对JS兼容性对处理。
第一,为了处理未来即将废弃或修改的JS方法,告诉大家别这样用了(直接抛出异常了)。
第二,为了未来兼容的JS实现,告诉大家以后的JS这样做是这个效果。
也就是为了JS更好的发展,作出的一些JS内容上的约束。
2. 声明位置
作为一个Directive Prolog,只需要使用'use strict'
这样的语法,就可以声明为Strict Mode,主要影响范围会有两个:
2.1 全局使用
也就是全局都声明为Strict Mode了,一般不推荐,因为会对所有的Script引用脚本造成影响,破坏力不可言喻,所以尽量不要这样使用。
2.2 方法中使用
这是推荐的方法,只对局部代码作用,这样也不会对其他内容产生影响,于是就这样在方法内使用吧。
function f(){
'use strict'
// 大括号内都有效了,不影响括号外内容
}
x = 1;
f()
当然这样也是可以的
(function(){
'use strict'
})()
3. 使用影响
3.1 对变量的影响
3.1.1 变量声明
使用不声明的变量,会抛出异常,防止莫名其妙的引入全局变量
'use strict'
x = 1 // 愉快的抛出了异常 Uncaught ReferenceError: x is not defined
3.1.2 变量命名
使用关键字/保留字作为变量名,抛出异常,常用的关键字:private
, package
,protected
, interface
, implements
,let
, static
, yield
,当然不仅仅局限于这些,可以自行尝试
function f(){
'use strict'
var private = 1 // 很友善的异常信息 Uncaught SyntaxError: Unexpected strict mode reserved word
}
f()
3.1.3 变量赋值
只读/只有get
方法/不可扩展对象属性赋值,都没办法正常赋值,都会抛出异常
function f(){
'use strict'
// 只读属性
var obj = {};
Object.defineProperty(obj, 'x', {value:1 , writable: false, configurable: true});
obj.x = 2; // Uncaught TypeError: Cannot assign to read only property 'x' of object '#<Object>'
// 只有get方法
var obj = {
get x() {
return 1;
}
}
obj.x = 2; // Uncaught TypeError: Cannot set property x of #<Object> which has only a getter
// 不可扩展对象
var obj = {};
Object.preventExtensions(obj);
obj.x = 2; // Uncaught TypeError: Cannot add property x, object is not extensible
}
f()
3.1.4 方法中形参命名
不能使用相同的形参,想起来也合理,如果两个参数名一样,谁知道指代的哪一个
(function(){
'use strict'
function f(a, a){} // Uncaught SyntaxError: Duplicate parameter name not allowed in this context
})()
3.1.5 八进制
阻止使用以0
开始的八进制表达方式,可以使用0o
的方式来定义八进制数据。当然避免010
这样的数据产生莫名其妙的结果了(根本不会想到非Strict Mode下 010 === 8
)
function f() {
'use strict'
var x = 010; // Uncaught SyntaxError: Octal literals are not allowed in strict mode.
var x = 0o10 // 可以这样用,x === 8
}
3.1.6 禁止给基本类型扩展属性
我想正常的我们都不会给一个布尔值,字符串,数字增加属性吧
function f() {
'use strict'
false.x = 1; // Uncaught TypeError: Cannot create property 'x' on boolean 'false'
'str'.x = 1; // Uncaught TypeError: Cannot create property 'x' on string 'str'
}
f()
3.1.7 (超特殊) 对象属性
对象中含有两个相同的属性名,根据官方文档时说要报错的,结果似乎被定义为bug了,目前并不会报错,只会覆盖了
function f() {
'use strict'
var obj = {x: 1, x: 2} // 据说会报错,结果现在只是后定义的回覆盖前面的值
console.log(obj.x);
}
f();
3.2 function和arguments
3.2.1 Function中this指代
这里其实涉及到另一个知识点this
的指代,Javascript中this
指代会有一点麻烦,不过这里如果在函数通常调用情况下,非Strict Mode返回是window
对象,而Strict Mode定义为undefined
(原因其实就是方法调用的时候没有指向任何对象)
function f() {
'use strict'
return this; // undefined
}
f();
window.f(); // 可以自行尝试,有很有意思的结果
3.2.2 callee, caller, function.arguments
在Strict Mode下使用function.caller
,function.arguments
,arguments.callee
,arguments.caller
,都会抛出异常,我想这么做是为了避免无缘无故就引入了循环引用导致堆栈溢出。
3.2.3 arguments使用
对于非箭头函数,方法中可以使用arguments
来获取传递参数,严格模式下对参数不会改变aguments对于值的获取的值。
(function(){
'use strict'
function f(a){
a = 2;
console.log(arguments[0]); // arguments[0] === 1
}
f(1);
})()
3.2.4 (特例)逻辑语句中定义方法
官方似乎是会产生异常的,但是逻辑语句中是不能定义方法的,但是自己尝试了,似乎都是可以的,也许新版本定义这是可行的?
3.3. eval函数
3.2.1 作为变量名,方法名,自增数
和关键字一样,不能用做以上内容,否则会抛出异常
3.2.2 对全局变量的影响
Strict Mode下不会对全局变量产生影响,声明的变量只在eval
自身的作用域范围内有效
'use strict'
var a = 1;
eval('var a = 2'); // 注意 eval('a = 2')是可以对a产生影响的
console.log(a); // a === 1;
3.2.3 直接使用和间接使用的区别
对于间接使用的时候,不受到Strict Mode的限制
'use strict'
eval('var x = 1');
console.log(x); // undefined
// 间接调用1
('indirect', eval)('var y = 2;'); // indirect
console.log(y); // 2
// 间接调用2
var ieval = eval;
ieval('var z = 3');
console.log(z);
3.2.4 this.eval和eval的区别
this
又一次立功,这时候也是可以对全局变量产生影响的
'use strict'
var a = 1;
this.eval('var a = 2');
console.log(a); // a === 2;
3.4. with函数
Strict Mode下使用with
函数会直接抛出异常
(function(){
// Uncaught SyntaxError: Strict mode code may not include a with statement
'use strict'
var obj = {x: 1};
with(obj){
console.log(x);
}
})()
3.5 delete
3.5.1 删除对象属性
当对象属性设置为configurable: false
的时候,如果删除对象属性会抛出异常,当然非Strict Mode也不能删掉,只是不会抛出异常
(function(){
'use strict'
var obj = {};
Object.defineProperty(obj, 'x', {value: 1, configurable: false});
delete obj.x; // Uncaught TypeError: Cannot delete property 'x' of #<Object>
})()
3.5.2 删除变量
变量是不能直接删除的,会抛出异常,同样非Strict Mode下虽然不会抛出异常,但是实际上不能删除的
(function(){
// Uncaught SyntaxError: Delete of an unqualified identifier in strict mode.
'use strict'
var a = 1;
delete a;
})()
3.5.3 删除this指代的全局变量
这是可以的,因为this
又一次立功,变成了删除某个对象的值,所以是可以的
(function(){
'use strict'
delete this.a;
console.log(this);
}).call({a: 1 , b: 2})
4.参考
所有以上内容都来自MDN,只是对结构按照自己理解做了一下调整和整合:
MDN strict mode
ECMA-262-5 in detail. Chapter 2. Strict Mode.
MDN Function caller
MDN arguments caller(已淘汰)
MDN arguments callee
5. 练习
最后来一个练习吧,猜猜结果:
(function(){
'use strict'
var a = b = 1;
console.log(a);
console.log(b);
})()
网友评论