JavaScript基本
★Enterキーでの送信を禁止
<form method="POST" onsubmit="return false;">
...
<input name="button1" value="登録" onclick="this.form.submit();" />
...
</form>
★RepaintとReflow
・Repaint
外観が変化する。DOMに影響なし。例:outline,visibility,background color
・Reflow
DOMに影響あり。Repaintを起こす。性能低下の原因の一つ。
発生タイミング
・DOM要素を追加・変更・削除:Reflow + Repaint
・DOM要素の字体・色を変更:Repaint
・ブラウザをResize・scroll:Reflow
・offsetLeft、offsetTop、offsetHeight、offsetWidthを取得:Reflow
Reflowを避ける方法
1.要素をdocumentから削除し、修正後に元の位置に復元する
2.要素のdisplayをnoneに設定し、修正後に元の設定値に復元する
list.style.display = "none";
list.appendChild(document.createTextNode('xxx'));
list.appendChild(document.createElement('yyy'));
list.appendChild(document.createTextNode('zzz'));
list.style.display = "";
3.複数の要素を追加する場合にDocumentFragmentを利用
var fragment = document.createDocumentFragment();
fragment.appendChild(document.createTextNode('xxx'));
fragment.appendChild(document.createElement('yyy'));
fragment.appendChild(document.createTextNode('zzz'));
document.body.appendChild(fragment);
4.スタイルについて
・cssTextを利用
element.style.width = "80px";
element.style.height = "90px";
element.style.border = "solid 1px";
↓
element.style.cssText = "width:80px;height:90px;border:solid 1px;";
或いは
.newStyle {
width:80px;
height:90px;
border:solid 1px;
}
element.className = "newStyle";
・属性をキャッシュ
var left = elem.offsetLeft;
・要素のpositionをabsolute/fixedに設定
・tableを使う際に、table-layoutをauto/fixedに設定
★thisについて
動的言語の場合:ランタイムで決まる
サンプル1
window.name = "from window";
var showName = function(){
return this.name;
};
alert(showName()); //from window
var obj1 = {};
obj1.name = "from obj1";
alert(showName.apply(obj1)); //from obj1
alert(showName.call(obj1)); //from obj1
var obj2 = {
"name" : "from obj2"
};
obj2.showName = showName;
alert(obj2.showName()); //from obj2
サンプル2
var obj = {
doSomething: function () {
console.log(this); // obj
(function () {
console.log(this); // window
}());
}
};
obj.doSomething();
this.title = "xxx"; // DOM
$(this).attr('title','xxx'); // jQuery
class Foo {
constructor(name) {
this.name = name;
//this.greet = this.greet.bind(this); // 対策2
}
greet() {
console.log('hello ', this.name);
}
someThingAsync() {
return Promise.resolve();
}
asyncGreet() {
this.someThingAsync().then(this.greet); // TypeError: this is undefined
//this.someThingAsync().then(this.greet.bind(this)); // 対策1
//this.someThingAsync().then(() =>{ // 対策3
// this.greet();
//});
}
}
new Foo('dog').asyncGreet();
★初期化
var a = new Object(); a.name = "aaa"; ×
var a = { name: "aaa" };
var b = new Boolean(true); ×
var b = true;
var c = new Array("aaa", "bbb"); ×
var c = ["aaa", "bbb"];
var d = new String("aaa"); ×
var d = "aaa";
var e = new Function("in", "alert(in);"); ×
var e = function(in) { alert(in); };
連想配列(オブジェクト)の初期化
var obj = { name: 'andy' };
var obj = { 'name': 'andy' };
var obj = {};
obj.name = 'andy';
var obj = {};
obj['name'] = 'andy';
var obj = new Object();
obj.name = 'andy';
//走査
for (var property in obj) {
console.log(property + ":" + obj[property]);
if(obj.hasOwnProperty(property) {
...
}
}
配列の初期化
var array = ['aaa', 'bbb'];
var array = new Array('aaa', 'bbb');
var array = new Array(2); //長さ:2
var array = Array('aaa', 'bbb');
var array = [];
array[0] = 'aaa';
array[1] = 'bbb';
var array = [];
array['0'] = 'aaa'
array['1'] = 'bbb'
var array = { 0: 'aaa', 1: 'bbb', length: 2 };
array.__proto__ = Array.prototype;
//走査
for(var key in array){
console.log( key + ":" + array[key]);
}
★配列の操作
var arr1 = [1,2,3];
var arr2 = arr1;
arr1 =[];// arr2は[1, 2, 3]
arr1.length =0;// arr1とarr2は[]
Math.max.apply(Math, [1,2,3]); //3
Math.min.apply(Math, [1,2,3]); //1
var arr = [];
arr.push(0); // 配列の最後に追加
arr.push(1, 2); // 長さを返す
while (arr.length !== 0) {
// 配列の最後を取り出す
console.log(arr.pop()); // 2, 1, 0
}
Array.prototype.push.apply(arr1, arr2); // マージ
var arr = [];
arr.unshift(0); // 配列の最初に追加
arr.unshift(1, 2); // 長さを返す
while (arr.length !== 0) {
// 配列の最初を取り出す
console.log(arr.shift()); // 2, 1, 0
}
var arr = [1, 2, 3, 4, 5];
delete arr[1]; // [1, undefined, 3, 4, 5]
arr.splice(1, 1); // [1, 3, 4, 5]
// 挿入
var a = [1,2,3,7,8,9];
var b = [4,5,6];
var insertIndex = 3;
a.splice.apply(a, Array.concat(insertIndex, 0, b));
var arr = [1, 2, 3];
arr.reverse(); // [3, 2, 1],元arr:[3, 2, 1]
var arr = [1, 2, 3];
arr.concat(); // [1, 2, 3],元arr:[1, 2, 3] Cloneに相当
arr.concat(4, 5); // [1, 2, 3, 4, 5],元arr:[1, 2, 3]
arr.concat(4, [5, 6]); // [1, 2, 3, 4, 5, 6]
arr.concat(4, [5, 6], 7); // [1, 2, 3, 4, 5, 6, 7]
// クリア
書き方1
var array = [1, 2, 3, 4];
array.splice(0, array.length);
書き方2
var array = [1, 2, 3, 4];
array.length = 0;
※Javaの配列のlengthはReadOnly
書き方3(効率よく)
var array = [1, 2, 3, 4];
array = [];
// Clone
var array = [1, 2, 3, 4];
var clone = array.slice(0); // array.slice()
array = [4, 3, 2, 1];
或いは
var clone = [].slice.call(array);
※cloneが変わらない
[1, 2, 3].toString(); // '1, 2, 3'
[1, 2, [3, 4]].toString(); // '1, 2, 3, 4'
// map
function action(input) {
return input.replace(/o/g, 'e');
}
var words = ["foot", "goose", "moose"];
var result = words.map(action); //["feet", "geese", "meese"]
var numbers = [1, 4, 9];
var result = numbers.map(Math.sqrt); //[1, 2, 3]
var arr = [1,2,3,4,5];
//filter
var even = function(item){
if(typeof item !== "number"){
return false;
}
return !(item & 1);
};
var filter1 = arr.filter(even);
var filter2 = window.myFilter(arr, even);
//map
var add = function(item){
return item + 10;
};
var map1 = arr.map(add);
var map2 = window.myMap(arr, add);
//reduce
var plus = function(a, b){
return a + b;
};
var reduce1 = arr.reduce(plus, 0);
var reduce2 = window.myReduce(arr, plus, 10);
function myFilter(arr, callback){
var out = [];
for(var i = 0; i < arr.length; i++){
if(callback(arr[i])){
out.push(arr[i]);
}
}
return out;
}
function myMap(arr, callback){
var len = arr && arr.length || 0;
var out = new Array(len);
for(var i = 0; i < len; i++){
out[i] = callback(arr[i]);
}
return out;
}
function myReduce(arr, callback, num){
var i, out;
if(num){
out = num;
i = 0;
}else{
out = arr[0];
i = 1;
}
for(; i < arr.length; i++){
out = callback(arr[i], out);
}
return out;
}
〇Array.forEach()とjQueryの$().each()
var arr = [2, 4, 6, 8];
arr.forEach( function(i){alert(i);} );
$(arr).each( function(){alert(this);} );
或いは
$(arr).each( function(index, item){alert(item);} );
〇Array.filter()とjQueryの$.grep()
var arr = [2, 4, 6, 8];
var newArr = arr.filter(function(item){return item > 4;}); //6,8
var newArr = $.grep(arr, function(item, index){
return item > 4;
});
〇Array.map()とjQueryの$.map()
var arr = [2, 4, 6, 8];
var newArr = arr.map(function(item){return item + 1;}); //3,5,7,9
var newArr = $.map(arr, function(item, index){return item + 1;});
〇Array.every()とArray.some() 条件に満たす
var arr = [2, 4, 6, 8, 10];
var result = arr.every( function(item){return item > 1} ); //true
var result = arr.every( function(item){return item > 2} ); //false
var result = arr.some( function(item){return item > 9;} ); //true
var result = arr.some( function(item){return item > 10;} ); //false
〇Array.sort()
var arr = [6, 10, 2, 8, 4];
arr.sort(); ×
arr.sort( function(a, b){return a - b} ); ○
[
{ name: "Andy", age: 31 },
{ name: "Tom", age: 25 },
{ name: "John", age: 27 }
].sort(function(obj1, obj2) {
return obj1.age - obj2.age; // 昇順
});
★slice vs splice
Array.slice(begin[, end])
Array.splice(index, howMany, [element1][, ..., elementN])
var arr = [1, 2, 3];
arr.slice(0, 2); // [1, 2],元arr:[1, 2, 3]
arr.slice(-2, -1); // [2] 負の数:最後から数える
arr.slice(1); // [2, 3]
var color = ['red', 'blue', 'yellow', 'black'];
// 追加
result = color.splice(2, 0, "xxx");
console.log(color); // red,blue,xxx,yellow,black
console.log(result); // 空
// 削除
result = color.splice(3, 1);
console.log(color); // red,blue,xxx,black
console.log(result); // yellow
// 置換
result = color.splice(2, 1, "yyy");
console.log(color); // red,blue,yyy,black
console.log(result); // xxx
// 置換(複数)
result = color.splice(0, 2, "xxx", "yyy", "zzz");
console.log(color); // xxx,yyy,zzz,yyy,black
console.log(result); // red,blue
var arr = [1, 2, 3];
arr.splice(1); // [2, 3],元arr:[1]
var arr = [1, 2, 3];
arr.splice(1, 0, ['hello', 'world']); // [],元arr: [1, ['hello', 'world'], 2, 3]
var arr = Array.prototype.slice.call(document.querySelectorAll("div"));
var arr = Array.prototype.slice.call(arguments);
var arr = [].slice.call(arguments);
DOM属性
DOMメソッド(更新系)
DOMメソッド(参照系)
★文字列について
var str = 'aaa' + 'bbb' + 'ccc'; ×
var str = ['aaa', 'bbb', 'ccc'].join(); //aaa,bbb,ccc
var str = ['aaa', 'bbb', 'ccc'].join(''); //aaabbbccc
var list = '<ul><li>' + arr.join('</li><li>') + '</li></ul>';
逆
var array = str.split(',');
var startTime = new Date();
var str = '';
for(var i = 0; i< 10; i++){
str += i + 'xxx';
}
alert(new Date() - startTime);
改善
var startTime = new Date();
var arr = [];
for(var i = 0; i< 10; i++){
arr.push(i, 'xxx'); 速い
//arr[arr.length] = i + 'xxx'; もっと速い
}
var str = arr.join('');
alert(new Date() - startTime);
また
var min = Math.min(a, b);
var min = a < b ? a : b; 速い
"bob".replace("b", "x"); // "xob"
"bob".replace(/b/g, "x"); // "xox"
"Bob".replace(/b/gi, "x"); // "xox" 大・小文字を無視
"bob".replace(new RegExp("b", "g"), "x"); // "xox"
※メソッドを呼ぶことより単純な計算の方が速い
ちなみに、文字列について
//var s = '0123'; × 無駄なオブジェクトが生成された
var s = new String('0123');
var len = s.length;
for( var i = 0; i < len; i++ ) {
s.charAt(i);
}
※文字列のメソッドとプロパティは文字列オブジェクトに定義されるため
★日付
var now = Date.now() || function(){
return + new Date();
};
new Date()
new Date(milliseconds)
new Date("2013/08/19")
new Date(year, month, day, hours, minutes, seconds, ms) ※monthは0から
new Date(Date.parse("8/2/2012")); // 〇 M/d/yyyy
new Date(Date.parse("2012-08-02")); // 〇 yyyy-MM-dd或いはyyyy/MM/dd
new Date(Date.parse("2012-8-2")); // ×
★変数の変換
alert(0xFF); 255 hex
alert(020); 16 octal
alert(1e3); 1000 Math.pow(10,3)と同じ
(1000).toExponential() 1e3
(3.1415).toFixed(3) 3.142 四捨五入
var a = 20;
var b = "10";
alert(a + b); //2010
alert(a + (+b)); //30
alert(a + +b); //30
alert(a ++b); //Compile error
var myVar = "3.14";
var str = "" + myVar; // to string
alert(typeof(str)); // string
var int = ~~myVar; // to integer
alert(typeof(int)); // number
var float = 1 * myVar; // to float
alert(typeof(float)); // number
var bool = myVar; // to boolean ""以外の文字列、0以外の数字の場合にtrue
alert(typeof(bool)); // boolean
var bool = Boolean(myVar); // to boolean
var array = [myVar]; // to array
alert(typeof(array)); // object
var date = new Date(myVar); // to Date
alert(typeof(date)); //object
var regExp = new RegExp(myVar); // to RegExp
alert(typeof(regExp)); //object
'10.5678'| 0 //10
-2.3456 | 0 // -2
var d = + new Date(); //Date⇒数値
(12).toString(16); // C int to hex
(12).toString(8); // 14 int to octal
parseInt(string, radix)
parseInt("FF",16) // 255 hex to int
parseInt("020",8) // 16 octal to int
parseInt("020", 10); // 20
parseInt("10"); //10
parseInt("19",10); //19 (10+9)
parseInt("11",2); //3 (2+1)
parseInt("17",8); //15 (8+7)
parseInt("1f",16); //31 (16+15)
typeof new Number(123); // object
typeof Number(123); // number
typeof 123; // number
typeof NaN; // number
"aaa" instanceof String //false
new String("aaa") instanceof String //true
["aaa", "bbb"] instanceof Array //true
new Array("aaa", "bbb") instanceof Array //true
typeof [] === 'object' // true
// 配列の判断
Array.isArray(var)
★グローバル変数
var g_var1 = 1;
g_var2 = 2; //属性と見られる
(function(){
g_var3 = 3; //属性と見られる
})();
delete g_var1; //NG
delete g_var2; //OK
delete g_var3; //OK
console.log( typeof g_var1 ); //number
console.log( typeof g_var2 ); //undefined
console.log( typeof g_var3 ); //undefined
※delete:属性はOK、変数はNG
★スコープ
JavaScriptではブロックスコープがない、関数スコープがある
for(var i=0; i<10; i++) {
console.log(i);
}
console.log(i); // 10
(function (){
for(var i=0; i<10; i++) {
console.log(i);
}
}());
console.log(i); // undefined
(function (){
console.log(x + 2); // Error(x is not defined)
x = 0; //global変数
}());
var x = 1;
(function (){
console.log(x + 2); // 2
x = 0;
}());
var x = 1;
(function (){
//書き方1
console.log(x + 2); //NaN
var x = 0;
//書き方2
var x;
console.log(x + 2); //NaN
x = 0;
}());
★画面遷移
window.location.href = "xxx.html?backurl=" + window.location.href;
window.history.back(-1);
window.navigate("xxx.html");
self.location = 'xxx.html';
top.location = 'xxx.html';
★NaN、null、undefined
var num = NaN;
num === NaN //false
isNaN(num) //true
var a;
a === null //false
a === undefined //true
チェック時
if(a !== null && a !== undefined) {
...
}
[1,2,3] === [1,2,3] // false
{a: 1} === {a: 1} // false
{} === {} // false
★argumentsについて
argumentsは配列に似ているオブジェクトである
(function(a){
alert(arguments[0]); //1
a = 2;
alert(arguments[0]); //2
}(1));
//argumentsオブジェクト ⇒ 配列
console.log(arguments instanceof Array); // false
var argsArray = Array.prototype.slice.call(arguments);
console.log(argsArray instanceof Array); // true
function args(){
return [].slice.call(arguments);
}
var array = args(1, 2, 3);
//arguments変更
function add(){
//arguments.push('abc'); ×
Array.prototype.push.call(arguments, 'abc');
return arguments;
}
console.log( add()[0] ); //abc
★applyとcall
callは可変引数で、第2引数以降に並べる
applyは固定引数で、第2引数に配列を渡す
var obj = { name:'Andy' };
obj.func = function (arg1, arg2) { console.log(arg1 + arg2 + this.name); };
obj.func('aaa', 'bbb');
var obj = { name:'Andy' };
var func = function (arg1, arg2) { console.log(arg1 + arg2 + this.name); };
func.apply(obj, ['aaa', 'bbb']);
或いは
func.call(obj, 'aaa', 'bbb');
function log() {
console.log(this + '|' + arguments.length);
}
log('abc'); // [object Window]|1
log.call('abc', [1, 2, 3]); // abc|1
log.apply('abc', [1, 2, 3]); // abc|3
var func = function(name){
...
};
func.apply(null, ['Andy']);
var obj = {
func: function(name){
...
}
};
func.apply(obj, ['Andy']);
★new
JavaScriptのnew処理とJava・C#のnew処理は違う
var obj = new person();
下記と同様
var obj ={};
person.apply(obj);
obj.__proto__ = person.prototype;
★instanceメソッドとstaticメソッド
function class() { };
class.staticMethod = function() { ... };
class.prototype.instanceMethod = function() { ... };
//staticメソッドの呼び出し
class.staticMethod();
//instanceメソッドの呼び出し
var instance = new class();
instance.instanceMethod();
★evalとnew Function
var str = "var a = 1; console.log(a);";
eval(str); //1
str = "var b = 2; console.log(b);";
new Function(str)(); //2
str = "var c = 3; console.log(c);";
(function(){
eval(str);
})(); //3
console.log(typeof a); //number
console.log(typeof b); //undefined
console.log(typeof c); //undefined
(function(){
var local = 1;
eval("console.log(typeof local); local = 2;"); //number
console.log(local); //2
})();
(function(){
var local = 1;
new Function("console.log(typeof local); local = 2;")(); //undefined
console.log(local); //1
})();
★javascript:void(expression)
void:expressionを計算し、戻り値を返さず
<a href="javascript:void(0)"></a>
<a href="javascript:void(document.form.submit())">送信</a>
★jQuery vs. Native JavaScript
Selectors
Event
$('.el').on('event',
function() {
...
});
[].forEach.call(document.querySelectorAll('.el'),
function (el) {
el.addEventListener('event', function() {
...
}, false);
});
DOM
★functionについて
定義方法1 function func(){...} 解析時、コードの最初に移動させる
定義方法2 var func = function(){...}
function method(){
console.log(aaa); //function
console.log(bbb); //undefinded
function aaa(){
...
}
var bbb = function(){
...
};
}
functionもオブジェクトである
書き方1
function add() {
console.log(++add.count);
}
add.count = 0;
add(); //1
add(); //2
書き方2
function add() {
if(!arguments.callee.count){
arguments.callee.count = 0;
}
console.log(++arguments.callee.count);
}
add(); //1
add(); //2
※arguments.calleeはfunctionのこと、つまりadd()
new漏れを防ぐ
function Person(name) {
if (!(this instanceof arguments.callee)) {
return new arguments.callee(name);
}
this.name = name;
}
var p1 = new Person('Andy');
var p2 = Person('Andy');
console.log(p1.name);
console.log(p2.name);
new vs func
function Person(name, age){
this.name = name;
this.age = age;
//if(this.constructor === arguments.callee){
//if(this instanceof arguments.callee){
if(this instanceof Person){
console.log('new');
}else{
console.log('func');
}
}
var p = new Person('Andy', 30); // new
Person(); // func
Curry化
function add(x, y){
if(typeof y === 'undefined'){
return function(y){
return x + y;
};
}
return x + y;
}
console.log( add(1) ); //function
console.log( add(1, 2) ); //3
console.log( add(1)(2) ); //3
自動実行
//書き方1
(function(){
...
}());
(function(){
...
})();
(function(data){
...
}('dummy'));
//書き方2
({
name:'Andy',
getName: function(){
return this.name;
},
init: function(){
console.log( this.getName() );
}
}).init();
CSS
var el = document.querySelector(".content");
$(el).addClass("someClass");
$(el).removeClass("someClass");
$(el).toggleClass("someClass");
if($(el).hasClass("someClass"))
$(el).css({
background: "#FF0000",
"box-shadow": "1px 1px 5px 5px red",
width: "100px",
height: "100px",
display: "block"
});
var el = document.querySelector(".content");
el.classList.add("someClass");
el.classList.remove("someClass");
el.classList.toggle("someClass");
if(el.classList.contains("someClass"))
el.style.background = "#FF0000";
el.style.width = "100px";
el.style.height = "100px";
el.style.display = "block";
el.style.boxShadow = "1px 1px 5px 5px red";
Ajax
$.get('url', function (data) {
...
});
$.post('url', {data: data}, function (data) {
...
});
var xhr = new XMLHttpRequest();
xhr.open('GET', url);
xhr.onreadystatechange = function (data) {
...
}
xhr.send();
var xhr = new XMLHttpRequest()
xhr.open('POST', url);
xhr.onreadystatechange = function (data) {
...
}
xhr.send({data: data});
★クラスの対象作成
function Car(color, doors){
this._color = color;
this._doors = doors;
this.drivers = new Array("Mike","John");
}
Car.prototype.showColor = function() {
alert(this._color);
};
var car1 = new Car("red", 5);
var car2 = new Car("blue", 3);
car1.drivers.push("Andy");
alert(car1.drivers); //"Mike","John","Andy"
alert(car2.drivers); //"Mike","John"
★メソッドの呼び方(10種)
console.log(1);
(_ => console.log(2))();
eval('console.log(3);');
console.log.call(null, 4);
console.log.apply(null, [5]);
new Function('console.log(6)')();
Reflect.apply(console.log, null, [7])
Reflect.construct(function(){console.log(8)}, []);
Function.prototype.apply.call(console.log, null, [9]);
Function.prototype.call.call(console.log, null, 10);
new (require('vm').Script)('console.log(11)').runInThisContext();
★bind用法
thisの問題を解決
var andy = new Person('Andy');
setTimeout(function() {
andy.sayHello(); // ×
}, 3000)
setTimeout(andy.sayHello.bind(andy), 3000);
関数の引数を束縛
var mul = function(a, b) {...}
mul(2, 3);
// 一つ目の関数を束縛
var mul2 = mul.bind(null, 2);
mul2(4);
// 二つ目の関数も束縛
var mul2x5 = mul2.bind(null, 5);
mul2x5();