search
HomeWeb Front-endJS TutorialA detailed explanation of JavaScript scopes and closures
A detailed explanation of JavaScript scopes and closuresDec 05, 2017 am 10:40 AM
javascriptScopeClosure

As a programmer, scope and closure are very important in JavaScript. Maybe it was difficult for us to understand what scope and closure are at the beginning, and how to use them in JavaScript? In this article we will introduce JavaScript scopes and closures in detail.

Scope

The scope of JavaScript limits which variables you can access. There are two types of scope: global scope and local scope.

Global scope

Variables defined outside all function declarations or braces are in the global scope.

However, this rule is only valid in JavaScript running in the browser. If you are in Node.js, variables in the global scope are different, but this article will not discuss Node.js.

const globalVariable = 'some value'`

Once you declare a global variable, you can use it anywhere, including inside functions.

const hello = 'Hello CSS-Tricks Reader!'

function sayHello () {
  console.log(hello)
}

console.log(hello) // 'Hello CSS-Tricks Reader!'
sayHello() // 'Hello CSS-Tricks Reader!'

Although you can define variables in the global scope, we do not recommend doing so. Because naming conflicts may occur, two or more variables use the same variable name. If you use const or let when defining a variable, you will receive an error if there is a naming conflict. This is not advisable.

// Don't do this!
let thing = 'something'
let thing = 'something else' // Error, thing has already been declared

If you use var when defining a variable, the second definition will overwrite the first definition. This also makes the code harder to debug and is undesirable.

// Don't do this!
var thing = 'something'
var thing = 'something else' // perhaps somewhere totally different in your code
console.log(thing) // 'something else'

So, you should try to use local variables instead of global variables

Local scope

Use it within a specific scope of your code Variables can be defined in the local scope. This is local variable.

There are two types of local scopes in JavaScript: function scope and block-level scope.

We start with function scope.

Function Scope

When you define a variable in a function, it can be used anywhere within the function. Outside the function, you can't access it.

For example, in the following example, the hello variable in the sayHello function:

function sayHello () {
  const hello = 'Hello CSS-Tricks Reader!'
  console.log(hello)
}

sayHello() // 'Hello CSS-Tricks Reader!'
console.log(hello) // Error, hello is not defined

Block-level scope

When you use curly braces, declare When you specify a const or let variable, you can only use the variable inside curly braces.

In the following example, hello can only be used within curly brackets.

{
  const hello = 'Hello CSS-Tricks Reader!'
  console.log(hello) // 'Hello CSS-Tricks Reader!'
}

console.log(hello) // Error, hello is not defined

Block-level scope is a subset of function scope, because functions need to be defined with braces (unless you explicitly use return statements and arrow functions).

Function promotion and scope

When defined using a function, the function will be promoted to the top of the current scope. Therefore, the following code is equivalent:

// This is the same as the one below
sayHello()
function sayHello () {
  console.log('Hello CSS-Tricks Reader!')
}

// This is the same as the code above
function sayHello () {
  console.log('Hello CSS-Tricks Reader!')
}
sayHello()

When defined using a function expression, the function is not hoisted to the top of the variable scope.

sayHello() // Error, sayHello is not defined
const sayHello = function () {
  console.log(aFunction)
}

Because there are two variables here, function promotion may cause confusion, so it will not take effect. So be sure to define the function before using it.

Functions cannot access the scope of other functions

When different functions are defined separately, although a function can be called in one function, one function still cannot access the scope of other functions. internal.

In the following example, second cannot access the firstFunctionVariable variable.

function first () {
  const firstFunctionVariable = `I'm part of first`
}

function second () {
  first()
  console.log(firstFunctionVariable) // Error, firstFunctionVariable is not defined
}

Nested scope

If a function is defined inside a function, the inner function can access the variables of the outer function, but not vice versa. The effect is lexical scoping.

The outer function cannot access the variables of the inner function.

function outerFunction () {
  const outer = `I'm the outer function!`

  function innerFunction() {
    const inner = `I'm the inner function!`
    console.log(outer) // I'm the outer function!
  }

  console.log(inner) // Error, inner is not defined
}

If you visualize the mechanism of scope, you can imagine a two-way mirror (single-sided see-through glass). You can see out from inside, but people outside can't see you.

A detailed explanation of JavaScript scopes and closures

Function scope is like a two-way mirror. You can look out from the inside, but you can't be seen from the outside.

The nested scope is a similar mechanism, but it is equivalent to having more two-way mirrors.

A detailed explanation of JavaScript scopes and closures

Multi-layer functions mean multiple two-way mirrors.

Understand the previous part about scope, and you can understand what closure is.

Closure

When you create another function within a function, it is equivalent to creating a closure. Inner functions are closures. Usually, in order to make the internal variables of the external function accessible, this closure is generally returned.

