求关注

【javascript基础】3、变量和作用域

前言

这篇和大家说一下javascript中的变量和作用域,由于是将基础嘛,主要给大家捋一下知识,不想翻开书复习的道友可以看一下,打算刚开始学习javascript的同学可以扫一眼。

PS:jQuery源码交流群( 239147101)等你来,群里高手云集,让我受益匪浅,尽量少灌水。

变量

javascript中有两种变量,分别是基本类型和引用类型,基本类型是Null,Undefined,String,Boolean,Number这五种,前面简单的介绍了。引用类型是指Object,Array,Date,RegExp,Function这些。创建这两种变量是类似的,都是创建一个变量然后给它赋值。不同的原因主要是在内存中位置和操作不同。

基本类型

基本类型比较简单,基本类型的值保存在栈中。看例子

var v = 1;

基本类型变量在内存中的表示,没有涉及堆

看看复制变量之后在内存中的表示,执行的代码如下

var v = 1;
var n = v;

解释一下,如果一个变量把基本类型的值复制给另一个变量时,会创建一个新的相同的值,把这个新的值赋值给新的变量,这样,内存中就有两个一样的值了,分别是新的变量和就的变量指向的值,虽然值是一样的。

引用类型

应用类型的变量创建和基本类型的创建是一样的,主要看在内存中的存储方式,代码

var obj = new Object();

可以看到,变量obj存储的是一个地址(个人的理解),其实obj的值是一个指针,指向了堆中的对象,可以找到堆中的new出来的对象。再看看复制一个变量时的情况

var obj = new Object();
var o = obj;

解释一下,这和基本类型的复制是不同的,我们可以看出,当一个变量向另一个变量复制引用类型的值时,也会将存储在变量对象中的值复制一个给新的变量。复制的这个值实际上就是一个指针,指向堆中的一个对象,由于指针是相同的,所以知指向的对象就是同一个对象,此时无论你该哪一个变量,都会影响另一个变量,因为指向的对象是用一个嘛,例如

var obj = new Object();
var o = obj;
o.name = "hainan";
console.log(obj.name);//"hainan"

而基本类型则不会,看例子

var v = 1;
var n = v;
v = 100;
console.log(n);//1
console.log(v);//100

这个比较简单,不懂的话看上面的那个内存的图就懂了。

区别

上面说了复制的那个区别,还有一个就是基本类型不能添加属性和方法,而引用类型则可以添加,看例子吧,很简单

//基本类型
var peo = "hainan";
peo .age = 25;
console.log(peo.age);//undefined,但是不报错
//引用类型
var people = new Object();
people.age = 25;
console.log(people.age);//25

函数传参

javascript中函数参数都是按值传递的,也就是把函数外部的值复制给函数内部的参数,和复制变量一样,无论传递的是什么类型,都和复制变量一样。首先看一下传递基本类型的例子

function test(n){
  return n = n+100;  
}
var num = 10;
var result = test(num);
console.log(num);//10 没有发生变化
console.log(result);//110

简单解释一下,调用函数时,传递一个基本类型的参数num给函数,此时,复制值给内部的参数n,这样num和n变量都有了相同的值,但是,这两个之间没有任何的关系,只是值相同而已,想想前面的图就清楚了,内部的变量也就是参数n改变了之后,num的值并没有发生改变。

接下来看看传递引用类型的例子

function test(obj){
  obj.name = "hainan";  
}
var people = new Object();
test(people);
console.log(people.name);//"hainan"

解释一下,其实这个也和复制引用类型变量是一样的,传递一个引用类型给函数参数时,把外部的值复制给内部的函数参数,由于是引用类型这个值是一个指针,所以外部的引用类型变量和内部的参数此时会指向同一个对象,回想上面复制的图,当内部的参数指向的对象改变时,外部的变量指向的对象一定会改变,是同一个对象嘛。

所以,现在你只需知道,传递参数和复制变量是同一回事,不会的时候回想内存中的存储方式就明白了,管它是按值还是按引用传递呢,只是一个说法罢了,呵呵,但是面试的时候可能会问,记住javascript是值传递就行了。

