一、变量声明

在JavaScript中,使用一个变量之前应当声明一个变量,变量是使用关键字var来声明的。如:

var a;
声明了一个变量未赋值的时候,默认值为underfined。可能你会说,我没有用var声明,也可以直接用这个数也可以啊,如:

a = 10
alert(a)
这里事先没有声明变量a,而是直接给变量a赋值,当运行的时候也是没问题,乍一看好像问题不大,其实还是有一些问题的。这样子未声明一个变量去使用这个变量的话,这个变量是全局变量,是JavaScript通过在全局对象中创建出来的一个同名属性,但是这是一个不好的习惯,会造成很多bug,在严格模式和ECMAScript标准中是严令禁止这样做的。在eslint中,如果声明了一个变量但是没有赋值也是会报错的。


二、变量作用域

一个变量的作用域是程序源代码中定义这个变量的区域。全局对象拥有全局作用域,在JavaScript的任何地方都是有定义的,而在函数内声明的变量只在函数内有定义,他们称为局部变量。函数的参数也是一个局部变量,只在函数体内有定义。

在函数体内,局部变量的优先级高于同名的全局 变量,也就是说当在函数体内局部变量与全局变量同名,全局变量会被局部变量所遮盖掉。如:

var a = 'a'
function ha () {
    var a = 'b'
    return a
}
alert(ha())    //返回的是b
局部变量一定要用var声明,如果函数体内的一个变量没有用var声明,而函数体外恰好有这个同名变量,这个时候就能体现出这个陋习的bug了。如:

function ha () {
    a = 'b'
    return a
}
ha()
alert(a)    //返回的是b
全局变量a在函数体内被修改了,所以声明变量的时候一定要加一个var,防止出现未知的bug。当声明了一个JavaScript全局变量的时候,实际上是定义了全局对象的一个属性。当使用var来声明变量时,这个创建的属性是不可配置的,也就是说这个变量是无法通过delete运算符删除的,但是没用var声明变量时,变量可以被delete删除。


三、声明提前和函数作用域

在java中,花括号内的每一段代码都有各自的作用域,并且变量在声明它们的作用域之外是不可见的,这称之为块级作用域。但是在JavaScript中是没有块级作用域这个概念,取而代之的是函数作用域。在函数作用域里面,变量在声明它们的函数体以及这个函数体嵌套的任意函数体内都是有定义的。如:

var o = new Date()
function test(o) {
    var i = 0
    if (typeof o == 'object') {
        var j = 0
        for (var k = 0;k < 10; k++) {
            console.log(k)
        }
        console.log(k)
    }
    console.log(j)
}
test(o)
第一个输出从0到9,第二个输出的是10,第三个输出的是0。用过java的可能会对这个结果产生一些疑问,为什么k和j的值都能够输出来呢?是因为在test()函数内,i,j,k都在同一个函数作用域内,在函数作用域内变量在声明它们的函数体以及这个函数体嵌套的任意函数体内都是有定义的。也就是说,i,j,k变量声明提前了,这样的话上述的代码等价于:

var o = new Date()
function test(o) {
    var i = 0
    var k
    var j
    if (typeof o == 'object') {
        j = 0
        for (k = 0;k < 10; k++) {
            console.log(k)
        }
        console.log(k)
    }
    console.log(j)
}
test(o)
JavaScript的函数作用域指的是在函数内声明的所有变量在函数体内始终可见的,这样子会让变量在声明之前已经可用,这一特性被称为声明提前。一个典型的例子如下所示:

var a = 'a'
function aa() {
    console.log(a)
    var a = 'b'
    console.log(a)
}
aa()
如果你以为第一个输出的是a就大错特错了,第一个输出的是underfined,第二个输出的是b。首先在JavaScript里,在函数体内定义一个变量是在整个函数内可见,也就是说即使你的变量声明放在最后写,它仍然会将这个变量声明提前。并且局部变量的优先级比全局变量的优先级高,在给变量a赋值之前它的初始值为underfined,所以上述的代码可以理解成这样:

var a = 'a'
function aa() {
    var a
    console.log(a)
    a = 'b'
    console.log(a)
}
aa()
在JavaScript编程中,如果在函数体内声明变量,尽量的把变量放在前面,这样方便查找变量,也能真实的反映变量的作用域。


四、作用域链

作用域链是一个对象列表或者链表,这组对象定义了代码中“作用域中”的变。当JavaScript需要查找变量x的值的时候,它会从链中的第一个对象开始查找,如果这个对象有一个名为x的属性,则会直接使用这个属性的值,如果没有则一直找,当作用域链上没有任何一个对象含有属性x,则会报一个引用错误的异常。下面有一个例子是举例说明作用域链的:

 
var x =1
function a() {
    var y = 2
    function aa() {
        var z = 3
        console.log(x + y + z)
    }
    aa()
}
a()

当函数执行到aa()方法里的console,它会找aa()里面有没有变量x和y,没有的话,则向上一级去a()方法里面查找变量x和y,a()方法里有y但是没有x,于是继续向上查找,找到了x,所以输出的结果是6。下面有另一个常见的例子更能说明这个问题:

var a = []
for(var i = 0; i < 3; i ++ ) {
    a[i] = function () {
        console.log(i)
    }
}
a[0]()
a[1]()
a[2]()
你认为会输出什么?如果你以为输出的是0,1,2其实就是大错特错了。因为在这个匿名函数里,根本就没有声明i这个变量,所以就通过作用域链来向上查找变量i,恰逢此时循环已经遍历完成,最终的i是等于3,所以最后这三个输出的结果为3。有关作用域链这个东西在《JavaScript权威指南》里并没有过多的讲述,由于本人水平有限可能讲的不够详细,特意推荐这两个不错的推文,都是讲作用域链的,作用域链对理解闭包和with语句有很大的帮助,下面就贴上那两个地址给大家参考。

http://www.cnblogs.com/lhb25/archive/2011/09/06/javascript-scope-chain.html

http://www.cnblogs.com/dolphinX/p/3280876.html



更多相关文章

  1. JavaScript学习-5——异步同步、回调函数
  2. 什么“返回此”在javascript函数中做什么?
  3. jQuery和AJAX - 使用Ajax添加的对象动态不适用于jQuery函数?
  4. javascript变量:全局?还是局部?这个得注意
  5. Javascript偏函数与柯里化
  6. 在Javascript / node.js中共享模块之间的变量?
  7. 当函数在单独的PHP文件中定义时,调用JavaScript函数onclick按钮事
  8. Python 内置函数及excel操作
  9. Python3 函数式编程(高阶函数)

随机推荐

  1. Android安装或者卸载应用APK
  2. Android单元测试-javaeye
  3. Debug native code using addr2line on A
  4. Android Loader详解
  5. 自定义Android editText
  6. android各种触摸事件的处理,touchEvent
  7. Android之日期及时间选择对话框
  8. Android 基本控件
  9. Android 动态布局
  10. android 调用系统应用