PythonとJavaScriptの微妙な違い
なんかささださんと話がかみ合っていない気配がするので、 とりあえずこちらが知っているPythonの話をまとめてみます。
Pythonの場合、関数内で代入が行われる変数はローカル変数です。 すでに外のスコープに変数があろうがそんなことはお構いなしにローカル変数です。 例外は、変数をglobal宣言した場合で、この場合だけグローバル変数になります。
>>> def func1():
global a
a = 1 # グローバル変数への代入になっている
b = 2
print locals()
>>> func1()
{'b': 2} # ローカル変数はbだけ
>>> a
1 # aはグローバル変数なのでアクセスできる
>>> def func2(): a = 1 global a SyntaxError: name 'a' is assigned to before global declaration (それでもって、一つの関数内で同じ名前でグローバル変数とローカル変数を作ったら、 グローバル変数がローカル変数で隠されている状態になるのが自然だと僕は思ったわけです。, line 1)
>>> def func2(): c = 1 # ローカル変数作成 globals()["c"] = 2 # グローバル変数作成 print c >>> func2() 1 # ローカルのcは1 >>> c 2 # グローバルのcは2でもって、ささださんの挙げたJavaScriptの例も、 "a"になるのが自然だな、でも違うのか、と思って試してみたらやっぱり"a"になったので、 「うむむ、これは何か話がかみ合ってないぞ?」と思った次第。
function test() {
var foo = "a";
function inner() {
foo = "b";
var foo = "c";
}
inner();
return foo;
}
document.write(test());
PythonでもJavaScriptと同じように"a"が返ります。
>>> def test(): foo = "a" def inner(): globals()["foo"] = "b" foo = "c" inner() return foo >>> test() 'a'ささださんが何を不自然だと感じたのか詳しいことはわからないですけど、 もし"b"が返ると思ったのだったらどういうことか考えてみると…。 きっと僕の脳内では
- JavaScriptの"x = 1"はグローバル変数のxを作成して1で初期化する命令
- グローバルスコープとは、トップレベルのスコープ
- 「foo = "b";」によってグローバル変数がいじられようが何だろうが、 関数testのローカル変数が影響を受けるはずがない
- 「var foo = "c";」はローカル変数だから関数の外に影響を及ぼすはずがない。
- JavaScriptの"var x"は変数xの有効範囲を関数内に限定する命令
- varの付いていないのは限定されていない変数
- 限定されていないってのはつまり、1枚外側のスコープってこと
- 「var foo = "c";」はローカル変数だから関数の外に影響を及ぼすはずがない。
- でも「foo = "b";」は普通の変数なんだから、「var foo = "a";」と同じスコープ。
- あれ?"a"って返ってくるぞ?
と勝手に憶測。
でもなんだかんだ考えた結果、JavaScriptもPythonと同じように 「varでスコープを限定したいのなら、最初の代入の時までに宣言しなければシンタックスエラー」 というようになればいいんじゃないかと思えてきました。 というわけで「え、エラー出すべきなの?」は撤回。
= うっ、これは後に代入された方が返るのか…。 これは気持ちが悪い…。当然"b"が返ると思ったのに…。
function inner() {
var foo = "b";
foo = "c";
document.write(foo);
}
inner();
Pythonなら代入の順序にかかわらず"b"が返ります。
>>> def test(): globals()["foo"] = "c" foo = "b" print foo foo = "b" globals()["foo"] = "c" print foo >>> test() b b
ふうむ。 Core JavaScript 1.5 Guide:Variables - MDC: 「JavaScript の変数に関して独特なこととして、後に宣言される変数を例外を発生させることなく参照できるということもあります。」 っていう実装なのか。 仮に関数の冒頭でfoo = 1と書いてあっても、 もしかすると関数の最後にvar fooと書いてあるかも知れないから 全部読むまではグローバル変数かどうかわからないわけか。 一方Pythonはfoo = 1が出てきた時点でローカル変数に決定、 仮に後からglobal fooが出てきても構文エラー、と。 global fooが先に出てきたらfooはグローバル変数に決定、と。
Python流の方がいいと思うけど、 JavaScript流の方がインタプリタの実装が楽そうな気配。
コードは下記。