作用域

执行环境

每个函数都有自己的执行环境(execution context),执行环境定义了变量和函数有权访问的数据,太官方了,就是每一个函数都有自己可以访问的范围,在自己的范围内的数据才可以得到。每一个执行环境中都有一个变量对象,保存着该环境中定义的变量和函数。全局执行环境是最外面的一个执行环境,就是window对象,所有的全局变量和函数就是它的属性。每一个执行环境执行完之后,这个环境就会被销毁,里面的变量对象也就没有了,当然变量对象中的变量和函数也就销毁了。

作用域

javascript的作用域是指变量和函数可以访问的范围,分为局部作用域和全局作用域,这个和C语言是类似的,但是不同点是javascript的作用域没有块级作用域,不像C语言的{}可以表示一个块级的作用域,javascript只有函数作用域,在函数内部声明的变量只能在函数体和子函数可以访问,这个函数的外部不能访问。看例子

//没有块级作用域
if(true){
  var n = 1;  
}
console.log(n);//1

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

上面的例子要是在C语言或者java中n和i会在{}语言执行完之后销毁,在javascript中可以看到,它们并没有销毁,说明并没有块级作用域。

function test(a,b){
  var sum = a + b;  
  return sum;
}
test(1,2);//3
console.log(sum);//sum is not defined

可以看出sum是在函数的外部访问不到的,因为sum函数是在函数的局部作用域中定义的,在函数执行结束时,内部的变量就会销毁,所以外部的作用域访问不到它,这就是函数的作用域。

作用域链

作用域链(scope chain)是保证在执行环境中有序的访问变量和函数,我们可以这样想,每个函数都有一个自己的执行环境,这个执行环境的嵌套就像套娃是一样的,大的套小的,内部的执行环境的变量有权访问外部的执行环境的数据,而外部的不可以反问内部的,所以当内外的执行环境中都有一个相同的变量或函数时,你是先访问哪一个呢?所以就有了作用域链这个概念。这个作用域链的每一个节点是一个变量对象,函数中有一个活动变量对象的概念,刚开始时只有函数的arguments对象,之后会把当前的变量对象中的变量和函数复制到活动对象中。作用域链的第一个变量对象是活动对象,之后就是下一个包含环境,之后是包含环境的包含环境......直到全局执行环境的变量对象。 看例子可能大家会明白

function test(a,b){
  var  sum = a + b;
  return sum; 
}
var s = test(1,2);

分析上面的这段代码,在未调用test()函数之前,看看作用域链情况

在未执行函数的时候,可以看到函数的作用域链只用一个全局的变量对象,里面有window和test函数等,在执行var s = test(1,2);语句的时候,作用域链会发生变化,会增加一个函数的活动对象到作用域链的前面,这个活动对象的初始值只有函数的arguments对象,之后会把函数的内部变量等复制到这个活动对象中,请看下面的图

这就是函数的作用域链,以后要访问某个变量的时候,会沿着这个作用域链进行查找,即沿着活动对象->外部函数的活动对象->外部函数的外部函数的活动对象->......->终点的全局执行环境,在这期间在某个活动对象中内部有这个值就会返回这个值,这个过程就会停止,不在进入另一个执行环境中,看个例子

//全局变量
var color = "red";
function getColor(){
    //内部变量
    var color = "blue";
    return color;
}
console.log(getColor());//blue

看到这里大家会明白了。

PS:其实javascript的闭包和作用域链有很大的联系,这里咱不讨论,闭包会单独讨论。

小结

这就是javascript的作用域了,主要大家还是好好看看变量内存那块,分析几个就会了。注意一下javascript没有块级作用域这一说法,这点大家要小心了。最近几天天天在群里看大家的聊天记录,论文也不想写了,哎,坐等回家吃猪肉了,希望老师别鄙视我。

 

 

 

 

 

 

 

posted on 2014-01-15 10:55  allenxing  阅读(1463)  评论(6编辑  收藏  举报