JS変数オブジェクトとは何ですか? JS変数オブジェクトと注意事項の詳細説明

php是最好的语言
リリース: 2018-07-23 11:10:50
オリジナル
2658 人が閲覧しました

JavaScript では、変数オブジェクトとは何ですか?この記事では、まず変数オブジェクトの概念、コンテキスト内の変数オブジェクトがどのように実行されるか、変数オブジェクト内のコードがどのように処理されるか、そして最後に変数とは何かを紹介します。

変数オブジェクトは、実行コンテキストスコープチェーンの間のブリッジです。
ネタバレ注意、謎の this が実行コンテキストに存在します。
もちろん、これが何なのかについては、いくつかのセクションに分けて徹底的に説明します(実際にはこれは非常に簡単です)。

次に本文を入力しましょう。

1. 実行コンテキストには何が含まれますか?

実行コンテキストはオブジェクトとして抽象的に理解できます。
各実行コンテキストには、関連するコードの実行の進行状況を追跡するために使用されるいくつかのプロパティ (コンテキスト状態とも呼ばれます) があります。

構造図を使用して説明します。

执行上下文环境 object

Variable Object は、変数オブジェクトを表します。
Scope Chainはスコープチェーンを表します。
thisValue は神秘的な this を表します。

スコープチェーンとこれについては後で説明します。今日はまず変数オブジェクトについて理解します。

2. 変数オブジェクト

変数オブジェクトは、コンテキストに関連付けられた特別なオブジェクトであり、コンテキスト内で定義される変数と関数宣言を格納します。変数オブジェクト) は、実行コンテキストに関連付けられたデータのスコープです。これは、コンテキストに関連付けられた特別なオブジェクトで、コンテキスト内で定義された変数と関数宣言を保存します。

Variable Object

(Variable Object -- 略して VO) は、実行コンテキストに関連する特別なオブジェクトを参照する抽象的な概念です: - 変数
(var) - 関数宣言。
(関数宣言、FD と略記)- 関数の仮パラメータ
(引数)変数オブジェクトは通常の ECMAScript オブジェクトであると仮定します:

VO = {};
ログイン後にコピー

前述のように、VO は実行コンテキスト属性:

activeExecutionContext = {
  VO: {
    // 上下文数据 (vars, FD, arguments)
  }
}
ログイン後にコピー

変数オブジェクトは抽象的な概念であるため、変数オブジェクトの名前を通じて直接アクセスすることはできませんが、他のメソッドを通じて間接的にアクセスできます。たとえば、グローバル コンテキスト環境の変数オブジェクトには属性ウィンドウ ( DOM では) 変数オブジェクト自体を参照でき、グローバル コンテキストの別の属性である this もグローバル コンテキストの変数オブジェクトを指します。

例:

var a = 2;

function foo (num) {
   var b = 5;
}

(function exp () {
   console.log(111);
})

foo(10);
ログイン後にコピー

ここでの対応する変数オブジェクトは次のとおりです:

// 全局上下文环境的变量对象
VO(globalContext) = {
   // 一些全局环境初始化时系统自动创建的属性: Math、String、Date、parseInt等等
   ···

   // 全局上下文的变量对象中有一个属性可以访问到自身,在浏览器中这个属性是 window ,在 node 中这个属性是 global
   window: global

   // 自己定义的属性
   a: 10,
   foo: <reference to function>
};

// foo 函数上下文的变量对象
VO(foo functionContext) = {
   num: 10,
   b: 5
};
ログイン後にコピー

注: 関数式は変数オブジェクトには含まれません。

3. さまざまな実行コンテキストの変数オブジェクト

実行コンテキストには、

グローバル コンテキスト、関数コンテキスト、および eval() コンテキストが含まれます。

グローバル コンテキスト内の変数オブジェクト

ここで、まずグローバル オブジェクトとは何かを理解します:

全局对象(global object)是指在进入任何执行上下文之前就已经创建了的对象。
这个对象只有一份,它的属性在程序中的任何地方都可以访问,全局对象的生命周期终止于程序退出的那一刻。
ログイン後にコピー

グローバル オブジェクトが初期化されると、システムは次のような一連のプリミティブ プロパティを作成して初期化します: Math、String、 Date、parseInt、window などに続いて、グローバル コンテキストで独自に定義したグローバル変数が続きます。 DOM では、グローバル オブジェクトの window プロパティはグローバル オブジェクト自体を参照でき、グローバル コンテキストの this プロパティもグローバル オブジェクトを参照できます。

