
JavaScriptのイベント処理:クリック、フォーム入力からバブリング・デリゲーションまで
はじめに JavaScriptのイベント処理は、インタラクティブなウェブアプリケーション開発の核心です。ユーザーのクリック、フォーム入力、キーボード操作など、あ […]
スコープとは、変数や関数がアクセス可能な範囲を決定する仕組みです。JavaScriptには主に以下のスコープがあります:
// グローバル変数
const globalVar = 'グローバル変数';
function checkGlobal() {
console.log(globalVar); // アクセス可能
}
checkGlobal();
console.log(globalVar); // アクセス可能
注意点:
function createGlobal() {
// var, let, constを付け忘れるとグローバル変数になる
accidentalGlobal = '意図しないグローバル変数';
}
createGlobal();
console.log(accidentalGlobal); // アクセス可能(非推奨)
function showLocal() {
// ローカル変数
const localVar = 'ローカル変数';
console.log(localVar); // アクセス可能
}
showLocal();
// console.log(localVar); // エラー(アクセス不可)
if (true) {
// let/constはブロックスコープ
let blockVar = 'ブロック変数';
const BLOCK_CONST = 'ブロック定数';
console.log(blockVar); // アクセス可能
}
// console.log(blockVar); // エラー(アクセス不可)
// console.log(BLOCK_CONST); // エラー(アクセス不可)
varの注意点:
if (true) {
var functionScoped = '関数スコープ';
}
console.log(functionScoped); // アクセス可能(ブロックスコープではない)
JavaScriptは変数を探す際、現在のスコープから外側のスコープへと順に探します。
const global = 'グローバル';
function outer() {
const outerVar = '外側';
function inner() {
const innerVar = '内側';
console.log(innerVar); // '内側'
console.log(outerVar); // '外側'
console.log(global); // 'グローバル'
}
inner();
// console.log(innerVar); // エラー(アクセス不可)
}
outer();
JavaScriptのスコープはコードを書いた時点で決定されます(実行時ではない)。
const name = 'グローバル';
function logName() {
console.log(name);
}
function wrapper() {
const name = 'ローカル';
logName(); // 'グローバル'(定義時のスコープが使われる)
}
wrapper();
関数が定義された時のスコープを「覚えて」いる機能。
function createCounter() {
let count = 0;
return function() {
count++;
return count;
};
}
const counter = createCounter();
console.log(counter()); // 1
console.log(counter()); // 2
即時実行関数式(IIFE:Immediately Invoked Function Expression)は、定義してすぐに実行される関数のことです。
// 基本構文
(function() {
// ここにコードを書く
})();
// ES6のアロー関数で
(() => {
// ここにコードを書く
})();
JavaScriptで「スコープの分離」や「グローバル汚染の防止」によく使われます。
// 👍 推奨
const localVar = '値';
// 👎 非推奨
var localVar = '値';
// ループ内でlet/constを使用
for (let i = 0; i < 5; i++) {
// iはループ内のみ有効
}
// グローバルを汚さない
const MyApp = {};
MyApp.utils = {};
console.log(hoistedVar); // undefined(エラーにならない)
var hoistedVar = '値';
// let/constはホイスティングされるがTDZ(Temporal Dead Zone)にある
// console.log(notHoisted); // エラー
let notHoisted = '値';
hoistedFunc(); // '動作する'
function hoistedFunc() {
console.log('動作する');
}
// 関数式はホイスティングされない
// notHoistedFunc(); // エラー
const notHoistedFunc = function() {};
const MyModule = (function() {
// プライベート変数
let privateVar = '外部からアクセス不可';
// プライベート関数
function privateMethod() {
return privateVar;
}
// 公開するインターフェース
return {
publicMethod: function() {
return privateMethod();
}
};
})();
console.log(MyModule.publicMethod()); // '外部からアクセス不可'
// console.log(MyModule.privateVar); // undefined
let x = 10;
function test() {
let x = 20;
console.log(x);
}
test();
console.log(x);
function printNumbers() {
for (var i = 0; i < 3; i++) {
console.log(i);
}
console.log('最後:', i);
}
printNumbers();
let counter = 0;
function increment() {
counter++;
console.log(counter);
}
var a = 1;
let b = 2;
{
var a = 3;
let b = 4;
console.log(a, b);
}
console.log(a, b);
createMultiplier(n)
は、引数をn倍する関数を返す。
const double = createMultiplier(2);
console.log(double(5)); // 10
console.log(value);
var value = 10;
function test() {
console.log('テスト');
}
let x = 1;
function outer() {
let x = 2;
function inner() {
console.log(x);
let x = 3;
}
inner();
}
outer();
function processData(data) {
if (data) {
var processed = data.toUpperCase();
console.log(processed);
}
console.log(processed); // アクセス可能だが非推奨
}
const Calculator = (function() {
// ここにプライベート変数/関数を定義
return {
// ここに公開メソッドを定義
add: function(a, b) { /* ... */ }
};
})();
for (var i = 0; i < 3; i++) {
setTimeout(function() {
console.log(i);
}, 100);
}
for (let j = 0; j < 3; j++) {
setTimeout(function() {
console.log(j);
}, 100);
}
createPrivateStack()
関数を実装しなさい。const stack = createPrivateStack();
stack.push(10);
stack.push(20);
console.log(stack.pop()); // 20
console.log(stack.items); // undefined(直接アクセス不可)
createScopeChain()
関数を実装しなさい。const scope = createScopeChain();
scope.set('x', 10);
scope.set('y', 20);
const childScope = scope.createChild();
childScope.set('x', 30);
console.log(childScope.get('x')); // 30
console.log(childScope.get('y')); // 20(親スコープから取得)
console.log(childScope.get('z')); // undefined
20
10
let x = 10;
function test() {
let x = 20;
console.log(x); // 20が出力される
}
test();
console.log(x); // 10が出力される
// varをletに変更
function printNumbers() {
for (let i = 0; i < 3; i++) {
console.log(i);
}
// console.log('最後:', i); // エラー(iはブロックスコープ内のみ)
}
const counterModule = (function() {
let counter = 0;
return {
increment: function() {
counter++;
console.log(counter);
}
};
})();
counterModule.increment();
3 4
3 2
var a = 1;
let b = 2;
{
var a = 3;
let b = 4;
console.log(a, b); // 3 4 ブロック内の値(letはブロックスコープ)
}
console.log(a, b); // 3 2 グローバルスコープの値(varはブロックスコープを無視して上書き)
createMultiplier
関数 function createMultiplier(n) {
return function(x) {
return x * n;
};
}
ホイスティング順番
var value;
function test() {
console.log('テスト');
}
console.log(value); // undefined
value = 10;
コードに順番の付け加え
// ①var value; 暗黙的value変数宣言
console.log(value); // ③ console.log()の実行
var value = 10; // ④ console.log()の実行
function test() { // ② function test()宣言とロード
console.log('テスト');
}
ReferenceError: Cannot access 'x' before initialization
// letのTDZ(一時的デッドゾーン)に引っかかる
let x = 1;
function outer() {
let x = 2;
function inner() {
console.log(x); // ブロックスコープの変数を参照するためエラーとなる
let x = 3;
}
inner();
}
outer();
function processData(data) {
if (data) {
let processed = data.toUpperCase();
console.log(processed);
}
// console.log(processed); // エラー(アクセス不可)
}
const Calculator = (function() {
let memory = 0;
function add(a, b) {
return a + b;
}
return {
add: add,
addToMemory: function(x) {
memory += x;
return memory;
}
};
})();
3 3 3 // varはループ終了後の値(関数スコープ)
0 1 2 // letはループごとに新しい変数(ブロックスコープ)
for (var i = 0; i < 3; i++) {
setTimeout(function() {
console.log(i); // 3回「3」が出力される
}, 100);
}
for (let j = 0; j < 3; j++) {
setTimeout(function() {
console.log(j); // 0, 1, 2 が順に出力される
}, 100);
}
createPrivateStack
関数function createPrivateStack() {
const items = [];
return {
push: function(item) {
items.push(item);
},
pop: function() {
return items.pop();
}
};
}
createScopeChain
関数function createScopeChain(parent) {
const vars = new Map();
return {
set: function(key, value) {
vars.set(key, value);
},
get: function(key) {
if (vars.has(key)) return vars.get(key);
if (parent) return parent.get(key);
return undefined;
},
createChild: function() {
return createScopeChain(this);
}
};
}