Javascript作用域与闭包详情

目录
  • 1、作用域
  • 2、作用域链
  • 3、词法作用域
  • 5、闭包的应用
  • 6、闭包的缺陷
  • 7、高频闭包面试题

1、作用域

简单来说,作用域是指程序中定义变量的区域,它决定了当前执行代码对变量的访问权限

在ES5中,一般只有两种作用域类型:

  • 全局作用域:全局作用域作为程序的最外层作用域,一直存在
  • 函数作用域:函数作用域只有在函数被定义时才会被创建,包含在父级函数作用域或全局作用域中

说完概念,我们来看下面这段代码:

var a = 100
function test(){
    var b = a * 2
    var a = 200
    var c = a/2
    console.log(b)
    console.log(c)
}
test()      // 这里会打印出什么?

解析:

  • 首先这段代码形成了全局作用域与函数作用域
  • 全局作用域有一个变量a赋值为100
  • 在test函数作用域中定义了局部变量b,a,c
  • 这里又存在变量提升,在函数作用域内先进行变量提升var b; var a; var c;
  • 再对b进行赋值,这时候a还没有被赋值,所以a的值为undefined,再将a*2,所以b为NaN
  • 再给a赋值为200,c赋值为a/2等于100

所以最终会打印出 NaN,100

在ES6中,新增了一种块级作用域

简单来说,花括号{...}内的区域就是块级作用域,但Javascript并不是原生支持块级作用域的,需要借助ES6提出的letconst来创建块级作用域

// ES5
if(true) {
  var name = '南玖'
}
console.log(name)  // 南玖

// ES6
if(true) {
  let age = 18
}
console.log(age)  // 这里会报错

2、作用域链

当可执行代码内部访问变量时,会先查找当前作用域下有无该变量,有则立即返回,没有的话则会去父级作用域中查找...一直找到全局作用域。我们把这种作用域的嵌套机制称为作用域链

3、词法作用域

词法作用域是作用域的一种工作模型,词法作用域是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,但真的是这样吗,我们一起来看下它的执行过程:

  • 首先执行全局代码,创建全局执行上下文,定义全局变量scope并赋值
  • 申明checkscope函数,并创建该函数的执行上下文,创建局部变量scope并赋值
  • 申明f函数,创建该函数的执行上下文
  • 执行checkscope函数,该函数又返回了一个f函数赋值给了变量s
  • 执行s函数,相当于执行了f函数。这里返回的scopelocal scope。至于为什么是local scope,我们上面讲到了词法

作用的基本规则:JavaScript函数是使用定义它们的作用域来执行的。在定义f函数的作用域中,变量scope的值为local scope

5、闭包的应用

闭包的应用,绝大多是都是在维护内部变量的场景下使用

6、闭包的缺陷

  • 由于闭包的存在可能会造成变量常驻内存,使用不当会造成内存泄漏
  • 内存泄漏可能会导致应用程序卡顿或崩溃

7、高频闭包面试题

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作用域与闭包内容请搜索我们以前的文章或继续浏览下面的相关文章希望大家以后多多支持我们!

(0)