// 全局执行上下文环境
EC(globalContext) = {
   // 全局对象(全局上下文环境的变量对象) 
   global: {
      Math: <...>,
      String: <...>,
      ...
      ...
      window: global     // 引用全局对象自身
   },

   // this 属性
   this: global

   // 作用域链
   ...
}
ログイン後にコピー

例:

var a = 10;

console.log(a);               // 10
console.log(window.a);        // 10
console.log(this.a);          // 10
ログイン後にコピー

したがって、グローバル コンテキストでは、変数オブジェクトはグローバル オブジェクトによって表されます。

関数コンテキストの変数オブジェクト

関数コンテキストでは、変数オブジェクトはアクティブ オブジェクト AO (Active Object) によって表されます。

VO(functionContext) = AO
ログイン後にコピー

アクティブ オブジェクトは、関数コンテキストに入るときに作成され、関数の argument プロパティを通じて初期化されます。引数もオブジェクトです。

AO = {
   arguments: {
      ...
   }
}
ログイン後にコピー

arguments はアクティブなオブジェクトのプロパティであり、次のプロパティが含まれます:

1. callee - 現在の関数への参照

2. length - 実際に渡されるパラメーターの数
3. Indexes - Index はい "1": "aa" などの文字列型の整数は配列型に似ており、arguments[1] を通じてアクセスすることもできますが、配列メソッド (push、pop など) は使用できません。さらに、properties-indexes の値と実際に渡されるパラメータは共有されます。一方が変更されると、他方も変更されます。

例:

function foo (x, y, z) {

   // 声明的函数参数数量
   console.log(foo.length);      // 3

   // 实际传递进来的参数数量
   console.log(arguments.length);      // 2

   // arguments 的 callee 属性指向当前函数
   console.log(arguments.callee === foo)   // true

   // 参数共享
   console.log(x === arguments[0]);      // true
   console.log(x);      // 10

   arguments[0] = 20;
   console.log(x);   // 20

   x = 30;
   console.log(arguments[0]);    // 30

   // 但是注意,没有传递进来的参数 z ,和第3个索引值是不共享的
   z = 40;
   console.log(arguments[2]);      // undefined

   arguments[2] = 50;
   console.log(z);      // 40
}

foo(10, 20);
ログイン後にコピー

4. コードの処理方法

セクション 1 では、JS コードのコンパイル プロセスについて説明しました。これは、コードが実行される前を意味します。コードが最初にコンパイルされ、字句スコープが設定されてから実行されるまでには、数マイクロ秒かかります。

次に、実行コンテキストのコードを 2 つの段階に分けて処理します。 1. 実行コンテキストを入力します (プリコンパイル済み) 2. コードを実行します

而变量对象的修改变化和这两个阶段是紧密相关的。
并且所有类型的执行上下文都会有这2个阶段。

进入执行上下文

当引擎进入执行上下文时(代码还未执行),VO 里已经包含了一些属性:
1. 函数的所有形参(如果是函数执行上下文)
由名称和对应值组成的一个变量对象的属性被创建,如果没有传递对应的实参,那么由名称和 undefined 组成的一种变量对象的属性也会被创建。

2.所有的函数声明(Function Declaration - FD)
由名称和对应值(函数对象 function object)组成的一个变量对象的属性被创建,如果变量对象已经存在相同名称函数的属性,则完全替换这个属性。

3.所有的变量声明(Variable Declaration - var)
由名称和对应值(在预编译阶段所有变量值都是 undefined)组成的一个变量对象的属性被创建,如果变量名和已经声明的形参或者函数相同,则变量名不会干扰已经存在的这类属性,如果已经存在相同的变量名,则跳过当前声明的变量名。

注意:变量碰到相同名称的变量是忽略,函数碰到相同名称的函数是覆盖。

举个例子:

function test(a, b, c) {
            
    console.log(a); // 函数体a
    console.log(b);  // 20
    function a() {
         console.log(1);
    }
    var a = 100;
    console.log(a);  // 100
    var b = 2;
    console.log(b); // 2

}
test(10,20,30);
ログイン後にコピー
function foo (a, b) {
   var c = 5;

   function bar () {};

   var d = function _d () {};

   (function f () {});
}

foo(10);
ログイン後にコピー

当进入带有实参10的 foo 函数上下文时(预编译时,此时代码还没有执行),AO 结构如下:

AO(foo) = {
   a: 10,
   b: undefined,

   c: undefined,
   bar: <reference to FunctionDelcaration "bar">,
   d: undefined 
};
ログイン後にコピー

注意,函数表达式 f 并不包含在活动对象 AO 内。
也就是说,只有函数声明会被包含在变量对象 VO 里面,函数表达式并不会影响变量对象。

行内函数表达式 _d 则只能在该函数内部可以使用, 也不会包含在 VO 内。

