📝

JavaScript 学習実施メモ 2

4 min read

初学者のメモです。間違い等ありましたらご指摘コメント待ってます。

https://zenn.dev/yuki_kanayama/articles/37022f0373e38d

前回の続きです。基本的には10こずつ記載していきます。

変数の参照とコピー

プリミティブ型の場合

1. let a = "hello";
2. let b = a; // bにaを代入
3. b = "bye"; // bへ再代入

上記の場合変数aを宣言時に"hello"とaがメモリ空間保存される。
その後にbへaを代入した際は別に”hello"が作成されメモリ空間に保存される。
なので、bに再代入してもaの値が変わることはない。コピーを実施しても各変数は独立して存在している。

オブジェクトの場合

1. let a = {
2.   hoge: "vhoge",
3.   props: "vprops",
4. }
5.  
6. let b = a;
7.  
8. b.props = "changeProps"

プリミティブと違い {   hoge: "vhoge",   props: "vprops", } への参照が格納されている。なのでb.propsでpropsの値を変更したときはa.propsも"changeProps"への参照になる。

分割代入

1. const a = {
2.   props :  1,
3.   hoge : "hoge",
4. };

というオブジェクトがあった場合に、

let  {hoge} = a;

と宣言することで分割代入をすることができる。これはaのオブジェクトからhogeの名前のプロパティをコピーしてhogeという名前で変数を作成して値は もともとの"hoge"が入る。このときhogeを再代入した場合は、変数hogeは値が変わるがオブジェクトは変わらない。

ちなみに多階層の場合、

1. const c = {
2.     prop1: {
3.         prop2: 0
4.     }
5. }
6.  
7. let { prop1 } = c;
8.  
9. prop1.prop2 = 1;

参照の比較

プリミティブ型は値で比較し、オブジェクトでは参照を比較する。

1. const a = {
2.     prop: 0
3. }
4.  
5. const b = {
6.     prop: 0
7. }
8.  
9. console.log(a === b);

この場合、お互い新しく宣言しているため同じ記述内容でもメモリの参照先は異なるためコンソールにはfalseが表示される。ちなみに、

1. const c = a;
2.  
3. console.log(a === c);

とすると、cにはaと同じオブジェクトへの参照がコピーされるためtrueが表示される。

関数式

1. function fn(a, b) {
2.   console.log(a, b);
3. }

関数は同じ名前のものも宣言できるが、そうした場合は最後に宣言されたものが実行される。これを防ぐには、関数式を使う。

1. const fn = (a, b) => {
2.   console.log(a, b);
3. }

変数を宣言して代入することで再宣言できないようにすれば同じ名前だとエラーになる。

arguments

1. function fn() {
2.     const a = arguments[0];
3.     const b = arguments[1];
4.     console.log(arguments);
5.     console.log(a, b);
6. }
7.  
8. fn(1, 10);

argumentsとは、関数コンテキストで引数として扱うことができる。

上記の場合、関数宣言時には引数を設定していないが関数使用時に引数がある場合その引数は関数コンテキスト内でargumentsとして扱われているので、表示結果は、

Arguments(2) [1, 10, callee: ƒ, Symbol(Symbol.iterator): ƒ]
1 10

が表示される。

ただ、配列になってるようでなっていないので扱いが難しく、ES6(2017)でレストパラメータができたので現在はあまり使われていない。

コールバック関数

他の関数に引数として渡される関数のこと。

1. function hello(name) {
2.   console.log('hello' + name);
3. }
4.  
5. function fn(cb) {
6.   cb('Tom');
7. }
8.  
9. fn(hello);

関数fnにコールバック関数として関数helloが渡されている。関数fnがまず実行され、そこで cb = hello となって

hello('Tom);

として実行される。

this

呼び出し元のオブジェクトへの参照を保持するキーワード。

ただし、関数として実行した場合は、グローバルオブジェクトへの参照を保持する(例外あり)

1. const person = {
2.     name: 'Tom',
3.     hello: function() {
4.         console.log('Hello' + this.name) 
5.     }
6. }
7.  
8. person.hello();

このとき this には 変数presonの参照先が入っている。よって、preson.name となっている。このときプロパティhelloの実行コンテキストからみてpersonはレキシカルスコープなのでそのまま実行できる。

1. window.name = 'John';
2.  
3. const person = {
4.     name: 'Tom',
5.     hello: function() {
6.         console.log('Hello ' + this.name);
7.     }
8. }
9. person.hello();
10.  
11. function fn(cb) {
12.     cb();
13. }
14.  
15. fn(person.hello);

bind

thisや引数を固定して新しい関数作成する。

bindで this や引数を固定した場合は、参照を複製するのではなく新しい関数として複製する。

ちなみに、引数を指定する場合、

1. const b = a.bind(null, 'Tim');

のように記述する。bind の第一引数は this の値の固定で、第二引数が関数aの引数の値の固定である。

applyとcall

bindと同じく、this と引数を固定する。(この場合thisを使用していないので第2引数に固定したい値を指定する)

ただ bind と違い、

1. function a(name) {
2.     console.log('hello ' + name);
3. }
4.  
5. const b = a.bind(null, 'Tim');
6.  
7. b(); // 実行される
8.  
9. a.bind(null, 'Tim'); // 実行されない
10.  
11. a.apply(null, ['Tim']); // 実行される
12.  
13. a.call(null, 'Tim'); // 実行される

上記のように、bind は実行する必要があるが、apply, call は宣言時にそのまま実行される。

apply と call の違いは、apply は引数の固定は配列で行う。call に関しては同時実行される以外は bind と同じ。

アロー関数

無名関数の簡略記法。ただし、機能が全て同じではなく、this,argumetns,new などの機能は使うことができない。
アロー関数はthisを持たないので、下記のように、

1. window.name = 'John';
2.  
3. const person = {
4.     name: 'Tom',
5.     hello: () => {
6.         console.log('Hello ' + this.name);
7.     }
8. }
9. person.hello(); // 結果は、「Hello Johe」が表示される

記述すると、hello関数の内部のthisはpersonではない。

スコープチェーンがはらたき、レキシカルスコープのwindowがthisにとなる。

アロー関数のthisはレキシカルスコープを参照する。