理解 JavaScript 作用域

Understanding Scope in JavaScript

Understanding Scope in JavaScript

理解 JavaScript 作用域

Introduction

简介

JavaScript has a feature called Scope. Though the concept of scope is not that easy to understand for many new developers, I will try my best to explain them to you in the simplest scope. Understanding scope will make your code stand out, reduce errors and help you make powerful design patterns with it.

JavaScript 有个特性称为作用域。尽管对于很多开发新手来说,作用域的概念不容易理解,我会尽可能地从最简单的角度向你解释它们。理解作用域能让你编写更优雅、错误更少的代码,并能帮助你实现强大的设计模式。

What is Scope?

什么是作用域?

Scope is the accessibility of variables, functions, and objects in some particular part of your code during runtime. In other words, scope determines the visibility of variables and other resources in areas of your code.

作用域是你的代码在运行时,各个变量、函数和对象的可访问性。换句话说,作用域决定了你的代码里的变量和其他资源各个区域中的可见性。

Why Scope? The Principle of Least Access

为什么是作用域?最小访问原则

So, what’s the point in limiting the visibility of variables and not having everything available everywhere in your code? One advantage is that scope provides some level of security to your code. One common principle of computer security is that users should only have access to the stuff they need at a time.

那么,限制变量的可见性,不允许你代码中所有的东西在任意地方都可用的好处是什么?其中一个优势,是作用域为你的代码提供了一个安全层级。计算机安全中,有个常规的原则是:用户只能访问他们当前需要的东西。

Think of computer administrators. As they have a lot of control over the company’s systems, it might seem okay to grant full access user account to them. Suppose you have a company with three administrators, all of them having full access to the systems and everything is working smooth. But suddenly something bad happens and one of your system gets infected with a malicious virus. Now you don’t know whose mistake that was? You realize that you should them basic user accounts and only grant full access privileges when needed. This will help you track changes and keep an account of who did what. This is called The Principle of Least Access. Seems intuitive? This principle is also applied to programming language designs, where it is called scope in most programming languages including JavaScript which we are going to study next.

想想计算机管理员吧。他们在公司各个系统上拥有很多控制权,看起来甚至可以给予他们拥有全部权限的账号。假设你有一家公司,拥有三个管理员,他们都有系统的全部访问权限,并且一切运转正常。但是突然发生了一点意外,你的一个系统遭到恶意病毒攻击。现在你不知道这谁出的问题了吧?你这才意识到你应该只给他们基本用户的账号,并且只在需要时赋予他们完全的访问权。这能帮助你跟踪变化并记录每个人的操作。这叫做最小访问原则。眼熟吗?这个原则也应用于编程语言设计,在大多数编程语言(包括 JavaScript)中称为作用域,接下来我们就要学习它。

As you continue on in your programming journey, you will realize that scoping parts of your code helps improve efficiency, track bugs and reduce them. Scope also solves the naming problem when you have variables with the same name but in different scopes. Remember not to confuse scope with context. They are both different features.

在你的编程旅途中,你会意识到作用域在你的代码中可以提升性能,跟踪 bug 并减少 bug。作用域还解决不同范围的同名变量命名问题。记住不要弄混作用域和上下文。他们是不同的特性。

Scope in JavaScript

JavaScript中的作用域

In the JavaScript language there are two types of scopes:

在 JavaScript 中有两种作用域

  • Global Scope
  • Local Scope
  • 全局作用域
  • 局部作用域

Variables defined inside a function are in local scope while variables defined outside of a function are in the global scope. Each function when invoked creates a new scope.

当变量定义在一个函数中时,变量就在局部作用域中,而定义在函数之外的变量则从属于全局作用域。每个函数在调用的时候会创建一个新的作用域。

Global Scope

全局作用域

When you start writing JavaScript in a document, you are already in the Global scope. There is only one Global scope throughout a JavaScript document. A variable is in the Global scope if it’s defined outside of a function.

当你在文档中(document)编写 JavaScript 时,你就已经在全局作用域中了。JavaScript 文档中(document)只有一个全局作用域。定义在函数之外的变量会被保存在全局作用域中。

// the scope is by default global
var name = 'Hammad';

Variables inside the Global scope can be accessed and altered in any other scope.

全局作用域里的变量能够在其他作用域中被访问和修改。

var name = 'Hammad';

console.log(name); // logs 'Hammad'

function logName() {
    console.log(name); // 'name' is accessible here and everywhere else
}

