JS 변수 객체란 무엇입니까? JS 변수 객체 및 주의사항에 대한 자세한 설명

php是最好的语言
풀어 주다: 2018-07-23 11:10:50
원래의
2667명이 탐색했습니다.

JavaScript에서 변수 객체란 무엇인가요? 이 글에서는 먼저 변수 객체의 개념을 소개하고, 컨텍스트 내 변수 객체가 어떻게 실행되는지, 변수 객체 안의 코드가 어떻게 처리되는지, 그리고 마지막으로 변수란 무엇일까요?

변수 개체실행 컨텍스트스코프 체인 사이를 연결하는 다리입니다.
스포일러 경고, 실행 컨텍스트에 신비한 this가 존재합니다!
물론, 이것이 무엇인지를 철저하게 설명하기 위해 몇 가지 별도의 섹션을 사용할 것입니다(실제로 이것은 매우 간단합니다).

다음으로 본문을 입력해보겠습니다.

1. 실행 컨텍스트에는 무엇이 포함되나요?

실행 컨텍스트를 객체로 추상적으로 이해할 수 있습니다.
각 실행 컨텍스트에는 관련 코드의 실행 진행 상황을 추적하는 데 사용되는 몇 가지 속성(컨텍스트 상태라고도 함)이 있습니다.

저는 구조 다이어그램을 사용하여 설명합니다.

执行上下文环境 object

Variable Object는 변수 개체를 나타냅니다.
Scope Chain은 스코프 체인을 나타냅니다.
thisValue는 신비한 this를 나타냅니다.

스코프 체인과 이에 대해서는 나중에 논의하겠습니다. 오늘은 먼저 변수 객체를 이해하겠습니다.

2. 변수 객체

변수 객체는 실행 컨텍스트와 관련된 데이터의 범위이며 컨텍스트 내에서 정의되는 변수 및 함수 선언을 저장하는 특수 객체입니다. 변수 객체)는 실행 컨텍스트와 연관된 데이터 범위입니다. 컨텍스트에 정의된 변수 및 함수 선언을 저장하는 컨텍스트와 연결된 특수 개체입니다.

Variable Object

(Variable Object - 줄여서 VO)는 실행 컨텍스트와 관련된 특수 객체를 참조하는 추상 개념입니다. - Variable
(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)是指在进入任何执行上下文之前就已经创建了的对象。
这个对象只有一份,它的属性在程序中的任何地方都可以访问,全局对象的生命周期终止于程序退出的那一刻。
로그인 후 복사

전역 개체가 초기화되면 시스템은 수학, 문자열, 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
로그인 후 복사

함수 컨텍스트에 들어갈 때 활성 개체가 생성됩니다. 함수의 인수 속성을 통해 초기화됩니다. 인수도 객체입니다.

AO = {
   arguments: {
      ...
   }
}
로그인 후 복사

arguments는 활성 객체의 속성이기도 하며 다음 속성을 포함합니다.

1. 호출 수신자 - 현재 함수에 대한 참조

2. 길이 - 실제로 전달된 매개변수 수
3. indexes - index 예 "1": "aa"와 같은 문자열 유형의 정수는 배열 유형과 유사하며 인수[1]를 통해서도 액세스할 수 있지만 배열 메소드(push, pop 등)는 사용할 수 없습니다. 또한 속성 인덱스의 값과 실제로 전달된 매개변수는 공유됩니다.

예:

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 코드의 컴파일 프로세스 중 하나를

사전 컴파일

이라고 합니다. 이는 코드가 실행되기 전을 의미합니다. . 코드를 먼저 컴파일하고 어휘 범위를 지정한 다음 실행하는 데 몇 마이크로초가 걸립니다. 그런 다음 실행 컨텍스트 코드를 두 단계로 나누어 처리할 수 있습니다.

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 중국어 웹사이트의 기타 관련 기사를 참조하세요!

관련 라벨:
원천:php.cn
본 웹사이트의 성명
본 글의 내용은 네티즌들의 자발적인 기여로 작성되었으며, 저작권은 원저작자에게 있습니다. 본 사이트는 이에 상응하는 법적 책임을 지지 않습니다. 표절이나 침해가 의심되는 콘텐츠를 발견한 경우 admin@php.cn으로 문의하세요.
최신 이슈
인기 튜토리얼
더>
최신 다운로드
더>
웹 효과
웹사이트 소스 코드
웹사이트 자료
프론트엔드 템플릿