专栏名称: klivitamJ
android/前端工程师
目录
相关文章推荐
产业互联网大视野  ·  产业互联网DeepSeek融合发展研讨会下周 ... ·  昨天  
铅笔道  ·  义乌杀出未来独角兽:一把融资5亿 ·  3 天前  
51好读  ›  专栏  ›  klivitamJ

关于作用域、变量提升和闭包的理解

klivitamJ  · 掘金  ·  · 2019-05-16 15:51

正文

阅读 2

关于作用域、变量提升和闭包的理解

我不得不承认我是一个幸运儿,我遇到的每一个人都是如此的优秀。在一群优秀的人中间逐渐修炼我的性格,完善我的技能 真的是一个不可多得的恩赐

一、 作用域

谈到作用域这一块,很多人其实都不怎么熟悉这一块,我今天趁着闲暇跟大家来扯一扯。

js(es6以前)没有块级作用域(你可以自己闭包或其他方法实现),只有函数级作用域,函数外面的变量函数里面可以找到,函数里面的变量外面找不到。

1、 全局作用域

来看下面一个代码

var a = 99;

function test(){
	console.log("test:",a);
}
test();// test: 99
复制代码

从上面的代码我们可以清楚的看出:最外层函数定义的变量拥有全局作用域,即对任何内部函数来说,都是可以访问的。

2、局部作用域

function test(){
	var a = 99;
}
console.log("test:",a);
test();// ReferenceError: a is not defined
复制代码

由上面的代码我们可以清楚的知道:局部作用域一般只在固定的代码片段内可访问到,而对于函数外部是无法访问的。但是有这么种情况:

var a = "hello"

function test(){
	var a;
	console.log("test:",a);
	a=99

}
test();// test: undefined
复制代码

当外层函数域定义的变量和函数内定义的变量同时存在的时候,会根据最近原则找本域下的变量,找不到才去外层域去寻找。

3、 块级作用域

谈到块级作用域,这我们就得分es5和es6了,在es5及以前的环境中,先看一段代码:

var a = true;

if(a){
	var b = "hello world!!!";
}

console.log("b:",b) // b: hello world!!!
复制代码

从上面可以清楚的发现,es6以前是没有块级作用域的。但是在es6中,增加了 let const 定义方法,就像下面的代码

var a = true;

if(a){
	let b = "hello world!!!";
}

console.log("b:",b) // ReferenceError: b is not defined
复制代码

4、变量作用域

再看一段代码:

function test(){
	a = "hello"
}
test();// test: hello
console.log("test:",a);
复制代码

由上面的代码我可以看出:如果没有定义变量(var,let,const等),默认情况下会将变量变成全局变量。

5、作用域链

关于作用域链,我也来写一段代码

function test(){
	a = "hello"
	var b ="klivitam"

	function test1(){
		console.log("test1",b)
	}

	function test2(){
		var b = "1221"
		console.log("test2",b)
	}
	test1() // test1 klivitam
	test2() // test2 1221

}
test();// test: hello
console.log("test:",a);
复制代码

关于作用域链,我的理解是:函数会按照就近原则,首先在本作用域内寻找 如果没有寻到就去外层作用域去寻找一直找到windows环境下。着一系列操作形成一个链式结构,所以被称为作用域链。

二、 js提升

1、 变量、函数提升

关于变量提升,最近看到好多前端基础题目都会提到这个,废话不多说,直接上代码吧。

console.log("global",global);// undefined

var global = "klivitam";

console.log("global",global); // klivitam


function fn () {
  console.log(a); // undefined
  var a = 'aaa';
  console.log(a); // aaa
}
fn();
console.log(f1); // [Function: f1]
function f1() {} // 函数提升,整个代码块提升到文件的最开始的位置 
复制代码

由上面的代码可以看出来:js会将变量、函数声明提升到它所在作用域的最开始的位置。

2、 值得注意的是

来看下面一段代码

console.log(f2); // undefined
var f2 = function() {}


console.log("a = "+a);
console.log("b = "+b);

let a = "ni hao!"; // ReferenceError: a is not defined
const b = "nice to meet u"; // ReferenceError: b is not defined
复制代码
  • 函数字面量是不会进行变量提升的,提升的依然是 var
  • const、let也是不会进行变量提升的。

三、 闭包

关于闭包,我的理解是: 闭包就是能够读取其他函数内部变量的函数 。 来看下面一段代码:

function f1(){
	n=999;
	return function(){
		console.log(n)
	}
}
var result = f1();
result(); // 999
复制代码

我在写文章的时候特意翻了一下 js高级程序设计 ,里面对闭包是这么描述的:闭包是定义在一个外部函数内部,并且能够访问(存取)外部函数中自由变量的函数。

其实在我的理解里面就是:闭包就是内存函数和外层函数的一个桥梁。

四、 闭包的使用

闭包的作用主要有两个 1、可以读取函数内部的变量 2、让这些变量的值始终保持在内存中。

首先来看一段代码

var count = 99;

function outer(){
	add = function inside(){
		return ++count;
	}

	function console1(){
		console.log(count);
	}
	return console1

}
var o = new outer();

o(); // 99
add();
o(); // 100
复制代码

上面的代码就能解释前面所说的两个定义。我们发现第一外层函数能够通过闭包访问到内存函数,并且再次执行的时候,count会进行累加。这说明count值一直处于内存中 没有被回收。如果想要进行回收操作,请在使用完闭包之后:

0 = null;
复制代码

那关于闭包的使用呢?因为我最近在狂补充基础知识,也多多少少看了一些面试指南什么的,我看到了几个面试题是这样的:

1、 定义五个a标签,然后添加绑定事件,要求点击哪个标签的时候就打印哪个?

function timeCount() {
	for (var i = 1; i < 5; i++) {
		// 用settimeout模拟 onclick,感觉类似
		setTimeout(function(){ 
            console.log(i)
        },2000)
    }
}

timeCount();    //5 5 5 5 5
复制代码

通常的小白都会这么来写,但是结果却发现不能如我们所愿。因为在js中,js会将所有的异步操作加入到一个异步队列中。会等所有的操作结束之后,在进行处理异步操作。所以打印出来的就全部都是5了。为了解决这个问题, 此时就需要用到闭包了。 修改的代码如下:







请到「今天看啥」查看全文