突然起動しなくなったファイルサーバ(Windows2000)の復旧

今日、客先で利用させてもらっていたWindows2000のファイルサーバに、突然ブルースクリーンが表示。
おいおい、と思いながら再起動すると

missing operating system

げげっ!シャレにならん!
実際にこんなエラーに遭遇するのは初めてだったのでかなり焦る。
ググって調べると、MBR(マスター・ブート・レコード)が破損している可能性があるらしいので
インストールディスクを借りて回復コンソールを立ち上げ、fixmbrコマンドを打ち込む。
再起動すると、

error loading operationg system

オーマイガー!! なんかさらにひどくなってそうなエラー表示が・・・
回復コンソールでCドライブにアクセスしようとすると、
「エラーが起きてアクセスできません」
mapコマンドでドライブの情報を表示させると、
Cドライブのファイルフォーマットが表示されていない。
ファイルシステムが破損しているのか!!?

マズい!ほんとにマズい!!

OSのブートレコードが破損している可能性もあり、fixbootコマンドで修復できるらしいことがわかった。
でも、ド素人がこれ以上いじくる前に最低限必要なデータはバックアップを取らねば!
と、調べていると、CDから起動できるKNOPPIXというLinuxを使って、データのバックアップが
できることがわかった!

早速CD-Rに焼いて、CDから起動。
1分ほどできれいなデスクトップが表示された。
「おぉ〜、なんかすごい」とちょっと感動。
デスクトップには、自動でマウントしてくれたドライブへのショートカットアイコンが。
すごい、すごいぞKNOPPIX
Cドライブにマウントしたディレクトリにアクセスしようとすると

Could not open ・・・・(忘れた)・・・

開かない! ファイルフォーマットが特定できないからマウント失敗でーす、みたいなこと言ってる・・・
バックアップできず・・・

もしかして、Cドライブがイカれたか・・・いや、ブートレコードが破損しているだけの可能性も。
もはやバックアップはあきらめて、最後の光「fixboot」コマンドをたたくしかない。
恐る恐るfixbootしてみると、

ブートレコードが破損しています。 ブートレコードを修復しました。

と表示された。祈りながら再起動すると、OSを認識!!!!
見慣れた起動画面が表示されました。
た、助かった!
速攻で別のマシンに必要なデータをバックアップしました。

うちのPCもこんな状態にならないうちにバックアップしないと、と思わせてくれた出来事でした。

calleeとthisで参照できるオブジェクト違い

呼び出されるたびに異なる値を返す関数として、
関数プロパティに変数を保持させて実現する例があった。

uniqueID = function() {   
  if (!arguments.callee.id) arguments.callee.id = 0;
    return arguments.callee.id++;
};

alert(uniqueID());    // 0
alert(uniqueID());    // 1
alert(uniqueID());    // 2

calleeじゃなくて、thisじゃだめなの?
と思い試してみると、

uniqueID = function() {   
  if (!this.id) this.id = 0;
    return this.id++;
};

alert(uniqueID());    // 0
alert(uniqueID());    // 1
alert(uniqueID());    // 2

な〜んだ、同じ結果じゃーん。って思っていたら、全然違う動作をしていた。

calleeを使った場合だと、idプロパティはFunctionオブジェクトのプロパティ。
thisを使った場合だと、呼び出し元、つまりGlobalオブジェクトのプロパティとして生成される。
試しに、alert(id); ってやってみると、「3」がアラートされました。

関数の中のthisは、呼び出し元のオブジェクトを参照するキーワードなので
Globalな関数の中のthisは、Globalオブジェクトとなります。

JavaScript第5版 P144 クロージャの例

クロージャとは、実行するコードとコードを実行するスコープを組み合わせたもの

//呼び出されるたびに異なる値を返す関数

//関数のプロパティに変数を保持させて実現する場合
uniqueID = function() {   
  if (!arguments.callee.id) arguments.callee.id = 0;
    return arguments.callee.id++;
};

