PatrickLh / blog

一些笔记和观点

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

Javascript学习笔记-Strict-Mode

PatrickLh opened this issue · comments

Strict Mode.png

代码中经常看到使用这样的声明,却一直不知道有什么作用,直到有一天。。。

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.callerfunction.argumentsarguments.calleearguments.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);
})()