function outerFunction () {
  const outer = `I see the outer variable!`

  function innerFunction() {
    console.log(outer)
  }

  return innerFunction
}

outerFunction()() // I see the outer variable!

Because the inner function returns a value, you can simplify the function declaration part:

function outerFunction () {
  const outer = `I see the outer variable!`

  return function innerFunction() {
    console.log(outer)
  }
}

outerFunction()() // I see the outer variable!

Because the closure can access the outer function variables, so they usually have two uses:

  1. Reduce side effects

  2. Create private variables

Use Closures to Control Side Effects

When you perform something when a function returns a value, some side effects usually occur. Side effects can occur in many situations, such as Ajax calls, timeouts, or even console.log output statements:

function (x) {
  console.log('A console.log is a side effect!')
}

当你使用闭包来控制副作用时,你实际上是需要考虑哪些可能会混淆代码工作流程的部分,比如Ajax或者超时。

要把事情说清楚,还是看例子比较方便:

比如说你要给为你朋友庆生,做一个蛋糕。做这个蛋糕可能花1秒钟的时间,所以你写了一个函数记录在一秒钟以后,记录做完蛋糕这件事。

为了让代码简短易读,我使用了ES6的箭头函数:

function makeCake() {
  setTimeout(_ => console.log(`Made a cake`, 1000)
  )
}

 

如你所见,做蛋糕带来了一个副作用:一次延时。

更进一步,比如说你想让你的朋友能选择蛋糕的口味。那么你就给做蛋糕 makeCake 这个函数加了一个参数。

function makeCake(flavor) {
  setTimeout(_ => console.log(`Made a ${flavor} cake!`, 1000))
}

 

因此当你调用这个函数时,一秒后这个新口味的蛋糕就做好了。

makeCake('banana')
// Made a banana cake!

 

但这里的问题是,你并不想立刻知道蛋糕的味道。你只需要知道时间到了,蛋糕做好了就行。

要解决这个问题,你可以写一个 prepareCake 的功能,保存蛋糕的口味。然后,在返回在内部调用 prepareCake 的闭包 makeCake 。

从这里开始,你就可以在你需要的时调用,蛋糕也会在一秒后立刻做好。

function prepareCake (flavor) {
  return function () {
    setTimeout(_ => console.log(`Made a ${flavor} cake!`, 1000))
  }
}

const makeCakeLater = prepareCake('banana')

// And later in your code...
makeCakeLater()
// Made a banana cake!

 

这就是使用闭包减少副作用:你可以创建一个任你驱使的内层闭包。

私有变量和闭包

前面已经说过,函数内的变量,在函数外部是不能访问的既然不能访问,那么它们就可以称作私有变量。

然而,有时候你确实是需要访问私有变量的。这时候就需要闭包的帮助了。

function secret (secretCode) {
  return {
    saySecretCode () {
      console.log(secretCode)
    }
  }
}

const theSecret = secret('CSS Tricks is amazing')
theSecret.saySecretCode()
// 'CSS Tricks is amazing'

 

这个例子里的 saySecretCode 函数,就在原函数外暴露了 secretCode 这一变量。因此,它也被成为特权函数。

使用DevTools调试

Chrome和Firefox的开发者工具都使我们能很方便的调试在当前作用域内可以访问的各种变量一般有两种方法。

第一种方法是在代码里使用 debugger 关键词。这能让浏览器里运行的JavaScript的暂停,以便调试。

下面是 prepareCake 的例子:

function prepareCake (flavor) {
  // Adding debugger
  debugger
  return function () {
    setTimeout(_ => console.log(`Made a ${flavor} cake!`, 1000))
  }
}

const makeCakeLater = prepareCake('banana')

 

打开Chrome的开发者工具,定位到Source页下(或者是Firefox的Debugger页),你就能看到可以访问的变量了。

A detailed explanation of JavaScript scopes and closures

使用debugger调试 prepareCake 的作用域。

你也可以把 debugger 关键词放在闭包内部。注意对比变量的作用域:

function prepareCake (flavor) {
  return function () {
    // Adding debugger
    debugger
    setTimeout(_ => console.log(`Made a ${flavor} cake!`, 1000))
  }
}

const makeCakeLater = prepareCake('banana')

 

A detailed explanation of JavaScript scopes and closures

调试闭包内部作用域

第二种方式是直接在代码相应位置加断点,点击对应的行数就可以了。

A detailed explanation of JavaScript scopes and closures

通过断点调试作用域

闭包和作用域并不是那么难懂。一旦你使用双向镜的思维去理解,它们就非常简单了。当你在函数里声明一个变量时,你只能在函数内访问。这些变量的作用域就被限制在函数里了。如果你在一个函数内又定义了内部函数,那么这个内部函数就被称作闭包。它仍可以访问外部函数的作用域。希望本文能帮助到大家。

相关推荐:

