javascript作用域和闭包

执行环境

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

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

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

作用域和作用域链

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

var color = "red"

function getSelfColor() {
    var color = "yellow"
    console.log(color)
}
function getColor() {
    console.log(color)
}

getSelfColor()//yellow
getColor()//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.fn
fn1()
//结果:
//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 color
console.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
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值