せかいや

いまいるここを、おもしろく http://sekai-in-the-box.appspot.com/

【JavaScript 】new呼び出し、「this instanceof fNOP」の意味。

 
■topic summary
study to understand this site "Polyfill" section
Function.prototype.bind() - JavaScript | MDN




事の発端

 

パーフェクトJavaScript (PERFECT SERIES 4)

パーフェクトJavaScript (PERFECT SERIES 4)

読んでいる本の中に

bindはapplyもしくはcallを使えば独自に実装可能です

とあった。

ふむふむ。そうなんだ。
 
Mozillaのリファレンスにも
「bindが定義されていなかったらこのコードを使って代用してね」
とコードが載っている。

The bind function is a recent addition to ECMA-262, 5th edition; as such it may not be present in all browsers. You can partially work around this by inserting the following code at the beginning of your scripts,

https://developer.mozilla.org/ja/docs/Web/JavaScript/Reference/Global_Objects/Function/bind

 
ならやってみよう。
できるかな。
 
・・・から始まりました。

 
そして2週間くらい嵌っていました。。。


 

ここで明らかにすること。

Mozillaに載っているコード

fNOP = function () {},
  fBound = function () {
    return fToBind.apply(this instanceof fNOP && oThis
               ? this
               : oThis,
               aArgs.concat(Array.prototype.slice.call(arguments)));
  };

fNOP.prototype = this.prototype;
fBound.prototype = new fNOP();
return fBound;

このコードが何をやっているのか理解する。
 
・どういう時に「this instanceof fNOP」がtrue?
・fBoundのプロトタイプにfNOPのインスタンスを代入?
など。




 
まずは基本。
 

ケース1:new 呼び出しのthisコンテキスト

生成されるインスタンス自身が「this」にsetされます。

http://qiita.com/Haru39/items/9935ce476a17d6258e27

具体的に書いてみると、

Foo = function(){
  console.log(this);
}
Foo.prototype.arg="ppp";
x = new Foo();
y = Foo();

 
■実行結果

Foo {arg: "ppp"} 
Window {top: Window, window: Window, location: Location, Proxy: Object, external: Object…}

(ブラウザで試行しているため、2個目はグローバルオブジェクトとしてwindowオブジェクトが返ってきている)


 

ケース2:new呼び出し時の返り値

returnで関数を返した場合は、その関数が返り値となる。
関数以外であればインスタンスが返る。

Goo = function(){
  return "hoge";
}
new Goo()
Foo = function(){
  fn = function(){console.log("jjj")};
  return fn;
};
new Foo()
||< 
■実行結果
>||
Goo {}
function (){console.log("jjj")}

 

If the constructor function doesn't explicitly return an object, the object created in step 1 is used instead.

https://developer.mozilla.org/ja/docs/Web/JavaScript/Reference/Operators/new

うーん。Stringもobjectだと思うから、この解説は良く分からないけど。。




 

本題

ここからが難しくなるところ!
 
まず、クラス(prototypeプロパティを持つオブジェクト)を返す関数を考える。
この関数をnew呼び出ししたときは、
thisコンテキストが生成されるインスタンスとなるので(∵ケース1)、
protorypeで定義したプロパティを読み取ることができる。
 

hoge = function () {
  fBound = function(){
    return console.log(this.arg);
  };
  fBound.prototype.arg = "ppp";
  return fBound;
};
boo = hoge();
x = new boo();

 
■実行結果

ppp

 

new呼び出しかどうかを判別する

注)ここからは憶測も入っているので確証ではないです。
探しても同種の内容に言及している資料が見つかりませんでした。
すみません。

自分が分からなかったのは、この
this instanceof fNOP、がtrueになるケース。

だってメソッド呼び出しできないわけだから、
thisは関数呼び出しの時のグローバルオブジェクトになるのでは?、
と思っていたわけです。

コンストラクタ呼び出しのときはthisがその返されるインスタンスになる。
このケースが頭になかったのです。

hoge = function (oThis) {
  var fNOP = function() {},
      fBound = function() {
        return console.log(this instanceof fNOP);  #<= ア
      };
  fBound.prototype = new fNOP();  #<= イ
  return fBound;
};
foo = hoge();
x = new foo();  #<= ウ

 
■実行結果

true

順に追っていくと、
 
hoge関数で返却された関数(クラス)をnew呼び出しすると、、
1)ウにてnew呼び出しをするため、
 アのthisはfBoundオブジェクトとなる(∵ケース1)。
2)イにて、fBoundはプロトタイプに
 fNOP.prototypeを継承したオブジェクトを持つ(※2)