JS的解析顺序和作用域以及严格模式的简单介绍

javascript中关于作用于作用域链的详解

JavaScript作用域与闭包的分析讲解

The above is the detailed content of A detailed explanation of JavaScript scopes and closures. For more information, please follow other related articles on the PHP Chinese website!

Statement
The content of this article is voluntarily contributed by netizens, and the copyright belongs to the original author. This site does not assume corresponding legal responsibility. If you find any content suspected of plagiarism or infringement, please contact admin@php.cn
es6数组怎么去掉重复并且重新排序es6数组怎么去掉重复并且重新排序May 05, 2022 pm 07:08 PM

去掉重复并排序的方法:1、使用“Array.from(new Set(arr))”或者“[…new Set(arr)]”语句,去掉数组中的重复元素,返回去重后的新数组;2、利用sort()对去重数组进行排序,语法“去重数组.sort()”。

JavaScript的Symbol类型、隐藏属性及全局注册表详解JavaScript的Symbol类型、隐藏属性及全局注册表详解Jun 02, 2022 am 11:50 AM

本篇文章给大家带来了关于JavaScript的相关知识,其中主要介绍了关于Symbol类型、隐藏属性及全局注册表的相关问题,包括了Symbol类型的描述、Symbol不会隐式转字符串等问题,下面一起来看一下,希望对大家有帮助。

原来利用纯CSS也能实现文字轮播与图片轮播!原来利用纯CSS也能实现文字轮播与图片轮播!Jun 10, 2022 pm 01:00 PM

怎么制作文字轮播与图片轮播?大家第一想到的是不是利用js,其实利用纯CSS也能实现文字轮播与图片轮播,下面来看看实现方法,希望对大家有所帮助!

JavaScript对象的构造函数和new操作符(实例详解)JavaScript对象的构造函数和new操作符(实例详解)May 10, 2022 pm 06:16 PM

本篇文章给大家带来了关于JavaScript的相关知识,其中主要介绍了关于对象的构造函数和new操作符,构造函数是所有对象的成员方法中,最早被调用的那个,下面一起来看一下吧,希望对大家有帮助。

JavaScript面向对象详细解析之属性描述符JavaScript面向对象详细解析之属性描述符May 27, 2022 pm 05:29 PM

本篇文章给大家带来了关于JavaScript的相关知识,其中主要介绍了关于面向对象的相关问题,包括了属性描述符、数据描述符、存取描述符等等内容,下面一起来看一下,希望对大家有帮助。

javascript怎么移除元素点击事件javascript怎么移除元素点击事件Apr 11, 2022 pm 04:51 PM

方法:1、利用“点击元素对象.unbind("click");”方法,该方法可以移除被选元素的事件处理程序;2、利用“点击元素对象.off("click");”方法,该方法可以移除通过on()方法添加的事件处理程序。

整理总结JavaScript常见的BOM操作整理总结JavaScript常见的BOM操作Jun 01, 2022 am 11:43 AM

本篇文章给大家带来了关于JavaScript的相关知识,其中主要介绍了关于BOM操作的相关问题,包括了window对象的常见事件、JavaScript执行机制等等相关内容,下面一起来看一下,希望对大家有帮助。

foreach是es6里的吗foreach是es6里的吗May 05, 2022 pm 05:59 PM

foreach不是es6的方法。foreach是es3中一个遍历数组的方法,可以调用数组的每个元素,并将元素传给回调函数进行处理,语法“array.forEach(function(当前元素,索引,数组){...})”;该方法不处理空数组。

See all articles

Hot AI Tools

Undresser.AI Undress

Undresser.AI Undress

AI-powered app for creating realistic nude photos

AI Clothes Remover

AI Clothes Remover

Online AI tool for removing clothes from photos.

Undress AI Tool

Undress AI Tool

Undress images for free

Clothoff.io

Clothoff.io

AI clothes remover

AI Hentai Generator

AI Hentai Generator

Generate AI Hentai for free.

Hot Article

R.E.P.O. Energy Crystals Explained and What They Do (Yellow Crystal)
2 weeks agoBy尊渡假赌尊渡假赌尊渡假赌
Repo: How To Revive Teammates
1 months agoBy尊渡假赌尊渡假赌尊渡假赌
Hello Kitty Island Adventure: How To Get Giant Seeds
4 weeks agoBy尊渡假赌尊渡假赌尊渡假赌

Hot Tools

Zend Studio 13.0.1

Zend Studio 13.0.1

Powerful PHP integrated development environment

Atom editor mac version download

Atom editor mac version download

The most popular open source editor

ZendStudio 13.5.1 Mac

ZendStudio 13.5.1 Mac

Powerful PHP integrated development environment

SublimeText3 Mac version

SublimeText3 Mac version

God-level code editing software (SublimeText3)

Dreamweaver Mac version

Dreamweaver Mac version

Visual web development tools