yusuke
Press →key to advance.
Zoom in/out: Ctrl or Command+ +/-
1章が30分で終わればいいとこの予定
是非、手元のjavascript engine(browser)でtrying!
※ alertか、debugモードでの実行を推奨
関数は、ある機能を呼び出せる形式で記述したもの
var add = function(a, b){
return a + b;
};
add(1, 2); // ==> 3
ここでの変数a, bは引数と呼ばれ、関数の入力"値"として利用出来る(input)
return を使うことで、関数の結果("値")を出力(返す)できる(output)
高階関数とは、関数を引数に取る関数
var add = function(a, b){
return a + b;
};
var sub = function(a, b){
return a - b;
};
var calc = function(a, b, c, math){
var x = math(a, b);
var y = math(x, c);
return y;
};
calc(1, 2, 3, add); // => 6
calc(1, 2, 3, sub); // => -4
関数calcは引数a,b,cに数値を受け取り、引数mathに関数を受け取り処理をする関数
関数を返す関数と、高階関数の組み合わせ
var calc_gen = function(a, b) {
return function(math){
return math(a, b);
};
};
var add = function(a, b){
return a + b;
};
var mul = function(a, b){
return a * b;
};
var calc = calc_gen(4, 5);
calc(add); // => 9
calc(mul); // => 20
値を固定しておいて、関数を"適用"して結果を変える(デザパタによくあるアレです)
複数の引数を取る関数のうち、引数を固定化(bind)すること
var dump = function(a, b, c){
return 'a = ' + a + ',';msg += 'b = ' + b + ','; msg += 'c = ' + c + ',';
};
var curryOne = function(func, arg1){
return function(arg2, arg3){
return func(arg1, arg2, arg3);
};
};
var curryTwo = function(func, arg1, arg2){
return function(arg3){
return func(arg1, arg2, arg3);
};
};
var curryThree = function(arg1, arg2, arg3){
return function(func){
return func(arg1, arg2, arg3);
};
};
のような定義があるとき
下記のような実行結果となる
alert(dump(1, 2, 3)); // a = 1,b = 2,c = 3 var one = curryOne(dump, 1); alert(one(2, 3)); // a = 1,b = 2,c = 3 alert(one(3, 4)); // a = 1,b = 3,c = 4 var two = curryTwo(dump, 7, 8); alert(two(9)); // a = 7,b = 8,c = 9 alert(two(0)); // a = 7,b = 8,c = 0 var three = curryThree(5, 6, 7); alert(three(dump)); // a = 5,b = 6,c = 7
javascriptは関数クロージャによってカリー化が実現出来ている
(クロージャ内に変数が閉じられているので、関数実行時に値の適用が行える)
状態を持たない関数は、常に同じ値を返却する
var add = function(a, b){
return a + b;
};
alert(add(1, 2)); // => 3
for(var i = 0; i < 100000; ++i){
alert(add(1, 2)); // => 3
}
これは、1000万回実行しても1億回実行しても add(1, 2) の結果はおなじになる。
関数addはステートレスな関数である
memoizeとは一般的な cache と同じこと。同じ引数である限り、キャシュの値を参照する
ここでのプロトタイプとは、プロトタイプベースのこと。
継承パターンの一つ
数珠つなぎのようび呼び出される様から、プロトタイプチェーンと呼ぶことも
(中略)
javascriptにおいて、functionはクラスにおけるコンストラクタになる
var MyClass = function (){
// constructor
};
var a = new MyClass();
javascriptはクラスベースのような振る舞いをもつ
そのため、新しいインスタンスを生成する場合は new を使う
メソッドは、prototypeに追加する
var MyClass = function (){];
MyClass.prototype.methodA = function (){
alert('hello world');
};
var a = new MyClass();
a.methodA();
なお、methodAはインスタンスメソッドなので、下記のような呼出は行えない
MyClass.methodA(); // error
prototypeを利用するか、コンストラクタでthisに設定する
var MyClass = function(){
this.propertyA = 'this is propertyA';
};
MyClass.prototype.propertyB = 'this is propertyB';
var a = new MyClass();
alert(a.propertyA); // ==> this is propertyA
alert(a.propertyB); // ==> this is propertyB
prototypeに親クラスのインスタンスを設定する
var ParentClass = function (){
this.name = 'i am ParentClass';
};
ParentClass.prototype.methodA = function (){
return 'i am ParentClass:methodA';
};
var MyClass = function (){};
MyClass.prototype = new ParentClass();
MyClass.prototype.methodB = function (){
return 'i am MyClass:methodB';
};
var a = new MyClass();
alert(a.name); // ==> i am ParentClass
alert(a.methodA()); // ==> i am ParentClass:methodA
alert(a.methodB()); // ==> i am MyClass:methodB
overrideは同じpropertyの上書きで行える
prototypeを指定する
var ParentClass = function (){
this.name = 'i am ParentClass';
};
ParentClass.prototype.methodA = function (){
return 'i am ParentClass:methodA';
};
var MyClass = function (){};
MyClass.prototype = ParentClass.prototype; // <== ここ
MyClass.prototype.methodB = function (){
return 'i am MyClass:methodB';
};
var a = new MyClass();
alert(a.name); // ==> i am ParentClass
alert(a.methodA()); // ==> i am ParentClass:methodA
alert(a.methodB()); // ==> i am MyClass:methodB
overrideは同じくpropertyの上書きで行える
prototypeのインスタンスが無くなった場合の動きが違う
var Parent = function(){
this.name = 'hello world';
}
var ClassA = function (){};
ClassA.prototype = new Parent;
var ClassB = function (){};
ClassB.prototype = Parent.prototype;
var a = new ClassA();
var b = new ClassB();
alert(a.name); // ==> hello world
alert(b.name); // ==> hello world
ClassA.prototype = null;
ClassB.prototype = null;
alert(a.name); // ==> hello world
alert(b.name); // ==> undefined
基本的にこんなことは無いだろうが、このような動きをする
同じprototypeをもつインスタンスで、プロトタイプチェーンの動きを確認
var MyClass = function (){};
MyClass.prototype.methodA = function (){ return 'methodA' };
var a = new MyClass();
var b = new MyClass();
alert(a.methodA()); // ==> methodA
alert(b.methodA()); // ==> methodA
MyClass.prototype.methodB = function (){ return 'methodB' };
alert(a.methodB()); // ==> methodB
alert(b.methodB()); // ==> methodB
alert(a.propertyC); // ==> undefined
MyClass.prototype.propertyC = 'propertyC';
alert(a.propertyC); // ==> propertyC
生成されているインスタンスにも影響を与える
この機能を使うことで、ベースクラスに拡張を行うことができる
Array.prototype.hoge = function (){
return 'hoge: ' + this;
};
String.prototype.foo = function (){
return 'foo: ' + this;
};
Boolean.prototype.bar = function (){
return 'bar: ' + this;
};
alert([1, 2, 3].hoge()); // ==> hoge: 1,2,3
alert('hello world'.foo()); // ==> foo: hello world
alert(false.bar()); // ==> bar: false
Object.prototypeに追加すると、すべてのオブジェクトに影響がある(=> オブジェクト汚染)
親コンストラクタは、call/apply(this) で行う
var Root = function(arg1, arg2){
this.value1 = arg1;
this.value2 = arg2;
};
var Foo = function(arg1){
//Root(arg1, 'bar'); // <- この呼出は this の参照が不定になるので、行うことができない
Root.apply(this, [arg1, 'bar']);
};
Foo.prototype = Root.prototype;
var foo = new Foo('hoge');
alert(foo.value1); // ==> hoge
alert(foo.value2); // ==> bar
Root.apply における Function.prototype.apply や Function.prototype.call は、
次回にて説明。また、 this の参照についても次回を予定
特異メソッドとは、インスタンス固有のメソッドのこと
var Foo = function (){};
Foo.prototype.methodA = function (){ return 'instance method' };
var foo = new Foo();
foo.methodB = function (){ return 'instance specific method' };
alert(foo.methodA()); // ==> instance method
alert(foo.methodB()); // ==> instance specific method
var foo2 = new Foo();
alert(foo2.methodA()); // ==> instance method
alert(foo2.methodB()); // ==> TypeError: foo2.methodB is not a function
動的にメソッド/プロパティは追加できるが、他のインスタンへは影響を及ぼさない
特異メソッドを利用することで、あたかもクラスから生成されたインスタンスのように振る舞える
var Foo = function (){};
Foo.prototype.valueA = function (){ return 'value A' };
Foo.prototype.valueB = function (){ return 'value B' };
var foo = new Foo();
var bar = {};
bar.valueA = function (){ return 'bar value A' };
bar.valueB = function (){ return 'bar value B' };
alert(foo.valueA()); // => value A
alert(foo.valueB()); // => value B
alert(bar.valueA()); // => bar value A
alert(bar.valueB()); // => bar value B
あたかも同じような振る舞いを持つことができるので、proxy パターンや delegate パターン等で活躍する
これぞインスタンスベースの真骨頂
var clone = function (base){
var f = function (){};
f.prototype = base;
return new f();
};
var Animal = clone({});
Animal.say = function(){ alert('...') };
var Dog = clone(Animal);
Dog.say = function(){ alert('bow bow') };
var Bird = clone(Animal);
Bird.say = function(){ alert('tweet') };
var Penguin = clone(Bird);
var Duck = clone(Bird);
Duck.say = function (){ alert('quack!!') };
Animal.say(); // => ...
Dog.say(); // => bow bow
Bird.say(); // => tweet
Penguin.say(); // => tweet
Duck.say(); // => quack!!
to be continued...