logName(); // logs 'Hammad'

Local Scope

局部作用域

Variables defined inside a function are in the local scope. And they have a different scope for every call of that function. This means that variables having the same name can be used in different functions. This is because those variables are bound to their respective functions, each having different scopes, and are not accessible in other functions.

定义在函数中的变量就在局部作用域中。并且函数在每次调用时都有一个不同的作用域。这意味着同名变量可以用在不同的函数中。因为这些变量绑定在不同的函数中,拥有不同作用域,彼此之间不能访问。

// Global Scope
function someFunction() {
    // Local Scope ##1
    function someOtherFunction() {
        // Local Scope ##2
    }
}

// Global Scope
function anotherFunction() {
    // Local Scope ##3
}
// Global Scope

Block Statements

块级声明

Block statements like if and switch conditions or for and while loops, unlike functions, don’t create a new scope. Variables defined inside of a block statement will remain in the scope they were already in.

块级声明包括ifswitch,以及forwhile循环,和函数不同,它们不会创建新的作用域。在块级声明中定义的变量从属于该块所在的作用域。

if (true) {
    // this 'if' conditional block doesn't create a new scope
    var name = 'Hammad'; // name is still in the global scope
}

console.log(name); // logs 'Hammad'

ECMAScript 6 introduced the let and const keywords. These keywords can be used in place of the var keyword.

ECMAScript 6 引入了letconst关键字。这些关键字可以代替var

var name = 'Hammad';

let likes = 'Coding';
const skills = 'Javascript and PHP';

Contrary to the var keyword, the let and const keywords support the declaration of local scope inside block statements.

var关键字不同,letconst关键字支持在块级声明中创建使用局部作用域。

if (true) {
    // this 'if' conditional block doesn't create a scope

    // name is in the global scope because of the 'var' keyword
    var name = 'Hammad';
    // likes is in the local scope because of the 'let' keyword
    let likes = 'Coding';
    // skills is in the local scope because of the 'const' keyword
    const skills = 'JavaScript and PHP';
}

console.log(name); // logs 'Hammad'
console.log(likes); // Uncaught ReferenceError: likes is not defined
console.log(skills); // Uncaught ReferenceError: skills is not defined

Global scope lives as long as your application lives. Local Scope lives as long as your functions are called and executed.

一个应用中全局作用域的生存周期与该应用相同。局部作用域只在该函数调用执行期间存在。

Context

上下文(Context)

Many developers often confuse scope and context as if they equally refer to the same concepts. But this is not the case. Scope is what we discussed above and Context is used to refer to the value of this in some particular part of your code. Scope refers to the visibility of variables and context refers to the value of this in the same scope. We can also change the context using function methods, which we will discuss later. In the global scope context is always the Window object.

很多开发者经常弄混作用域和上下文,似乎两者是一个概念。但并非如此。作用域是我们上面讲到的那些,而上下文通常涉及到你代码某些特殊部分中的this值。作用域指的是变量的可见性,而上下文指的是在相同的作用域中的this的值。我们当然也可以使用函数方法改变上下文,这个之后我们再讨论。在全局作用域中,上下文总是 Window 对象。

// logs: Window {speechSynthesis: SpeechSynthesis, caches: CacheStorage, localStorage: Storage…}
console.log(this);

function logFunction() {
    console.log(this);
}
// logs: Window {speechSynthesis: SpeechSynthesis, caches: CacheStorage, localStorage: Storage…}
// because logFunction() is not a property of an object
logFunction(); 

If scope is in the method of an object, context will be the object the method is part of.

如果作用域定义在一个对象的方法中,上下文就是这个方法所在的那个对象

class User {
    logName() {
        console.log(this);
    }
}

(new User).logName(); // logs User {}

(new User).logName() is a short way of storing your object in a variable and then calling the logName function on it. Here, you don’t need to create a new variable.

(new User).logName()是创建对象关联到变量并调用logName方法的一种简便形式。通过这种方式你并不需要创建一个新的变量。

One thing you’ll notice is that the value of context behaves differently if you call your functions using the new keyword. The context will then be set to the instance of the called function. Consider one of the examples above with the function called with the new keyword.

你可能注意到一点,就是如果你使用new关键字调用函数时上下文的值会有差异。上下文会设置为被调用的函数的实例。考虑一下上面的这个例子,用new关键字调用的函数。

function logFunction() {
    console.log(this);
}