3)(1)(2)より、thisのプロトタイプチェインでfNOP.prototypeを持つため、
 アのinstanceofはtrueとなる(※3)

 
※2

When the code new foo(...) is executed, the following things happen:
A new object is created, inheriting from foo.prototype.

https://developer.mozilla.org/ja/docs/Web/JavaScript/Reference/Operators/new

 
※3

instanceof 演算子は、object のプロトタイプチェインで constructor.prototype の存在を確認します。

https://developer.mozilla.org/ja/docs/JavaScript/Reference/Operators/instanceof


 
あらためて、事の発端となったMozillaのコードを見返してみると、、
Function.prototype.bind() - JavaScript | MDN

fBound = function () {
  return fToBind.apply(this instanceof fNOP && oThis
                         ? this
                         : oThis,  #<= (ア)
                       aArgs.concat(Array.prototype.slice.call(arguments)));
};
fNOP.prototype = this.prototype;
fBound.prototype = new fNOP();
return fBound;

 
(ア)の三項演算子は、
関数呼び出し(this instanceof fNOPがfalse)⇒第一引数がoThis(bindの第一引数)
new呼び出し(this instanceof fNOPがtrue)⇒第一引数が生成されるインスタンス
という違いを表現している。

new呼び出しをしてもreturnで指定したapply関数の評価結果が返る(∵ケース2)

 
実際に書いてみると、

hoge = function(){console.log("-----");console.log(this)};
x = {age: 10};
fn = hoge.bind(x);
fn();
new fn();

 
■実行結果

-----
Object {age: 10}
-----
fBound {} 

fn()では thisがx
new fn()では thisがfBoundインスタンス

 

何がうれしいの?

fBoundインスタンスだと何がうれしいのか。
fBoundクラスは内部に隠蔽されているわけだし?

答えは

fNOP.prototype = this.prototype;
fBound.prototype = new fNOP();

のところ。
fBoundは、thisのプロトタイプを継承している。


 
つまり、thisのプロトタイプを定義してみると、

hoge = function(){console.log("-----");console.log(this)};
hoge.prototype.arg1 = "hello";
x={age: 10};
fn = hoge.bind(x);
fn();
new fn();

 
■実行結果

-----
Object {age: 10}
-----
fBound {arg1: "hello"} 

new呼び出しのときは、プロトタイプを継承している!


 
以上を、
・プロトタイプを持っているものをクラスとみなして先頭大文字にする
というルールで見やすく整理すると

Hoge = function(){console.log(this.age)};
Hoge.prototype.age = 999;
x={age: 10};
fn = Hoge.bind(x);
fn();
new fn();

 
■実行結果

10
999

 

 
どうだ!


 

まとめ

まとめると、
bindはthisを第一引数に差し替える関数だが、
bindの返り値をnew呼び出しすると、その法則が無視されて、
thisはバインドのレシーバーオブジェクトのインスタンスに差し替えられる。


 
。。。。つかれたぶひー。

【Ruby】【メタプログラミング】言語内DSL(文法チェックあり)

 
■topic summary
study about DSL. it has grammer check.

 
昨日作成した言語内DSLは文法チェックがなかったけど、
今回は文法チェックを追加したパターン。

 

学んだこと

親クラスで暮らすメソッドを定義することにより、
子クラスで地の文チックに使用できる
 ⇒elementメソッド


class_eval内でのメソッド定義

