JS笔记——关于变量和作用域问题

时间:2021-08-28 06:09:11

1.前言

1.1变量

ECMAScript的变量是松散类型的,所谓松散类型就是可以用来保存任何类型的数据。换句话说,每个变量仅仅是用于保存值的占位符而已。

1.2.数据类型

ECMAScript中有5中简单的数据类型(也称为基本数据类型):Undefined、Null、Boolean、Number、String。还有一种复杂数据类型——Object。

ECMAScript不支持任何穿件自定义类型的机制,而所有值最终都将是上述6种数据类型之一。

2.基本类型和引用类型的值

2.1基本类型和引用类型对值的操作

5种基本数据类型是按值访问的,可以操作保存在变量中的实际的值。而对于引用类型来说,由于引用类型的值是保存在内存中的对象,与其他语言不同,JS不允许直接访问内存中的位置,也就是说不能直接操作对象的内存空间。故,在操作对象时,实际上市在操作对象的引用而不是实际的对象,为此,引用类型的值时按引用访问的。

2.2基本类型复制变量值

对于基本类型来说,如果从一个变量向另一个变量上复制基本类型的值,会在变量对象上创建一个新值,然后把该值复制到位新变量分配的位置上来。
上图:

JS笔记——关于变量和作用域问题

2.3引用类型复制变量值

对于引用类型来说,如果从一个变量向另一个变量复制引用类型的值时,同样也会将存储在变量对象中的值复制一份放到为新变量分配的内存中。不同的是,这个值的副本实际上是一个指针,而这个指针指向存储在堆中的一个对象。复制操作结束后,两个变量实际上将易用同一个对象。因此,改变其中一个变量,将会改变另一个对象。

JS笔记——关于变量和作用域问题

2.4传递参数

ECMAScript中所有函数的参数都是按值传递的。也就是说,把函数外部的值复制给函数内部的参数,就和把值从一个变量复制到另一个变量一样。基本雷翔值的传递就如同基本类型变量的复制一样,而引用类型值的传递,则如同引用类型变量的复制一样。

在向参数传递基本类型的值时,被传递的值会被复制给一个局部变量。在向参数传递引用类型的值时,会把这个值在内存中的地址复制给一个局部变量,因此这个局部变量的变化会反映在函数的外部。

请看这个例子:

var z = 10;

function foo(){
console.log(z);
}

(function(funArg){
var z = 20;
funArg();
})(foo);

答案是10,由于在向参数传递引用类型的值的时候是按值传递的,传递进来的参数和全局函数的foo都是指向内存堆中的同一对象,因此在调用funArg()的时候就相当于在全局中调用foo。所以输出10。

为了说明是按值传递的,请看下面代码

function setName(obj){
obj.name = "fiona";
obj = new Object();
obj.name = "fiona123";
}
var person = new Object();
setName(person);
console.log(person.name); //输出结果是fiona

如果是按引用传递的,那么此时的输出结果应当是fiona123。实际上,当在函数内部重写obj时,这个变量引用的就是一个局部变量了。而这个局部变量会在函数执行完毕后立即被销毁。需要注意的是,在这个函数内部,obj和person引用的是同一个对象。换句话说,即使这个变量是按值传递的,obj也会按引用来访问同一个对象。

3.执行环境及作用域

执行环境定义了变量或函数有权访问的其他数据,决定了它们各自的行为。 每个执行环境都有一个与之关联的变量对象,环境中定义的所有变量和函数都保存在这个变量中。

当一个代码在一个执行环境中,会创建变量对象的一个作用域链。作用域链的用途,是保证对执行环境有权访问的所有变量和函数的有序访问。

标识符解析是沿着作用域链一级一级地搜索标识符的过程。

3.1延长作用域链

  • try-catch语句的catch块;
  • with语句

这两个语句都会在作用域链的前端添加一个变量对象。对with语句来说,会将制定的对象添加到作用域链中。对catch语句来说,会创建一个新的变量对象,其中包含的是被抛出错误对象的声明。

3.2没有块级作用域

(ES6已支持,详细参考关键字let)

最经典的例子就是在for语句中声明了一个var i;在for语句外部仍然可以访问到这个变量。

3.3垃圾收集

  • 标记清除
  • 引用计数