星期四, 4月 13, 2017

[JavaScript] Strict Mode

[JavaScript] Strict Mode


ECMAScript 5的Strict Mode不是ECMAScript 的子集。
Strict Mode 執行上跟Non-Strict Mode下有明顯差異。
Strict Mode 跟Non-Strict Mode可並存。

Strict Mode 將JavaScript再執行一些陷阱語法直接丟明顯的錯誤。
Strict Mode 修正JavaScript Engine難以優化的錯誤。
Strict Mode 禁止了一些未來ECMAScript可能會使用的關鍵字。


開啟Strict Mode

"use strict";

  1. 放在語法前。
  2. 不要將”use strict”用在{}使用,他會失去效果。

{
"use strict";
}

  1. 可以在function scope開啟strict mode。
!function strict() {
  "use strict";
  function nested() {return "So am I!";}
  return "I am strict mode "+ nested();
}();
!function nonStrict() {return "I am non Strict";}();

Strict Mode執行上的不同

  1. 拼寫錯誤的變數(或沒有宣告就存取的變數)會拋出異常。
"use strict";
var nAme;
nEme = 17; // ReferenceError

  1. Silently Fail (程式碼執行不報錯也沒有效果的錯誤)會拋出異常。
"use strict";
var obj1 = {};
Object.defineProperty(obj1, "x", {value: 42, writeable: false}});
obj1.x = 9; //TypeError

var obj2 = {get x(){return 17;}};
obj2.x = 5; // TypeError

var fixed = {};
Object.preventExtensions(fixed);
fixed.newProp = "ohai"; // TypeError

  1. 試圖刪除不可刪除之屬性會拋出異常
"use strict";
delete Object.prototype; // TypeError


  1. Gecko 34以前下方指令會丟出錯誤(ECMAScript6不存在)
"use strict";
var o = {p:1, p:2};

  1. 函數的參數要唯一
function sum(a, a, c) {
  "use strict";
  return a+b+c;
}

  1. 禁止八進位語法
var a = 0o10;
parseInt(a);

"use strict";
var sum = 015 + // SyntaxError
          197 +
          142;

  1. 禁止設置primitive值屬性
!function() {
"use strict";

false.true = "";              //TypeError
(14).sailing = "home";        //TypeError
"with".you = "far away";      //TypeError

}();


簡化變數(variable)的使用


  1. 禁止使用with
"use strict";
var x = 17;
with (obj) // SyntaxError
{
  x; // x mean obj.x or var x? it will make engine can not process optimization. 
}


  1. Strict Mode下的eval不在讓surrounding scope(包圍eval的scope),注入新變數。例如eval(‘var’)

function a () {
  eval('var x=10');
  console.log(x); //x will be create.
}


var x = 17;
var evalX = eval("'use strict'; var x = 42; x"); //this x !== var x=17
console.assert(x === 17);
console.assert(evalX === 42);

2.1 如果eval在Strict Mode下面執行,其代碼會被當作在Strict Mode下為執行。
function strict1(str) {
  'use strict';
  return eval(str); // str will be treated as strict mode code
}
function strict2(f, str) {
  'use strict';
  return f(str); // not eval(...): str is strict if and only
                 // if it invokes strict mode
}
function nonstrict(str) {
  return eval(str); // str is strict if and only 
                    // if it invokes strict mode
}

strict1("'Strict mode code!'");
strict1("'use strict'; 'Strict mode code!'");
strict2(eval, "'Non-strict code.'");
strict2(eval, "'use strict'; 'Strict mode code!'");
nonstrict("'Non-strict code.'");
nonstrict("'use strict'; 'Strict mode code!'");

  1. Strict Mode禁止刪除變數
'use strict';
var a={x:10};
delete a.x; // It can delete

var x;
delete x; // SyntaxError

eval('var y; delete y;'); // SyntaxError



簡化eval跟arguments

  1. 對eval跟arguments誤用的範例如下
"use strict";
eval = 17;
arguments++;
++eval;
var obj = { set p(arguments) { } };
var eval;
try { } catch (arguments) { }
function x(eval) { }
function arguments() { }
var y = function eval() { };
var f = new Function("arguments", "'use strict'; return 17;");

  1. Strict Mode 函數arguments的值,不會隨著參數變數被修改而跟著改變。
function f(a){
  "use strict";
  a = 42; //a 改變,但是函數的arguments仍是f(17);
  return [a, arguments[0]];
}
var pair = f(17);
console.assert(pair[0] === 42);
console.assert(pair[1] === 17);

  1. 不再支援arguments.callee (為了最佳化)
"use strict";
var f = function() { return arguments.callee; };
f(); // TypeError


安全的 JavaScript


  1. Strict Mode下回傳this將不再將整個this包裝(boxed)返回出去,而是undefined, 或是call, apply, bind的this。一般模式下會將this回傳出去,可能會包含許多額外資訊(ex window 物件),同時也有助效能提升。
  • 這也意味瀏覽器將不能透過Strict Mode下的function 回傳this來取得window物件捕捉額外資訊。
'use strict';
function fun() { return this; }
console.assert(fun() === undefined);
console.assert(fun.call(2) === 2);
console.assert(fun.apply(null) === null);
console.assert(fun.call(undefined) === undefined);
console.assert(fun.bind(true)() === true);

  1. Strict Mode下的arguments不再提供”walk” to JavaScript Stack的下面兩個方式 function.caller(誰呼叫我)跟function.arguments(呼叫我的函式的參數),因為這會導致不安全的存取。
function restricted()
{
  "use strict";
  restricted.caller;    // TypeError
  restricted.arguments; // TypeError
}
function privilegedInvoker()
{
  return restricted();
}
privilegedInvoker();

  1. Strict Mode下的arguments不再提供這個函數相關變數的存取方式,例如arguments.caller。這個在現在許多瀏覽器都已經不再存取的到。
 'use strict';
function fun(a, b) {
  'use strict';
  var v = 12;
  return arguments.caller; // throws a TypeError
}
fun(1, 2); // doesn't expose v (or a or b)

保留ECMAScript未來擴充的關鍵字

  1. 關鍵字範例如下
implements, interface, let, package, private, protected, public, static, yield

function package(protected){ // !!!
  "use strict";
  var implements; // !!!

  interface: // !!!
  while (true)
  {
    break interface; // !!!
  }

  function private() { } // !!!
}
function fun(static) { 'use strict'; } // !!!

  1. Strict Mode也禁止了Function Declaration 一些的宣告方式,這也是未來ECMAScript會制訂函式宣告式未來的一些規範。
 'use strict';
if (true)
  function f() { } // SyntaxError
  
for (var i = 0; i < 5; i++) 
  function f2() { } // SyntaxError


function baz() { // ok
  function eit() { } // ok
}


Reference: MDN Strict Mode 嚴格模式