alert(uniqueID());    // 0
alert(uniqueID());    // 1
alert(uniqueID());    // 2
uniqueID.id = 0;      
alert(uniqueID());    // 0  関数の外から値を変更できてしまう


//クロージャを使えば、この問題は解決できる

uniqueID = (function() {
  var id = 0;
  return function() { return id++; };
})();

alert(uniqueID());   // 0
alert(uniqueID());   // 1
alert(uniqueID());   // 2
uniqueID.id = 99;
alert(uniqueID());   // 3
alert(uniqueID.id);  // 99

実行コンテキストとスコープチェーンについて整理した

JavaScript第5版「変数のスコープの概念」の説明でこんがらがってしまったので整理してみた。

  • グローバル変数Globalオブジェクトのプロパティ
  • Globalオブジェクトはインタプリタ起動時にJavaScriptコードを実行する前に生成される
  • ローカル変数はCallオブジェクトのプロパティ
  • 個々の関数がコールされるたびに、関数ごとに新しい実行コンテキストが生成される
  • 関数外部のJavaScriptコードの実行コンテキストでは、Globalオブジェクトが変数定義に使われる
  • 関数内部でJavaScriptコードの実行コンテキストでは、Callオブジェクトが変数定義に使われる
  • JavaScriptの実行コンテキストには、コンテキストごとにスコープチェーンがある。
  • スコープチェーンはGlobalオブジェクトやCallオブジェクトを並べたもの

実行コンテキスト=(Globalオブジェクト|Callオブジェクト)と一瞬思ったが
よく読むと別物のようだ。
なんだか文章ではわかりにくいかったので、絵で描いてみた。

実行コンテキストにGlobalオブジェクトやCallオブジェクトへの参照が存在するイメージ。(たぶん)
(絵の中ではオブジェクトが存在するように書いてますが)

JavaScript第5版 P123〜P140

関数

  • 関数もオブジェクト
  • 関数はデータとしても扱える
  • 関数の引数は省略可能
  • 可変長引数(argumentsプロパティで参照できる)
    • argumentsの実態は配列のようなArgumentsオブジェクト
    • arguments.calleeで実行中の関数を参照できる
    • arguments.callee.lengthで関数定義時の引数の個数を取得できる
function square(x) { return x*x; }        //function文
var square = function(x) { return x*x; }  //関数リテラル

//引数は省略可能
function f(x, y, z) { ・・・ }
f(1);       // x == 1, y == undefined, z == undefined
f(1, 2);
f(1, 2, 3);

//可変長引数
function max() { 
  for (var i = 0; i < arguments.length; i++) {
    /* 渡された引数の中で最大値を返す */
  }
}
  • thisキーワード
    • メソッドを呼び出すときに使用したオブジェクトが、メソッド本体でthisによって参照できる

apply() と call()

あるオブジェクトのメソッドであるかのように関数を呼び出せる

  • 最初の引数は対象となるメソッド。関数本体のthisキーワードの値となる
  • 2番目以降は関数の引数
f.call(obj, 1, 2);

//以下は上の例とほぼ同じ意味になる
obj.m = f;
obj.m(1, 2);
delete obj.m;

apply()は関数に渡す引数を配列で渡す点でcall()と異なる

f.apply(obj, [1, 2]);

//配列の要素の中から最大値を見つけたいときに便利
var max = Math.max.apply(null, array);

JavaScript第5版 P105〜P122

配列のメソッド

join

配列の要素を文字列に変換し、連結する。

var a = [1, 2, 3];
var s = a.join();    // s == "1,2,3"
s = join(", ");      // s == "1, 2, 3" 区切り文字指定
reverse

配列の要素の順番を逆にする。新しい配列は作らない。

var a = [1, 2, 3];
a.reverse();    // a == [3, 2, 1]
sort

配列の要素をソートする。新しい配列は作らない。

var a = ["banana", "cherry", "apple"];
//引数がない場合はアルファベット順にソートする
a.sort();       // a == ["apple", "banana", "cherry"]

