sasanquaneuf
@sasanquaneuf

Are you sure you want to delete the question?

Leaving a resolved question undeleted may help others!

これは面白いと思ったHoisting:巻き上げみたいな挙動(言語不問)

いろんな言語で、Hoisting(巻き上げ)みたいな挙動で「なるほど」と思ったものを教えて下さい。

Hoistingとは:
https://developer.mozilla.org/en-US/docs/Glossary/Hoisting

例)pythonで面白いなと思ったもの:

正常にうごくやつ
a = 1
def b():
    def c():
         print(a)
    c()

b()
エラーになるやつ
a = 1
def b():
    def c():
         print(a)
    c()
    a = 1

b()

あまりpythonでHoistingみたいな現象を意識した事が無かったので、遭遇してなるほど!と思いました。

※ここでHoistingみたいといっているのは、クロージャの参照が、上から順番に単純に読んだ時よりも下に出てくるものに引きずられてしまうという事を言っています。本来の関数定義のHoistingの意味だと、宣言の内容を実際にその行よりも上で参照できるので、そこはHoistingとは違っています。

(pybabelのgettextをas _でimportしていたら、使用しない変数に割り当てた_が上記のaにあたる動きをして、エラーになって気づきました)

0

例と似ていますが、Python は👇のようにfor文でも変数宣言扱いになってエラーになりますね。

repl.it

x = 0

def f():
    def g():
        print(x)
  
    g()

    for x in range(10):
        pass

f() # NameError: free variable 'x' referenced before assignment in enclosing scope

JavaScriptでも似たような現象が起きますが、var で宣言した場合、hoisting は起こりますがそれは変数の「宣言」だけで、値は undefined であり、元の位置の宣言文が実行されて初めて初期値の代入がされます。ちなみに letconst で宣言した場合も同様ですが、初期値が代入される前にアクセスすると例外が発生します(ただし下の例で var の代わりに let を使うと、そのスコープは for 文の中に限定されるので、例外は発生しません)。

let x = 1

function f() {
  console.log(x) // undefined

  if (1 === 2) {
    for (var x = 0; x < 10; x++) {}
  }
}

f()
1Like

JavaScript では、function 文を使うともう少し面白いことができます。

var f = 0

function f() {}

console.log(f) // 0

代入よりも関数宣言の方が後に書いてあるのに、実際はホイスティングによって関数宣言が先に行われるため、このような振る舞いになります。

ちなみに、この場合、最初の var は有っても無くても同じ動作をします。

2Like

@righteous
なるほど、面白い例をありがとうございます!
pythonのforは、scopeがforを書いているところと同じになって、forを抜けてもxを参照できるので、確かにそうですね。
また、到達不能なところのxによって挙動が変わる件は、pythonではyieldが文中に存在すると、それがたとえ到達できない領域であってもgeneratorになるのも、ある意味で似ているなと思いました。
JSの場合、ifの中のfunction functionName(){}形式の宣言はHoistingされない(変数としてのHoistingはされるが、代入はされない)というのもあって、ifをめぐるこの辺の挙動は中々興味深いなと思いますね。

console.log(a);
if (1 === 2) {
    function a() {
        alert('hello')
    }
}
// undefined
console.log(a);
function a() {
    alert('hello')
}
// ƒ a() {
//     alert('hello')
// }

@unsigned-wrong-wrong-int

面白い例をありがとうございます!
たしかに、Hoistingを利用した上下逆の例(?)というのもありますね。
これも、やはりifを挟むと結果が変わりますね。

var f = 0
if (true) function f() {}
console.log(f)  // ƒ f() {}
0Like

Your answer might help someone💌