变量
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。