ホームページ  >  記事  >  ウェブフロントエンド  >  JS の 3 つの山の詳細な説明: スコープとクロージャ、プロトタイプとプロトタイプ チェーン、非同期とシングル スレッド

JS の 3 つの山の詳細な説明: スコープとクロージャ、プロトタイプとプロトタイプ チェーン、非同期とシングル スレッド

青灯夜游
青灯夜游転載
2023-04-18 17:16:511118ブラウズ

js はフロントエンドのバックボーンとして機能します。では、JavaScript の三大山とは何かご存知ですか?

1️⃣ スコープとクロージャ

スコープ は現在のコードを参照します コンテキスト変数と関数の可視性とライフサイクルを制御します。最大の機能は変数を分離することで、異なるスコープにある同じ名前の変数が競合しないようにします。

スコープ チェーン は、現在のスコープで値が見つからない場合、グローバル スコープに到達するまで上位スコープをクエリすることを意味します。このような検索プロセスはチェーンです。形成されたものをスコープチェーンと呼びます。 [推奨学習: JavaScript ビデオ チュートリアル ]

スコープは階層構造にスタックでき、子スコープは親スコープにアクセスできますが、その逆はできません。

スコープは 4 つのタイプに細分できます: グローバル スコープ モジュール スコープ 関数スコープ #、ブロックレベル スコープ

##グローバル スコープ: コードはプログラム内にあり、どこからでもアクセスできます。ウィンドウオブジェクトなど。ただし、グローバル変数はグローバル名前空間を汚染し、名前の競合を引き起こしやすくなります。

モジュール スコープ: 元のスクリプトは小さくて単純だったため、初期の js 構文にはモジュール定義がありませんでした。その後、スクリプトがますます複雑になるにつれて、モジュール型ソリューション (AMD、CommonJS、UMD、ES6 モジュールなど) が登場しました。通常、モジュールはファイルまたはスクリプトであり、このモジュールには独自の独立したスコープがあります。

関数スコープ: 名前が示すように、関数によって作成されるスコープ。クロージャはこのスコープ内で生成されますが、これについては後で別途紹介します。

ブロックレベルのスコープ: js 変数のプロモーションには変数カバレッジや変数汚染などの設計上の欠陥があるため、ES6 ではこれらの問題を解決するためにブロックレベルのスコープ キーワードが導入されています。典型的なケースは、let の for ループと var の for ループです。

// var demo
for(var i=0; i<10; i++) {
    console.log(i);
}
console.log(i); // 10

// let demo
for(let i=0; i<10; i++) {
    console.log(i);
}
console.log(i); //ReferenceError:i is not defined

スコープを理解したら、それについて話しましょう

クロージャ: 関数 A には関数 B が含まれており、関数 B は関数 A の変数を使用し、次に関数 B Aクロージャまたはクロージャは、関数 A の内部変数を読み取ることができる関数です。

クロージャは関数スコープ内のプロダクトであることがわかります。クロージャは外側の関数の実行と同時に作成されます。関数とそのバンドルへの参照の組み合わせです。周囲の環境状態。つまり、

クロージャは、内部関数 によって外部関数の変数が解放されないことです。

クロージャの特徴:

    関数内に関数があり、
  • 内部関数は外部関数のスコープにアクセスできます。
  • パラメータと変数は GC されず、常にメモリ内に存在します。
  • クロージャはメモリがある場所にのみ存在します。
したがって、クロージャを使用するとメモリが消費され、不適切に使用するとメモリ オーバーフローの問題が発生するため、関数を終了する前に、未使用のローカル変数をすべて削除する必要があります。特定のニーズがない場合、関数内に関数を作成することは賢明ではなく、クロージャーは処理速度とメモリ消費の点でスクリプトのパフォーマンスに悪影響を及ぼします。

クロージャの適用シナリオを以下にまとめます。

// demo1 输出 3 3 3
for(var i = 0; i < 3; i++) {
    setTimeout(function() {
        console.log(i);
    }, 1000);
} 
// demo2 输出 0 1 2
for(let i = 0; i < 3; i++) {
    setTimeout(function() {
        console.log(i);
    }, 1000);
}
// demo3 输出 0 1 2
for(let i = 0; i < 3; i++) {
    (function(i){
        setTimeout(function() {
        console.log(i);
        }, 1000);
    })(i)
}
/* 模拟私有方法 */
// 模拟对象的get与set方法
var Counter = (function() {
var privateCounter = 0;
function changeBy(val) {
    privateCounter += val;
}
return {
    increment: function() {
    changeBy(1);
    },
    decrement: function() {
    changeBy(-1);
    },
    value: function() {
    return privateCounter;
    }
}
})();
console.log(Counter.value()); /* logs 0 */
Counter.increment();
Counter.increment();
console.log(Counter.value()); /* logs 2 */
Counter.decrement();
console.log(Counter.value()); /* logs 1 */
/* setTimeout中使用 */
// setTimeout(fn, number): fn 是不能带参数的。使用闭包绑定一个上下文可以在闭包中获取这个上下文的数据。
function func(param){ return function(){ alert(param) }}
const f1 = func(1);setTimeout(f1,1000);
/* 生产者/消费者模型 */
// 不使用闭包
// 生产者
function producer(){
    const data = new(...)
    return data
}
// 消费者
function consumer(data){
    // do consume...
}
const data = producer()

