let コマンドと const コマンド
1.させてください
- コンセプト:ブロックスコープ。
- 変数のプロモーションはありません。宣言する前に変数を使用すると、エラーが報告されます。
- 一時的なデッド ゾーン: 閉じられたスコープが形成され、コード ブロックでは、
let
変数が宣言されるまで変数は使用できません。文法的には になります「一時的なデッドゾーン」(時間的デッドゾーン、略してTDZ)。
if (true) {
// TDZ开始
tmp = 'abc'; // ReferenceError
console.log(tmp); // ReferenceError
let tmp; // TDZ结束
console.log(tmp); // undefined
tmp = 123;
console.log(tmp); // 123
}
上記のコードでは、変数 tmp が let コマンドで宣言される前は、変数 tmp の「デッド ゾーン」に属しています。
4. 繰り返しの宣言を許可しない:let
同じスコープ内で同じ変数を繰り返し宣言することができます。
2. ブロックレベルのスコープ
1. ブロックレベルのスコープが必要なのはなぜですか?
最初のシナリオでは、内部変数が外部変数をオーバーライドする可能性があります。
var tmp = new Date();
function f() {
//函数变量提升
console.log(tmp);
if (false) {
var tmp = 'hello world';
}
}
f(); // undefined
上記のコードの本来の意味は、if
コードブロックの外側は外層を使用しtmp变量
、内層は内層を使用することですtmp变量
。ただし、関数 f を実行した後の出力結果は、undefined
次のとおりです。変数巻き上げこれにより、tmp
内側の層の変数がtmp
外側の層の変数をオーバーライドします。
2 番目のシナリオでは、カウントに使用されるループ変数がグローバル変数として漏洩します。(ごく普通)
var s = 'hello';
for (var i = 0; i < s.length; i++) {
console.log(s[i]);
}
console.log(i); // 5
上記のコードでは変数はi
ループの制御にのみ使用されていますが、ループ終了後も変数は消えずにグローバル変数に漏れ込みます。
2. ブロックレベルのスコープ
let
JavaScript用に追加ブロックスコープ。
function f1() {
let n = 5;
if (true) {
let n = 10;
}
console.log(n); // 5
}
実行後の出力5。これは、外側のコード ブロックが内側のコード ブロックの影響を受けないことを意味します。var
定義された変数が2 回使用された場合n
、最終的な出力値は 10 になります。
3.定数
- 基本的な使用法:
const
読み取り専用の定数を宣言します。宣言した定数の値は変更できないため、初期値を割り当てる必要があります。
const PI = 3.1415;
PI // 3.1415
PI = 3;
// TypeError: Assignment to constant variable.
const
宣言された変数は値を変更してはなりません (単純なデータ型を指します)。つまり、const
変数が宣言された後は、値を変更する必要があります。すぐに初期化する、後の割り当てのために残すことはできません。const
コマンドと同じスコープを持ちlet
、それが宣言されているブロックレベルのスコープ内でのみ有効です。const
コマンドによって宣言された定数も昇格されません。一時的なデッドゾーン、宣言された位置の後にのみ使用できます。const
実際、保証されているのは、変数の値が変更できないことではなく、変数が指すメモリ アドレスに格納されているデータが変更できないことです。単純なタイプのデータ (数値、文字列、ブール値) の場合、値は変数が指すメモリ アドレスに格納されるため、定数と同等です。しかし、複合データ (主にオブジェクトと配列) の場合、変数が指すメモリ アドレスは実際のデータへのポインタにすぎず、ポインタが固定されていること (つまり、常に別の固定アドレスを指していること) のみが保証されますconst
。 ). それが指すデータ構造が可変であるかどうかに関しては、完全に制御不能です。したがって、オブジェクトを定数として宣言する場合には細心の注意を払う必要があります。
const foo = {
};
// 为 foo 添加一个属性,可以成功
foo.prop = 123;
foo.prop // 123
// 将 foo 指向另一个对象,就会报错
foo = {
}; // TypeError: "foo" is read-only
上記のコードでは、定数はfoo
オブジェクトを指すアドレスを格納します。このアドレスのみが不変です。つまり、foo
別のアドレスを指すことはできませんが、オブジェクト自体は変更可能であるため、新しい属性を追加することができます。
変数の構造化代入
1. 配列の分割代入
- 基本的な使い方
以前は、変数に値を割り当てるには、値を直接指定することしかできませんでした。
let a = 1;
let b = 2;
let c = 3;
ES6 ではこのように記述できます。
let [a, b, c] = [1, 2, 3];
多くの例を挙げてください
let [foo, [[bar], baz]] = [1, [[2], 3]];
foo // 1
bar // 2
baz // 3
let [ , , third] = ["foo", "bar", "baz"];
third // "baz"
let [x, , y] = [1, 2, 3];
x // 1
y // 3
let [head, ...tail] = [1, 2, 3, 4];
head // 1
tail // [2, 3, 4]
let [x, y, ...z] = ['a'];
x // "a"
y // undefined
z // []
構造化が失敗した場合、変数の値は に等しくなりますundefined
。
let [foo] = [];
// foo = undefined
let [bar, foo] = [1];
// foo = undefined
もう一つの状況は、不完全な解体つまり、等号の左側のパターンは、等号の右側の配列の一部とのみ一致します。この場合でも、分解は成功する可能性があります。
let [x, y] = [1, 2, 3];
x // 1
y // 2
let [a, [b], d] = [1, [2, 3], 4];
a // 1
b // 2
d // 4
構造体の場合Set
、配列の分割代入を使用することもできます。
let [x, y, z] = new Set(['a', 'b', 'c']);
x // "a"
- デフォルト
代入を分割すると、デフォルト値を指定できます。
let [foo = true] = [];
foo // true
let [x, y = 'b'] = ['a']; // x='a', y='b'
let [x, y = 'b'] = ['a', undefined]; // x='a', y='b'
2. オブジェクト分割代入
- 基本的な使い方
let {
foo, bar } = {
foo: 'aaa', bar: 'bbb' };
foo // "aaa"
bar // "bbb"
オブジェクトの分割は、1 つの重要な点で配列とは異なります。配列の要素は順序どおりに配置され、変数の値はその位置によって決まりますが、オブジェクトのプロパティには順序がなく、正しい値を取得するには変数の名前がプロパティと同じである必要があります。
let {
bar, foo } = {
foo: 'aaa', bar: 'bbb' };
foo // "aaa"
bar // "bbb"
let {
baz } = {
foo: 'aaa', bar: 'bbb' };
baz // undefined
構造化が失敗した場合、変数の値は と等しくなりますundefined
。
オブジェクトの分割代入により、既存のオブジェクトのメソッドを変数に簡単に代入できます。
// 例一
let {
log, sin, cos } = Math;
// 例二
const {
log } = console; // console.log
log('hello') // hello
上記のコードの例 1 では、Math
オブジェクトの対数、サイン、コサインのメソッドを対応する変数に割り当てています。これは、より使いやすくなります。例 2 は変数console.log
に代入します。log
実際オブジェクト分解代入の内部メカニズムは、まず同じ名前の属性を見つけて、それを対応する変数に割り当てることです。実際に割り当てられるのは前者ではなく後者です。
let {
foo: baz } = {
foo: 'aaa', bar: 'bbb' };
baz // "aaa"
foo // error: foo is not defined
上記のコードでは、foo
一致するパターンbaz
は変数です。baz
実際に割り当てられるのはパターンではなく変数ですfoo
。
3. 文字列分割代入
文字列は分解することもできます。これは、この時点で文字列が配列のようなオブジェクトに変換されるためです。
const [a, b, c, d, e] = 'hello';
a // "h"
b // "e"
c // "l"
d // "l"
e // "o"
4. 数値とブール値の構造化代入
代入を分割するとき、等号の右側が数値またはブール値の場合、最初にオブジェクトに変換されます。
let {
toString: s} = 123;
s === Number.prototype.toString // true
let {
toString: s} = true;
s === Boolean.prototype.toString // true
構造化代入のルールは、等号の右側の値がオブジェクトまたは配列でない限り、最初にそれをオブジェクトに変換することです。未定義および null はオブジェクトに変換できないため、分解して代入するとエラーが報告されます。
let {
prop: x } = undefined; // TypeError
let {
prop: y } = null; // TypeError
5. 目的
(1) 変数の値を交換する
let x = 1;
let y = 2;
[x, y] = [y, x];
(2) 関数から複数の値を返す
関数は 1 つの値のみを返すことができ、複数の値を返したい場合は、配列またはオブジェクトでのみ返すことができます。構造化代入を使用すると、これらの値を抽出するのが非常に便利です。
// 返回一个数组
function example() {
return [1, 2, 3];
}
let [a, b, c] = example();
// 返回一个对象
function example() {
return {
foo: 1,
bar: 2
};
}
let {
foo, bar } = example();
(3) JSONデータの抽出
割り当ての構造化は、JSON オブジェクト内のデータを抽出する場合に特に便利です。
let jsonData = {
id: 42,
status: "OK",
data: [867, 5309]
};
let {
id, status, data: number } = jsonData;
console.log(id, status, number);
// 42, "OK", [867, 5309]
(4) マップ構造をトラバースする
for...of
ループを走査し、変数の構造化割り当てと連携してキー名とキー値を取得すると非常に便利です。
const map = new Map();
map.set('first', 'hello');
map.set('second', 'world');
for (let [key, value] of map) {
console.log(key + " is " + value);
}
// first is hello
// second is world
(5)入力モジュールの指定方法
モジュールをロードするとき、多くの場合、どのメソッドをインポートするかを指定する必要があります。代入を分割すると、入力ステートメントが非常に明確になります。
const {
SourceMapConsumer, SourceNode } = require("source-map");