金曜日JS:Smalltalkに触発されて地獄に行く方法

ボブ・ナイストロムという名の人が書いた「ゲーム開発パターン」という本を読んだとき(ロシア語で彼の名前の綴りはわかりません。発音の仕方が分からないので) Smalltalkは、時代をはるかに超えた、すべての最新のオブジェクト指向言語の先祖としての役割を果たしています。 私の人生の中で、あらゆる種類のビンテージ言語に否定できない愛情を持っているので、当然、私はそれについてグーグルで検索しました。 そしてもちろん、この経験から何か有益なことを学ぶ代わりに、悪いことを学びました。











私の意見では、Smalltalk言語の特異性は、特別に訓練された制御構造がないことです。 代わりに、オブジェクトにメッセージを送信することにより、制御フローが実装されます。 たとえば、追加の引数としてコードブロックを使用してブール型のオブジェクトにifTrueメッセージを送信する場合、このコードはブールオブジェクトの値がtrueの場合にのみ実行されます。



result := a > b ifTrue:[ 'greater' ] ifFalse:[ 'less or equal' ]
      
      





Smalltalkに単純な値がないことを知らない場合、「ブールオブジェクト」というフレーズは少し奇妙に聞こえます。各値はオブジェクトです。 「ちょっと待って! 「何かがこれを思い出させます!」と叫び、それをすべてまとめて...



免責事項
本番コードで同様のことを行うと、あなたは地獄に行くでしょう。 誰もあなたと友達になりません。 ヒトラーでさえ。 少なくとも、ヒトラーにはいくつかの目的がありました。


JavaScriptでは、すべての値がオブジェクトであるとは限りません。 ただし、さらに面白いものがあります。自動型キャストです。 単純な値をオブジェクトとして(たとえば、そのプロパティにアクセスするために)使用しようとするたびに、対応するオブジェクトラッパーで「ラップ」されます。 これにより、 true.toString()のようなものを書くことができます。 trueにtoStringメソッドはなく、 新しいブール(true)オブジェクトがあります。 考えてみれば、これは皮肉なことです。明示的な型変換を試みたとしても、暗黙的に(トートロジーについてはごめんなさい)暗黙的に使用します。



このオブジェクトラッパー、より正確にはプロトタイプに、独自のメソッドを「フック」できます。 これは良い考えではありません。誰もがそうし始めると、遅かれ早かれ紛争が発生します。 小さなクリップボードライブラリは、ユーザー入力を検証するためのウィジェットを以前に定義したString.prototype.fooメソッドをオーバーライドします。 ウィジェットはそれを気に入らないでしょう。 しかし、今日は金曜日であり、私たちは行くつもりはありません(私たちは行きませんか?)無実の人々が後で働くコードでこれを使用するために、私たちはいくつかのダークアーツを許可することができます。



Smoltock ifTrueの類似物から始めましょう。



 Boolean.prototype.ifThenElse = function(trueCallback, falseCallback){ return this.valueOf() ? trueCallback() : falseCallback(); }
      
      





その後、ママを動揺させることを恐れないのであれば、次のようなことができます:



 (2 * 2 == 5).ifThenElse( //,  2017        () => alert("Freedom is Slavery"), () => alert("O brave new world!") )
      
      





いくつかのニュアンスがあります。 まず、関数を引数としてではなく、何か他のもの(たとえば、何も)を渡さない場合、インタープリターは私たちを誓います。 第二に、JSには、 if構文で論理値だけでなく、一般的には何でも使用する伝統(ブール型がなかったCから戻ってきました)があります。 通常の場合、自動キャストはすべてのダーティな作業を行い、「偽の」値をfalseに、残りをtrueに変更しますが、このケースではこれは発生しません。



 (2 * 2).ifThenElse( () => alert("Freedom is Slavery"), () => alert("O brave new world!") ) //      ,  undefined is not a function
      
      





だから、より高く登る必要があります。 ブールプロトタイプにメソッドを追加する代わりに、 オブジェクトプロトタイプに追加します。 素晴らしい計画のようですね。



 function call(arg){ return typeof arg == "function" ? arg() : arg; } Object.prototype.ifThenElse = function(trueCallback, falseCallback){ if(this.valueOf()){ return call(trueCallback); }else{ return call(falseCallback); } }
      
      





ほぼ良い。 現在、私たちのメソッドは、数値、文字列、オブジェクトのためのものです...しかし、 未定義でもnullでもありません。 幸いなことにまたは不幸なことに、オブジェクトラッパーはありません。 残念ながら、これらは非常に一般的な偽の値です。 ただし、この問題は簡単に解決できます。



 const nil = { valueOf: () => false } //  nil  null //  , ?
      
      





さらに便利なメソッドをいくつか定義しましょう。



 Number.prototype.for = function(callback){ for(let i = 0; i < this.valueOf(); i++){ callback(i); } } function countdown(n){ console.log(10 - n); } 10..for(countdown); //  ,      js       
      
      





もちろん、これは本当のforループの淡い影にすぎません(カルマを台無しにして次の人生でオポッサムに生まれるのを恐れない場合)は、空のボディで記述でき、すべてのロジックを条件と最終的な表現にします。 一方、これは、最も頻繁に発生するforループを使用する最も単純なシナリオです。



 Object.prototype.forIn = function(callback){ for(let key in this){ callback(key, this[key], this); } } Object.prototype.forOwn = function(callback){ for(let key in this){ if(this.hasOwnProperty(key)){ callback(key, this[key], this); } } } var obj = {foo: "bar"}; obj.forIn(key => console.log(key)); // "forIn", "forOf", "foo" //  "ifThenElse",          obj.forOwn(key => console.log(key)); // "foo"
      
      





すでに何か有用なもののように見えます。 この類似性をごまかさないでください。



 Function.prototype.while = function(callback){ while(this()){ callback(); } } var power = 5; var result = 2; (() => --power).while( () => result *= 2 )
      
      





デュースを5度上げる必要があるが、上記のコードよりも賢いものを考え出していない場合-銃のように見えるオブジェクトで最寄りの店に侵入し、レジ係とすべてのバイヤーを人質にして、誰かにそれを数えるよう要求する方が良いあなた。 アイデアもまあまあですが、2つの悪のうち、小さい方を選択する必要があります。



そして最後に:



 String.prototype.switch = function(callbackObject){ var f = callbackObject[this.valueOf()]; return typeof f == "function" ? f() : f; }; ("1" + 2).switch({ "12": () => console.log(" JS"), "3": () => console.log("  JS") })
      
      





標準のJS スイッチと比較すると、この方法にはいくつかの欠点があります。 まず、文字列に対してのみ機能します。 希望するなら、代わりにMapオブジェクトを使用して任意の値に拡張できますが、不道徳なことをしたいという私の欲求は今日は枯渇しており、好奇心の強い読者にこれを提供します。 第二に、 デフォルトはありません。 第三に、そのようなことをする方法はありません。



 switch(value){ case 1: case 2: console.log("   "); break; case 3: console.log(" "); case 4: console.log("  "); }
      
      





しかし、多くの人はこれはむしろ美徳だと言うでしょう。



まあ、あなたが私と同じくらい楽しんだことを願っています。 そして、仕事に戻る必要があります-まあ、実際の仕事に。 彼らがそのように書かないのです まあ、あなたは私を理解しています。



All Articles