new logFunction(); // logs logFunction {}

When a function is called in Strict Mode, the context will default to undefined.

当在严格模式(strict mode)中调用函数时,上下文默认是 undefined。

Execution Context

执行环境

To remove all confusions and from what we studied above, the word context in Execution Context refers to scope and not context. This is a weird naming convention but because of the JavaScipt specification, we are tied to it.

为了解决掉我们从上面学习中会出现的各种困惑,“执行环境(context)”这个词中的“环境(context)”指的是作用域而并非上下文。这是一个怪异的命名约定,但由于 JavaScript 的文档如此,我们只好也这样约定。

JavaScript is a single-threaded language so it can only execute a single task at a time. The rest of the tasks are queued in the Execution Context. As I told you earlier that when the JavaScript interpreter starts to execute your code, the context (scope) is by default set to be global. This global context is appended to your execution context which is actually the first context that starts the execution context.

JavaScript 是一种单线程语言,所以它同一时间只能执行单个任务。其他任务排列在执行环境中。当 JavaScript 解析器开始执行你的代码,环境(作用域)默认设为全局。全局环境添加到你的执行环境中,事实上这是执行环境里的第一个环境。

After that, each function call (invocation) would append its context to the execution context. The same thing happens when an another function is called inside that function or somewhere else.

之后,每个函数调用都会添加它的环境到执行环境中。无论是函数内部还是其他地方调用函数,都会是相同的过程。

Each function creates its own execution context.

每个函数都会创建它自己的执行环境。

Once the browser is done with the code in that context, that context will then be popped off from the execution context and the state of the current context in the execution context will be transferred to the parent context. The browser always executes the execution context that is at the top of the execution stack (which is actually the innermost level of scope in your code).

当浏览器执行完环境中的代码,这个环境会从执行环境中弹出,执行环境中当前环境的状态会转移到父级环境。浏览器总是先执行在执行栈顶的执行环境(事实上就是你代码最里层的作用域)。

There can only be one global context but any number of function contexts.
The execution context has two phases of creation and code execution.

全局环境只能有一个,函数环境可以有任意多个。
执行环境有两个阶段:创建和执行。

CREATION PHASE

创建阶段

The first phase that is the creation phase is present when a function is called but its code is not yet executed. Three main things that happen in the creation phase are:

第一阶段是创建阶段,是函数刚被调用但代码并未执行的时候。创建阶段主要发生了 3 件事。

  • Creation of the Variable (Activation) Object,
  • Creation of the Scope Chain, and
  • Setting of the value of context (this)
  • 创建变量对象
  • 创建作用域链
  • 设置上下文(this)的值

Variable Object

变量对象

The Variable Object, also known as the activation object, contains all of the variables, functions and other declarations that are defined in a particular branch of the execution context. When a function is called, the interpreter scans it for all resources including function arguments, variables, and other declarations. Everything, when packed into a single object, becomes the the Variable Object.

变量对象(Variable Object)也称为活动对象(activation object),包含所有变量、函数和其他在执行环境中定义的声明。当函数调用时,解析器扫描所有资源,包括函数参数、变量和其他声明。当所有东西装填进一个对象,这个对象就是变量对象。

'variableObject': {
    // contains function arguments, inner variable and function declarations
}

Scope Chain

作用域链

In the creation phase of the execution context, the scope chain is created after the variable object. The scope chain itself contains the variable object. The Scope Chain is used to resolve variables. When asked to resolve a variable, JavaScript always starts at the innermost level of the code nest and keeps jumping back to the parent scope until it finds the variable or any other resource it is looking for. The scope chain can simply be defined as an object containing the variable object of its own execution context and all the other execution contexts of it parents, an object having a bunch of other objects.

在执行环境创建阶段,作用域链在变量对象之后创建。作用域链包含变量对象。作用域链用于解析变量。当解析一个变量时,JavaScript 开始从最内层沿着父级寻找所需的变量或其他资源。作用域链包含自己执行环境以及所有父级环境中包含的变量对象。

'scopeChain': {
    // contains its own variable object and other variable objects of the parent execution contexts
}

The Execution Context Object

执行环境对象

The execution context can be represented as an abstract object like this:

执行环境可以用下面抽象对象表示:

executionContextObject = {
    'scopeChain': {}, // contains its own variableObject and other variableObject of the parent execution contexts
    'variableObject': {}, // contains function arguments, inner variable and function declarations
    'this': valueOfThis
}

