前回のブログに引き続き、分かりやすく、詳しく
ブログアドレス:2023年Webフロントエンド開発のJavaScript基盤(5)基盤が完成_周暁のブログがんばれ - CSDNブログ
学習内容
スコープ、変数の昇格、クロージャーなどの言語機能を学び、JavaScript の理解を深め、変数の割り当てと関数の宣言の簡潔な構文を習得し、コードの冗長性を減らします。
- スコープがプログラムの実行に与える影響を理解する
- プログラムの実行範囲を分析する能力
- クロージャーの性質を理解し、クロージャーを使用して分離スコープを作成する (重要)
- 変数巻き上げと関数巻き上げとは何かを理解する
- アロー関数などの簡潔な構文を習得し、残りのパラメーターを解析する (強調)
1. 範囲
プログラムの実行に対するスコープの影響とスコープ チェーンの検索メカニズムを理解し、クロージャ関数を使用して分離されたスコープを作成し、グローバル変数の汚染を回避します。
Scope (スコープ) は、変数にアクセスできる「スコープ」を指定し、この「スコープ」のままでは変数にアクセスできません
スコープは、グローバル スコープとローカル スコープに分けられます。
1.1 ローカルスコープ
ローカルスコープは、関数スコープとブロックスコープに分けられます。
1.2 機能範囲
関数内で宣言された変数は、関数内でのみアクセスでき、外部から直接アクセスすることはできません
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<script>
//声明conunter 函数
function conunter(x,y){
//函数内部声明的变量
const s = x + y
console.log(s) // 输出18
}
//调用counter 函数
conunter(10,8) // 传入实参
//直接在外部访问 函数内部的变量s
console.log(s) // 报错
</script>
</body>
</html>
要約:
- 関数内で宣言された変数は、関数外ではアクセスできません
- 関数パラメーターは、関数内のローカル変数でもあります
- 異なる関数内で宣言された変数は相互にアクセスできません
- 関数が実行された後、関数内の変数は実際にクリアされます
1.3 ブロックスコープ
JavaScript で {} でラップされたコードはコード ブロックと呼ばれ、コード ブロック内で宣言された変数は外部からアクセスできなくなります (おそらく)。
コードの実装効果は次のとおりです。
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<script>
//代码块
{
//内部声明变量
let age = 12
console.log(age) //12
}
//代码块外部使用 变量age
console.log(age) // 报错
let flag = true
if(flag){
//str 只能在该代码块中被访问
let str = "代码块内部"
console.log(str) // 正常输出 代码块内部
}
//外部访问str 变量
console.log(str) //报错
for(let t = 1; t<=6;t++){
//t 只能在该代码块中被访问
console.log(t); //正常输出
}
//变量t 定义在for循环里
//在外部访问 t
console.log(t) //报错
</script>
</body>
</html>
変数に加えて、JavaScript には yield があります. 定数と変数の本質的な違いは [定数には値が必要であり、再割り当てが許可されていない] です. 定数値はオブジェクトであり、特に再割り当てを許可するプロパティとメソッドです.
<script>
// 设置常量 必须要有值
const version = '1.0.0';
// 常量 不能重新赋值
// version = '1.0.1';
// 常量值为对象类型
const user = {
name: '小明',
age: 18
}
// 不能重新赋值
user = {};
// 属性和方法允许被修改
user.name = '小小明';
user.gender = '男';
</script>
要約:
- let で宣言された変数はブロックスコープを生成し、var はブロックスコープを生成しません. var が変数を設定すると、外部からもアクセスできます.
- const で宣言された定数にもブロックスコープがあります
- 異なるコード ブロック間の変数は相互にアクセスできません
- let または const を使用することをお勧めします
注: let と const は開発中に使い分けられることが多く、ある値が変更されるのが心配な場合は、const を使用して定数として宣言するしかありません。
1.4 グローバルスコープ
<script>タグと.jsファイルの【最外層】はいわゆるグローバルスコープで、ここで宣言した変数は関数内でもアクセス可能です。
<script>
// 此处是全局
function sayHi() {
// 此处为局部
}
// 此处为全局
</script>
次のコードに示すように、グローバル スコープで宣言された変数は、他の任意のスコープからアクセスできます。
<script>
// 全局变量 name
const name = '小明'
// 函数作用域中访问全局
function sayHi() {
// 此处为局部
console.log('你好' + name)
}
// 全局变量 flag 和 x
const flag = true
let x = 10
// 块作用域中访问全局
if(flag) {
let y = 5
console.log(x + y) // x 是全局的
}
</script>
要約:
- window オブジェクトに動的に追加されるプロパティもデフォルトでグローバルであり、お勧めしません!
- 関数内でキーワードなしで宣言された変数はグローバル変数であり、推奨されません! ! !
- グローバル変数が汚染されるのを防ぐために、グローバル変数をできるだけ少なく宣言する
JavaScript のスコープは、プログラムが実行されるときの基本的なメカニズムです. このメカニズムを理解することは、コードの記述方法を標準化し、スコープによって引き起こされる文法エラーを回避するのに役立ちます.
1.5 スコープチェーン
スコープ チェーンとは何かを説明する前に、コードを見てみましょう。
<script>
// 全局作用域
let a = 1
let b = 2
// 局部作用域
function f() {
let c
// 局部作用域
function g() {
let d = 'yo'
}
}
</script>
関数内に新しい関数を作成することができ、f 関数内で作成された新しい関数 g が新しい関数スコープを生成するため、スコープが入れ子の関係にあることがわかります。
下図のように、親子関係のスコープが連鎖して連鎖構造を形成しており、スコープ連鎖の名称はこれに由来します。
スコープ チェーンは基本的に変数の検索メカニズムです. 関数が実行されると, 最初に現在の関数スコープ内の変数を検索します. 現在のスコープが見つからない場合, グローバル スコープになるまで親スコープを段階的に検索します. 、次のコードに示すように:
実験について詳しくはこちら
<script>
// 全局作用域
let a = 1
let b = 2
// 局部作用域
function f() {
let c
// let a = 10;
console.log(a) // 1 或 10
console.log(d) // 报错
// 局部作用域
function g() {
let d = 'yo'
// let b = 20;
console.log(b) // 2 或 20
}
// 调用 g 函数
g()
}
console.log(c) // 报错
console.log(d) // 报错
f();
</script>
要約:
- ネストされた関係のスコープが連結されて、スコープ チェーンが形成されます。
- 小さいものから大きいものへの規則に従って、同じスコープ チェーン内の変数を検索します
- 子スコープは親スコープにアクセスできますが、親スコープは子スコープにアクセスできません
考える:
1. スコープ チェーンの本質は何ですか? Ø スコープ チェーンは、本質的には変数検索メカニズムの根底にあります。 2. スコープ チェーン検索のルールは何ですか? Ø 現在の関数スコープ内の変数の検索を優先します Ø 見つからない場合は、グローバル スコープまで親スコープを段階的に検索します
2. JSガベージコレクションの仕組み(要理解)
2.1 ガベージコレクションの仕組みとは
GCと呼ばれるガベージコレクション(ガベージコレクション)
JS でのメモリの割り当てと回復は自動的に行われ、メモリは使用されていないときにガベージ コレクターによって自動的に回復されます。
ガベージ コレクターの村のため、JS はメモリ管理の問題をあまり気にする必要はないと多くの人が考えています。
しかし、JS のメモリ管理メカニズムを理解していないと、メモリ リークが発生しやすくなります (メモリを再利用できません)。
使用されず、時間内に解放されないメモリは、メモリ リークと呼ばれます。
2.2 メモリのライフサイクル
JS 環境で割り当てられたメモリには、通常、次のライフサイクルがあります。
- メモリ割り当て: 変数、関数、およびオブジェクトを宣言すると、システムは自動的にメモリを割り当てます。
- メモリ使用量: 変数、関数などを使用したメモリの読み取りと書き込み。
- メモリの回復: 使用後、不要になったメモリはガベージ コレクションによって自動的に再利用されます
- 説明します:
- 一般に、グローバル変数はリサイクルされません (ページのリサイクルをオフにします)
- 通常、ローカル変数の値は、使用されていないときに自動的に再利用されます。
要約:
1.ガベージコレクションの仕組みとは?Ø 略して GC Ø JS でのメモリの割り当てと回復は自動的に行われ、使用されていないときはガベージ コレクタによってメモリが自動的に回復されます
2. メモリ リークとは何ですか? Ø 使用されなくなり、時間内に解放されないメモリは、メモリ リークと呼ばれます
3. 記憶のライフサイクルとは? Ø メモリの割り当て、メモリの使用、およびメモリの再利用 Ø グローバル変数は通常再利用されません; 通常の状況では、ローカル変数の値は、使用されていない場合、自動的に再利用されます
Expansion-JS ガベージ コレクション メカニズム - アルゴリズムの説明:
スタック スペース割り当ての違い:
1.1 スタック (オペレーティング システム): オペレーティング システムは、関数のパラメーター値、ローカル変数などを自動的に割り当てて解放し、基本的なデータ型をスタックに入れます。
1.2 ヒープ (オペレーティング システム): 通常、プログラマによって割り当てられ、解放されます. プログラマが解放しない場合は、ガベージ コレクション メカニズムによって回復されます. 複雑なデータ型はヒープに配置されます.
参照カウントとマーク スイープの 2 つの一般的なブラウザ ガベージ コレクション アルゴリズムを以下に紹介します。
1.1 参照カウント方法:
「使用されなくなったメモリ」を定義する IE で採用されている参照カウント アルゴリズムは、オブジェクトがそれへの参照を持っているかどうかを確認することであり、参照がない場合、オブジェクトはリサイクルされます。 アルゴリズム: 1. 番号を追跡して記録する参照数 2. 1 回参照された場合, その回数を記録する 1, 複数の参照が蓄積される ++ 3. 参照を減らす場合は 1 を引く -- 4. 参照回数が 0 の場合はメモリを解放する
let a = new Object() // 引用次数初始化为1
let b = a // 引用次数2,即obj被a和b引用
a=null // 引用次数1
b=null // 引用次数0,
... // GC回收此引用类型在堆空间中所占的内存
しかし、この方法にはいくつかの問題もあります。つまり、ネストされた参照 (循環参照) です。
2 つのオブジェクトが相互に参照されている場合、それらは使用されなくなりますが、ガベージ コレクターはリサイクルされず、メモリ リークが発生します。
function fn(){ // fn引用次数为1,因为window.fn = fn,会在window=null即浏览器关闭时回收
let A = new Object() // A: 1
let B = new Object() // B: 1
A.b = B // B: 2
B.a = A // A: 2
}
// A对象中引用了B,B对象中引用了A,两者引用计数都不为0,永远不会被回收。
// 若执行无限多次fn,那么内存将会被占满,程序宕机
fn();
// 还有就是这种方法需要一个计数器,这个计数器可能要占据很大的位置,因为我们无法知道被引用数量多少。
それらの参照カウントは決して 0 にならないからです。このような相互参照が多数存在すると、多数のメモリ リークが発生します。
1.2 マークアンドスイープ法
作戦はMark(マーク)とSweep(クリア)の2段階に分かれています。
マーク段階:
- 実行時に、メモリ内のすべての変数を 0 (ガベージ) としてマークします。
- 各ルート オブジェクトからトラバースし、非ガベージ変数を 1 としてマークする
スイープステージ:
- 0 とマークされたすべての変数のメモリを解放します
function fn(a){ // 开始执行此函数时,将其作用域中a、B以及匿名函数标记为0
alert(a) // 0
let B = new Object() // 0
return function (){ // 由于这里return出去会被其他变量引用,故标记变为1
altert(B) // 由于这里的闭包,B的标记变为1
}
... // 执行函数完毕,销毁作用域,在某个GC回收循环时会清理标记为0的变量a,B和匿名函数被保留了下来即非垃圾变量
}
let fn2 = fn(new Object())
// 补充一下:fn和fn2作为window.fn和window.fn2,标记一直为1,仅仅当手动设置fn=null和fn2=null才会标记为0
3. 締めくくり(重要)
Closure は特別な種類の関数で、関数のスコープ内の変数にアクセスするために使用できます。次のコードに示すように、コード形式から、クロージャーは戻り値としての関数です。
クロージャーの意味: 内部関数 + 外部関数変数
閉鎖の適用: データのプライバシーを実現します。統計関数呼び出し
クロージャーは、関数の呼び出し回数をカウントするように記述されています
<body>
<script>
// 1. 闭包含义 : 内层函数 + 外层函数变量
// function outer() {
// const a = 1 //外层函数变量
//
// function f() { //内层函数
// console.log(a)
// }
//
// f()
//
// }
// outer()
// 2. 闭包的应用: 实现数据的私有.统计函数的调用次数
// let count = 1
// function fn() {
// count++
// console.log(`函数被调用${count}次`)
// }
// 不影响外部count = 1 的值,实现数据的私有
// 3. 闭包的写法 统计函数的调用次数
function outer() {
let count = 1
function fn() {
count++
console.log(`函数被调用${count}次`)
}
return fn
}
const re = outer()
// const re = function fn() {
// count++
// console.log(`函数被调用${count}次`)
// }
re()
re()
// const fn = function() { } 函数表达式
// 4. 闭包存在的问题: 可能会造成内存泄漏
</script>
</body>
要約:
1. 閉鎖をどう理解するか?
-
-
- クロージャー = 内部関数 + 外部関数の変数
-
それは次のように私に夜明けしました:
2.閉鎖の機能は何ですか?
-
-
- データを閉じ、操作を提供し、外部から関数内の変数にアクセスすることもできます
-
-
-
- クロージャーは、関数が操作対象のデータ (環境) に関連付けられ、データを非公開にするため、便利です。
-
3. 閉鎖によってどのような問題が発生する可能性がありますか?
-
-
- メモリーリーク
-
4. 変数昇格
目標: 変数巻き上げとは何かを学ぶ
- JS の初心者は、変数の巻き上げに慣れるのに多くの時間を費やすことが多く、予期しないバグが発生することがよくあります.このため、ES6 ではブロックレベルのスコープが導入され、let または const を使用して変数が宣言され、コードの記述がより標準化され、人間化されたものになります。
- 変数の昇格は、JavaScript では比較的 "奇妙な" 現象であり、宣言される前に変数にアクセスできます (var 宣言変数にのみ存在します)。
<script>
// 访问变量 str
console.log(str + 'world!');
// 声明变量 str
var str = 'hello ';
</script>
注: 1. 変数が宣言されずにアクセスされると、構文エラーが報告されます. 2. 変数は var 宣言の前にアクセスされ、変数の値は未定義です. 3. 宣言された変数の変数昇格はありません. let / const. 4. 変数昇格は 5. 実際の開発では、同じスコープ内で変数を宣言してからアクセスすることをお勧めします
質問:
1. 変数の巻き上げを引き起こすために変数を宣言するために使用されるキーワードはどれですか?
-
-
-
- だった
-
-
2. 変数昇格のプロセスとは?
-
-
-
- 最初に var 変数を現在のスコープの前に上げます
- 変数の割り当てではなく、変数の宣言のみが巻き上げられます
- 次に、コードを順番に実行します
-
-
var を使用して変数を宣言することはお勧めしません
5. 高度な機能
関数パラメーターのデフォルト値、動的パラメーター、および残りのパラメーターの詳細を理解し、関数アプリケーションの柔軟性を向上させ、アロー関数の構文と通常の関数との違いを知る
1.機能改善
関数の昇格は変数の昇格に似ています。つまり、関数を宣言する前に呼び出すことができます。
<script>
// 调用函数
foo()
// 声明函数
function foo() {
console.log('声明之前即被调用...')
}
// 不存在提升现象
bar() // 错误
var bar = function () {
console.log('函数表达式不存在提升现象...')
}
</script>
まとめ: 1. 関数昇格により、関数の宣言と呼び出しがより柔軟にできる 2. 関数式に昇格現象がない 3. 関数昇格が同じスコープに現れる
2. 関数パラメータ
関数パラメーターの使用方法の詳細により、関数の適用の柔軟性が向上します。
2.1 デフォルト値
<script>
// 设置参数默认值
function sayHi(name="小明", age=18) {
document.write(`<p>大家好,我叫${name},我今年${age}岁了。</p>`);
}
// 调用函数
sayHi();
sayHi('小红');
sayHi('小刚', 21);
</script>
要約:
- 関数がパラメータのデフォルト値であることを宣言するときに仮パラメータに値を割り当てる
- パラメータがデフォルト値をカスタマイズしない場合、パラメータのデフォルト値は未定義です
- 関数の呼び出し時に対応する実パラメータが渡されない場合、パラメータのデフォルト値が実パラメータとして渡されます。
2.2 動的パラメータ
引数は、関数内の組み込み疑似配列変数であり、関数を呼び出すときに渡されるすべての実際のパラメーターを含みます。
<script>
// 求生函数,计算所有参数的和
function sum() {
// console.log(arguments)
let s = 0
for(let i = 0; i < arguments.length; i++) {
s += arguments[i]
}
console.log(s)
}
// 调用求和函数
sum(5, 10)// 两个参数
sum(1, 2, 4) // 三个参数
</script>
要約:
- 引数は疑似配列です
- 引数の役割は、関数の実パラメータを動的に取得することです
2.3 残りのパラメータ
<script>
function config(baseURL, ...other) {
console.log(baseURL) // 得到 'http://baidu.com'
console.log(other) // other 得到 ['get', 'json']
}
// 调用函数
config('http://baidu.com', 'get', 'json');
</script>
要約:
- ... は、最後の関数パラメーターの前に配置され、冗長な実パラメーターを取得するために使用される構文記号です。
- ... によって取得された残りの引数は真の配列です
3.スプレッドオペレーター
The Expansion operator ( ... ) expands an array. 一般的なアプリケーション シナリオ: 配列の最大 (最小) 値の検索、配列の結合など。
スプレッド演算子または残りの引数
残りのパラメータ: 関数パラメータの使用、真の配列展開演算子の取得: 配列での使用、配列展開
3.アロー機能(強調)
アロー関数は、関数を宣言するための簡潔な構文です. 通常の関数と本質的な違いはありません. 違いは構文形式に反映されます.
3.1 基本的な書き方
<body>
<script>
// const fn = function () {
// console.log(123)
// }
// 1. 箭头函数 基本语法
// const fn = () => {
// console.log(123)
// }
// fn()
// const fn = (x) => {
// console.log(x)
// }
// fn(1)
// 2. 只有一个形参的时候,可以省略小括号
// const fn = x => {
// console.log(x)
// }
// fn(1)
// // 3. 只有一行代码的时候,我们可以省略大括号
// const fn = x => console.log(x)
// fn(1)
// 4. 只有一行代码的时候,可以省略return
// const fn = x => x + x
// console.log(fn(1))
// 5. 箭头函数可以直接返回一个对象
// const fn = (uname) => ({ uname: uname })
// console.log(fn('刘德华'))
</script>
</body>
要約:
- アロー関数は式関数であるため、関数の昇格はありません
- アロー関数のパラメータが 1 つしかない場合は、括弧 ( ) を省略できます
- アロー関数の関数本体が1行のコードの場合は中括弧 { } を省略でき、戻り値として自動的に返されます。
- 写真のように:
コードは以下のように表示されます:
<body>
<script>
// const fn = function () {
// console.log(123)
// }
// 1. 箭头函数 基本语法
// const fn = () => {
// console.log(123)
// }
// fn()
// const fn = (x) => {
// console.log(x)
// }
// fn(1)
// 2. 只有一个形参的时候,可以省略小括号
// const fn = x => {
// console.log(x)
// }
// fn(1)
// // 3. 只有一行代码的时候,我们可以省略大括号
// const fn = x => console.log(x)
// fn(1)
// 4. 只有一行代码的时候,可以省略return
// const fn = x => x + x
// console.log(fn(1))
// 5. 箭头函数可以直接返回一个对象
// const fn = (uname) => ({ uname: uname })
// console.log(fn('刘德华'))
</script>
</body>
3.2 アロー関数のパラメータ
アロー関数には引数がありません。使用できるのは... 実パラメータを動的に取得する
<body>
<script>
// 1. 利用箭头函数来求和
const getSum = (...arr) => {
let sum = 0
for (let i = 0; i < arr.length; i++) {
sum += arr[i]
}
return sum
}
const result = getSum(2, 3, 4)
console.log(result) // 9
</script>
3.3 アロー関数 this
アロー関数は独自の this を作成せず、スコープ チェーンの前のレベルから this を継承するだけです。
<script>
// 以前this的指向: 谁调用的这个函数,this 就指向谁
// console.log(this) // window
// // 普通函数
// function fn() {
// console.log(this) // window
// }
// window.fn()
// // 对象方法里面的this
// const obj = {
// name: 'andy',
// sayHi: function () {
// console.log(this) // obj
// }
// }
// obj.sayHi()
// 2. 箭头函数的this 是上一层作用域的this 指向
// const fn = () => {
// console.log(this) // window
// }
// fn()
// 对象方法箭头函数 this
// const obj = {
// uname: 'pink老师',
// sayHi: () => {
// console.log(this) // this 指向谁? window
// }
// }
// obj.sayHi()
const obj = {
uname: 'pink老师',
sayHi: function () {
console.log(this) // obj
let i = 10
const count = () => {
console.log(this) // obj
}
count()
}
}
obj.sayHi()
</script>
アロー関数は独自の this を作成せず、スコープ チェーンの前のレベルからの this のみを使用します。