// 使用闭包
function process(){
    var data = new (...)
    return function consumer(){
        // do consume data ...
    }
}
const processer = process()
processer()
/* 实现继承 */
// 以下两种方式都可以实现继承,但是闭包方式每次构造器都会被调用且重新赋值一次所以,所以实现继承原型优于闭包
// 闭包
function MyObject(name, message) {
  this.name = name.toString();
  this.message = message.toString();
  this.getName = function() {
    return this.name;
  };

  this.getMessage = function() {
    return this.message;
  };
}
// 原型
function MyObject(name, message) {
  this.name = name.toString();
  this.message = message.toString();
}
MyObject.prototype.getName = function() {
  return this.name;
};
MyObject.prototype.getMessage = function() {
  return this.message;
};

クロージャの概念は理解しているようですが、何かが足りないようですか?その意味はまだ終わっていません。私もクロージャで迷っていましたが、クロージャのライフサイクルを読んだ後、再び自分自身を発見しました。

JS の 3 つの山の詳細な説明: スコープとクロージャ、プロトタイプとプロトタイプ チェーン、非同期とシングル スレッド

学習したら、簡単なテストをしてみましょう

function test(a, b){
  console.log(b);
  return {
    test: function(c) {
      return test(c,a);
    }
  }
}

var a = test(100);a.test(101);a.test(102);
var b = test(200).test(201).test(202);
var c = test(300).test(301);c.test(302);

// undefined  100  100
// undefined  200 201
// undefined  300 301

2️⃣ プロトタイプとプロトタイプ チェーン

オブジェクト

prototype があり、各オブジェクトは内部のプロパティであるプロトタイプ(プロトタイプ)を初期化し、プロトタイプには共有プロパティやメソッドが格納されます。オブジェクトのプロパティにアクセスすると、JS エンジンはまず現在のオブジェクトにこのプロパティがあるかどうかを確認します。ない場合は、プロトタイプ オブジェクトにこのプロパティがあるかどうかを確認し、Object 組み込みオブジェクトが取得されるまで同様に確認します。このような検索プロセスは、プロトタイプ チェーンの概念を形成しました。

プロトタイプを理解するために最も重要なことは、__proto__、プロトタイプ、コンストラクターの関係を明確にすることです。最初にいくつかの概念を見てみましょう:

  • __proto__属性在所有对象中都存在,指向其构造函数的prototype对象;prototype对象只存在(构造)函数中,用于存储共享属性和方法;constructor属性只存在于(构造)函数的prototype中,指向(构造)函数本身。
  • 一个对象或者构造函数中的隐式原型__proto__的属性值指向其构造函数的显式原型 prototype 属性值,关系表示为:instance.__proto__ === instance.constructor.prototype
  • 除了 Object,所有对象或构造函数的 prototype 均继承自 Object.prototype,原型链的顶层指向 null:Object.prototype.__proto__ === null
  • Object.prototype 中也有 constructor:Object.prototype.constructor === Object
  • 构造函数创建的对象(Object、Function、Array、普通对象等)都是 Function 的实例,它们的 __proto__ 均指向 Function.prototype。

看起来是不是有点乱??别慌!!一张图帮你整理它们之间的关系

JS の 3 つの山の詳細な説明: スコープとクロージャ、プロトタイプとプロトタイプ チェーン、非同期とシングル スレッド

相同的配方再来一刀

const arr = [1, 2, 3];
arr.__proto__ === Array.prototype; // true
arr.__proto__.__proto__ === Object.prototype; // true
Array.__proto__ === Function.prototype; // true

3️⃣ 异步和单线程

JavaScript 是 单线程 语言,意味着只有单独的一个调用栈,同一时间只能处理一个任务或一段代码。队列、堆、栈、事件循环构成了 js 的并发模型,事件循环 是 JavaScript 的执行机制。

为什么js是一门单线程语言呢?最初设计JS是用来在浏览器验证表单以及操控DOM元素,为了避免同一时间对同一个DOM元素进行操作从而导致不可预知的问题,JavaScript从一诞生就是单线程。

既然是单线程也就意味着不存在异步,只能自上而下执行,如果代码阻塞只能一直等下去,这样导致很差的用户体验,所以事件循环的出现让 js 拥有异步的能力。

JS の 3 つの山の詳細な説明: スコープとクロージャ、プロトタイプとプロトタイプ チェーン、非同期とシングル スレッド

更多编程相关知识,请访问:编程教学!!

以上がJS の 3 つの山の詳細な説明: スコープとクロージャ、プロトタイプとプロトタイプ チェーン、非同期とシングル スレッドの詳細内容です。詳細については、PHP 中国語 Web サイトの他の関連記事を参照してください。

声明:
この記事はjuejin.cnで複製されています。侵害がある場合は、admin@php.cn までご連絡ください。