CODE EXECUTION PHASE

代码执行阶段

In the second phase of the execution context, that is the code execution phase, other values are assigned and the code is finally executed.

执行环境的第二个阶段就是代码执行阶段,进行其他赋值操作并且代码最终被执行。

Lexical Scope

词法作用域

Lexical Scope means that in a nested group of functions, the inner functions have access to the variables and other resources of their parent scope. This means that the child functions are lexically bound to the execution context of their parents. Lexical scope is sometimes also referred to as Static Scope.

词法作用域的意思是在函数嵌套中,内层函数可以访问父级作用域的变量等资源。这意味着子函数词法绑定到了父级执行环境。词法作用域有时和静态作用域有关。

function grandfather() {
    var name = 'Hammad';
    // likes is not accessible here
    function parent() {
        // name is accessible here
        // likes is not accessible here
        function child() {
            // Innermost level of the scope chain
            // name is also accessible here
            var likes = 'Coding';
        }
    }
}

The thing you will notice about lexical scope is that it works forward, meaning name can be accessed by its children’s execution contexts. But it doesn’t work backward to its parents, meaning that the variable likes cannot be accessed by its parents. This also tells us that variables having the same name in different execution contexts gain precedence from top to bottom of the execution stack. A variable, having a name similar to another variable, in the innermost function (topmost context of the execution stack) will have higher precedence.

你可能注意到了词法作用域是向前的,意思是子执行环境可以访问name。但不是由父级向后的,意味着父级不能访问likes。这也告诉了我们,在不同执行环境中同名变量优先级在执行栈由上到下增加。一个变量和另一个变量同名,内层函数(执行栈顶的环境)有更高的优先级。

Closures

闭包

The concept of closures is closely related to Lexical Scope, which we studied above. A Closure is created when an inner function tries to access the scope chain of its outer function meaning the variables outside of the immediate lexical scope. Closures contain their own scope chain, the scope chain of their parents and the global scope.

闭包的概念和我们刚学习的词法作用域紧密相关。当内部函数试着访问外部函数的作用域链(词法作用域之外的变量)时产生闭包。闭包包括他们自己的作用域链、父级作用域链和全局作用域。

A closure can not only access the variables defined in its outer function but also the arguments of the outer function.

闭包不仅能访问外部函数的变量,也能访问外部函数的参数。

A closure can also access the variables of its outer function even after the function has returned. This allows the returned function to maintain access to all the resources of the outer function.

即使函数已经return,闭包仍然能访问外部函数的变量。这意味着return的函数允许持续访问外部函数的所有资源。

When you return an inner function from a function, that returned function will not be called when you try to call the outer function. You must first save the invocation of the outer function in a separate variable and then call the variable as a function. Consider this example:

当你的外部函数return一个内部函数,调用外部函数时return的函数并不会被调用。你必须先用一个单独的变量保存外部函数的调用,然后将这个变量当做函数来调用。看下面这个例子:

function greet() {
    name = 'Hammad';
    return function () {
        console.log('Hi ' + name);
    }
}

greet(); // nothing happens, no errors

// the returned function from greet() gets saved in greetLetter
greetLetter = greet();

 // calling greetLetter calls the returned function from the greet() function
greetLetter(); // logs 'Hi Hammad'

The key thing to note here is that greetLetter function can access the name variable of the greet function even after it has been returned. One way to call the returned function from the greet function without variable assignment is by using parentheses () two times ()() like this:

值得注意的是,即使在greet函数return后,greetLetter函数仍可以访问greet函数的name变量。如果不使用变量赋值来调用greet函数return的函数,一种方法是使用()两次()(),如下所示:

function greet() {
    name = 'Hammad';
    return function () {
        console.log('Hi ' + name);
    }
}

greet()(); // logs 'Hi Hammad'

Public and Private Scope

共有作用域和私有作用域

In many other programming languages, you can set the visibility of properties and methods of classes using public, private and protected scopes. Consider this example using the PHP language:

在许多其他编程语言中,你可以通过 public、private 和 protected 作用域来设置类中变量和方法的可见性。看下面这个 PHP 的例子

// Public Scope
public $property;
public function method() {
  // ...
}

// Private Sccpe
private $property;
private function method() {
  // ...
}

// Protected Scope
protected $property;
protected function method() {
  // ...
}

