JavaScript 作用域(链)、预解析、闭包函数

作用域

通常来说,一段程序代码中所用到的名字并不总是有效和可用的,而限定这个名字的可用性的代码范围就是这个名字的作用域。作用域的使用提高了程序逻辑的局部性,增强了程序的可靠性,减少了名字冲突

JS作用域(ES6之前)

全局作用域

整个script标签或者是一个单独的js文件

局部作用域(函数作用域)

作用于函数内部的代码环境

注意:
JS在ES6之前没有块级作用域:块作用域由 { } 包括,比如 if { } ; for { } 。因此 if 和 for 中定义的变量在外侧可以随意使用

变量的作用域

  • 全局变量:在函数外部定义的变量(显示定义)或在内部不用var定义,直接赋值的变量(隐式定义) 在浏览器页面关闭后才销毁,比较占内存资源空间
  • 局部变量:在函数内部用var定义的变量或函数的形参 函数调用结束后就销毁,节省内存资源
  • 块级变量:ES6中使用let关键字在语句块中定义的变量 当语句块结束就销毁,更节省内存空间

作用域链

在一个函数内部声明另一个函数时,内层函数只能在外层函数作用域内执行,在内层函数执行过程中,若引入某个变量,先在当前作用域中寻找,若未找到,则继续向上一层级作用域中寻找,直到全局作用域,即采用链式查找

采取就近原则的方式来查找变量最终的值。

var num=10;
function fn(){
	var num=20
	function fun(){
	console.log(num) //输出结果为:20
	}
	fun()
}
fn()

预解析

JavaScript 代码是由浏览器中的 JavaScript 解析器来执行的。
JavaScript 解析器在运行 JavaScript 代码的时候分为两步:预解析代码执行
预解析: 在当前作用域下, JS 代码执行之前,浏览器会默认把带有 varfunction 声明的变量在内存中进行提前声明或者定义
代码执行: 从上到下依次执行

变量预解析

变量预解析也称变量提升:变量的声明会被提升到当前作用域的最上面,变量的赋值不会提升

console.log(num) //执行结果为:undefined
var num=10
//以上两行相当于执行以下代码
var num
console.log(num)
num=10
fun()
var fun=function(){
	console.log(22)  //执行报错:fun is not a function
}
//相当于执行以下代码
var fun
fun()
fun=function(){
	console.log(22)
}
//函数表达式 调用必须写在函数表达式下面

函数预解析

函数预解析也称函数提升:函数的声明会被提升到当前作用域的最上面,但是不调用函数

fn()
function fn(){
	console.log(11) //执行结果为:11
}

预解析综合案例

var num=10
fun()
function fun() {
	console.log(num)
	var num=20
}
//相当于执行以下代码
var num
function fun(){
	var num
	console.log(num) //根据作用域链就近原则,输出结果为undefined
	num=20
}
num=10
fun();
f1()
console.log(c)
console.log(b)
console.log(a)
function f1() {
  var a = b = c = 9 //与var a=9,b=9,c=9 集体声明不同
  console.log(a)
  console.log(b)
  console.log(c)
}
//相当于执行以下代码
function f1() {
  var a 
  a=b=c=9 
  // b和c直接赋值,没有var声明,相当于 全局变量
  console.log(a)  //输出结果:9
  console.log(b)  //9
  console.log(c)  //9
}
f1()
console.log(c)   //9
console.log(b)   //9
console.log(a)   //报错:a is not defined

闭包函数

有权访问另一函数作用域内变量的函数

简单理解:一个作用域可以访问另一个函数的局部变量

function fn() {
    var times=0
   /* var c=function(){
        return ++times
    }
    return c*/
    return function(){
		return ++times
	}
}
//js中对闭包的语法规范
//在函数外部访问函数内部的变量
var count=fn()  //类似于函数表达式
//js解析器使得times不再初始化,给它一个空间存储
console.log(fn)
console.log(fn())
console.log(count())
console.log(count())
console.log(count())

在这里插入图片描述
应用:
1、点击对应的 li 输出当前索引号
2、3秒钟之后打印 li 内容

//点击li输出当前索引号
// 1、利用动态添加属性方式
var lis = document.querySelector('.nav').querySelectorAll('li')
for (var i = 0; i < lis.length; i++) {
    lis[i].index = i
    lis[i].onclick = function () {
        console.log(this.index)
    }
}
//2、闭包方式
for (var i = 0; i < lis.length; i++) {
    (function (i) {
        lis[i].onclick = function () {
            console.log(i) //使用了闭包
        }
    })(i) //立即执行函数均为闭包,函数里任何一个函数都可以使用括号里的变量
}

// 3秒钟之后,打印所有li元素内容
var lis = document.querySelector('.nav').querySelectorAll('li')
//for是同步任务,定时器是异步任务
for (var i = 0; i < lis.length; i++) {
    (function (i) {
        setTimeout(function () {
            console.log(lis[i].innerHTML)
        }, 3000)
    })(i)

思考案例:以下两种条件有无闭包的产生

var name="The Window"
var object={
    name:"My Object",
    getNameFunc:function(){
        return function(){
            return this.name
        }
    }
}
console.log(object.getNameFunc()()) //输出 The Window
//分解类似于
var f=object.getNameFunc()
f() 
//进一步分解类似于
var f=function(){
    return this.name
}
f() //相当于立即执行函数 function(){}()
//无闭包的产生
var name="The Window"
var object={
    name:"My Object",
    getNameFunc:function(){
        var that=this  //在函数里,指向的是object
        return function(){
            return that.name
        }
    }
}
console.log(object.getNameFunc()()) //输出 My Object
//分解类似于
var f=object.getNameFunc()
f()
//进一步分解
var f=function(){
    return that.name
}
f()
//有闭包的产生 在函数外部访问了that变量 变量所在的函数getNameFunc()是闭包函数

用途

  • 在函数外部读取函数内部的变量
  • 延伸了变量的作用范围
  • 让变量的值始终保持在内存中

注意:由于闭包会使得函数中变量一直被保存在内存中,内存消耗很大,所以闭包的滥用可能会降低程序的处理速度,造成内存损耗等问题

  • 4
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 12
    评论
评论 12
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值