class_eval %Q{
  def #{tagname}(attributes={} ,&b)
    tag(:#{tagname}, attributes ,&b)
  end
}

のところ。
なぜtagnameを展開してシンボル化するのか。
第二引数のattributesはなぜ#{}展開がいらないのか
を理解しました。
これは別記事にて詳しく書きました。


 
裸のハッシュ(※1)の後に
ブロックを続ける場合は{}ではなくdo-endを使う。

※1

6.4.4
ハッシュリテラルがメソッドの最後の引数なら(あるいは、その後ろに続くのがブロック引数だけなら)、ハッシュリテラルを囲む中かっこを省略できるようにしている。中かっこなしのハッシュは、裸のハッシュ(bare hash)と呼ばれることがあり・・・

http://www.amazon.co.jp/%E3%83%97%E3%83%AD%E3%82%B0%E3%83%A9%E3%83%9F%E3%83%B3%E3%82%B0%E8%A8%80%E8%AA%9E-Ruby-%E3%81%BE%E3%81%A4%E3%82%82%E3%81%A8-%E3%82%86%E3%81%8D%E3%81%B2%E3%82%8D/dp/4873113946

正式なリファレンスがググりきれなかったので、本からの引用


 

DSLコード

HTMLForm.generate(STDOUT) do
  comment "simple form"
  form name: "registration", action: "http://hoge" do
    content "Adress:"
    textarea name: "address", rows:6, cols: 40 do
      "please enter your address"
    end
  end
end

 
■実行結果

<!-- simple form --><form name='registration' action='http://hoge' method='GET' entype='application/www'>Adress:<textarea name='address' rows='6' cols='40' readonly='readonly'>please enter your address</textarea></form>

 

実装

class XMLGrammer
  def initialize(out)
    @out = out
  end
  def self.generate(out, &b)
    self.new(out).instance_eval(&b)
  end
  def self.element(tagname, attributes={} ,&b)
    @allowed_attributes ||= {}
    @allowed_attributes[tagname] = attributes
    class_eval %Q{
      def #{tagname}(_attributes={} ,&b)
        tag(:#{tagname}, _attributes ,&b)
      end
    }
  end
  def self.allowed_attributes
    @allowed_attributes
  end
  def tag(tagname, attributes={}, &b)
    allowed = self.class.allowed_attributes[tagname]
    @out << "<#{tagname}"
    
    #認められた属性か
    attributes.each do |key, val|
      raise "unknown attribute" if !allowed.include?(key)
      @out << " #{key}='#{val}'"
    end
    #デフォルト値を出力
    allowed.each do |key, val|
      next if attributes.include?(key)
      if val == REQ
        raise "required attribute missing" 
      elsif val == BOOL
        @out << " #{key}='#{key}'"
      elsif val.is_a? String
        @out << " #{key}='#{val}'"
      end
    end
    @out << ">"
    if block_given?
      content = yield
      @out << content if content
    end
    @out << "</#{tagname}>"
    nil
  end
  def comment(text)
    @out << "<!-- #{text} -->"
    nil
  end
  def content(text)
    @out << "#{text}"
  end
  OPT = :opt
  REQ = :req
  BOOL = :bool
end

class HTMLForm < XMLGrammer
  element :form, action: REQ, method: "GET", entype: "application/www", name: OPT
  element :textarea, name: REQ, rows: REQ, cols: REQ, readonly: BOOL
end

 


疲れたぶー。
 

【Ruby】【メタプログラミング】メソッド名を動的に変更。 class_eval

 
■topic summary
study about class_eval


 

変数を展開した値のメソッド名を定義したい

たとえば、こうやって書くと、hogeメソッドが定義される。

kokoメソッドを定義するときはどうすればいいのか?
メソッド内で"koko"を表示するにはどうすればいいのか?

という話。

class Test
  hoge="koko"
  def hoge
    p hoge
  end
end
p Test.new.methods

 
■実行結果

[:hoge, :nil?, :===, :=~, : ・・・

 
hogeメソッドが定義されてしまっている。



 

ヒント

こうやって#{}を使うことで文字列内で展開できる。

class Test
  hoge="koko"
  p hoge
  p "#{hoge}"
end

  
■実行結果

"koko"
"koko"

 

 
でも、このまま使うと構文エラーになる。

class Test
  hoge="koko"
  p hoge
  def "#{hoge}"
    p hoge
  end
end

 
■実行結果

syntax error, unexpected tSTRING_BEG

そりゃそうだー。

 
そこでclass_evalの出番。
class_evalとinstance_evalの違いは前に検証したところ。

 

class Test
  hoge="koko"
  class_eval "def #{hoge}; p 'hello'; end"
end
Test.new.koko

 
■実行結果

hello

 
eval内に変数を渡したいときはこう。

class Test
  hoge="koko"
  class_eval "def #{hoge}(_foo); p 'hoge';p _foo; end"
end
foo = "bar"
Test.new.koko(foo)

 
■実行結果

"hoge"
"bar"


 

eval内の変数

こうやって書くと、hogeが解決できないとエラーになる。これは分かりやすい。

class Test
  hoge="koko"
  class_eval "def #{hoge}(_foo); p hoge; end"
end
foo = "bar"
Test.new.koko(foo)

 
■実行結果

 `koko': undefined local variable or method `hoge' for #<Test:0x2693d90> (NameError)


 

ここで嵌った。。

分かりにくいのが、変数を#{}で展開してしまったケース
 

class Test
  hoge="koko"
  class_eval "def #{hoge}(_foo); p #{hoge}; end"
end
foo = "bar"
Test.new.koko(foo)

 
■実行結果

(eval):1:in `koko': wrong number of arguments (0 for 1) (ArgumentError)
        from (eval):1:in `koko'

 
これは、hogeを展開してkokoになり、さらにそのkokoを展開しようとしている。
kokoはeval内でメソッドとして定義されているため、
NameErrorとはならずに、引数エラーとなっている。
#引数の形が同じだと、無限ループとなりStackOverFlowになる。
 ##分かりやすくするために引数を1つとる形でkokoメソッドを定義した。


 
素朴に"koko"を表示したいときはどうするのか?
ここでシンボルの出番。

class Test
  hoge="koko"
  class_eval "def #{hoge}; p :#{hoge}.to_s; end"
end
Test.new.koko

 
■実行結果

"koko"

シンボル化することで変数が展開されない。
 
うーん。なるほど。

 

【Ruby】【メタプログラミング】言語内DSLの実装。StringScannerを使ったインデントパーサーの実装

 
■topic summary
study about DSL
 

この本に載っているDSLをお勉強。

プログラミング言語 Ruby

プログラミング言語 Ruby

出力のインデントは自作。

 

DSLコード

pagetitle = "xml generate test page"
out = ""
XML.generate(out) do
  html do
    head do
      title{pagetitle}
      comment "this is test"
    end
    body do
      h1(style: "font:red",size: "5px"){pagetitle}
      ul type: "sequre" do
        li {RUBY_VERSION}
        li {Time.now.to_s}
      end
    end
  end
end
XML.show(out)

 
 

出力コード

<html>
  <head>
    <title>
      xml generate test page
    </title>
    <!-- this is test -->
  </head>
  <body>
    <h1 style='font:red' size='5px'>
      xml generate test page
    </h1>
    <ul type='sequre'>
      <li>
        2.0.0
      </li>
      <li>
        2013-11-23 19:38:28 +0900
      </li>
    </ul>
  </body>
</html>

 

 

ポイント

instance_evalに渡すブロックの中にさらにブロックが存在
method_missingを利用(第一引数にメソッド名が渡される)


 

実装

require'strscan'

class XML
  def initialize(out)
    @out = out
  end
  def comment(text)
    @out << "<!-- #{text} -->"
    nil
  end
  def tag(tagname, attribute={})
    @out << "<#{tagname}"
    attribute.each do |key, val|
      @out << " #{key}='#{val}'"
    end
    @out << ">"
    if block_given?
      content = yield
      @out << content if content
    end
    @out << "</#{tagname}>"
    nil
  end

  alias method_missing tag
  def self.generate(out, &b)
    XML.new(out).instance_eval(&b)
  end
  def self.show(text)
    result = ""
    nest_level = 0
    s = StringScanner.new(text)
    while !s.eos?
      if s.scan(/<[^>]+>/)
        if s[0].index("</")
          nest_level -= 1
          result << "\t"*nest_level + s[0] + "\n"
        elsif s[0].index("/>") || s[0].index("<!--") 
          result << "\t"*nest_level + s[0] + "\n"
        else  #開始タグ
          result << "\t"*nest_level + s[0] + "\n"
          nest_level += 1
        end
      else s.scan(/[^<]+/)
        result << "\t"*nest_level + s[0] + "\n"
      end
    end
    print result
  end
end

 


ふむふむ。

 

【Ruby】【メタプログラミング】require,load,クラス定義,プログラム終了のトレーシング

 
■topic summary
study about tracing require, load, define-class, exit-program

 

ポイント

・at_exitはブロックを登録(再定義しない)
・loadメソッドはファイル名を指定(※1)


※1

(ここでは簡単な例としてライブラリをロードしてみたが、)フルパスを指定して リソースファイルをロードしたりするのがloadのまっとうな使いかたである。

http://www.loveruby.net/ja/rhg/book/load.html

 

module ClassTrace
  alias original_require require
  alias original_load load
  T = []
  def require(file)
    T << ["require: ", file, caller[0]]
    original_require(file)
  end
  def load(file)
    T << ["load: ", file, caller[0]]
    original_load(file)
  end
  def Object.inherited(c)
    T << ["inherited: ", c, caller[0]]
  end
  at_exit{T.each {|t| p t}}
end
include ClassTrace
require "bigdecimal"
load "test.rb"
class Test
end

 
 
■実行結果

["require: ", "bigdecimal", "C:/Users/HOGE_ADMIN/Desktop/test/arrays.rb:22:in `<main>'"]
["require: ", "rubygems/requirement", "C:/Ruby200/lib/ruby/2.0.0/rubygems/dependency.rb:4:in `<top (required)>'"]
["inherited: ", Gem::Dependency, "C:/Ruby200/lib/ruby/2.0.0/rubygems/dependency.rb:6:in `<top (required)>'"]
["inherited: ", Gem::PathSupport, "C:/Ruby200/lib/ruby/2.0.0/rubygems/path_support.rb:6:in `<top (required)>'"]
["inherited: ", BigDecimal, "C:/Ruby200/lib/ruby/2.0.0/rubygems/core_ext/kernel_require.rb:45:in `require'"]
["load: ", "test.rb", "C:/Users/HOGE_ADMIN/Desktop/test/arrays.rb:23:in `<main>'"]
["require: ", "benchmark", "test.rb:1:in `<top (required)>'"]
["inherited: ", Benchmark::Job, "C:/Ruby200/lib/ruby/2.0.0/benchmark.rb:306:in `<module:Benchmark>'"]
["inherited: ", Benchmark::Report, "C:/Ruby200/lib/ruby/2.0.0/benchmark.rb:344:in `<module:Benchmark>'"]
["inherited: ", Benchmark::Tms, "C:/Ruby200/lib/ruby/2.0.0/benchmark.rb:380:in `<module:Benchmark>'"]
["inherited: ", Test, "C:/Users/HOGE_ADMIN/Desktop/test/arrays.rb:24:in `<main>'"]


ふむふむ。

 

【Ruby】【メタプログラミング】チェイン(フック)& 特異メソッド を使ったメソッド呼び出しのトレーシング

 
■topic summary
how to trace method calling with using eigenclass.

 
委譲とmethod_missingを使ったトレーシングは前にやったけど、
今度はチェイン(フック)を使ったトレーシングを実装します。

 

ポイント

特異メソッドを使ったトレーシング

・式展開が2レベルに渡る(特異メソッド定義時、実行時)
インスタンス変数は不要になった時点で削除(@_traced)
・evalで定義するメソッドが、
 ⇒引数にブロックを持つ場合・・・evalの引数は文字列
 ⇒ブロックを持たない場合・・・evalの引数はブロックでOK
・特異クラス生成イディオム(1個め、2個めのselfはそれぞれ別オブジェクトを参照)


 

コード

class Object
  def trace!(*methods)
    @_traced = @_traced || []
    methods = public_methods(false) if methods.size == 0
    methods.map!{|m| m.intern}
    methods -= @_traced
    return if methods.empty?
    @_traced |= methods
    eigenclass = class << self; self; end
    STDOUT << "Tracing #{methods.join(",")} on #{object_id}\n"
    methods.each do |m|
      eigenclass.class_eval %Q{
        def #{m}(*args, &b)
          STDOUT << "Entering #{m}(\#{args.join(",")})\n"
          result = super || "nil"
          STDOUT << "Exiting #{m} with \#{result}\n"
          result
        end
      }
    end
  end
  def untrace!(*methods)
    if methods.size == 0
      methods = @_traced
    else
      methods &= @_traced
    end
    STDOUT << "Untracing #{methods.join(",")} on #{object_id}\n"
    methods.map!{|m| m.intern}
    eigenclass = class << self; self; end
    eigenclass.class_eval do
      methods.each do |m|
        remove_method m
      end
    end
    @_traced -= methods
  end
end
b = [1,2,3]
b.trace!(:index)
b.index("a")
b.untrace!(:index)
b.index("a")

 
■実行結果

Tracing index on 22638228
Entering index(a)
Exiting index with nil
Untracing index on 22638228


 
ふむふむ。

【Ruby】【メタプログラミング】class_eval と instance_eval

 
■topic summary
difference between "class_eval" and "instance_eval"

 

class_eval と instance_eval

class String
  class_eval("p self")
  instance_eval("p self")
end

 
■実行結果

String
String

コンテキストは同じ。

 

生成されるメソッドタイプが違う

class String
  class_eval("def hoge1;end")
  instance_eval("def hoge2;end")
  p instance_methods.index(:hoge1)
  p public_methods.index(:hoge2)
end

 
■実行結果

110
1

class_evalがインスタンスメソッドで
instance_evalがクラスメソッド


 
前に、attrメソッドを自作しようとして四苦八苦している過程でclass_evalを発見したけど、この実装で大きくずれてないみたい。
 
・moduleにattrメソッドを実装
・class_evalの実行によるインスタンスメソッド作成
・文字列でのメソッド定義
 (自分はヒアドキュメントを使っていたけど、
   オライリーでは空文字列 "" にadd(<<)していく形で実装してる)


 
良かった良かった。