a = [5, 11, 4, 3, 1];
//ソート関数を指定した場合は関数に従ってソートする
a.sort(function(a, b) {
  //小さい順にソートする
  return a - b;         //aをbよりも前にしたい場合は0より小さい数字を返す
});
concat

配列に要素を追加して、新しい配列を生成する。

var a = [1, 2, 3];
a.concat(4, 5);            // [1, 2, 3, 4, 5]
a.concat([4, 5]);          // [1, 2, 3, 4, 5]
a.concat(4, [5, [6, 7]]);  // [1, 2, 3, 4, 5, [6, 7]]  配列を再帰的には展開しない
slice

部分配列を返す。元の配列は変化なし

var a = [1, 2, 3, 4, 5];
a.slice(2, 4);             // [3, 4]  2番目の要素から4番目の要素まで(4番目は入らない)
a.slice(2);                // [3, 4, 5]
a.slice(1, -1)             // [2, 3, 4]
splice

配列に要素を挿入したり、配列から要素を削除したりする汎用的なメソッド。
元の配列を変更する。

var a = [1, 2, 3, 4, 5, 6];
//要素を削除し、削除した要素を配列で返す
a.splice(4);          // [5, 6], a == [1, 2, 3, 4] 
a.slice(1, 2);        // [2, 3],  a == [1, 4]
a.slice(1, -1)        // [2, 3, 4]

//要素の追加、置き換え
a = [1, 2, 3, 4, 5];
a.splice(2, 0, 'a', 'b');  // [], a == [1, 2, 'a', 'b', 3, 4, 5]
a.splice(2, 2, [7, 8], 9);      // ['a', 'b'], a== [1, 2, [7, 8], 9, 3, 4, 5]
                                // 配列は展開せずに挿入される
push, pop

配列の最後に要素を追加し、配列の長さを返す。(push)
配列の最後の要素を削除し、削除した要素を返す。(pop)

var stack = [];
stack.push(1, 2, 3);     // 3を返す。 stack == [1, 2, 3]
stack.pop();             // 3を返す。 stack == [1, 2] 
unshift, shift

配列の先頭で出し入れする。
追加がunshift。削除がshift。

var a = [];
a.unshift(1, 2);        // 2を返す。 a == [1, 2]  一括挿入は順番を保持
a.unshift(3);           // 3を返す。 a == [3, 1, 2]
a.shift();              // 3を返す。 a == [1, 2] 
toString, toLocalString

個々の文字列をカンマで区切ったリスト文字列を返す。
toLocalStringはtoStringのローカライズ版(区切り文字は実装依存

JavaScript第5版 P91〜P104

for/in文

オブジェクトのプロパティごとにループが実行される

var obj = new Object();
obj.a = 3;
obj.b = "abc";
obj.c = function() { alert("call z"); };
obj.d = [1,2,3];
obj.e;
for (var prop in obj) {
    alert(prop);    //a、b、c、dの順番に表示
}

var文

var文で変数を宣言すると、宣言する場所が関数内であれば、その関数のCallオブジェクトのプロパティとして生成する。関数外であれば、グローバルオブジェクトのプロパティとして生成する。

function文

JavaScriptパーサが関数定義を検出すると、var文と同様にCallオブジェクトのプロパティとして関数を生成する。関数は定義されるだけで実行はされない。

alert(f(5));     // 25が表示される。定義前でも呼び出せる
var f = 0;
function f(x) {  // 関数の定義は、f=0 よりも先に行われる
  return x * x;
}
alert(f);        // 0が表示される。f()は上書きされている

with文

指定したオブジェクトがスコープチェーンに追加される

with(frames[1].document.forms[0]) {
  // forms[0]に直接アクセスできる
  name.value = "";
  address.value = "";
}

しかし、コードの最適化が難しく実行速度が遅くなるのが欠点。
withはあまり使わないほうが良いみたい。

空文

「;」だけ。何もしない文。役に立つこともある。

//配列aの初期化
for (var i = 0; i < a.length; a[i++] = 0) ; //ループ本体は空