Javascript深入之作用域与闭包
相信绝大多数同学都听过闭包这个概念,但闭包具体是什么估计很少有人能够说的很详细。说实话闭包在我们平时开发中应该是很常见的,并且在前端面试中闭包也是常见的重要考点,在学习闭包之前我们先来看看作用域与作用域链,因为这是闭包的关键。
作用域
简单来说,作用域是指程序中定义变量的区域,它决定了当前执行代码对变量的访问权限
在ES5中,一般只有两种作用域类型:
- 全局作用域:全局作用域作为程序的最外层作用域,一直存在
- 函数作用域:函数作用域只有在函数被定义时才会被创建,包含在父级函数作用域或全局作用域中
说完概念,我们来看下面这段代码:
var a = 100
function test(){
var b = a * 2
var a = 200
var c = a/2
console.log(b)
console.log(c)
}
test() // 这里会打印出什么?
解析:
1.首先这段代码形成了全局作用域与函数作用域
2.全局作用域有一个变量a赋值为100
3.在test函数作用域中定义了局部变量b,a,c
4.这里又存在变量提升,在函数作用域内先进行变量提升var b; var a; var c;
5.再对b进行赋值,这时候a还没有被赋值,所以a的值为
undefined,再将a*2,所以b为NaN6.再给a赋值为200,c赋值为a/2等于100
所以最终会打印出 NaN,100
在ES6中,新增了一种块级作用域
简单来说,花括号
{...}内的区域就是块级作用域,但Javascript并不是原生支持块级作用域的,需要借助ES6提出的let、const来创建块级作用域
// ES5
if(true) {
var name = '南玖'
}
console.log(name) // 南玖
// ES6
if(true) {
let age = 18
}
console.log(age) // 这里会报错
作用域链
当可执行代码内部访问变量时,会先查找当前作用域下有无该变量,有则立即返回,没有的话则会去父级作用域中查找...一直找到全局作用域。我们把这种作用域的嵌套机制称为
作用域链
词法作用域
词法作用域是作用域的一种工作模型,词法作用域是JavaScript中使用的一种作用域类型,词法作用域也可以被叫做静态作用域。
所谓的词法作用域就是在你写代码时将变量和作用域写在哪里来决定的,也就是词法作用域是静态的作用域,在你写代码时就决定了。函数作用域取决于它申明的位置,与实际调用的位置无关
MDN对闭包的定义:
一个函数和对其周围(词法环境)的引用捆绑在一起(或者说函数被引用包围),这样一个组合就是闭包(closure)
也就是说,闭包让你可以在一个内层函数中访问到其外层函数的作用域。在JavaScript中,每当创建一个函数,闭包就会在函数创建的同时被创建出来。
我们可以得出:
闭包 = 函数 + 外层作用域
我们先来看段代码:
var name = '前端南玖'
function say() {
console.log(name)
}
say()
解析:say函数可以访问到外层作用域的变量a,那么这样不就是形成了一个闭包吗?
在《Javascript权威指南》书中有这样一句话:严格来讲,所以JavaScript函数都是闭包
但这只是理论上的闭包,与我们平时使用的不太一样。上面这个例子只是一个简单的闭包。
ECMAScript对闭包的定义:
- 从理论上来讲:所有函数都是闭包。因为它们在创建的时候就已经上层上下文的数据保存起来了。
- 从实践上来讲:闭包应该满足两个条件:1.在代码中引用了外层作用域的变量;2.即使创建它的上下文已经销毁,它仍然存在;
我们再看一段《JavaScript权威指南》上的代码:
let scope = 'global scope'
function checkscope(){
let scope = 'local scope'
function f(){
return scope
}
return f
}
let s = checkscope()
s() // 这里返回什么?
很多同学可能觉得是global scope,但真的是这样吗,我们一起来看下它的执行过程:
1.首先执行全局代码,创建全局执行上下文,定义全局变量scope并赋值
2.申明checkscope函数,并创建该函数的执行上下文,创建局部变量scope并赋值
3.申明f函数,创建该函数的执行上下文
4.执行checkscope函数,该函数又返回了一个f函数赋值给了变量s
5.执行s函数,相当于执行了f函数。这里返回的scope是
local scope。至于为什么是local scope,我们上面讲到了词法作用的基本规则:JavaScript函数是使用定义它们的作用域来执行的。在定义f函数的作用域中,变量scope的值为local scope
闭包的应用
闭包的应用,绝大多是都是在维护内部变量的场景下使用
闭包的缺陷
- 由于闭包的存在可能会造成变量常驻内存,使用不当会造成内存泄漏
- 内存泄漏可能会导致应用程序卡顿或崩溃
高频闭包面试题
var arr = []
for(var i=0;i<3;i++){
arr[i] = function(){
console.log(i)
}
}
arr[0]() // 3
arr[1]() // 3
arr[2]() // 3
// 这里在执行的时候i已经变成了3
// 使用闭包解决
var arr = []
for(var i=0;i<3;i++){
arr[i] = (function(i){
return function(){
console.log(i)
}
})(i)
}
arr[0]() // 0
arr[1]() // 1
arr[2]() // 2
Javascript深入之作用域与闭包的更多相关文章
- javascript(面向对象,作用域,闭包,设计模式等)
javascript(面向对象,作用域,闭包,设计模式等) 1. 常用js类定义的方法有哪些? 参考答案:主要有构造函数原型和对象创建两种方法.原型法是通用老方法,对象创建是ES5推荐使用的方法.目前 ...
- javascript 函数和作用域(闭包、作用域)(七)
一.闭包 JavaScript中允许嵌套函数,允许函数用作数据(可以把函数赋值给变量,存储在对象属性中,存储在数组元素中),并且使用词法作用域,这些因素相互交互,创造了惊人的,强大的闭包效果.[upd ...
- JavaScript中的作用域和闭包
首先强烈安利<你不知道的JavaScript>,JS初学者进阶必读. 对于从C++.Java等静态语言转向JavaScript的初学者(比如我)来说,JS一些与众不同而又十分要紧的特性使得 ...
- JavaScript概念总结:作用域、闭包、对象与原型链
1 JavaScript变量作用域 1.1 函数作用域 没有块作用域:即作用域不是以{}包围的,其作用域完成由函数来决定,因而if /for等语句中的花括号不是独立的作用域. 如前述,JS的在函数中定 ...
- Javascript中关于作用域和闭包和域解释的面试题
<script type="text/javascript"> function fn() { var i = 10; return function (n) { co ...
- JavaScript作用域和闭包
在JavaScript中,作用域是执行代码的上下文.作用域有3种类型: 1.全局作用域 2.局部作用域---(又叫函数作用域) 3.eval作用域 var foo =0;//全局作用域console. ...
- 剖析JavaScript函数作用域与闭包
在我们写代码写到一定阶段的时候,就会想深究一下js,javascript是一种弱类型的编程语言,而js中一个最为重要的概念就是执行环境,或者说作用域.作用域重要性体现在哪呢?首先,函数在执行时会创建作 ...
- 《你不知道的JavaScript》第一部分:作用域和闭包
第1章 作用域是什么 抛出问题:程序中的变量存储在哪里?程序需要时,如何找到它们? 设计 作用域 的目的:为了更好地存储和访问变量. 作用域:根据名称查找变量的一套规则,用于确定在何处以及如何查找变量 ...
- JavaScript【5】高级特性(作用域、闭包、对象)
笔记来自<Node.js开发指南>BYVoid编著 1.作用域 if (true) { var somevar = 'value'; } console.log(somevar); Jav ...
随机推荐
- Linux系统的ssh与sshd服务
当主机中开启openssh服务,那么就对外开放了远程连接的接口 ssh为openssh服务的客户端,sshd为openssh服务的服务端 远程管理工具ssh具有数据加密传输.网络开销小以及应用平台范围 ...
- vue 引用省市区三级联动(element-ui select)
npm 下载 axios npm install --save axios static 静态文件夹里 创建 json 文件夹 json 文件夹里创建 map.json map.json 文件里写 ( ...
- MySQL主主互备不同步的解决方法
MySQL主主互备不同步 首先在服务器上执行show slave satus;可以看到很多同步的参数: Master_Log_File: SLAVE中的I/O线程当前正在读取的主服务器二进制日志文件的 ...
- noip模拟46
A. 数数 排好序从两头贪心即可 B. 数树 首先很容易想到容斥 如果选择的边集的相关点集有点的度数大于 \(1\) 是不合法的 也就是说一定形成若干条长度不一的链 要给这些链上的点安排排列中的数,方 ...
- Linux上使用设置printf显示的颜色
我们经常看到别的屏幕五颜六色的很是羡慕,看着很炫是吧.其实我们也可以自己做一个简单的修改,是我们的显示结果也呈现出不同的颜色.shell脚本可能设置的比较多,但是我们平常使用C语言却很少设置它的颜色, ...
- 【转】shell脚本中echo显示内容带颜色的实现方法
shell脚本中echo显示内容带颜色的实现方法 shell脚本里使用echo输出颜色
- 【PHP数据结构】图的存储结构
图的概念介绍得差不多了,大家可以消化消化再继续学习后面的内容.如果没有什么问题的话,我们就继续学习接下来的内容.当然,这还不是最麻烦的地方,因为今天我们只是介绍图的存储结构而已. 图的顺序存储结构:邻 ...
- deecms栏目页调用自定义字段方法
{dede:arclist addfields='suoxu_jifen,shichang_jiage' typeid='13' row='15' channelid='3'} <li>& ...
- Nginx系列(10)- Nginx配置文件详解
nginx文件结构 ... #全局块 events { #events块 ... } http #http块 { ... #http全局块 server #server块 { ... #server全 ...
- Linux系列(34) - yum源文件(1)
yum源文件各参数含义 在[/etc/yum.repos.d/]目录中,默认有4个yum源文件,其中[CentOS-Linux-BaseOS.repo]是基本yum源文件,如果我们能上网,那它是默认生 ...