Encapsulating functions from the public (global) scope saves them from vulnerable attacks. But in JavaScript, there is no such thing as public or private scope. However, we can emulate this feature using closures. To keep everything separate from the global we must first encapsulate our functions within a function like this:

将函数从公有(全局)作用域中封装,使它们免受攻击。但在 JavaScript 中,没有 共有作用域和私有作用域。然而我们可以用闭包实现这一特性。为了使每个函数从全局中分离出去,我们要将它们封装进如下所示的函数中:

(function () {
  // private scope
})();

The parenthesis at the end of the function tells the interpreter to execute it as soon as it reads it without invocation. We can add functions and variables in it and they will not accessible outside. But what if we want to access them outside, meaning we want some of them to be public and some of them to be private? One type of closure, we can use, is called the Module Pattern which allows us to scope our functions using both public and private scopes in an object.

函数结尾的括号告诉解析器立即执行此函数。我们可以在其中加入变量和函数,外部无法访问。但如果我们想在外部访问它们,也就是说我们希望它们一部分是公开的,一部分是私有的。我们可以使用闭包的一种形式,称为模块模式(Module Pattern),它允许我们用一个对象中的公有作用域和私有作用域来划分函数。

THE MODULE PATTERN

模块模式

The Module Pattern looks like this:

模块模式如下所示:

var Module = (function() {
    function privateMethod() {
        // do something
    }

    return {
        publicMethod: function() {
            // can call privateMethod();
        }
    };
})();

The return statement of the Module contains our public functions. The private functions are just those that are not returned. Not returning functions makes them inaccessible outside of the Module namespace. But our public functions can access our private functions which make them handy for helper functions, AJAX calls, and other things.

Module 的return语句包含了我们的公共函数。私有函数并没有被return。函数没有被return确保了它们在 Module 命名空间无法访问。但我们的共有函数可以访问我们的私有函数,方便它们使用有用的函数、AJAX 调用或其他东西。

Module.publicMethod(); // works
Module.privateMethod(); // Uncaught ReferenceError: privateMethod is not defined

One convention is to begin private functions with an underscore, and returning an anonymous object containing our public functions. This makes them easy to manage in a long object. This is how it looks:

一种习惯是以下划线作为开始命名私有函数,并返回包含共有函数的匿名对象。这使它们在很长的对象中很容易被管理。向下面这样:

var Module = (function () {
    function _privateMethod() {
        // do something
    }
    function publicMethod() {
        // do something
    }
    return {
        publicMethod: publicMethod,
    }
})();

IMMEDIATELY-INVOKED FUNCTION EXPRESSION (IIFE)

立即执行函数表达式(IIFE)

Another type of closure is the Immediately-Invoked Function Expression (IIFE). This is a self-invoked anonymous function called in the context of window, meaning that the value of this is set window. This exposes a single global interface to interact with. This is how it looks:

另一种形式的闭包是立即执行函数表达式(Immediately-Invoked Function Expression,IIFE)。这是一种在 window 上下文中自调用的匿名函数,也就是说this的值是window。它暴露了一个单一全局接口用来交互。如下所示:

(function(window) {
    // do anything
})(this);

Changing Context with .call(), .apply() and .bind()

使用 .call(), .apply() 和 .bind() 改变上下文

Call and Apply functions are used to change the context while calling a function. This gives you incredible programming capabilities (and some ultimate powers to Rule The World). To use the call or apply function, you just need to call it on the function instead of invoking the function using a pair of parenthesis and pass the context as the first argument. The function’s own arguments can be passed after the context.

Call 和 Apply 函数来改变函数调用时的上下文。这带给你神奇的编程能力(和终极统治世界的能力)。你只需要使用 call 和 apply 函数并把上下文当做第一个参数传入,而不是使用括号来调用函数。函数自己的参数可以在上下文后面传入。

function hello() {
    // do something...
}

hello(); // the way you usually call it
hello.call(context); // here you can pass the context(value of this) as the first argument
hello.apply(context); // here you can pass the context(value of this) as the first argument

The difference between .call() and .apply() is that in Call, you pass the rest of the arguments as a list separated by a comma while apply allows you to pass the arguments in an array.

.call().apply()的区别是 Call 中其他参数用逗号分隔传入,而 Apply 允许你传入一个参数数组。

function introduce(name, interest) {
    console.log('Hi! I\'m '+ name +' and I like '+ interest +'.');
    console.log('The value of this is '+ this +'.')
}

