javaScript es5 作用域、作用域链、变量提升、闭包(es5)、内存泄漏

es5 作用域、变量提升

参考 变量与函数的提升(ES5)
js 在es5规范中,作用域只分为全局作用域和函数作用域

js 在es5规范中,var 定义的变量,和函数定义,有 ‘提升’属性;
变量提升和函数提升:
将所有变量声明和函数声明提升到所属作用域的顶端,即所谓的变量提升和函数提升。(如果声明不在任意函数内,则视为在全局作用域的顶部)
变量提升只提升变量声明,不提升赋值初始化:

// 最简单的‘变量提升’
console.log(a);
var a = 1;
// 相当于
var a; // 只提升变量声明
console.log(a); // undefined
a = 1;

函数声明提升

function f() { console.log('I am outside!'); }
 
(function () {
  if (false) {
    // 重复声明一次函数f
    function f() { console.log('I am inside!'); }
  }
 
  f();
}());

实际运行的代码如下

// ES5 环境
function f() { console.log('I am outside!'); }
 
(function () {
  var f = undefined
  if (false) {
    // 重复声明一次函数f
    function f() { console.log('I am inside!'); }
  }
 
  f();
}());

函数作用域里的‘变量提升’

var t = '全局'
function fx () {
    console.log(t+'--1') // undefined,当前函数作用域用var定义的变量t的变量声明被提升到此作用域的顶层
    if (true) {
        var t = 'hello'
    }
    function fe () {
      console.log(t+'--2') // undefined,当前函数作用域用var定义的变量t被提升到此作用域的顶层
         if (true) {
           var t = 'hello-2'
         }
    }
    fe()
}
fx() 
console.log(t) //  '全局' 

输出:
undefined--1
undefined--2
全局
for (var i = 0; i < 3; i++) {

}
console.log(i) // 3

for 循环中的用 var 定义的变量被提升到所属作用域(当前函数作用域或全局作用域)的顶端。
if语句同理

js:先解析最外层定义的所有变量,再执行。调用函数时也是先解析函数内定义的变量,再执行。再往内也是如此。

es5 作用域:全局作用域、函数作用域
es6 作用域:全局作用域、函数作用域、块级作用域
块级作用域:{ }内即为块级作用域且通过let 实现

作用域链:

  1. 存储执行期的上下文对象集合。js通过作用域链查找变量,沿作用域链从里往外找,当读取一个变量时,先从当前作用域查找,如果没有,则查找上一级,以此类推;
  2. js通过作用域链查找变量只能单向找,无法从外往里找(当前作用域无法访问当前作用域内其他作用域的内部变量),var 和 let 都遵循此规则。

作用域链执行机制:

 let a = 1 
 funciton fx() {
    let a =2
    let b = 4
 }

步骤1: 未执行fx()时,作用域链是这样的:scopes[0]在全局作用域中,a=1, function fx
步骤2: 当fx被调用,scopes[0] => a = 2,b=4
scopes[1] => 步骤1的移到这里来

  1. var 在全局作用域、函数作用域的各级作用域中定义的同名变量互不干扰
// es5 es6
function foo() {
	var a = 1;
	function fc() {
        var a = '23'
        console.log(a) // 22
    }
    fc()
    console.log(a) //1
}
foo()
  1. let 在各级块级作用域(只要是{}括起来的)的各级作用域中定义的同名变量互不干扰
function foo() {
	let b = 2;
    
	{
		let b = 'kk'
		
		console.log(b); // kk
        if(true){
            let b = 'fdsf' 
            console.log(b); // fdsf
        }
	}
	
}	
foo()
  1. var 声明变量会提升声明至当前所属作用域(全局作用域、函数作用域)顶部,即使是在块级作用域中,因为var 只认全局作用域、函数作用域(es5)。
function foo() {
	console.log(a); // jj 
	{	
        if(false){
            var a = 'jj'  
        }
	}
	
}	
foo()

为什么使用块级作用域?
目的:取得合理的变量,严谨、避免隐性问题,即当前作用域没有定义的变量,则向上一级找。当前作用域有定义的变量,则取当前作用域定义和初始化的值,当前作用域先读取后定义则报错。
并且let 不存在变量提升,不允许先读取后定义变量(不会读取到非预期的变量);不允许在同一作用域内重复声明同名变量,更合理严谨。

let t = 1
fuc {
读取 t = > 1 向上找,在全局找到
}

let t = 1
fuc {
let t = 2
读取 t = > 2 当前作用域有定义t变量,取当前。
}

let t = 1
fuc {
读取 t = > 2 报错!let 不存在变量提升, 当前作用域有定义t变量,但未初始化就读取,这在es6的let语法是不允许的,更合理严谨
let t = 2
}

解决var定义取得的不合理的值
例1:

var t = 1
fuc {
读取 t = > undefined // 当前作用域定义的t变量被提升到当前顶端
var t = 2
}

例2:

for (var i = 0; i < 3; i++) {

}
console.log(i) // 用于for循环的计数变量被暴露为全局

补充:

es5中,var重复声明不报错

 var a =4; var a = 5

es6 中 let 不可重复声明

 let a =4; let a = 5
VM690:1 Uncaught SyntaxError: Identifier 'a' has already been declared

拓展阅读
同时支持变量提升和块级作用域:【ES6】JavaScript块级作用域的实现原理
【灵魂拷问】当面试官问你JavaScript预编译
【底层原理】图解JavaScript作用域链

闭包

由于js作用域链的只能从里往外查找变量的特性,为了在外部读取到A函数内部的变量,在A函数内再套一个闭包函数,这个闭包函数读取到A函数内部变量后返回到外部被引用,这时外部可访问。
注意:闭包有可能导致变量不再使用又不被回收(内存泄漏),比如赋值给外部作用域的变量,全局变量是不会被回收的(因为js不知道你何时会在用到)。

function outer() {
     var  a = '变量1'
     var  inner = function () {
            console.info(a)
     }
     return inner    // inner 就是一个闭包函数,因为他能够访问到outer函数的作用域,如:a
}
var  inner = outer()   // 获得inner闭包函数
inner()   //"变量1"

// inner 变量不被回收

如果直接在外部作用域直接单独立即执行,不赋值给任何变量,则会被回收;或者赋值执行后置为null,也会被回收。

function outer() {
     var  a = '变量1'
     var  inner = function () {
            console.info(a)
     }
     return inner    
}
outer()() // 直接立即执行,执行后,outer的a变量会被回收

// 或
var inner = outer()
inner()
inner = null

js 闭包

js 内存泄漏
变量未被回收,又没用到就叫内存泄漏
变量回收原则:js认为变量你可能还有用,就不会回收。
1、全局变量不会被回收。
2、函数内部定义的变量在不被其他作用域引用的情况下,函数执行后,内部定义的变量和函数全部销毁回收。
3、变量被另一个作用域引用就不会回收,比如闭包。
4、- 循环引用
js 循环引用

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值