关于javascript:JS变量和执行环境

63次阅读

共计 2951 个字符,预计需要花费 8 分钟才能阅读完成。

1. 数据类型

1.1 根本类型和援用类型的值

在 JavaScript 中,变量的数据类型分为根本类型和援用类型。
根本类型指那些简略的数据段,包含 Boolean、Number、String、Undefined、Null、Symbol,根本数据类型是间接按值拜访的,能够操作保留在变量中的理论的值。
援用类型的值是保留在内存中的对象,JavaScript 不能间接拜访内存中的地位,而是通过指针将变量与内存中的对象分割起来。因而在操作对象时,实际上是在操作对象的援用。

1.2 栈内存和堆内存

为了更加深刻的了解 JS 变量根本类型和援用类型的区别,咱们还须要理解 JS 变量的值在内存中的存储形式。

1.2.1 栈内存

栈,是一种数据结构,有着先进后出、后进先出的特点。就像一个羽毛球筒,只有一个口(即是进口也是入口),最先进入的球只能最初拿进去:

如上图,入栈的程序:1、2、3,出栈的程序:3、2、1

1.2.2 栈内存中的数据存储

栈内存用来保留根本类型的变量或变量的指针,举个例子:

var num1 = 3;

变量 num1 在内存中的保留模式为:

对于根本类型的变量,当咱们进行复制操作时,比方这个例子:

var num1 = 3;
vat num2 = num1;

此时 JS 会创立一个新值并将新值调配给新的变量,内存中的变量对象示意为:

变量 num2 失去的是一个全新的值,与变量 num1 中的值无关。

1.2.3 堆内存

堆内存与栈内存不同,对于变量值的保留没有须要遵循的法则。

1.2.4 堆内存中的数据存储

堆内存用来保留对象类型的变量值,对象的内容和大小也会随时变动。而此时变量对象中的变量保留的是一个指向堆内存中对象的指针,因为存在这种援用关系,对象也被称为援用类型。例:

var num1 = 3;
var obj = {a: 1}

当咱们对对象类型的值进行复制操作时,实际上 copy 的是对象的指针,因而两个变量指向的是同一个对象,例:

var obj 1 = {a: 1}
var obj2 = obj1;

在内存中体现为:

此时,批改变量 obj1 的属性等同于批改变量 obj2,例:

var obj1 = {a: 1};
vat obj2 = obj1;
obj1.b = 2;
alert(obj2.b); // 2

1.3 包装对象

对于援用类型的值,咱们能够为其增加属性和办法,也能够扭转和删除其属性和办法:

var person = new Object();
person.name = 'Ian';
alert(person.name); // 'Ian'

然而,咱们不能给根本类型的值增加属性,只管这样做不会导致谬误:

var name = 'Ian';
name.age = 29;
alert(name.age); // undefined

咱们可能听过一种说法,根本类型的值是没有属性或办法的。可有意思的是,上述给根本类型的值增加属性的操作不会报错,咱们还能读取某些根本类型的值,比方咱们能够读到 String 类型值的 length 属性:

var name = 'Ian';
name.length; // 3
那么根本类型值的属性是从哪里来的呢?

其实,当咱们操作根本类型值的属性时,JS 会创立一个 长期的包装对象,咱们操作的实际上是这个包装对象的属性,而这个包装对象是用完立刻销毁的。在下面的例子中,name.length 的 length 属性实际上来自包装对象。

var name = 'Ian';
name.length;
var nameObj = new String(name); // 包装对象
nameObj.length; // 3

因为包装对象用完立刻销毁的个性,咱们对根本类型值的属性批改都是“有效”的:

var name = 'Ian';
name.length = 4;
name.length; // 3

下面的例子中,当咱们再次读取 name.length 时,实际上又创立了一个新的包装对象,读取的是新包装对象的 length 属性。

2. 执行环境

执行环境(也称环境)是 JavaScript 重要的概念,执行环境定义了变量或函数有权拜访的数据。每个执行环境都有一个相关联的 变量对象,这个变量对象中保留了以后执行环境中定义的所有变量和函数。

2.1 全局执行环境

JS 代码执行时最外层的执行环境称为 全局执行环境 ,因为宿主环境的不同,示意全局执行环境的对象也不同,在 web 浏览器中将window 对象作为全局执行环境,全局的变量和函数都是作为 window 对象的属性和办法申明的。

2.2 执行环境栈

函数也有执行环境,当开始执行一个函数时,函数执行环境会被压入一个 执行环境栈 中,在函数执行性实现后,执行环境栈将函数环境弹出,将主导权交给下层环境。咱们通过一段代码来展现执行环境栈的变动过程:

var outerName = 'Ian';
function changeName() {
  var innerName = 'Jack';
  outerName = innerName;
}
alert(outerName); // 'Jack'

在初始状态下,执行环境栈是这样的:

当开始执行函数 changeName 时,函数的环境被压入环境栈:

当函数 changeName 执行实现后,该环境被弹出环境栈并销毁(环境栈复原到上一步的状态),保留在其中的变量和函数的定义也随之销毁。全局环境只到程序退出,在 web 浏览器中敞开网页或浏览器程序时才会销毁。
JS 这种执行机制也引出了另一个重要的概念——作用域链。

2.3 作用域链

JS 代码在进入一个新的执行环境时会创立一个作用域链(scope chain),用来规定对以后执行环境有权拜访的变量或函数的拜访程序。作用域链是由执行环境的 变量对象 组成的,如果是函数环境就将函数的 流动对象 作为变量对象。
作用域链的最前端永远是以后执行环境的变量对象,上一级变量对象来自上一层执行环境,直到全局环境的变量对象。当咱们拜访一个变量时,会先在以后的变量对象中查找,如果找不到就会沿着作用域链逐级向上查找,直到返回变量的值或报错(变量未声明)。
上面咱们通过一个例子来了解这种构造:

var outerName = 'Ian';
function changeName() {
  var innerName = 'Jack';
  outerName = innerName;
  function alertName() {alert(outerName); // 'Jack'
  }
}
alert(innerName); // Uncaught ReferenceError: innerName is not defined

在下面这段代码中,函数能够拜访到内部定义的变量 outerName,而在全局环境中不能拜访函数外部定义的变量 innerName。当代码进入 alertName 执行环境时,作用域链如下图所示:

3. 小结

在 JavaScript 中,变量的数据类型分为根本类型和援用类型,他们具备以下特点:

  • 根本类型的值大小固定保留在栈内存中,当复制根本类型的值时,会创立一个值的正本;
  • 援用类型的值是对象,保留在堆内存中,而变量保留的是对象的援用,当复制援用类型的值时,复制的是对象的援用,两个变量都指向同一个对象;
  • 全局执行环境和函数执行环境;
  • 在进入一个新的执行环境时会创立一个作用域链(scope chain),用来规定对以后执行环境有权拜访的变量或函数的拜访程序;
  • 函数部分环境能拜访父级(直到全局)环境的变量,而全局(父级)环境不能拜访函数的局部变量;

正文完
 0