javascript作用域和闭包

来源:互联网 发布:知乎 wings颁奖 编辑:程序博客网 时间:2024/06/05 08:38

执行环境

在了解作用域前,先了解一下执行环境,也叫执行上下文。每个函数都有一个自己的执行环境,书中(javascript高级程序设计)说明

每个执行环境都有一个 与之关联的变量对象(variable object)。

我们不妨将这个执行环境看做这个对象,它里面包含了在函数中定义的变量、函数等。函数定义时,会生成该函数的执行环境。当函数被执行时,该执行环境(该对象)被推入环境栈中,执行完毕时,该对象会被弹出销毁。然后执行环境会改为当前栈顶的对象。
默认的环境变量是全局环境,与之关联的对象就是window。局部环境的关联对象开始只有一个变量-arguments

作用域和作用域链

作用域控制着变量与参数的可见性及生命周期。关于执行环境和作用域,书中没有明确指出作用域和执行环境的异同。执行环境应该指的是上下文环境,和作用域还是有点区别的。例如,函数中,作用域用于定义函数时,上下文环境还包括调用函数时的环境。
当代码在一个环境中执行时,会创建变量对象的一个作用域链。作用域链的用途,是保证对执行环境有权访问的所有变量和函数的有序访问。当函数执行时,函数内部访问的变量会首先在当前作用域(即作用域链的前端)寻找,如果找不到,则会依次向后寻找,直到找到变量,否则会报错。举个例子:

var color = "red"function getSelfColor() {    var color = "yellow"    console.log(color)}function getColor() {    console.log(color)}getSelfColor()//yellowgetColor()//red
在该例子默认的全局环境是window,在该环境中定义了color变量和getSelfColor、getColor两个函数,当getSelfColor和getColor函数被调用时, 会创建该函数环境关联对象的一个作用域链
window
color
getSelfColor
color
getColor

当getSelfColor执行时,该函数的关联对象推入栈顶,里面定义了color变量,当调用console.log()函数时,优先在当前环境中查找变量,color为yellow。
当getSelfColor执行时,里面没有color变量,当调用console.log()函数时,优先在当前环境中查找变量,因为并没有找到,所以沿作用域链向后查找,color为red。

this

js中的this是个很关键的值,它与函数的执行环境有关。this的值在函数定义时是无法确定的,只有当函数执行时,才能确定this。

window.name = "llx"var a = {    name: 'A',    fn: function() {        console.log(this.name)    }}a.fn()a.fn.call({name: 'B'})var fn1 = a.fnfn1()//结果://A//B//llx

js没有块级作用域

js中没有块级作用域,只有函数和全局作用域。即在{}中定义的变量会被添加到当前作用域中,当{}内部的代码被执行完毕以后,并不会销毁变量,因为变量所在的环境的代码并没有被执行完。举例:

if (true) {    var color = "red"}console.log(color)//red

此处的{}里面的定义的变量可以被外部访问到,因为里面的部分并不会单独构建新的作用域。

变量提升

在当前作用域的变量定义会被提升到当前环境下代码运行的最前面,这就是javascript的变量提升。举例:

console.log(color)var color = "red"concole.log(color)//结果//undefined//red

第一行的打印并没有报错,就是因为javascript的变量提升机制,代码实际的解释执行是这样的:

var colorconsole.log(color)var color = "red"concole.log(color)

闭包

闭包是指有权访问另一个函数作用域中的变量的函数,闭包的常见形式是在一个函数内部创建另一个函数,一般用来包裹的外层函数是匿名函数。但是闭包并不是指外层的匿名函数。看一个闭包的小例子:

for (var i = 0; i < 10; i ++) {        (function(i) {            var a = document.createElement('a')            a.innerHTML = i + '<br>'            a.style.cursor = "pointer"            a.addEventListener('click', function(e) {                e.preventDefault()                alert(i)            })            document.body.appendChild(a)        })(i)    }//例子为创建10个a标签,并且为其添加点击事件,输出a标签的序号

可以看到在上面的例子中,for循环的内部添加一个自执行的函数,并传入变量i,在为a标签绑定事件时,该函数可以获取到上层作用域的变量i,然后打印。

闭包主要用于封装变量,收敛权限。

function isFirstLoad() {        var _list = []        return function (id) {            if (_list.indexOf(id) >= 0) {                return false            } else {                _list.push(id)                return true            }        }    }    //使用闭包    var firstLoad = isFirstLoad()    console.log(firstLoad(10))    console.log(firstLoad(10))    console.log(firstLoad(20))    //结果    //true    //false    //true
原创粉丝点击