浅析JavaScript变量、执行环境、作用域链

本文深入探讨了JavaScript中变量的基本类型与引用类型的区别,并详细解释了执行环境的概念及其工作原理,包括变量对象、作用域链等内容。

变量

js里的变量可能且只能包含两种不同数据类型的值中的一种:基本类型值和引用类型值。

基本类型

基本类型有:Undefined,NUll,Boolean,Number和String,这些类型分别在内存中占有固定的大小空间,他们的值保存在栈内存,我们通过按值来访问

引用类型

除基本类型外的类型都是引用类型。如果赋值的是引用类型的值,由于这种值的大小不固定,因此不能把他们保存到栈内存中,则必须在堆内存中为这个值分配空间。但内存地址大小是固定的,因此可以将内存地址(对象的引用)保存在栈内存中。操作引用类型的值时,实际上操作的是对象的引用,引用类型的值是按引用访问的

看起来有点晦涩,画个图看看:



复制变量值

基本类型值的复制

当一个变量向另一个变量复制基本类型的值时,会把存储在变量中的值复制一份放为新变量分配的空间中。改变其中一个变量,不会影响另一个变量。

举例:

var number1 = 1;
var number2 = number1; // 此时number2保存的值为1
number2 = 2;
console.log(number1); // 1, 改变number的值并不会影响number1
复制代码

图解:

复制前:



复制后:



从图上可以看出来,两个值互相独立,单独操作number1或number2并不会影响另一个值

引用类型值的复制

当一个变量向另一个变量复制引用类型的值时,同样会把存储在变量中的值复制一份放为新变量分配的空间中。但改变其中一个变量,影响另一个变量。

举例:

var obj1 = {};
var obj2 = obj1;
obj2.color = 'red';
console.log(obj1.color);//此时obj1已经被改变
复制代码

图解:

复制前:



复制后:



可以看到,引用类型的值复制时,把引用复制给了新的变量,于是两个变量指向同一个对象,当改变obj2时会影响obj1.

执行环境

执行环境是javascript中最为重要的概念之一,执行环境定义了变量或函数有权访问的其他数据。每个环境都有一个与之关联的变量对象 (variable object),环境中定义的所有变量和函数都保存在这个对象中。

全局执行环境是最外围的执行环境,在web浏览器中,全局执行环境是window对象,因此,所有的全局变量的函数都是作为window的属性和方法创建的。

var name = 'abc';           //声明全局变量
function setName(){
	var newName = 'aaa'
	return newName;
}
      
alert(window.name);       
alert(window.setName()); 
复制代码

当执行环境内的代码执行完毕后,该环境被销毁,保存其中的变量和函数也随之销毁(除闭包外),如果是全局环境,则页面关闭后才会被销毁。

另一个例子:

var a = 1;              //在全局环境下声明变量a
function fn1(){		//在全局环境下声明函数fn1
  var a = 2;
  function fn2(){       //在fn1的局部环境下声明函数
    console.log(a);
  }
  function fn3(){       //在fn1的局部环境下声明函数fn3
    var a = 4;          //在fn3的局部环境下声明变量a
    fn2();
  }
  return fn3;
}

var fn = fn1() //由于引用了fn3,所以fn1调用完毕后,内部定义的函数、变量等并没有被销毁
复制代码

不同执行环境下的变量

变量没有在函数内声明或者声明的时候没有带var就是全局变量,在任何地方都可以访问。

去掉var之后的局部变量

var name = 'abc';           //定义全局变量
function setName(){
    newName = 'aaa'
    return newName;
}

setName()
console.log(newName)// aaa
复制代码

函数内的参数

函数的参数虽然没有使用var但仍然是局部变量。

function sayHi(name){
    console.log('Hello ' + name )
}
var name = 'Jet';
sayHi(name);

//相当于:
function sayHi(name){
    var name = arguments[0];//在函数内声明,局部变量
    console.log('Hello ' + name )
}
var name = 'Jet';
sayHi(name);
复制代码

没有块级作用域

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

console.log(i) //10, 此时依然可以访问变量i
复制代码

作用域链

当代码在一个环境中执行时,会创建变量对象(variable object)的一个作用域链。作用域链的用途是保证对执行环境有权访问的所有变量和函数的有序访问。作用域链的前端,始终都是当前执行的代码所在环境的变量对象。

还是上面的例子:

var a = 1
function fn1(){
  var a = 2
  function fn2(){
    console.log(a)
  }
  function fn3(){
    var a = 4
    fn2()
  }
  return fn3
}
var fn = fn1()
fn()
复制代码

在上面的例子中,函数fn2的作用域链包含三个对象:它自己的变量对象、fn1的变量对象,全局环境的变量对象,注意虽然在fn3中调用了,但fn3并不是fn2的外部环境。fn3不在fn2的作用域链中。

图例:



矩形表示特点的执行环境,其中,内部的环境可以通过作用域链访问所有外部环境,但外部环境不能访问内部环境中的任何变量和函数,这些环境之间的联系是线性,有次序的。也可以看到fn2的环境跟fn3的环境没有任何关系。每个局部环境开始时会先在自己的变量对象中搜索变量和函数名,如果搜索不到则再次搜索上一级作用域链。fn2里没有变量a,于是它往上找,在fn1的执行环境中找到了a。

参考:

JavaScript高级程序设计


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值