Javascriptのプログラミングで最初はなかなか理解しづらいのが、thisです。
thisの使い方をマスターすると、複雑な関数や、クラスの構築に非常に役立ちます。
ターミナルコマンドで、自分の居場所を認識するのと同じで、プログラミングでも、今自分が処理している
居場所を認識するのが重要で、それがこのthisの役割になります。
Javascript学習 : thisの概念
- Javascriptのthisサンプルコード
- bind() , call() , apply()によるthisの理解
- 他の言語のthis類似機能
Javascriptのthisサンプルコード
グローバルコンテキストでのthis
console.log(this);
// グローバルオブジェクト(通常はウィンドウオブジェクト)が表示されます
この場合のthisは、javascriptのグローバルである、windowの事を指しています。
そして、関数やオブジェクト内で書かれたthisは、それぞれの関数を指していると思って見てみてください。
オブジェクトリテラル内でのthis
const person = {
firstName: "John",
lastName: "Doe",
getFullName: function() {
return this.firstName + " " + this.lastName;
}
};
console.log(person.getFullName());
// "John Doe" と表示されます
この場合のthisは、
personオブジェクトです。
色々な書き方がありますが、thisを使うと、自分の兄弟データと連携できる事を意識してください。
イベントハンドラ内でのthis
document.getElementById("myButton").addEventListener("click", function() {
console.log(this); // クリックされたボタンのDOM要素が表示されます
});
イベントとオブジェクトのどちらも、関数内でthisが使われていますが、それらが無名関数になっているところに注目してください。
その親要素がthisになります。
コンストラクタ関数内でのthis
function Car(make, model) {
this.make = make;
this.model = model;
}
const myCar = new Car("Toyota", "Corolla");
console.log(myCar.make); // "Toyota" と表示されます
console.log(myCar.model); // "Corolla" と表示されます
インスタンス化された関数内のthisは、どの値を参照しているかをちゃんと認識しておかなければいけないので、書き方によってthisの値が変わることを理解しましょう。
prototypeの理解
function Foo(){
this.sample = 'name-1'
}
Foo.sample = 'name-2'
Foo.prototype.sample = 'name-3'
console.log(new Foo().sample) // "name-1" と表示されます
console.log(Foo.sample) // "name-2" と表示されます
console.log(Foo.prototype.sample) // "name-3" と表示されます
prototpyeは、Javascript特有の機能です。
インスタンス化された関数の子階層をprototypeというプロパティ(この言い方がややこしい)を用いて設定することができます。
上記のサンプルコードでは、それぞれsampleという属性にアタッチしていますが、Foo関数の中のthis.sampleをコメントアウトすると、次のようになります。
function Foo(){
// this.sample = 'name-1'
}
Foo.sample = 'name-2'
Foo.prototype.sample = 'name-3'
console.log(new Foo().sample) // "name-3" と表示されます
console.log(Foo.sample) // "name-2" と表示されます
console.log(Foo.prototype.sample) // "name-3" と表示されます
これは、prototypeで設置されたsample属性を、Foo()をnewでインスタンス化した時に、内部処理のthis.sampleで上書きしているため、コメントアウトすると、最初に設置されていたsample値(prototype)が表示されるようになります。
ちなみに、Foo.sampleと書かれた書き方の方がシンプルで使いやすいと思いますが、これは、Fooは関数ですが、オブジェクトとしてその子階層を設置している書き方になります。
インスタンス化をすると、その属性値には制約が無くなりますが、オブジェクトの場合は、色々な予約属性が言語でセットされているので、トラブルを避けるために、インスタンスを使うほうがオススメです。
(よく使われるname属性をセットしてみると、インスタンス化されていないと、関数名がそのまま表示されてしまいます。)
この場合、インスタンス化とは切り離されてしまうので、同じ属性を設置しても、値は切り分けられてしまいます。
prototypeを使うと、インスタンス用属性で、関数内でthisを使うと、クラスのprivateと考えていいです。
そして、単なる属性は、staticの扱いになるという風に理解しましょう。
(慣れないと難しいかも・・・)
newインスタンスではなく、Objectとして作ってみる
// オブジェクトリテラルで新しいオブジェクトを作成
const personPrototype = {
greeting: function() {
return "Hello, I'm " + this.name + ".";
}
};
// 新しいオブジェクトを作成し、プロトタイプを設定
const person1 = Object.create(personPrototype);
person1.name = "John";
console.log(person1.greeting()); // "Hello, I'm John."
const person2 = Object.create(personPrototype);
person2.name = "Alice";
console.log(person2.greeting()); // "Hello, I'm Alice."
thisがどの親を指しているのかを理解しておきましょう。
bind() , call() , apply()によるthisの理解
thisを自由にコントロールできる、3つの機能を覚えましょう。
bind()
bind()は、インスタンス(関数を格納した変数のような無名関数の事)が実行される時、thisが参照する親要素を変更させる事ができる機能です。
まず最初に、簡単な関数構造を作ってみます。
const Foo = {
value : 'カレー',
lunch : function(){
return `今日のランチは、${this.value}です。`
}
}
上記のlunch関数を実行してみると、
console.log(Foo.lunch())
> 今日のランチは、カレーです。
この時のthisは、Fooを参照していることが分かります。
次に、このlunch関数のみをインスタンスとして、変数に保存して、実行してみると。
const Foo2 = Foo.lunch
console.log(Foo2())
> 今日のランチは、undefinedです。
これは、Foo2がインスタンス化されて(切りぬかれて)、親要素が無くなったために、値を参照できなくなってしまいました。
ちなみに、この時のthisは、windowを参照しています。
この参照を強制的に行うのが、bind()です。
const Foo3 = Foo.lunch.bind(Foo)
console.log(Foo3())
> 今日のランチは、カレーです。
そして、次のようにすると、また別の結果が得られます。
const Var = {
value : 'お寿司'
}
const Foo4 = Foo.lunch.bind(Var)
console.log(Foo4())
> 今日のランチは、お寿司です。
bindの使い方には、もう一つ重要なポイントがあり、引数を指定できるという機能があります。
const Foo = {
value : 'カレー',
lunch : function(add_menu){
add_menu = add_menu || 'サラダ'
return `今日のランチは、${this.value}と${add_menu}です。`
}
}
const Var = {
value : 'お寿司'
}
上記を普通に実行すると、
console.log(Foo.lunch())
> 今日のランチは、カレーとサラダです。
何も指定がない時は、"サラダ"が返りますが、指定すればその値が受け渡されます。
const Foo4 = Foo.lunch.bind(Foo , "ラッキョ")
console.log(Foo4())
> 今日のランチは、カレーとラッキョです。
これをbindを使う場合は、次のようになります。
const Foo4 = Foo.lunch.bind(Foo , "ラッキョ")
console.log(Foo4())
> 今日のランチは、カレーとサラダです。
const Foo5 = Foo.lunch.bind(Var , "焼肉")
console.log(Foo5())
> 今日のランチは、お寿司と焼肉です。
.bind(親となる関数または変数 , 受け渡す値 ...)
この構文で書くことで、値を受け渡すことができます。
ちなみに、受け渡し値は、,(カンマ)で繋げることで、何個でも書くことができます。関数の仕様に沿って書くことが重要です。
call()
call()は、bindと使い方は似ていますが、実行の特性が少し変わります。
const Foo = {
value : 'カレー',
lunch : function(add_menu){
add_menu = add_menu || 'サラダ'
return `今日のランチは、${this.value}と${add_menu}です。`
}
}
const Var = {
value : 'お寿司'
}
console.log(Foo.lunch("ラッキョ"))
> 今日のランチは、カレーとラッキョです。
const Foo7 = Foo.lunch.call(Var,"焼肉")
console.log(Foo7)
> 今日のランチは、お寿司と焼肉です。
callは、指定した時点で実行形式になるため、インスタンスを作った場合は、インスタンスに値が格納される形になります。
()カッコ、付く、付かない問題ですね。
apply()
apply()は、callとほぼほぼ同じですが、受け渡しの値を、配列にするというお作法になります。
console.log(Foo.lunch.apply(Var,["焼肉"]))
> 今日のランチは、お寿司と焼肉です。
bind()と、call()と、apply()、それぞれが問題なく使いこなそうと思ったら、やっぱりthisをしっかりと理解しないといけないという事です。
他の言語のthis類似機能
PHPの、$thisやself::
self::
class MyClass {
public static $staticProperty = "Hello, I'm a static property.";
public static function staticMethod() {
echo "This is a static method in " . self::$staticProperty . "\n";
}
}
MyClass::staticMethod(); // 出力: This is a static method in Hello, I'm a static property.
$this :
class MyClass {
public $instanceProperty = "Hello, I'm an instance property.";
public function instanceMethod() {
echo "This is an instance method in " . $this->instanceProperty . "\n";
}
}
$obj = new MyClass();
$obj->instanceMethod(); // 出力: This is an instance method in Hello, I'm an instance property.
Pythonのself
class MyClass:
def my_method(self):
print("This is a method in MyClass.")
obj = MyClass()
obj.my_method() # メソッド内でselfを使用してインスタンスにアクセス
Javaのthis
public class MyClass {
public void myMethod() {
System.out.println("This is a method in MyClass.");
}
public static void main(String[] args) {
MyClass obj = new MyClass();
obj.myMethod(); // メソッド内でthisを使用してインスタンスにアクセス
}
}
C#のthis
public class MyClass
{
public void MyMethod()
{
Console.WriteLine("This is a method in MyClass.");
}
public static void Main(string[] args)
{
MyClass obj = new MyClass();
obj.MyMethod(); // メソッド内でthisを使用してインスタンスにアクセス
}
}
Rubyのself
class MyClass
def my_method
puts "This is a method in MyClass."
puts "The value of self: #{self}" # selfを使用してインスタンス自体を参照
end
end
obj = MyClass.new
obj.my_method
0 件のコメント:
コメントを投稿