相关推荐

  • JavaScript闭包与作用域链实例分析

    本文实例讲述了JavaScript闭包与作用域链.分享给大家供大家参考,具体如下: 闭包定义 闭包指的是有权访问另一个函数作用域中的变量的函数.创建闭包的常见方式,就是在一个函数A内部创建另一个函数B,那么函数B就是一个闭包,可以访问函数A作用域中的所有变量. JavaScript的闭包与作用域链密不可分,因此本文可以和JavaScript的作用域链相对照分析,一定可以对JavaScript的闭包和作用域链有更深的理解. 下面我们仍然以createComparisonFunction为例进行闭包

  • Javascript作用域与闭包详情

    目录 1.作用域 2.作用域链 3.词法作用域 5.闭包的应用 6.闭包的缺陷 7.高频闭包面试题 1.作用域 简单来说,作用域是指程序中定义变量的区域,它决定了当前执行代码对变量的访问权限 在ES5中,一般只有两种作用域类型: 全局作用域:全局作用域作为程序的最外层作用域,一直存在 函数作用域:函数作用域只有在函数被定义时才会被创建,包含在父级函数作用域或全局作用域中 说完概念,我们来看下面这段代码: var a = 100 function test(){ var b = a * 2 var

  • JavaScript作用域、闭包、对象与原型链概念及用法实例总结

    本文实例讲述了JavaScript作用域.闭包.对象与原型链概念及用法.分享给大家供大家参考,具体如下: 1 JavaScript变量作用域 1.1 函数作用域 没有块作用域:即作用域不是以{}包围的,其作用域完成由函数来决定,因而if /for等语句中的花括号不是独立的作用域. 如前述,JS的在函数中定义的局部变量只对这个函数内部可见,称之谓函数作用域. 嵌套作用域变量搜索规则:当在函数中引用一个变量时,JS会搜索当前函数作用域,如果没有找到则搜索其上层作用域,一直到全局作用域. var va

  • 浅谈JavaScript作用域和闭包

    作用域和闭包在JavaScript里非常重要.但是在我最初学习JavaScript的时候,却很难理解.这篇文章会用一些例子帮你理解它们. 我们先从作用域开始. 作用域 JavaScript的作用域限定了你可以访问哪些变量.有两种作用域:全局作用域,局部作用域. 全局作用域 在所有函数声明或者大括号之外定义的变量,都在全局作用域里. 不过这个规则只在浏览器中运行的JavaScript里有效.如果你在Node.js里,那么全局作用域里的变量就不一样了,不过这篇文章不讨论Node.js. `const

  • 深入理解javascript作用域和闭包

    作用域 作用域是一个变量和函数的作用范围,javascript中函数内声明的所有变量在函数体内始终是可见的,在javascript中有全局作用域和局部作用域,但是没有块级作用域,局部变量的优先级高于全局变量,通过几个示例来了解下javascript中作用域的那些"潜规则"(这些也是在前端面试中经常问到的问题). 1. 变量声明提前 示例1: var scope="global"; function scopeTest(){ console.log(scope); v

  • javascript作用域和闭包使用详解

    作用域的嵌套将形成作用域链,函数的嵌套将形成闭包.闭包与作用域链是 JavaScript 区别于其它语言的重要特性之一. 作用域JavaScript 中有两种作用域:函数作用域和全局作用域. 在一个函数中声明的变量以及该函数的参数享有同一个作用域,即函数作用域.一个简单的函数作用域的例子: 复制代码 代码如下: function foo() {    var bar = 1;    {        var bar = 2;    }    return bar; // 2} 不同于C等其它有块

  • 深入了解JS之作用域和闭包

    作用域和闭包 ECMAScript5: JS 的代码没有代码块:使用函数运行的机制进行创建闭包:闭包就是作用域的意思: ES5中,JS中只有函数才可以创建能操作的作用域: JavaScript中的内存也分为栈内存和堆内存.一般来说,栈内存中存放的是存储对象的地址,而堆内存中存放的是存储对象的具体内容.对于原始类型的值而言,其地址和具体内容都存在与栈内存中:而基于引用类型的值,其地址存在栈内存,其具体内容存在堆内存中.堆内存与栈内存是有区别的,栈内存运行效率比堆内存高,空间相对推内存来说较小,反之

  • javascript 词法作用域和闭包分析说明

    复制代码 代码如下: var classA = function(){ this.prop1 = 1; } classA.prototype.func1 = function(){ var that = this, var1 = 2; function a(){ return function(){ alert(var1); alert(this.prop1); }.apply(that); }; a(); } var objA = new ClassA(); objA.func1(); 大家应

  • 浅析JavaScript作用域链、执行上下文与闭包

    闭包和作用域链是JavaScript中比较重要的概念,这两天翻阅了一些资料,把相关知识点给大家总结了以下. JavaScript 采用词法作用域(lexical scoping),函数执行依赖的变量作用域是由函数定义的时候决定,而不是函数执行的时候决定.以下面的代码片段举例说明,通常来说(基于栈的实现,如 C 语言) foo 被调用之后函数内的本地变量 scope 会被释放,但是从词法上看 foo 的内嵌匿名函数中 scope 应该指的是 foo 的本地变量 scope ,并且实际上代码的运行结

  • javascript中的作用域和闭包详解

    一.JavaScript作用域 JavaScript变量实际上只有两种作用域,全局变量和函数的内部变量.在函数内部任何一个地方定义的变量(var scope)其作用域都是整个函数体. 全局变量:指的是window对象下的对象属性. 作用域划分:基于上下文,以函数进行划分的,而不是由块划分的. 强调两点: 1. 在同一作用域中,JavaScript是允许变量的重复定义,并且后一个定义将覆盖前一个定义. 2. 函数内部如果不加关键字var而定义的变量,默认为全局变量. var scope="glob

  • 详解JavaScript作用域 闭包

    JavaScript闭包,是JS开发工程师必须深入了解的知识.3月份自己曾撰写博客<JavaScript闭包>,博客中只是简单阐述了闭包的工作过程和列举了几个示例,并没有去刨根问底,将其弄明白! 现在随着对JavaScript更深入的了解,也刚读完<你不知道的JavaScript(上卷)>这本书,所以乘机整理下,从底层和原理上去刨一下. JavaScript并不具有动态作用域,它只有词法作用域.词法作用域是在写代码或者说定义时确定的,而动态作用域是在运行时确定的.了解闭包前,首先我

随机推荐