这之后,就会进入第2个阶段,代码执行阶段。

代码执行

在这个阶段,AO/VO 已经有了属性(并不是所有的属性都有值,大部分属性的值还是系统默认的初始值 undefined)。

AO 在代码执行阶段被修改如下:

AO[&#39;c&#39;] = 5;
AO[&#39;d&#39;] = <reference to FunctionDelcaration "_d">
ログイン後にコピー

再次要提醒大家,因为函数表达式 _d 已经保存到了声明的变量 d 上面,所以变量 d 仍然存在于 VO/AO 中。我们可以通 d() 来执行函数。但是函数表达式 f 却不存在于 VO/AO 中,也就是说,如果我们想尝试调用 f 函数,不管在函数定义前还是定义后,都会出现一个错误"f is not defined",未保存的函数表达式只有在它自己的定义或递归中才能被调用。

再来一个经典例子:

console.log(x);      // function

var x = 10;
console.log(x);      // 10

x = 20;

function x () {};

console.log(x);      // 20
ログイン後にコピー

这里为什么是这样的结果呢?

上边我们说过,在代码执行之前的预编译,会为变量对象生成一些属性,先是形参,再是函数声明,最后是变量,并且变量并不会影响同名的函数声明。

所以,在进入执行上下文时,AO/VO 结构如下:

AO = {
   x: <reference to FunctionDeclaration "x">

   // 在碰到变量声明 x 时,因为已经存在了函数声明 x ,所以会忽略
}
ログイン後にコピー

紧接着,在代码执行阶段,AO/VO 被修改如下:

AO[&#39;x&#39;] = 10;
AO[&#39;x&#39;] = 20;
ログイン後にコピー

希望大家可以好好理解变量对象,对于理解我们后边要讲的作用域链有很大的帮助。

5. 变量

有一些文章说过:

不管是使用 var 关键字(在全局上下文)还是不使用 var 关键字(在任何地方),都可以声明一个变量。

请记住,这是错误的观念。

任何时候,变量都只能通过使用 var 关键字来声明(ES6 之前)

a = 10;
ログイン後にコピー

上面的赋值语句,仅仅是给全局对象创建了一个新属性(在非严格模式,严格模式下会报错),但注意,它不是变量。“不是变量”并不是说它不能被改变,而是指它不符合ECMAScript 规范中变量的概念。

让我们通过一个例子来看一下两者的区别:

console.log(a);        // undefined
console.log(b);        // 报错,b is not defined

b = 10;
var a = 20;
ログイン後にコピー

只要我们很好的理解了:变量对象、预编译阶段和执行代码阶段,就可以迅速的给出答案。

预编译(进入上下文)阶段:

VO = {
   a: undefined
}
ログイン後にコピー

我们可以看到,因为 b 不是通过 var 声明的,所以这个阶段根本就没有 b ,b 只有在代码执行阶段才会出现。但是在这个例子中,还没有执行到 b 那就已经报错了。

我们稍微更改一下示例代码:

console.log(a);      // undefined

b = 10;
console.log(b);             // 10 代码执行阶段被创建
console.log(window.b);      // 10
console.log(this.b);        // 10

var a = 20;
console.log(a);      // 20 代码执行阶段被修改
ログイン後にコピー

关于变量,还有一个很重要的知识点。

变量不能用 delete 操作符来删除。

a = 10;

console.log(window.a);    // 10

console.log(delete a);    // true

console.log(window.a);    // undefined

var b = 20;
console.log(window.b);    // 20

console.log(delete b);    // false

console.log(window.b);    // 20
ログイン後にコピー

注意:这个规则在 eval() 上下文中不起作用。

eval(&#39;var a = 10;&#39;);
console.log(window.a);    // 10

console.log(delete a);    // true

console.log(window.a);    // undefined
ログイン後にコピー

 相关推荐:

 js高级面向对象和组件开发视频教程

js 多种变量定义(对象直接量,数组直接量和函数直接量)_javascript技巧

以上がJS変数オブジェクトとは何ですか? JS変数オブジェクトと注意事項の詳細説明の詳細内容です。詳細については、PHP 中国語 Web サイトの他の関連記事を参照してください。

関連ラベル:
ソース:php.cn
このウェブサイトの声明
この記事の内容はネチズンが自主的に寄稿したものであり、著作権は原著者に帰属します。このサイトは、それに相当する法的責任を負いません。盗作または侵害の疑いのあるコンテンツを見つけた場合は、admin@php.cn までご連絡ください。
最新の問題
人気のチュートリアル
詳細>
最新のダウンロード
詳細>
ウェブエフェクト
公式サイト
サイト素材
フロントエンドテンプレート