【もりたけんじ問題集】練習問題１～２１【随時追加】

# 前書き


https://gist.github.com/kenmori/1961ce0140dc3307a0e641c8dde6701d

JavaScriptの問題集がないか探していたところ、こちらを見つけました。
[もりたけんじ](https://profile.yoshimoto.co.jp/talent/detail?id=3871)さんが作成されたようです。

ざっと見てみたところかなり勉強になりそうなので、こちらに掲載されている問題を解きつつ、私及び初心者向けにさらに深く解説を載せていこうと思います。※許可を得ています。
解いたら随時追加していきます。


# 身についたこと

大体上から順番

+ `Object.assign()`
+ `slice()`
+ `forEach()`
+ `Array.isArray()`
+ `typeof`演算子
+ `void`演算子
+ `for...in` と `hasOwnProperty()`
+ `join()`
+ `delete`
+ `sort()`
+ ジェネレータ
+ スプレッド構文
+ `splice()`
+ `splice()`, `slice()`
+ `join()`, `split()`
+ `Object.create()`
+ `prototype`

# 練習問題１

## 問題

```
const a = { a: 'a' }とconst b = { b: 'b' } をマージしたc を出力してください e.g{ a:'a', b:'b' }
```

## 私の解答

```js
const a = {a: 'a'};
const b = {b: 'b'};
const c = {...a, ...b};

console.log(c);
// --> { a: 'a', b: 'b' }
```
正解。
[スプレッド演算子](https://developer.mozilla.org/ja/docs/Web/JavaScript/Reference/Operators/Spread_syntax)を使用した方法。

> 配列にしろオブジェクトにしろスプレッド構文はshallow copyなので、ネストしている場合は注意が必要です。

引用元⇩

https://qiita.com/akisx/items/682a4283c13fe336c547

## 別解

```js
const c = Object.assign({}, a, b);
```

[Object.assign()](https://developer.mozilla.org/ja/docs/Web/JavaScript/Reference/Global_Objects/Object/assign)メソッドを使用した方法。
こちらも`Shallow Copy`である点に注意が必要。
詳しくは⇩

https://qiita.com/SE-studying-now/items/ecdbc0317ba1806aed61

### 実験してみた
```js
const originObj = {a:1, b:{a:2}};
const assignObj = Object.assign({}, originObj);
console.log(assignObj);
// --> { a: 1, b: { a: 2 } }

originObj.b.a = 3;
console.log(assignObj);
// --> { a: 1, b: { a: 3 } }

originObj.a = 4;
console.log(assignObj);
// --> { a: 1, b: { a: 3 } }

assignObj.b.a = 5;
console.log(originObj);
// --> { a: 4, b: { a: 5 } }
console.log(assignObj);
// --> { a: 1, b: { a: 5 } }

assignObj.a = 6;
console.log(originObj);
// --> { a: 4, b: { a: 5 } }
console.log(assignObj);
// --> { a: 6, b: { a: 5 } }
```



# 練習問題２

## 問題
```
const arry = ['aa','bb','cc','dd','ee','ff','gg'];
のdd,ee,ffを新たな配列として返してください
```

## 私の解答
```js
const arry2 = ['aa', 'bb', 'cc', 'dd', 'ee', 'ff', 'gg'];
const ansArry2 = arry2.slice(3,3);

console.log(ansArry2);
// --> []
```
間違い。
恐らく[`splice`メソッド](https://developer.mozilla.org/ja/docs/Web/JavaScript/Reference/Global_Objects/Array/splice)の、
```js
let 切り取られた配列 = 元の配列.splice( 開始位置, 長さ, [, 要素追加1, 要素追加2, ... ] );
// []内は省略可
```
と混同していた。
`splice`メソッドは`slice`メソッドと違い、元の配列に対して影響を及ぼす。
## 模範解答
```js
const newArry = arry.slice(-4,-1);
//or
const newArry = arry.slice(3,-1);
```

[`slice`メソッド](https://developer.mozilla.org/ja/docs/Web/JavaScript/Reference/Global_Objects/Array/slice)は、
```js
let 切り取られた配列 = 元の配列.slice( [ 開始位置, 終了位置 ] );
// []内は省略可
```
と書く。
負の数を引数に持たせると、絶対値が後ろから数えて何番目か(0スタート)を表す。

参考：[独習JavaScript](https://www.amazon.co.jp/%E7%8B%AC%E7%BF%92JavaScript-%E6%96%B0%E7%89%88-CodeMafia-%E5%A4%96%E6%9D%91%E5%B0%86%E5%A4%A7-ebook/dp/B09HC854HS/ref=sr_1_1?adgrpid=54017031740&gclid=Cj0KCQjwteOaBhDuARIsADBqRejE-p0AZdxSdZrgEM6TM4FmOQ_5y_d9OLIKe41T4D5g5nksVp8BFGYaAogzEALw_wcB&hvadid=611330011090&hvdev=c&hvlocphy=1009250&hvnetw=g&hvqmt=e&hvrand=3008905333624669452&hvtargid=kwd-335422086638&hydadcr=4074_13255661&jp-ad-ap=0&keywords=%E7%8B%AC%E7%BF%92javascript&qid=1666848424&qu=eyJxc2MiOiIxLjkwIiwicXNhIjoiMS4xNSIsInFzcCI6IjEuMTYifQ%3D%3D&sr=8-1)

# 練習問題３

## 問題
```
['a','b’] の要素をconsole出力してください e.g 'a'と'b'
```

## 私の解答
```js
const arry3 = ['a', 'b'];
arry3.forEach((element)=>{
    console.log(element);
})
// --> a
// --> b
```
正解。模範解答とほぼ一致したため、模範解答は省略
[`forEach`メソッド](https://developer.mozilla.org/ja/docs/Web/JavaScript/Reference/Global_Objects/Array/forEach)を用いた。

# 練習問題４

## 問題
```
['a', 'b']の各要素にindex値を足した文字列を出力してくださいe.g 'a0'と'b1'
```

## 私の解答
```js
const arry4 = ['a', 'b'];
arry4.forEach((element, index)=>{
    console.log(`${element}${index}`);
})
// --> a0
// --> b1
```
正解。
[`forEach`メソッド](https://developer.mozilla.org/ja/docs/Web/JavaScript/Reference/Global_Objects/Array/forEach)と[テンプレートリテラル](https://developer.mozilla.org/ja/docs/Web/JavaScript/Reference/Template_literals)を用いた。
模範解答はテンプレートリテラルでなく、
```js
console.log(element+index);
```
としていた。


# 練習問題５

## 問題
```
任意の変数名の[1,2]を定義して配列かどうかを評価してください e.g true
```
## 私の解答
```js
const arry5 = [1, 2];
console.log(arry5.isArray());
// --> エラー
```
不正解。分からなかった。

## 模範解答
```js
Array.isArray(arry5);//true
```
[`Array.isArray()`メソッド](https://developer.mozilla.org/ja/docs/Web/JavaScript/Reference/Global_Objects/Array/isArray)は、与えられた値が`Array`かどうかを返す。
`isArray()`が標準組み込みオブジェクト`Array`のメソッドであることを理解していなかった。

# 練習問題６

## 問題

```
//1
if (typeof x === 'undefined') {
 ???
}

//2
if(x === undefined){
 ???
}
変数xが定義されていない場合上の1、2は実行されますか?
```

## 私の解答

分からない

## 解答
```
//1は実行される
//2は実行されない(ReferenceError)
```

## 解説

[`typeof演算子`](https://developer.mozilla.org/ja/docs/Web/JavaScript/Reference/Operators/typeof)は、

> 未評価のオペランドの型を示す文字列を返します。

試しに
```js
if(typeof x === undefined){
    console.log('1');
}
console.log('2');
```
を実行してみると、`2`とだけ出力された。

//2 が実行されないのは、定義されていない変数について評価しようとしたことが原因でエラーとなったから。

# 練習問題７

## 問題

```
//1
let x;
if (x === void 0) {
}

//2
// 直前まで y は宣言されていない
if (y === void 0) {
}
1,2はそれぞれ実行されますか
```

## 私の解答

```
//1 は実行される
//2 は実行されない。
```

## 解説
正解。
[`void`演算子](https://developer.mozilla.org/ja/docs/Web/JavaScript/Reference/Operators/void)は必ずundefinedを返す。
JavaScriptにおける`void 0`が持つ意味に関する参考記事

https://techplay.jp/column/559


# 練習問題８

## 問題

```
const obj = {
 key: 'aa',
 key2: 'bb'
}
の中のkeyとvalueを自身のプロパティのみ全て出力しなさい
```

## 私の解答
分からなかった。

## 模範解答
```js
const obj = {
 key: 'aa',
 key2: 'bb'
}
for (key in obj){
 if(obj.hasOwnProperty(key)){
   console.log(key, obj[key])
  }
}
//key aa
//key2 bb

//or use Object.values, Object.keys
```

## 解説
[`for...in`文](https://developer.mozilla.org/ja/docs/Web/JavaScript/Reference/Statements/for...in)は、キーが文字列であるオブジェクトの列挙可能プロパティすべてに対して、継承された列挙可能プロパティも含めて反復処理を行う。
また、[`hasOwnProperty()`メソッド](https://developer.mozilla.org/ja/docs/Web/JavaScript/Reference/Global_Objects/Object/hasOwnProperty)は、オブジェクト自身が（継承されていない）指定されたプロパティを持っているかどうかを示す真偽値を返す。
似たものに`in`があるが、この違いは、
`in`はprototypeチェーンをさかのぼるのに対し、`hasOwnProperty()`はさかのぼらない。
参考↓

https://qiita.com/shuhei/items/dabf0ca097f05264baf9

# 練習問題９

## 問題

```
こちらの ['a', 'b', 'c'] 配列の中の全ての要素を結合し、1つの文字列として出力してください。
```

## 私の解答
```js
arry9.join('');
// --> abc
```
## 解説
正解。[`join()`メソッド](https://developer.mozilla.org/ja/docs/Web/JavaScript/Reference/Global_Objects/Array/join)は、配列（または[配列風オブジェクト](https://developer.mozilla.org/ja/docs/Web/JavaScript/Guide/Indexed_collections#%E9%85%8D%E5%88%97%E9%A2%A8%E3%82%AA%E3%83%96%E3%82%B8%E3%82%A7%E3%82%AF%E3%83%88%E3%82%92%E5%88%A9%E7%94%A8%E3%81%99%E3%82%8B)）の全要素を順に連結した文字列を新たに作成して返す。

配列風オブジェクトとは、例えば以下のようなもの。
```html
<body>
    <ul>
        <li class="nodelist">a</li>
        <li class="nodelist">b</li>
        <li class="nodelist">c</li>
    </ul>
    <script>
        console.log(document.getElementsByClassName("nodelist"));
        console.log(document.getElementsByTagName("li"));
    </script>
</body>
```
実行すると、
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2666100/89a3ef88-66e0-aef9-4787-17427f16a6ce.png)
のようになる。他にも[`arguments`オブジェクト](https://developer.mozilla.org/ja/docs/Web/JavaScript/Reference/Functions/arguments)などがある。

[`Array.from()`メソッド](https://developer.mozilla.org/ja/docs/Web/JavaScript/Reference/Global_Objects/Array/from)を用いることで、配列風オブジェクトからシャローコピーされた新しい配列を返すことができる。

# 練習問題１０

## 問題

```
こちら
x = 43
let y = 3
の2つの変数。deleteできるのはどちらですか？
```

## 私の解答

deleteがなんだか分からず、解けなかった。

## 模範解答

```js
x = 43;
delete x 
//true //暗黙に定義されたglobale変数なので

delete y
//false //削除できない
```

## 解説

[`delete`演算子](https://developer.mozilla.org/ja/docs/Web/JavaScript/Reference/Operators/delete)自体が初耳だったので、調べてみた。

> JavaScript の delete 演算子は、オブジェクトからプロパティを削除します。同じプロパティへの参照がそれ以上保持されていない場合は、自動的に解放されます。

また、

> `delete expression`
expression は下記のように、プロパティへの参照として評価されるべきものです。
`delete object.property
delete object['property']`

オブジェクトのプロパティを削除するために使うことが推奨されているようだ。


# 練習問題１１

## 問題

```
let arry =[
  {id:1,name:'morita'},
  {id:2,name:'kenji'},
  {id:4,name:'uro'},
  {id:3,name:'ken'}
  ];
をid番号が若い順にソートしたオブジェクトを含む配列を出力してください
```

## 私の解答

```js
let arry =[
  {id:1,name:'morita'},
  {id:2,name:'kenji'},
  {id:4,name:'uro'},
  {id:3,name:'ken'}
 ];
let tmp;
for(let i=0;i<=arry.length-2;i++){
    if(arry[i].id>arry[i+1].id){
        tmp = {...arry[i]};
        arry[i] = {...arry[i+1]};
        arry[i+1] = {...tmp};
    }
}
console.log(arry);
// --> 
[
  { id: 1, name: 'morita' },
  { id: 2, name: 'kenji' },
  { id: 3, name: 'ken' },
  { id: 4, name: 'uro' }
]
```

## 解説
正解。ではあるが、長い。
`i`番目と`i+1`番目のオブジェクトのidを比べ、`i`番目の方が大きかったら入れ替えるという記述。
`スプレッド構文`はオブジェクトに対しても使える。

https://qiita.com/akisx/items/682a4283c13fe336c547

## 模範解答
```js
arry.sort(function(a,b){
 return a.id > b.id
})
```
または
```js
arry.sort(function(a,b){
 if(a.id > b.id) return 1;
 if(a.id < b.id) return -1;
});
```

[`sort()`メソッド](https://developer.mozilla.org/ja/docs/Web/JavaScript/Reference/Global_Objects/Array/sort)を使ったもの。


|`compareFn(a, b)`の返り値  |ソート順  |
|---|---|
|>0  |`a`を`b`の後に並べる  |
|<0  |`a`を`b`の前に並べる  |
|===0 |`a`と`b`の元の順序を維持する|

関数`compareFn`が省略された場合、すべての配列要素は文字列に変換される。

```js
const tesarr = [1, 5, 2, 4, 3, 10, 246];
tesarr.sort();
console.log(tesarr);
// --> [1, 10, 2, 246, 3, 4, 5]
```

# 練習問題１２

## 問題

```
a, bの変数はデフォルトとしてaは5、bは7を持ち、aに1を代入してconsole出力してください。
```

## 私の解答
「デフォルトとして」の意味が分からない。
```js
let a=5; let b=7; a=1;
console.log(a,b);
```

## 模範解答

```js
const [a=5, b=7] = [1];
// or
const {a = 5, b = 7} = {a: 1};
```

デフォルトとして値を持たせたいので、単に代入している私の解答では間違いっぽい。
模範解答のように配列かオブジェクトとして宣言して代入することで、宣言と同時に値が与えられる
-> デフォルトとして値を持つ？

# 練習問題１３

## 問題

```
next()を実行しただけ返り値が1増える関数を定義してください
```

## 私の解答
ジェネレータだ...

```js
const increment = function*(num){
    while(1){
        yield num+=1;
    }
}
let doInc = increment(0);
console.log(doInc.next().value);//1
console.log(doInc.next().value);//2
console.log(doInc.next().value);//3
```
## 解説
正解。
ジェネレータ構文自体は忘れていたので、[私の記事](https://qiita.com/scythercas/items/897f281f63ab06bad345)を参考にしつつ解いた。

ジェネレータを扱う際は`nextメソッドを実行すると、前のyield地点から次のyieldかreturnまで処理が進む`という前提が大事になりそう。

## 模範解答
```js
const setUp = function(){
  let count = 0;
  return function(){
    return (count += 1);
  }
};
const next = setUp();　// ①
next();//1
next();//2
next();//3
```

ジェネレータを使っていなかった。
①
`setUp()`で`関数setUp`を実行し、`変数count`を宣言、初期化する。
返り値となる無名関数を`変数next`に格納し、次回以降は`next()`だけで、その無名関数部分のみが実行される（`変数count`の再宣言、代入は行われない）仕組みと考えた。

実際に、`console.log(next);`とすると、
```js
f(){
    return (count += 1);
}
```
となった。

# 練習問題１４

## 問題

```
fun(1,2,3)を実行したら引数が全て配列で返る関数funを定義しなさい
```

## 私の解答
```js
const fun = (...arr) => {
    console.log(arr);
}
fun(1, 2, 3);
```

## 解説
間違い。問題文は`配列で返る関数を定義しなさい`と言っているので、'return arr;'としなければいけなかった。
`...arr`はたびたび登場しているがスプレッド演算子によるもの。この場合は引数をまとめて配列に格納してくれる便利なもの。

## 模範解答
```js
function fun(){
 return Array.from(arguments)
}
// or
function fun (...arg){
  return arg;
}
```
[`Array.from()`メソッド](https://developer.mozilla.org/ja/docs/Web/JavaScript/Reference/Global_Objects/Array/from)
はこちら

２個目の解答は、アロー関数を用いることでかなり記述を減らせそう。
```js
const fun = (...arg) => arg; //ok
```

# 練習問題１５

## 問題
```
配列
const array = ['a1','a2','a3','a4','a5']
の0〜2番目の要素をそれぞれ
red, green, yellow
に置き換えて配列にしてください。また実行した際の返り値を教えてください
```

## 私の解答
何らかの配列のメソッドを使うんだろうとは思ったが、思いつかなかったので参考書を見て解いた。
```js
const array = ['a1','a2','a3','a4','a5'];
let arr2 = array.splice(0, 3, 'red', 'green', 'yellow');
console.log(array); // --> [ 'red', 'green', 'yellow', 'a4', 'a5' ]
console.log(arr2); // --> [ 'a1', 'a2', 'a3' ]
```

## 解説
[`splice()`メソッド](https://developer.mozilla.org/ja/docs/Web/JavaScript/Reference/Global_Objects/Array/splice)によるもの。

> splice() メソッドは、その場で既存の要素を取り除いたり、置き換えたり、新しい要素を追加したりすることで、配列の内容を変更します。

模範解答と一致した。

# 練習問題１６

## 問題

```
const array = ['a1','a2','a3','a4','a5']
のインデックス2〜4の要素を取り出し、 配列として出力しなさい。 
実行された後のarrayの要素を教えてください
```

## 私の解答

```js
const array = ['a1', 'a2', 'a3', 'a4', 'a5'];
console.log(array.splice(2,3));
// --> [ 'a3', 'a4', 'a5' ]
console.log(array);
// --> [ 'a1', 'a2' ]
```

## 模範解答

```js
const array = ['a1','a2','a3','a4','a5']
const newArray = array.slice(1,4);
newArray
//['a2', 'a3', 'a4']
array
//['a1','a2','a3','a4','a5']
```

## 解説
インデックス2~4て添え字のことじゃないんかい！
問題文の意図は伝わっていなかったが、間違ったことはしていないと思う。
[`splice()`メソッド](https://developer.mozilla.org/ja/docs/Web/JavaScript/Reference/Global_Objects/Array/splice)
[`slice()`メソッド](https://developer.mozilla.org/ja/docs/Web/JavaScript/Reference/Global_Objects/Array/slice)
**引数の違い**、**元の配列を操作するか否か**に注意が必要。

# 練習問題１７

## 問題

```
const array = ['a1','a2','a3','a4','a5']
の全ての要素を"/"で結合した文字列を出力し、
さらにその文字列を'/'区切りで配列に直してください
```

## 私の解答
```js
const array = ['a1','a2','a3','a4','a5'];
const text = array.join('/')
console.log(text);
// --> a1/a2/a3/a4/a5
const newArray = text.split('/');
console.log(newArray)
// --> [ 'a1', 'a2', 'a3', 'a4', 'a5' ]
```

## 解説
正解。[`join()`メソッド](https://developer.mozilla.org/ja/docs/Web/JavaScript/Reference/Global_Objects/Array/join)と[`split()`メソッド](https://developer.mozilla.org/ja/docs/Web/JavaScript/Reference/Global_Objects/String/split)を使ったもの。

### join()メソッド

引数を省略すると、`,`で連結される。

### split()メソッド

第二引数に自然数を持たせることができ、分割する回数に制限を与えられる。

# 練習問題１８

## 問題
```
配列['おはよう','こんにちは','おやすみなさい']の要素がランダムに出力される関数を書いてください。
(配列に要素が追加される事を仮定してたものにしてください)
```

## 私の解答
```js
const greeting = ['おはよう', 'こんにちは', 'こんばんは'];
let rand = Math.floor(Math.random() * (greeting.length));
console.log(greeting[rand]);
// --> こんにちは
```

## 解説
正解。
`greeting.length`によって乱数の幅を設定しているので、新しく配列の要素が追加されても対応してくれるようになっている。
変数`rand`を`let`で宣言したが、`const`でよかった。

乱数を生成したいときによく参考にするサイト↓

https://developer.mozilla.org/ja/docs/Web/JavaScript/Reference/Global_Objects/Math/random

# 練習問題１９

## 問題

```
Object.createで空のオブジェクトを作成し、値が1のプロパティpを出力してください
```

## 私の解答
ぜんっっっっぜんわからん

## 模範解答

```js
const obj = Object.create({}, {p: {value: 1}});
obj.p //1
```

## 解説

[`Object.create()`メソッド](https://developer.mozilla.org/ja/docs/Web/JavaScript/Reference/Global_Objects/Object/create)を用いたもの（問題文で指示があるが）。

`Object.create()`メソッドは、
+ 第一引数に、新たに生成されるオブジェクトのプロトタイプになるべきオブジェクト
+ 第二引数に、新たに生成されるオブジェクトに追加されるプロパティ
の2つの引数を持つ（第二引数は省略可能）。

# 練習問題２０

## 問題
```
コンストラクタWhoの初期化時に'morita'(String)を渡しインスタンスプロパティnameに代入、 
インスタンスメソッドgetNameの返り値がWho.prototype.name値になるいわゆる「classのようなもの」を作成してください 
※インスタンスメソッドはprototypeに代入してください
```
## 模範解答
```js
function Who(name){
 this.name = name;
};
Who.prototype.getName = function(){
 console.log('Myname is ' + this.name);
};
let o = new Who('morita');
o.getName()
```

## 解説
結果出来上がっているものは、以下の記述と同じようなものか。
```js
class Who {
    constructor(name){this.name = name;}
    getName(){console.log('My name is ' + this.name);}
}
const o = new Who('morita');
o.getName();
```
```js
function Who (name) {
    this.name = name;
    this.getName = function(){
        console.log('My name is ' + this.name);
    }   
} 
```

https://rara-world.com/js-prototype-constructor/

この記事を見ると、メモリの節約のためになるべくプロトタイプ拡張を用いた方が良いらしい


# 練習問題２１

## 問題
```
浅いコピー(shallow copy)と深いコピー(deep copy)の違いを説明してください
```

## 私の解答

シャローコピーはアドレスをコピーするのに対し、ディープコピーはそのアドレスが格納する値をコピーする。


## 模範解答
シャローコピーはプロパティ値や要素値だけのコピーでその先の参照まではコピーしない。
ディープコピーはコピー元とコピー先が別のオブジェクトを参照していること。プロパティが別のオブジェクトを参照していれば参照先のオブジェクトも含めてコピーします。deepcopyが必要な場面はない。自分で実装する必要がある。


## 解説
私の解答は言いたいことは合っているがアドレスという単語がJSになかった。プロパティ値というべきか。

# 練習問題２１

## 問題
```
下記
let array = ['e','a','k','B','c'];
array.sort();
を実行した結果を答えてください
```

## 私の解答
```js
['B', 'a', 'c', 'e', 'k']
```

## 解説
正解。
大文字の方が文字コードは小さい。