introduce('Hammad', 'Coding'); // the way you usually call it
introduce.call(window, 'Batman', 'to save Gotham'); // pass the arguments one by one after the contextt
introduce.apply('Hi', ['Bruce Wayne', 'businesses']); // pass the arguments in an array after the context

// Output:
// Hi! I'm Hammad and I like Coding.
// The value of this is [object Window].
// Hi! I'm Batman and I like to save Gotham.
// The value of this is [object Window].
// Hi! I'm Bruce Wayne and I like businesses.
// The value of this is Hi.

Call is slightly faster in performance than Apply.

Call 比 Apply 的效率高一点。

The following example takes a list of items in the document and logs them to the console one by one.

下面这个例子列举文档中所有项目,然后依次在控制台打印出来。

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Things to learn</title>
</head>
<body>
    <h1>Things to Learn to Rule the World</h1>
    <ul>
        <li>Learn PHP</li>
        <li>Learn Laravel</li>
        <li>Learn JavaScript</li>
        <li>Learn VueJS</li>
        <li>Learn CLI</li>
        <li>Learn Git</li>
        <li>Learn Astral Projection</li>
    </ul>
    <script>
        // Saves a NodeList of all list items on the page in listItems
        var listItems = document.querySelectorAll('ul li');
        // Loops through each of the Node in the listItems NodeList and logs its content
        for (var i = 0; i < listItems.length; i++) {
          (function () {
            console.log(this.innerHTML);
          }).call(listItems[i]);
        }

        // Output logs:
        // Learn PHP
        // Learn Laravel
        // Learn JavaScript
        // Learn VueJS
        // Learn CLI
        // Learn Git
        // Learn Astral Projection
    </script>
</body>
</html>

The HTML only contains an unordered list of items. The JavaScript then selects all of them from the DOM. The list is looped over till the end of the items in the list. Inside the loop, we log the content of the list item to the console.

HTML文档中仅包含一个无序列表。JavaScript 从 DOM 中选取它们。列表项会被从头到尾循环一遍。在循环时,我们把列表项的内容输出到控制台。

This log statement is wrapped in a function wrapped in parentheses on which the call function is called. The corresponding list item is passed to the call function so that the the keyword in the console statement logs the innerHTML of the correct object.

输出语句包含在由括号包裹的函数中,然后调用call函数。相应的列表项传入 call 函数,确保控制台输出正确对象的 innerHTML。

Objects can have methods, likewise functions being objects can also have methods. In fact, a JavaScript function comes with four built-in methods which are:

对象可以有方法,同样函数对象也可以有方法。事实上,JavaScript 函数有 4 个内置方法:

  • Function.prototype.apply()
  • Function.prototype.bind() (Introduced in ECMAScript 5 (ES5))
  • Function.prototype.call()
  • Function.prototype.toString()

Function.prototype.toString() returns a string representation of the source code of the function.

Function.prototype.toString()返回函数代码的字符串表示。

Till now, we have discussed .call(), .apply(), and toString(). Unlike Call and Apply, Bind doesn’t itself call the function, it can only be used to bind the value of context and other arguments before calling the function. Using Bind in one of the examples from above:

到现在为止,我们讨论了.call().apply()toString()。与 Call 和 Apply 不同,Bind 并不是自己调用函数,它只是在函数调用之前绑定上下文和其他参数。在上面提到的例子中使用 Bind:

(function introduce(name, interest) {
    console.log('Hi! I\'m '+ name +' and I like '+ interest +'.');
    console.log('The value of this is '+ this +'.')
}).bind(window, 'Hammad', 'Cosmology')();

// logs:
// Hi! I'm Hammad and I like Cosmology.
// The value of this is [object Window].

Bind is like the call function, it allows you pass the rest of the arguments one by one separated by a comma and not like apply, in which you pass the arguments in an array.

Bind 像call函数一样用逗号分隔其他传入参数,不像apply那样用数组传入参数。

Conclusion

结论

These concepts are radical to JavaScript and important to understand if you want to approach more advanced topics. I hope you got a better understanding of JavaScript Scope and things around it. If something just didn’t click, feel free to ask in the comments below.

这些概念是 JavaScript 的基础,如果你想钻研更深的话,理解这些很重要。我希望你对 JavaScript 作用域及相关概念有了更好地理解。如果有东西不清楚,可以在评论区提问。

Scope up your code and till then, Happy Coding!

作用域常伴你的代码左右,享受编码!