JavaScriptを雰囲気で使っていたので勉強し直したら知らなかったこと
小ネタ。chromeで動作確認
何となく使っていたので改めて勉強してみたら知らなかったことなどのまとめ
this
関数の呼び出し元のオブジェクトがthis
になる。ただし、呼び出し元のオブジェクトの指定がない場合は、windowオブジェクトがセットされる。ただし、厳格モードuse strict
の場合はundefinedがセットされる
const foo = {
bar: function() {
// 呼び出し元はfoo.bar()
console.log(this); // foo object
const baz = function() {
// 呼び出し元はbaz()でオブジェクトの指定がない
console.log(this); // window object
}
baz();
const qux = function() {
'use strict';
// 呼び出し元はqux()でオブジェクトの指定がないかつ厳格モード
console.log(this); // undefined
}
qux();
}
}
foo.bar();
また、classの場合はuse strict
の指定なしでも厳格モードになる。以下は引用
this に値が付けられずに静的メソッドまたはプロトタイプメソッドが呼ばれると、this の値はメソッド内で undefined になります。たとえ "use strict" ディレクティブがなくても同じふるまいになります。なぜなら、class 本体の中のコードは常に Strict モードで実行されるからです。
class Foo {
bar() {
// 呼び出し元はfoo.bar()
console.log(this); // Foo object
const baz = function() {
// 呼び出し元はbaz()でオブジェクトの指定がないかつクラス内なので厳格モード
console.log(this); // undefined
}
baz();
}
}
const foo = new Foo();
foo.bar();
call、apply、bindでthisを指定することができる
const foo = {
bar: function() {
const baz = function() {
// 呼び出し元はbaz.apply(this)。
// applyに指定しているthisの呼び出し元はfoo.bar()なのでfooとなる
console.log(this) // foo object
}
baz.apply(this)
const qux = function() {
// 呼び出し元はqux()で、quxにはbind(this)が指定されている。
// bindに指定されているthisの呼び出し元はfoo.bar()なのでfooとなる
console.log(this) // foo object
}.bind(this)
qux();
}
}
foo.bar();
また、アロー関数内のthisはアロー関数が定義されているスコープに基づく。以下は引用
アロー関数は、そのアロー関数が定義されているスコープに基づいて "this" を確立する
const foo = {
bar: function() {
const baz = () => {
// 呼び出し元はbaz()
// bazが宣言されているbar()の呼び出し元はfoo.bar()なのでfooとなる
console.log(this); // foo object
}
baz();
}
}
foo.bar();
シャローコピー
以下は引用の翻訳。引用にあるコピー操作ではシャローコピー(浅いコピー)となることがあり、コピー先を変更することでコピー元に影響を与えることがある
JavaScript では、標準的な組み込みのオブジェクトコピー操作 (スプレット構文, concat(), slice(), Array.from(), Object.assign(), Object.create()) はすべて、ディープコピーではなくシャローコピーを作成します。
以下の例では、nameは変わらないが、schoolは書きかわる
const user = {
name: "user",
school: { name: "school" }
}
const user2 = {...user}
user2.name = "user2"
user2.school.name = "school2"
console.log(user); // {name: "user", school: {name: "school2"} }
console.log(user2); // {name: "user2", school: {name: "shool2"} }
ディープコピー(深いコピー)を使用することで回避する。以下は引用
Javascript のオブジェクトのディープコピーを作成する一つの方法は、そのオブジェクトが シリアライズ 可能であれば JSON.stringify() でオブジェクトを JSON 文字列に変換し、 JSON.parse() で文字列から(完全に新しい) Javascript のオブジェクトに変換することです。
new演算子
クラスでなく、関数に対しても使うことができる。関数がnew演算子で呼ばれるとき、コンストラクタ関数という。コンストラクタ関数として呼び出すとオブジェクトが生成される
const Foo = function() {
this.foo = "bar"
return 1;
}
// 通常の関数として呼び出す
console.log(Foo()); // 1
// コンストラクタ関数として呼び出す
console.log(new Foo()); // Foo{foo: "bar"}オブジェクト
以下はnew演算子のドキュメント
構文の引用
new constructor[([arguments])]
引数 constructor
オブジェクトインスタンスの型を指定するクラスまたは関数です。
解説の引用
- 空のプレーンな JavaScript オブジェクトを生成します。
- 新しいオブジェクトにプロパティ (proto) を追加し、コンストラクター関数のプロトタイプオブジェクトに結びつけます。
- 新しく生成されたオブジェクトインスタンスを this コンテキストとして結びつけます。 (すなわち、コンストラクター関数内の this へのすべての参照は、最初のステップで作成されたオブジェクトを参照するようになります。)
- 関数がオブジェクトを返さない場合は this を返します。
コンストラクタ関数の戻り値として、オブジェクトを返す場合とそうでない場合で挙動が異なる
オブジェクトを返さない例
function Foo() {
this.bar = 1;
}
const foo = new Foo();
console.log(foo); // Foo { bar: 1 }
オブジェクトを返す例
function Foo() {
this.bar = 1;
return { baz: 1 };
}
const foo = new Foo();
console.log(foo); // { baz: 1 }
console.log(foo.bar) // undefined
プロトタイプベース
objectやarrayをconsole.logしたときに見かける[[Prototype]]
や__proto__
の正体。知らないと困るというケースがあるかどうかはわからないが、以下あたりを1度読んでおくと雰囲気がわかる
図で理解するJavaScriptのプロトタイプチェーン - Qiita
継承とプロトタイプチェーン - JavaScript | MDN
ただ、classから作ったオプジェクトもプロトタイプベースであることは変わらないので理解はしておくべき
変数や関数の巻き上げ(Hoisting)
以下はドキュメントの引用
変数や関数の宣言が物理的にコードの先頭に移動されることを示唆していますが、実際にはそうではありません。変数や関数の宣言はコンパイル時にメモリに格納されますが、コード内で入力された場所は変わりません
ただし
定義のみが巻き上げられ、初期化はそうでありません。変数が使用された後に定義や初期化された場合、値は undefined になります
関数の場合、宣言の前に読んでもエラーにはならない
foo();
function foo() {
console.log("foo");
}
変数の場合
foo(); // Uncaught TypeError: foo is not a function
var foo = () => console.log("foo");
引用のとおり定義のみが巻き上げられるので、実行時は以下のイメージとなり、変数としては存在するが、関数として呼ぼうとするとエラーになる
var foo;
foo(); // この時点でfoo は undefined
foo = () => console.log("foo")
その他
オブジェクトを返すアロー関数の書き方。以下は引用
オブジェクトリテラル式を返す場合は、式の周りに括弧が必要です。
// NG
const foo = () => {bar: "baz"}
// OK
const foo = () => ({bar: "baz"})
Discussion
ちょうどつまづいたり、うっかり忘れでミス起きやすい部分でいい復習になりました。