strict モードの違い
- カテゴリ:
- JavaScript
- コメント数:
- Comments: 0
◆ 通常モードと strict モードでの変更点まとめ
class はともかくとして module を使うと強制 strict モードになるので どう変化するのかを調べてみました
すでに知ってるものや ECMAScript の spec (現時点で 2017) を流し見してまとめたものなので すべてを網羅できているかはわかりません
確認は Chrome と Firefox で確認して同じ動きになってました
エラーメッセージについてはブラウザごとに違います
strict モードではこれが使えなくなります
通常はその関数自身の参照が入っています
再帰関数を使う場合には 便利なので使えたほうがいいのですけどね
自身の関数名を書く方法ではコピペして使う場合に毎回関数名を変える必要があります
それを避けるために
arguments オブジェクトには caller プロパティもありました
ES2017 のスペックによると「ES2017 より前のスペックには strict モードだとこのプロパティでもエラーになるように指定されていたけど このプロパティの機能はもう仕様に含んでないので strict モードでエラーになるというルールもなくした」ようです
単純にいうと Chrome/Firefox/Edge だとこのコードは有効です
非 strict モードでは自分を呼び出した関数と arguments オブジェクトを取得できます
私は全く使ったこと無いのでこれは消えても困らない機能です
一見便利そうですが自分を呼び出した関数を知れるってなんか変な感じがしますし
一応 strict モードでも関数名だけならエラーの stack から取れます
取れはしますがフォーマットはブラウザ依存ですし バージョンアップで変わってることもあるのでやらないほうがいいと思います
上のコードは Chrome では動きますが Firefox だと異なる結果になります
strict モードではエラーになるのを調べます
arguments と eval は変数(関数)なのですが代入や宣言はできません
引数名や catch で受け取る変数名としても使えません
non strict モードでも let や const の中に let という名前は使えないなど特殊な制限もあります
var だと大丈夫です
文字列中のエスケープシーケンスで \ のあとに数字を書くと 8 進数の文字コードとして文字になります
数値リテラルと違って最初の 0 はなくてもいいです
逆に 3 桁の場合に 0 をつけて 4 桁にすると正しく入力できなくなります
これも strict モードでは禁止されています
代わりに o を使って 0o12 のようにする 8 進数リテラルは使えます
0x や 0b にあわせる形です
ただしエスケープシーケンスの方はできません
16 進数の \u や \x を使うことになります
通常は変更されず何も起きません
消せなくてもこういうエラーにならないケースもあります
これがあるといらない引数を同じ名前で受け取ってしまえないので不便です
こういう使い方ができません
昔はダメだったのですが今では許可されています
引数もこうなってくれればいいのですけどね
IE では
strict モードでは、プロパティの複数定義は許可されません。
とエラーになります
許可された理由はたぶん ... を使ってこういうことができるようになったから
重複した場合は後にあるものが優先されます
非 strict モードではグローバルオブジェクトを参照できます
また strict モードでは this がオブジェクトとは限りません
通常の動作では bind 等で null を this に設定すると自動でグローバルオブジェクトに置き換えられましたが strict モードではそのままの値です
プリミティブ値もオブジェクト化されずそのままです
prototype 拡張してメソッドをつくるときの this もそのままの値です
関数宣言のみで式には使えます
非 strict モードでの動作として仕様的許可されたのは ES2015 の spec かららしいです
ただ実際のところそれ以前でも動いていたようです
そもそもラベル機能自体を使う機会がないので影響受けることはなさそうです
スコープは同じく strict だとブロックスコープです
意味ないですけど
strict モードだとこれが禁止になっています
通常でも使えるのは var による宣言時のみです
let ではエラーになります
let による宣言ができるようになったほうが後なので禁止しても問題は起きないのですが var だと互換性を壊さないためにそのままにしてこんなことになってるようです
strict モードでは eval 内で宣言した変数は eval を実行したスコープと別のスコープに宣言されます
Function コンストラクタで関数を作って実行するタイプの eval に近いです
変数宣言する処理を動的に作れないので稀に不便でもあります
var が関数の最初で宣言される巻き上げの影響を受けずに 外側の値を参照できて かつ同じ変数名を宣言できたりするので禁止にしたいというのもわからなくはないです
ところで let/const で作るなら通常の動作でも strict モードと同じです
ただそもそも TCO が Safari でしか実装されていないので今の所気にすることはなさそうです
https://stackoverflow.com/questions/42788139/es6-tail-recursion-optimisation-stack-overflow
https://stackoverflow.com/questions/37224520/are-functions-in-javascript-tail-call-optimized
strict モードではこれが禁止されています
意図しないグローバル変数を作ってしまっているのはわりと見かける光景ですしこれは良い部分だと思います
グローバル変数を作れないわけではなく明示的に window のプロパティに代入すれば可能です
プロパティでなくその値自体を書き換えても もう一方が書き換わります
ですが stric モードでは別々のものとなり 書き換えの影響を受けません
toString みたいに参照はできるものでも実体はプロトタイプにあるもので プリミティブ値への書き込みはできないのでエラーです
通常の動作では実行自体は可能ですがオブジェクトではないので値は保持されず無意味です
私としては困るのは let が予約語になることと 引数名の重複禁止です
const で let という変数を作れませんが 関数なら作れますしたまに作ってます
引数名については上で書いたとおりです
他には eval のスコープと this の動きの違いはあんまり好みじゃないですが困るというほどではないです
Node.js や MongoDB など新しい JavaScript 実行環境に触れたときに グローバルの名前がわからないときはとりあえず this 参照でグローバルオブジェクトを取得していたのですが将来全部 strict モードになったりしたらどうやってアクセスすればいいのでしょうね
あと 書き換え不可プロパティへ代入しても何も起きないことを利用して いちいちチェックしないで書き換えるようにしている作りは少なからずあります
C# や PHP での面倒な null チェックをなくすための ?. とか ?? とか また jQuery でのセレクタに当てはまるのがなければ何もしないという動作も近いものがあると思います
できないなら何もしない を自動でやってくれるのは使う側からしては便利ですからね
読む側はけっこう大変ですけど
それに JavaScript や HTML/CSS などウェブのフロントエンドって基本エラー出さない系だと思います
おかしくても動く部分だけでとりあえず動かすという感じ
個人的にはそういうのが好きで 作ってる途中でとりあえず動くところまで動かさせてほしいです
コンパイルいる言語だとすべてが完璧でないと実行すらできないのでそういう面が苦手です
これまでだと 普段はゆるくてきつめにチェックしたいときにはスクリプトの一行目に全部 use strict つけて strict モードで確認できるという必要なら使うというくらいで使えました
でも module などでは強制となってしまうのでどちらかと言えば嫌な仕様です
強制されないときにあえて使う必要があるかというと 最近なら静的解析とかでも十分チェックしてくれるので 無効な処理のチェックの用途ならわざわざ全部に use strict 書いてまでしなくてもよいと思います
どちらかというと this のような動作の違いをみて好みの方を選べば良いと思います
すでに知ってるものや ECMAScript の spec (現時点で 2017) を流し見してまとめたものなので すべてを網羅できているかはわかりません
確認は Chrome と Firefox で確認して同じ動きになってました
エラーメッセージについてはブラウザごとに違います
arguments.callee
引数一覧にアクセスするための arguments オブジェクトのプロパティに自身の関数を参照する callee プロパティがありますstrict モードではこれが使えなくなります
function foo(){
"use strict"
console.log(arguments.callee)
}
foo()
// 'caller', 'callee', and 'arguments' properties may not be accessed on strict mode functions or the arguments objects for calls to them
"use strict"
console.log(arguments.callee)
}
foo()
// 'caller', 'callee', and 'arguments' properties may not be accessed on strict mode functions or the arguments objects for calls to them
通常はその関数自身の参照が入っています
function foo(){
console.log(arguments.callee)
}
foo()
// ƒ foo(){
// console.log(arguments.callee)
// }
console.log(arguments.callee)
}
foo()
// ƒ foo(){
// console.log(arguments.callee)
// }
再帰関数を使う場合には 便利なので使えたほうがいいのですけどね
自身の関数名を書く方法ではコピペして使う場合に毎回関数名を変える必要があります
それを避けるために
function fn(){
return function recur(){
if(0){
recur()
}
}()
}
こういう風に あえて共通になるようローカルスコープに関数を作って実行させるなどしているのですが 一手間増えていますreturn function recur(){
if(0){
recur()
}
}()
}
arguments オブジェクトには caller プロパティもありました
ES2017 のスペックによると「ES2017 より前のスペックには strict モードだとこのプロパティでもエラーになるように指定されていたけど このプロパティの機能はもう仕様に含んでないので strict モードでエラーになるというルールもなくした」ようです
単純にいうと Chrome/Firefox/Edge だとこのコードは有効です
function foo(){
"use strict"
console.log(arguments.caller)
}
foo()
// undefined
IE はエラーです"use strict"
console.log(arguments.caller)
}
foo()
// undefined
func.caller, arguments
arguments.callee と似たもので 関数の caller と arguments プロパティも strict モードでは禁止されていてアクセスするとエラーですfunction callFoo(){
foo()
}
function foo(){
"use strict"
console.log(foo.caller)
}
callFoo()
// 'caller', 'callee', and 'arguments' properties may not be accessed on strict mode functions or the arguments objects for calls to them
function foo(){
"use strict"
console.log(foo.arguments)
}
callFoo()
// 'caller', 'callee', and 'arguments' properties may not be accessed on strict mode functions or the arguments objects for calls to them
foo()
}
function foo(){
"use strict"
console.log(foo.caller)
}
callFoo()
// 'caller', 'callee', and 'arguments' properties may not be accessed on strict mode functions or the arguments objects for calls to them
function foo(){
"use strict"
console.log(foo.arguments)
}
callFoo()
// 'caller', 'callee', and 'arguments' properties may not be accessed on strict mode functions or the arguments objects for calls to them
非 strict モードでは自分を呼び出した関数と arguments オブジェクトを取得できます
function callFoo(){
foo()
}
function foo(){
console.log(foo.caller)
console.log(foo.arguments)
}
callFoo()
// ƒ callFoo(){
// foo()
// }
// Arguments [callee: ƒ, Symbol(Symbol.iterator): ƒ]
foo()
}
function foo(){
console.log(foo.caller)
console.log(foo.arguments)
}
callFoo()
// ƒ callFoo(){
// foo()
// }
// Arguments [callee: ƒ, Symbol(Symbol.iterator): ƒ]
私は全く使ったこと無いのでこれは消えても困らない機能です
一見便利そうですが自分を呼び出した関数を知れるってなんか変な感じがしますし
一応 strict モードでも関数名だけならエラーの stack から取れます
function callFoo(){
"use strict"
foo()
}
function foo(){
"use strict"
const stack = new Error().stack
const call_stack = stack.split("\n").slice(1)
.map(e => e.replace(/^.*?at (.*?) .*$/, "$1"))
.filter(e => !e.startsWith(" "))
console.log(call_stack)
}
callFoo()
// ["foo", "callFoo"]
"use strict"
foo()
}
function foo(){
"use strict"
const stack = new Error().stack
const call_stack = stack.split("\n").slice(1)
.map(e => e.replace(/^.*?at (.*?) .*$/, "$1"))
.filter(e => !e.startsWith(" "))
console.log(call_stack)
}
callFoo()
// ["foo", "callFoo"]
取れはしますがフォーマットはブラウザ依存ですし バージョンアップで変わってることもあるのでやらないほうがいいと思います
上のコードは Chrome では動きますが Firefox だと異なる結果になります
Reserved words
strict モードでは予約語がちょっと多いですfunction foo(){
"use strict"
let public = true
return public
}
foo()
// Unexpected strict mode reserved word
"use strict"
let public = true
return public
}
foo()
// Unexpected strict mode reserved word
function foo(){
let public = true
return public
}
foo()
// true
let public = true
return public
}
foo()
// true
strict モードではエラーになるのを調べます
const keywords = ["arguments", "await", "break", "case", "catch", "class", "const", "continue", "debugger", "default",
"delete", "do", "else", "eval", "export", "extends", "finally", "for", "function", "if", "import", "in",
"instanceof", "let", "new", "return", "static", "super", "switch", "this", "throw", "try", "typeof",
"var", "void", "while", "with", "yield", "implements", "package", "protected", "interface", "private", "public"]
for(const keyword of keywords){
if(strict(keyword) ^ nonStrict(keyword)){
console.log(keyword)
}
function strict(name){
"use strict"
try{
eval("var " + name)
return true
}catch(err){
return false
}
}
function nonStrict(name){
try{
eval("var " + name)
return true
}catch(err){
return false
}
}
}
"delete", "do", "else", "eval", "export", "extends", "finally", "for", "function", "if", "import", "in",
"instanceof", "let", "new", "return", "static", "super", "switch", "this", "throw", "try", "typeof",
"var", "void", "while", "with", "yield", "implements", "package", "protected", "interface", "private", "public"]
for(const keyword of keywords){
if(strict(keyword) ^ nonStrict(keyword)){
console.log(keyword)
}
function strict(name){
"use strict"
try{
eval("var " + name)
return true
}catch(err){
return false
}
}
function nonStrict(name){
try{
eval("var " + name)
return true
}catch(err){
return false
}
}
}
arguments
eval
let
static
yield
implements
package
protected
interface
private
public
eval
let
static
yield
implements
package
protected
interface
private
public
arguments と eval は変数(関数)なのですが代入や宣言はできません
引数名や catch で受け取る変数名としても使えません
non strict モードでも let や const の中に let という名前は使えないなど特殊な制限もあります
var だと大丈夫です
let let = 1
const let = 1
// let is disallowed as a lexically bound name
const let = 1
// let is disallowed as a lexically bound name
Octal literals
8 進数は 0 から始めることで書くことが出来ますが strict モードでは禁止されていますfunction foo(){
"use strict"
console.log(010)
}
foo()
// Octal literals are not allowed in strict mode.
"use strict"
console.log(010)
}
foo()
// Octal literals are not allowed in strict mode.
function foo(){
console.log(010)
}
foo()
// 8
console.log(010)
}
foo()
// 8
文字列中のエスケープシーケンスで \ のあとに数字を書くと 8 進数の文字コードとして文字になります
数値リテラルと違って最初の 0 はなくてもいいです
逆に 3 桁の場合に 0 をつけて 4 桁にすると正しく入力できなくなります
これも strict モードでは禁止されています
function foo(){
"use strict"
console.log("\041")
}
foo()
// Octal escape sequences are not allowed in strict mode.
"use strict"
console.log("\041")
}
foo()
// Octal escape sequences are not allowed in strict mode.
function foo(){
console.log("\041")
console.log("\101")
}
foo()
// !
// A
console.log("\041")
console.log("\101")
}
foo()
// !
// A
代わりに o を使って 0o12 のようにする 8 進数リテラルは使えます
0x や 0b にあわせる形です
ただしエスケープシーケンスの方はできません
16 進数の \u や \x を使うことになります
function foo(){
"use strict"
console.log(0o10)
console.log("\0o41")
console.log("\x41")
}
foo()
// 8
// o41
// A
"use strict"
console.log(0o10)
console.log("\0o41")
console.log("\x41")
}
foo()
// 8
// o41
// A
Invalid assignment
getter しかなかったり 変更不可にされているプロパティに書き込むと strict モードではエラーになりますfunction foo(){
"use strict"
const obj = {get bar(){return 1}}
obj.bar = 1
}
foo()
// Cannot set property bar of #<Object> which has only a getter
"use strict"
const obj = {get bar(){return 1}}
obj.bar = 1
}
foo()
// Cannot set property bar of #<Object> which has only a getter
通常は変更されず何も起きません
function foo(){
const obj = {get bar(){return 1}}
obj.bar = 1
}
foo()
// no error
const obj = {get bar(){return 1}}
obj.bar = 1
}
foo()
// no error
Invalid delete
delete 演算子で削除できない変数やプロパティを消そうとすると strict モードではエラーになりますfunction foo(){
"use strict"
const value = 1
delete value
}
foo()
// Delete of an unqualified identifier in strict mode.
function foo(){
"use strict"
const obj = Object.create({}, {prop: {writable: false, value: 1}})
delete obj.prop
}
foo()
// Cannot delete property 'prop' of #<Object>
"use strict"
const value = 1
delete value
}
foo()
// Delete of an unqualified identifier in strict mode.
function foo(){
"use strict"
const obj = Object.create({}, {prop: {writable: false, value: 1}})
delete obj.prop
}
foo()
// Cannot delete property 'prop' of #<Object>
function foo(){
const value = 1
delete value
console.log(value)
const obj = Object.create({}, {prop: {writable: false, value: 1}})
delete obj.prop
console.log(obj)
}
foo()
// 1
// {prop: 1}
const value = 1
delete value
console.log(value)
const obj = Object.create({}, {prop: {writable: false, value: 1}})
delete obj.prop
console.log(obj)
}
foo()
// 1
// {prop: 1}
消せなくてもこういうエラーにならないケースもあります
function foo(){
"use strict"
let x = 1
delete x++
}
foo()
function foo(){
"use strict"
delete 1
}
foo()
function foo(){
"use strict"
delete {}
}
foo()
"use strict"
let x = 1
delete x++
}
foo()
function foo(){
"use strict"
delete 1
}
foo()
function foo(){
"use strict"
delete {}
}
foo()
Duplicate parameter name
引数の名前が重複すると strict モードではエラーですfunction foo(a, a){
"use strict"
return a
}
foo(1, 2)
// Duplicate parameter name not allowed in this context
"use strict"
return a
}
foo(1, 2)
// Duplicate parameter name not allowed in this context
function foo(a, a){
return a
}
foo(1, 2)
// 2
return a
}
foo(1, 2)
// 2
これがあるといらない引数を同じ名前で受け取ってしまえないので不便です
こういう使い方ができません
text.replace(regexp, (_, _, flag, body) => { /* */ })
_1, _2, .... という感じで連番にするか いったん全部受け取って中で展開ということもできますtext.replace(regexp, (...a) => {
const [,, flag, body] = a
/* */
})
複数行あるならこれでもいいですけど 一行で書ける場合はあまり使いたくない方法ですconst [,, flag, body] = a
/* */
})
Duplicate property name
オブジェクトリテラルの中に同じ名前を書いた場合です昔はダメだったのですが今では許可されています
引数もこうなってくれればいいのですけどね
function foo(){
"use strict"
const obj = {a:1, a:2}
return obj.a
}
foo()
// 2
"use strict"
const obj = {a:1, a:2}
return obj.a
}
foo()
// 2
IE では
strict モードでは、プロパティの複数定義は許可されません。
とエラーになります
許可された理由はたぶん ... を使ってこういうことができるようになったから
const subobj = {a:10, b: 20}
const obj = {a:1, ...subobj, b:2}
const obj = {a:1, ...subobj, b:2}
重複した場合は後にあるものが優先されます
this
strict モードでの this のデフォルトはグローバルオブジェクトでなく undefined ですfunction foo(){
"use strict"
return function(){return this}()
}
foo()
// undefined
"use strict"
return function(){return this}()
}
foo()
// undefined
非 strict モードではグローバルオブジェクトを参照できます
function foo(){
return function(){return this}()
}
foo()
// Window {postMessage: ƒ, blur: ƒ, focus: ƒ, close: ƒ, frames: Window, …}
return function(){return this}()
}
foo()
// Window {postMessage: ƒ, blur: ƒ, focus: ƒ, close: ƒ, frames: Window, …}
また strict モードでは this がオブジェクトとは限りません
通常の動作では bind 等で null を this に設定すると自動でグローバルオブジェクトに置き換えられましたが strict モードではそのままの値です
function foo(){
"use strict"
return function(){return this}.bind(null)()
}
foo()
// null
"use strict"
return function(){return this}.bind(null)()
}
foo()
// null
function foo(){
return function(){return this}.bind(null)()
}
foo()
// Window {postMessage: ƒ, blur: ƒ, focus: ƒ, close: ƒ, frames: Window, …}
return function(){return this}.bind(null)()
}
foo()
// Window {postMessage: ƒ, blur: ƒ, focus: ƒ, close: ƒ, frames: Window, …}
プリミティブ値もオブジェクト化されずそのままです
function foo(){
"use strict"
return function(){return this}.bind(1)()
}
foo()
// 1
"use strict"
return function(){return this}.bind(1)()
}
foo()
// 1
function foo(){
return function(){return this}.bind(1)()
}
foo()
// Number {1}
return function(){return this}.bind(1)()
}
foo()
// Number {1}
prototype 拡張してメソッドをつくるときの this もそのままの値です
String.prototype.foo = function(){
"use strict"
console.log(this)
}
"text".foo()
// text
"use strict"
console.log(this)
}
"text".foo()
// text
String.prototype.foo = function(){
console.log(this)
}
"text".foo()
// String {"text"}
console.log(this)
}
"text".foo()
// String {"text"}
with
変数参照にオブジェクトを割り込ませる with 機能は strict モードでは使えませんfunction foo(){
"use strict"
with({a:1}){
return a
}
}
foo()
// Strict mode code may not include a with statement
"use strict"
with({a:1}){
return a
}
}
foo()
// Strict mode code may not include a with statement
function foo(){
with({a:1}){
return a
}
}
foo()
// 1
with({a:1}){
return a
}
}
foo()
// 1
Labelled FunctionDeclaration
関数宣言にラベルを使うことができません関数宣言のみで式には使えます
function foo(){
"use strict"
label: function f(){}
}
foo()
// In strict mode code, functions can only be declared at top level or inside a block.
"use strict"
label: function f(){}
}
foo()
// In strict mode code, functions can only be declared at top level or inside a block.
function foo(){
label: function f(){}
}
foo()
label: function f(){}
}
foo()
非 strict モードでの動作として仕様的許可されたのは ES2015 の spec かららしいです
ただ実際のところそれ以前でも動いていたようです
そもそもラベル機能自体を使う機会がないので影響受けることはなさそうです
Block-scope FunctionDeclaration
strict モードでは関数宣言のスコープはブロックですfunction foo(){
"use strict"
{
function f(){ return 1 }
}
return f()
}
foo()
// f is not a function
"use strict"
{
function f(){ return 1 }
}
return f()
}
foo()
// f is not a function
function foo(){
{
function f(){ return 1 }
}
return f()
}
foo()
// 1
{
function f(){ return 1 }
}
return f()
}
foo()
// 1
If FunctionDeclaration
if がある場合の関数宣言は実際に通った部分のみ行われますスコープは同じく strict だとブロックスコープです
function foo(){
"use strict"
if(true){
function t(){console.log("t")}
}else{
function f(){console.log("f")}
}
typeof t === "function" && t()
typeof f === "function" && f()
}
foo()
// no output
"use strict"
if(true){
function t(){console.log("t")}
}else{
function f(){console.log("f")}
}
typeof t === "function" && t()
typeof f === "function" && f()
}
foo()
// no output
function foo(){
if(true){
function t(){console.log("t")}
}else{
function f(){console.log("f")}
}
typeof t === "function" && t()
typeof f === "function" && f()
}
foo()
// t
if(true){
function t(){console.log("t")}
}else{
function f(){console.log("f")}
}
typeof t === "function" && t()
typeof f === "function" && f()
}
foo()
// t
for-in の初期値
for-in 文でも変数に初期値を設定することは一応可能です意味ないですけど
strict モードだとこれが禁止になっています
function foo(){
"use strict"
const obj = {a: 1, b: 2, c: 3}
for (var key = 0 in obj) {
console.log(key)
}
}
foo()
// for-in loop variable declaration may not have an initializer.
"use strict"
const obj = {a: 1, b: 2, c: 3}
for (var key = 0 in obj) {
console.log(key)
}
}
foo()
// for-in loop variable declaration may not have an initializer.
function foo(){
const obj = {a: 1, b: 2, c: 3}
for (var key = 0 in obj) {
console.log(key)
}
}
foo()
// a
// b
// c
const obj = {a: 1, b: 2, c: 3}
for (var key = 0 in obj) {
console.log(key)
}
}
foo()
// a
// b
// c
通常でも使えるのは var による宣言時のみです
let ではエラーになります
let による宣言ができるようになったほうが後なので禁止しても問題は起きないのですが var だと互換性を壊さないためにそのままにしてこんなことになってるようです
function foo(){
const obj = {a: 1, b: 2, c: 3}
for (let key = 0 in obj) {
console.log(key)
}
}
// for-in loop variable declaration may not have an initializer.
const obj = {a: 1, b: 2, c: 3}
for (let key = 0 in obj) {
console.log(key)
}
}
// for-in loop variable declaration may not have an initializer.
eval
予約語のところで arguments と eval は宣言したり代入できない特殊なものになってると書きましたが 動きの面でも違いがありますstrict モードでは eval 内で宣言した変数は eval を実行したスコープと別のスコープに宣言されます
Function コンストラクタで関数を作って実行するタイプの eval に近いです
function foo(){
"use strict"
eval("var bar = 10")
console.log(bar)
}
foo()
// bar is not defined
"use strict"
eval("var bar = 10")
console.log(bar)
}
foo()
// bar is not defined
function foo(){
eval("var bar = 10")
console.log(bar)
}
foo()
// 10
eval("var bar = 10")
console.log(bar)
}
foo()
// 10
変数宣言する処理を動的に作れないので稀に不便でもあります
var x = 10
function foo(){
var y = x
eval("var x")
x = y + 1
console.log(x)
}
foo()
// 11
console.log(x)
// 10
function foo(){
var y = x
eval("var x")
x = y + 1
console.log(x)
}
foo()
// 11
console.log(x)
// 10
var が関数の最初で宣言される巻き上げの影響を受けずに 外側の値を参照できて かつ同じ変数名を宣言できたりするので禁止にしたいというのもわからなくはないです
ところで let/const で作るなら通常の動作でも strict モードと同じです
function foo(){
"use strict"
eval("let bar = 10")
console.log(bar)
}
foo()
// bar is not defined
"use strict"
eval("let bar = 10")
console.log(bar)
}
foo()
// bar is not defined
function foo(){
eval("let bar = 10")
console.log(bar)
}
foo()
// bar is not defined
eval("let bar = 10")
console.log(bar)
}
foo()
// bar is not defined
TailCallOptimization
仕様的には TCO は strict モード内でのみ使えるとされていますただそもそも TCO が Safari でしか実装されていないので今の所気にすることはなさそうです
https://stackoverflow.com/questions/42788139/es6-tail-recursion-optimisation-stack-overflow
https://stackoverflow.com/questions/37224520/are-functions-in-javascript-tail-call-optimized
Global assignment
通常は 宣言していない変数名に代入しようとすると global オブジェクトへ代入されますstrict モードではこれが禁止されています
意図しないグローバル変数を作ってしまっているのはわりと見かける光景ですしこれは良い部分だと思います
function foo(){
"use strict"
globalvar = 1
}
foo()
// globalvar is not defined
"use strict"
globalvar = 1
}
foo()
// globalvar is not defined
function foo(){
globalvar = 1
console.log(globalvar)
}
foo()
// 1
globalvar = 1
console.log(globalvar)
}
foo()
// 1
グローバル変数を作れないわけではなく明示的に window のプロパティに代入すれば可能です
function foo(){
"use strict"
window.globalvar = 1
console.log(globalvar)
}
foo()
// 1
"use strict"
window.globalvar = 1
console.log(globalvar)
}
foo()
// 1
Independent arguments and parameter
通常は 引数と引数をまとめたものである arguments オブジェクトは共有された値となっていますプロパティでなくその値自体を書き換えても もう一方が書き換わります
ですが stric モードでは別々のものとなり 書き換えの影響を受けません
function foo(a, b){
"use strict"
arguments[0] = 100
console.log(a)
b = 200
console.log(arguments[1])
}
foo(10, 20)
// 10
// 20
"use strict"
arguments[0] = 100
console.log(a)
b = 200
console.log(arguments[1])
}
foo(10, 20)
// 10
// 20
function foo(a, b){
arguments[0] = 100
console.log(a)
b = 200
console.log(arguments[1])
}
foo(10, 20)
// 100
// 200
arguments[0] = 100
console.log(a)
b = 200
console.log(arguments[1])
}
foo(10, 20)
// 100
// 200
Primitive property
strict モードではプリミティブ値へのプロパティの追加は禁止されていますfunction foo(){
"use strict"
const num = 100
num.prop = 200
return num.prop
}
foo()
// Cannot create property 'prop' on number '100'
"use strict"
const num = 100
num.prop = 200
return num.prop
}
foo()
// Cannot create property 'prop' on number '100'
toString みたいに参照はできるものでも実体はプロトタイプにあるもので プリミティブ値への書き込みはできないのでエラーです
通常の動作では実行自体は可能ですがオブジェクトではないので値は保持されず無意味です
function foo(){
const num = 100
num.prop = 200
return num.prop
}
foo()
// undefined
const num = 100
num.prop = 200
return num.prop
}
foo()
// undefined
strict が使えない場合
関数に strict モードが使えないケースがあります- default parameter (a = 1)
- rest parameter (...a)
- destructuring parameter ({a, b})
function foo(a, b = 1) {
"use strict"
}
// Illegal 'use strict' directive in function with non-simple parameter list
function foo(...args) {
"use strict"
}
// Illegal 'use strict' directive in function with non-simple parameter list
function foo([a, b]) {
"use strict"
}
// Illegal 'use strict' directive in function with non-simple parameter list
"use strict"
}
// Illegal 'use strict' directive in function with non-simple parameter list
function foo(...args) {
"use strict"
}
// Illegal 'use strict' directive in function with non-simple parameter list
function foo([a, b]) {
"use strict"
}
// Illegal 'use strict' directive in function with non-simple parameter list
強制 strict モード
クラス構文での定義と module としてロードされたスクリプト内は強制的に strict モードにされますclass Foo {
constructor(x, x){ }
}
// Duplicate parameter name not allowed in this context
constructor(x, x){ }
}
// Duplicate parameter name not allowed in this context
<script type="module">
function foo(x, x){ }
</script>
<!-- Duplicate parameter name not allowed in this context -->
function foo(x, x){ }
</script>
<!-- Duplicate parameter name not allowed in this context -->
strict モードを使うかどうか
全体的はほぼ誰も使ってないような機能が多いと思います私としては困るのは let が予約語になることと 引数名の重複禁止です
const で let という変数を作れませんが 関数なら作れますしたまに作ってます
引数名については上で書いたとおりです
他には eval のスコープと this の動きの違いはあんまり好みじゃないですが困るというほどではないです
Node.js や MongoDB など新しい JavaScript 実行環境に触れたときに グローバルの名前がわからないときはとりあえず this 参照でグローバルオブジェクトを取得していたのですが将来全部 strict モードになったりしたらどうやってアクセスすればいいのでしょうね
あと 書き換え不可プロパティへ代入しても何も起きないことを利用して いちいちチェックしないで書き換えるようにしている作りは少なからずあります
C# や PHP での面倒な null チェックをなくすための ?. とか ?? とか また jQuery でのセレクタに当てはまるのがなければ何もしないという動作も近いものがあると思います
できないなら何もしない を自動でやってくれるのは使う側からしては便利ですからね
読む側はけっこう大変ですけど
それに JavaScript や HTML/CSS などウェブのフロントエンドって基本エラー出さない系だと思います
おかしくても動く部分だけでとりあえず動かすという感じ
個人的にはそういうのが好きで 作ってる途中でとりあえず動くところまで動かさせてほしいです
コンパイルいる言語だとすべてが完璧でないと実行すらできないのでそういう面が苦手です
これまでだと 普段はゆるくてきつめにチェックしたいときにはスクリプトの一行目に全部 use strict つけて strict モードで確認できるという必要なら使うというくらいで使えました
でも module などでは強制となってしまうのでどちらかと言えば嫌な仕様です
強制されないときにあえて使う必要があるかというと 最近なら静的解析とかでも十分チェックしてくれるので 無効な処理のチェックの用途ならわざわざ全部に use strict 書いてまでしなくてもよいと思います
どちらかというと this のような動作の違いをみて好みの方を選べば良いと思います