破棄されたブログ

このブログは破棄されました。

レキシカルスコープ

顕在化する Perl リスクを横目に、どこのサーバにも入っててシェルスクリプトより強力だし PHP みたいにカスじゃないって理由で Perl のイントロダクション perlintro を読んでたら、変数スコープのところでちょっと気になる記述があった。

my creates lexically scoped variables

perlintro - perldoc.perl.org

このレキシカルスコープって用語はたまに聞くんだけど、意味が分かってなかった。わからん知らんじゃどうしようもないんで調べてみたところわかりやすい解説が見つかった。
レキシカルスコープとダイナミックスコープ | すぐに忘れる脳みそのためのメモ

#!/bin/perl
use strict;

my $x = "foo"; # (1)

sub foo {
  return $x;
}

sub bar {
  my $x = "bar"; # (2)
  return foo();
}

print bar() . "\n"; # bar

つまるところ、foo() が bar() 内の $x (2) を参照できないのがレキシカルスコープ(静的スコープ)ってことらしい。つうかそれが普通じゃねとか思ったら、その点についても解説があった。なんとも親切。
じゃあなんで、わざわざレキシカルスコープなんて書いてあるんだと思ったら Perl にはグローバルスコープ(動的スコープ)もあるらしい。ややこしそうだから読み進めて出てくるまでは保留にしとこ。

っとここで、あれでもこれ JavaScript なら参照できんじゃねとか思ったけど、勘違いだった。

#!/bin/node

// var x = "foo";

function foo() {
  return x;
}

function bar() {
  var x = "bar";
  return foo();
}

console.log(bar()); // ReferenceError: x is not defined

グローバルに何さらしとんじゃボケとかいうのは置いておいて、書いてみたら参照できないのは当たり前だった。 foo() も bar() もグローバルのプロパティだから、スコープチェーンを辿れば関数自身の次はグローバルオブジェクトに至るにのは当たり前だよね。グローバルで変数 x の宣言がないんだからエラーがでるのは当たり前だよね。
じゃあなんで勘違いしたんだろと思ったけど、関数内関数からは変数 x が参照できるからこっちと勘違いしたみたい。こっちの foo() は bar() のプロパティだから、bar() 内の変数 x を参照できる。

#!/bin/node

var x = "foo";

function bar() {
  var x = "bar";
  return foo();

  function foo() {
    return x;
  }
}

console.log(bar()); // bar

クソどうでもいいけど PHP の場合。PHP は global してやらないと関数の外側の変数を参照できないから、エラー吐いて落ちる。使いにくいけど、ここで global しないで関数の外側を参照できるような言語仕様だと大事故起こしまくるのがぺちぱー。ぼくもぺちぱー

<?php

$x = "foo";

function foo() {
  return $x; // undefined variable
}

function bar() {
  $x = "bar";
  return foo();
}

echo bar();