1. ==と===
1 == '1'; //true
1 === '1'; //false
true == '1'; //true
false == 0; //true
true === 1; //false
null == undefined; //true
null === undefined; //false
JavaScriptでは==
を使う場合、型変換される。
詳細:https://qiita.com/zawascript/items/a25eaf7a222ac3671275
基本的には、===
を使う。
2. 変数
var
var a = 1;
b = 1;
- ES5以前から唯一あった変数宣言。
- 変数宣言なしで変数を表記した場合は自動的にvarになる。(関数内だとグローバル変数)
(strictモードではエラー) - 再宣言が可能。
- 巻き上げが起こる
a = 1;
var a;
とした場合ブラウザは
var a;
a = 1;
と見なす。
varのスコープ(関数スコープ)
関数スコープのため、意図しない上書きの可能性がある。
var a = 3;
(() => {
//巻き上げによりここにvar a;がくるとみなされる
console.log(a); //undefined(7行目がなければ3)
a = 2; //ローカル変数aを更新(7行目がなければグローバル変数aを上書き)
b = 50; //グローバル変数bを宣言
var a;
var c = 100; //ローカル変数cを宣言
console.log(c); //100
})();
(() => {
var a = 'hi'; //ローカル変数aを宣言
console.log(a); //hi
console.log(c); //Error: c is not defined(実際には処理はここで止る)
})();
console.log(a); //3(7行目がなければ2)
console.log(b); //50
console.log(c); //Error: c is not defined
let
let a = 1;
let a = 2; //Error: 'a' has already been declared
- ES6で登場
- 再宣言が不可
letのスコープ(ブロックスコープ)
ブロックとは{ }
で囲まれた部分
(() => {
let a = 1;
var b = 1;
{//ブロック
let a = 2;
var b = 2;//上書き
}
console.log(a,b); //1 2
})();
const
const a = 1;
a = 2; //Error: 'a' has already been declared
const a = 2; //Error: 'a' has already been declared
- ES6で登場
- ブロックスコープ
- 再宣言と再代入が不可
const a = {
first : 100
};
a.second = 200;
const b = {};
b.first = 100;
b.second = 200;
delete b['first'];
b = {}; //Error: Assignment to constant variable
ただし、オブジェクトのプロパティは再代入可能なので注意
基本的には、letとconstを使う。
for (let i = 0; i < 5; i++); //forでもletを使う
3. script要素の位置
<script type="module" src="module.js"></script>
<script type="text/javascript" src="main.js"></script>
<script>//typeを省略した場合、自動的にtype="text/javascript"にな
console.log(1);
</script>
script要素が複数ある場合、先頭に書いたものから読み込まれる
→ライブラリはhead要素内で読み込むことが多い
HTMLは、1行目から順々に読み込まれるのでscript要素をどこに置くかは重要
DOMを読み込まれる前にアクセスしようとするとエラーになる
<body>
<script>
document.getElementById('header').innerText = 'See you';
//Cannot set property 'innerText' of null
</script>
<h1 id='header'>Hello</h1>
<p>Welcom to World Wide Web.</p>
</body>
回避する方として
- script要素をbody要素の末尾にもってくる
<body>
<h1 id='header'>Hello</h1>
<p>Welcom to World Wide Web.</p>
<script>
document.getElementById('header').innerText = 'See you';
</script>
</body>
- JavascriptでDOMの読み込みを待つ
<body>
<script>
window.addEventListener("DOMContentLoaded",() => {
document.getElementById('header').innerText = 'See you';
});
</script>
<h1 id='header'>Hello</h1>
<p>Welcom to World Wide Web.</p>
</body>
ただし、この場合script要素を読み込んだ後にDOMを読み込むことには変わりないので、scriptの読み込み時間分DOMの表示が遅くなる
→defer属性を使う
<body>
<script src="main.js" defer></script>
<h1 id="header">Hello</h1>
<p>Welcom to World Wide Web.</p>
</body>
main.js
window.addEventListener("DOMContentLoaded",() => {
document.getElementById('header').innerText = 'See you';
});
defer属性を使うとJavaScriptの実行をHTMLのパース完了後に行うので多少高速化する
ただし、
- 外部ファイルしか使えない
- パース完了後(DOMContentLoadedの直前)なのでDOMContentLoadedは必要
詳細:https://qiita.com/phanect/items/82c85ea4b8f9c373d684
基本的には、script要素をbody要素の末尾にもってくる
追記:type="module"
を使えば、インラインでもDOM構築後に実行する
http://var.blog.jp/archives/77519253.html
4. コメント
//コメント
/*
複数行コメント
*/
/**
* ドキュメンテーションコメント
*/
ドキュメンテーションコメントについてはこちらを参照のこと
https://ics.media/entry/6789
基本的には、//
でコメントする。
5. strictモードを使う
strictモードにすることでより安全なコードを書けるようになる。
詳細:https://developer.mozilla.org/ja/docs/Web/JavaScript/Strict_mode
6. HTML要素へのアクセス
getElementBy系
document.getElementById('ID_NAME');
document.getElementsByClassName('CLASS_NAME');
document.getElementsByTagName('div');
querySelector
document.querySelector('#ID_NAME');
document.querySelectorAll('.CLASS_NAME');
document.querySelectorAll('div');
このコードは上記のgetElementBy系のコードと同じ値を返す。
querySelectorはCSSのセレクタと同じ表記でDOMの操作が可能
document.querySelectorAll('div > .CLASS_NAME');
これと同じ処理はgetElementBy系では書けない??
また、以下のコードはそれぞれ同じ値を返す
document.querySelector('.CLASS_NAME');
document.getElementsByClassName('CLASS_NAME')[0];
querySelectorの方が可読性が高く便利だが、getElementBy系の方が速い
詳細:https://developer.mozilla.org/ja/docs/Web/API/Document/querySelector
基本的には、querySelectorを使う。
追記:querySelectorAll()
はNodeList
、getElementsByTagName()/getElementsByClassName
はHTMLCollection
を返し、DOMが操作されたときの結果が静的/動的という違いがある。
参考:http://ginpen.com/2015/12/22/nodelist-vs-htmlcollection/
7. 関数
以下のコードはそれぞれ同じ動作をする(functionの場合は巻き上げが起こる)
//functionによる関数宣言
function test(){
console.log(1);
}
//関数式
const test = function(){
console.log(1);
};
//アロー関数
const test = () => console.log(1);
アロー関数は関数式の簡易表記(厳密には異なる)
詳細:https://qiita.com/mejileben/items/69e5facdb60781927929
※関数式の値のことを関数リテラルと呼ぶ
詳細:http://amimimi.hatenablog.com/entry/2013/11/16/174019
functionによる関数宣言は、意図せぬ上書きの危険がある
function test(){
console.log(1);
}
test(); //2
function test(){
console.log(2);
}
test(); //2
関数式を使うと関数の上書きを防げる
const test = function(){
console.log(1);
}
test(); //1
const test = () => console.log(1); //Error: test has already been declared
//実際には処理はここで止る
test(); //1
アロー関数は1行の時returnを省略できる
const add = (x, y) => x + y;
console.log(add(1, 2)); //3
基本的には、アロー関数を使う。
8. 変数のスワップ
従来の書き方
var a = 1;
var b = 2;
var tmp;
tmp = a;
a = b;
b = tmp;
console.log(a, b); //2 1
モダンな書き方(分割代入)
let [a, b] = [1, 2];
[a, b] = [b, a];
console.log(a, b); //2 1
詳細:https://qiita.com/amamamaou/items/1ec21316b8bf05ba9c34
分割代入を使う。
9. 文字列リテラル
従来の書き方
var tax = 1.08;
console.log('りんごが税抜き100円、税込みで' + 100 * tax + '円');
モダンな書き方(テンプレート文字列)
const tax = 1.08;
console.log(`りんごが税抜き100円、税込みで${100 * tax}円`);
バッククォートで囲む。
ダブルクオーテーションをそのまま表記できる
const str = `<div id="test"></div>`
詳細:https://qiita.com/kura07/items/c9fa858870ad56dfec12
タグ付きテンプレートリテラル
const enc = str => encodeURIComponent(str);
console.log(enc`あいうえお`); //%E3%81%82%E3%81%84%E3%81%86%E3%81%88%E3%81%8A
- 入力がテンプレート文字列
- 出力が文字列
のとき、関数を括弧を省略して呼べる
入力に${}
を含む場合、第二引数を可変長にする(${}
の中身がレンダリングされてやってくる
先頭に${}
が来ている場合str2がstrより一つ短くなるので例外処理すること(5行目)
const enc = (str, ...str2) => {
let res = '';
for (let i = 0; i < str.length; i++) {
res += encodeURIComponent(str[i]);
res += str2[i] ? encodeURIComponent(str2[i]) : '';
}
return res;
}
const dat = 'Hi!'
console.log(enc`${dat}あいうえお${1+1}かかいくけこ${dat}`);
ワンライナーにすると
const enc = (str, ...str2) => str.map((v, i) => encodeURIComponent(v) + (str2[i] ? encodeURIComponent(str2[i]) : '')).join('');
テンプレート文字列を使う
10. [参考]配列、オブジェクトをfor...ofで回す
配列
const arr = [100, 200, 300];
for(const [index, value] of arr.entries()){
console.log(index, value);
}
オブジェクト
const obj = {
first: 100,
second: 200,
third: 300
};
for(const [key, value] of Object.entries(obj)){
console.log(key, value);
}
11. addEventListenerの第一引数
document.addEventListener('pointerdown', function);
pointer系を使うとクリックとタッチ両方対応
※現状clickでもタッチイベントは発行する
詳細:https://developers.google.com/web/fundamentals/design-and-ux/input/touch/?hl=ja#_5
pointer系を使う。
https://caniuse.com/#feat=pointer
2018/10/10現在Safariは対応してないので、単純なクリックやタップなら’click’で
それ以上をやりたいときは両方書くしかないようです
12. 参照渡しと値渡し
const a = [1,2,3];
const b = a;
b[0] = 'あ';
console.log(a); //["あ", 2, 3]
console.log(b); //["あ", 2, 3]
配列とオブジェクトは参照渡しで、メモリ上のアドレスがコピーされるだけなので注意
詳細:https://qiita.com/migi/items/3417c2de685c368faab1
配列の場合
const a = [1,2,3];
const b = [a[0],a[1],a[2]];
b[0] = 'あ';
console.log(a); //[1, 2, 3]
console.log(b); //["あ", 2, 3]
こうしてやるとa[0]を値渡しできるので、問題ない
ES6のスプレッド演算子を使えばシンプルに書ける
const a = [1,2,3];
const b = [...a];
b[0] = 'あ';
console.log(a); //[1, 2, 3]
console.log(b); //["あ", 2, 3]
オブジェクトの場合
const a = {
one: 1,
two: 2
};
const b = {...a};
配列/オブジェクトの値渡しには、スプレッド演算子を使う
13. [Tips]オブジェクトのプロパティに変数を使う
name = 'bob'
a = {
name : 'boy'
}
console.log(a.bob); //undefined
console.log(a.name); //boy
nameのところにはbobが入ってほしい
name = 'bob'
a = {
[name] : 'boy'
}
console.log(a.bob); //boy
console.log(a.name); //undefined
ちなみに組み込みのメソッドでも使える
console['log'](1); //1
14. [参考]短絡評価で条件分岐からの代入を短く書く
短絡評価
console.log(true || 1); //true
console.log(true && 'Hi' || 1); //Hi
console.log(1 || true); //1
console.log(false || 1); //1
||で区切って、trueならばその値をreturnする
詳細:https://qiita.com/Imamotty/items/bc659569239379dded55
ifを無くす
if
const a = [1, -2, 3];
a.forEach((v, i) => {
if(v >= 0) a[i] = v;
else a[i] = -v;
});
console.log(a); //[1, 2, 3]
短絡評価
const a = [1, -2, 3];
a.forEach((v, i) => {
a[i] = v >= 0 && v || -v;
});
console.log(a); //[1, 2, 3]
ちなみに、この場合はmapとMath.abs()を使ったほうがシンプルだったりする
const a = [1, -2, 3];
a = a.map(v => {
return Math.abs(v);
});
console.log(a)
switchを無くす
switch
const a = [2, 1, 2, 3, 1];
a.forEach((v, i) => {
switch (v) {
case 1:
a[i] = 'One';
break;
case 2:
a[i] = 'Two';
break;
case 3:
a[i] = 'Three';
break;
}
});
console.log(a); //["Two", "One", "Two", "Three", "One"]
短絡評価
const a = [2, 1, 2, 3, 1];
a.forEach((v, i) => {
a[i] = v == 1 && 'One' || v == 2 && 'Two' || v == 3 && 'Three';
});
console.log(a); //["Two", "One", "Two", "Three", "One"]
※短絡評価は、(==
と同じように)型変換して評価するので、||
の前にfalseやfalsyな値を返そうとするとうまく動かない。
例えば、正の数かどうか判定する関数を書くとき
const f1 = n => n >= 0 && true || false;
const f2 = n => n < 0 && false || true;
f1(-1); //false
f2(-1); //true
f2()
は常にtrue
を返してしまう。
選択肢が2つの場合は、三項演算子のほうが安全
const f2 = n => n < 0 ? false : true;
f2(-1); //false
15. [Tips]文字列のHTMLをパースする
DOMParserを使う
const str = '<html><head><title>hello</title></head><body></body></html>';
const parser = new DOMParser();
const doc = parser.parseFromString(str, 'text/html');
console.log(doc.title); //hello
詳細:https://developer.mozilla.org/ja/docs/Web/API/DOMParser
16. 要素を追加する
古い書き方(createElement)
const element = document.createElement('div')
element.classList.add('test')
element.innerText = 'HelloWorld!';
document.body.appendChild(element);
モダンな書き方(insertAdjacentHTML + テンプレート文字列)
const element = `<div class="test">Hello world!</div>`;
document.body.insertAdjacentHTML('afterend', element);
テンプレート文字列なら改行やダブルクオーテーションもOK
const element = `
<div class="test">
Hello world!
</div>
`;
document.body.insertAdjacentHTML('afterend', element);
詳細:https://developer.mozilla.org/ja/docs/Web/API/Element/insertAdjacentHTML
insertAdjacentHTML + テンプレート文字列を使う
17. [参考]Object.freezeで値の上書きを防ぐ
constはインデックスやキーからアクセスすると値の上書きが可能
const ary = [1, 2, 3];
const obj = {
A: 1,
B: 2,
C: 3
};
ary[0] = 0;
obj.A = 0;
console.log(ary, obj); //[0, 2, 3] {A: 0, B: 2, C: 3}
これをObject.freezeで防ぐ
const ary = Object.freeze([1, 2, 3]);
const obj = {
A: 1,
B: 2,
C: 3
};
Object.freeze(obj);
ary[0] = 0;
obj.A = 0;
console.log(ary, obj); //[1, 2, 3] {A: 1, B: 2, C: 3}
ただし、1階層しか保護されないので二次元になると上書きできる
const ary = Object.freeze([[1], 2, 3]);
const obj = {
A: [1],
B: 2,
C: 3
};
Object.freeze(obj);
ary[0][0] = 0;
obj.A[0] = 0;
console.log(ary, obj); //[[0], 2, 3] {A: [0], B: 2, C: 3}
そのため、それぞれにObject.freezeする必要がある
const ary = Object.freeze([Object.freeze([1]), 2, 3]);
const obj = Object.freeze({
A: Object.freeze([1]),
B: 2,
C: 3
});
ary[0][0] = 0;
obj.A[0] = 0;
console.log(ary, obj); //[[1], 2, 3] {A: [1], B: 2, C: 3}
詳細:http://abouthiroppy.hatenablog.jp/entry/2017/12/05/100339
クラスにも使える
const Dog = class {
constructor(age, breed) {
this.age = age;
this.breed = breed;
Object.freeze(this);
}
};
const pochi = new Dog(2, 'Siberian Husky');
pochi.age = 5;
console.log(pochi.age); //2
18. [参考]forで複数変数を回す
for (let i = 0, j = 9; i <= 9; i++, j--) {
console.log(i, j);
}
19. [Tips]引数のデフォルト値を指定する
const greet = (name = 'anonymous') => console.log(`Hello, ${name}`);
greet(); //Hello, anonymous
greet(undefined); //Hello, anonymous
greet(''); //Hello,
greet(null); //Hello, null
greet('Bob'); //Hello, Bob
20. ifの中括弧省略
const [x, y] = [1, 2];
if (x === 1){
if (y === 1){
console.log('x === 1 && y === 2');
}
}else{
console.log('x !== 1');
}
このコードは何も出力しないが、中括弧省略すると
const [x, y] = [1, 2];
if (x === 1)
if (y === 1)
console.log('x === 1 && y === 2');
else
console.log('x !== 1');
出力
x !== 1
なぜこうなるかというと、中括弧省略のelseは直前のifにかかるため。
インデントを正しくすると
const [x, y] = [1, 2];
if (x === 1)
if (y === 1)
console.log('x === 1 && y === 2');
else
console.log('x !== 1');
基本的には、ifの中括弧は省略しない。
21. [Tips]String()とtoString()の違い
String()はGlobalオブジェクトで、toString()はオブジェクトごとに持っているメソッド
Number.prototype.toString()
123.toString(); //Uncaught SyntaxError
(123).toString(); //"123"
数値から直接呼ぶ場合は、()
でくくるhttps://developer.mozilla.org/ja/docs/Web/JavaScript/Reference/Global_Objects/Number/toString#.E4.BE.8B
数値のあとに
(空白)か.
をいれてるのでも良い
123 .toString(); //"123"
123..toString(); //"123"
ただし以下はエラーになる
1.23..toString(); //Uncaught SyntaxError
そもそも.toString()
の.
と小数点の.
が区別できない
→整数の場合、明示的に.0
を表記する
→0を略して.
を追加する
という流れなので
1.23.toString(); //1.23
でよい。
また、指数表記の場合、指数は小数点にならないので.
を加えるとエラーになる
123e-2..toString(); //Uncaught SyntaxError
123e-2.toString(); //"1.23"
Array.prototype.toString()
[123].toString(); //"123"
Object.prototype.toString()
({key:123}).toString(); //"[object Object]"
(new Object(123)).toString(); //"123"
String()
String(123); //"123"
String([123]); //"123"
String({key:123}); //"[object Object]"
null/undefinedの扱い
(null).toString(); //Uncaught TypeError
String(null); //"null"
String()はnull/undefinedでもエラーにならない
https://developer.mozilla.org/ja/docs/Web/JavaScript/Reference/Global_Objects/String#%E4%BE%8B
null/undefinedをArrayオブジェクトに格納した場合もエラーにはならない
(Array(null)).toString(); //""
[null].toString(); //""
22. isNaN()/isFinite()
isNaN()にはGlobalオブジェクトのisNan()
とNumberオブジェクトのメソッドとしてのNumber.isNan()
があり挙動が違う。
isNaN()
は引数が数値以外の場合、暗黙の型変換が行われる(文字列はNaNになる)
isNaN('a'); //true
Number.isNaN('a'); //false
isFInite()も同様に
isFinite('1'); //true
Number.isFinite('1'); //false
基本的には、Number.isNan()/Number.isFinite()を使う。
23. [Tips]encodeURI()とencodeURIComponent()とescape()の違い
encode/decodeURI() → ;
,
/
?
:
@
&
=
+
$
はエンコード/デコードされない
encode/decodeURIComponent() → ;
,
/
?
:
@
&
=
+
$
がエンコード/デコードされる
escape()/unescape() → ブラウザによって挙動が違う
24. [黒魔術]動的に変数、関数を生成する
eval('var a = 1'); //const,letは不可
eval('a = e => console.log(e)');
const a = new Function('e', 'console.log(e)'); //関数の引数、中身を動的生成可
※ハック以外にはオブジェクトやクラスを使う
25. [Tips]引数を必須にする
const required = () => new Error('Parameter requied');
const func = (a = required(), b = 0) => console.log(a, b);
func(); //Error: Parameter requied
func(1); //1 0
26. [Tips]可変長の引数
const func = (...input) => console.log(input);
func(1, 2, 3, ['a'], { b: 'c' }); //[1, 2, 3, ['a'], { b: 'c' }]
27. [Tips]名前付き引数
分割代入を使う
const func = ({ name, age }) => console.log(`Hi ${name}. You are ${age} y/o.`);
func({ name: 'Bob', age: NaN }); //Hi Bob. You are NaN y/o.
28. [Tips]constructorでの値の流し込みを楽にする
いちいちthis.age = ageとか書きたくない。
const Dog = class {
constructor(age, breed) {
Object.assign(this, { age, breed });
}
};
const pochi = new Dog(2, 'Siberian Husky');
console.log(pochi.age); //2