Javescript——变量声明的区别
原文链接:ES6 Syntax and Feature Overview
Keyword | Scope | Hoisting | Can Be Reassigned | Can Be Redeclared |
---|---|---|---|---|
var |
Function scope | Yes | Yes | Yes |
let |
Block scope | No | Yes | No |
const |
Block scope | No | No | No |
Understanding Variables, Scope, and Hoisting in JavaScript
In JavaScript, there are three keywords used to declare a variable — var
, let
, and const
— and each one affects how the code will interpret the variable differently.
In JavaScript, the value contained inside a variable can be any JavaScript data type, including a number, string, or object.
We can use var
to demonstrate the concept of a variable itself.
As mentioned previously, variables can be used to represent any JavaScript data type. In this example, we’ll declare variables with string, number, object, Boolean, and null values.
Variables store data in memory which can later be accessed and modified. Variables can also be reassigned and given a new value.
Naming Variables
Understanding Syntax and Code Structure in JavaScript,
JavaScript also has the convention of using camel case (sometimes stylized as camelCase) in the names of functions and variables declared with var
or let
. This is the practice of writing the first word lowercase, and then capitalizing the first letter of every subsequent word with no spaces between them.
Most variables that are not constants will follow this convention, with some exceptions. The names of variables that are constant, declared with the const
keyword, are typically written in all uppercase.
Difference Between var
, let
, and const
JavaScript has three different keywords to declare a variable, which adds an extra layer of intricacy to the language. The differences between the three are based on scope, hoisting, and reassignment.
Keyword | Scope | Hoisting | Can Be Reassigned | Can Be Redeclared |
---|---|---|---|---|
var |
Function scope | Yes | Yes | Yes |
let |
Block scope | No | Yes | No |
const |
Block scope | No | No | No |
You may be wondering which of the three you should use in your own programs. A commonly accepted practice is to use const
as much as possible, and let
in the case of loops and reassignment. Generally, var
can be avoided outside of working on legacy code.
Variable Scope
Scope in JavaScript refers to the current context of code, which determines the accessibility of variables to JavaScript. The two types of scope are local and global:
- Global variables are those declared outside of a block
- Local variables are those declared inside of a block
We learned that variables can be reassigned. Using local scope, we can actually create new variables with the same name as a variable in an outer scope without changing or reassigning the original value.
In the example below, we will create a global species
variable. Within the function is a local variable with the same name. By sending them to the console, we can see how the variable’s value is different depending on the scope, and the original value is not changed.
// Initialize a global variable
var species = "human"; function transform() {
// Initialize a local, function-scoped variable
var species = "werewolf";
console.log(species);
} // Log the global and local variable
console.log(species);
transform();
console.log(species);
#Output
human
werewolf
human
In this example, the local variable is function-scoped. Variables declared with the var
keyword are always function-scoped, meaning they recognize functions as having a separate scope. This locally-scoped variable is therefore not accessible from the global scope.
The new keywords let
and const
, however, are block-scoped. This means that a new, local scope is created from any kind of block, including function blocks, if
statements, and for
and while
loops.
To illustrate the difference between function- and block-scoped variables, we will assign a new variable in an if
block using let
.
var fullMoon = true; // Initialize a global variable
let species = "human"; if (fullMoon) {
// Initialize a block-scoped variable
let species = "werewolf";
console.log(`It is a full moon. Lupin is currently a ${species}.`);
} console.log(`It is not a full moon. Lupin is currently a ${species}.`);
#Output
It is a full moon. Lupin is currently a werewolf.
It is not a full moon. Lupin is currently a human.
In this example, the species
variable has one value globally (human
), and another value locally (werewolf
). If we were to use var
, however, there would be a different result.
// Use var to initialize a variable
var species = "human"; if (fullMoon) {
// Attempt to create a new variable in a block
var species = "werewolf";
console.log(`It is a full moon. Lupin is currently a ${species}.`);
} console.log(`It is not a full moon. Lupin is currently a ${species}.`);
# Output
It is a full moon. Lupin is currently a werewolf.
It is not a full moon. Lupin is currently a werewolf.
In the result of this example, both the global variable and the block-scoped variable end up with the same value, werewolf
. This is because instead of creating a new local variable with var
, you are reassigning the same variable in the same scope. var
does not recognize if
to be part of a different, new scope
It is generally recommended that you declare variables that are block-scoped, as they produce code that is less likely to unintentionally override variable values.
Hoisting
Javascript Hoisting(国内一般翻译为 变量提升)
Javascript Hoisting:In javascript, every variable declaration is hoisted to the top of its declaration context.我的理解就是在Javascript语言中,变量的声明(注意不包含变量初始化)会被提升(置顶)到声明所在的上下文,也就是说,在变量的作用域内,不管变量在何处声明,都会被提升到作用域的顶部,但是变量初始化的顺序不变。
If we attempt to use a variable before it has been declared and initialized, it will return undefined
.
// Attempt to use a variable before declaring it
console.log(x); // Variable assignment
var x = 100;
# Output
undefined
However, if we omit the var
keyword, we are no longer declaring the variable, only initializing it. It will return a ReferenceError
and halt the execution of the script.
// Attempt to use a variable before declaring it
console.log(x); // Variable assignment without var
x = 100;
# Output
ReferenceError: x is not defined
The reason for this is due to hoisting, a behavior of JavaScript in which variable and function declarations are moved to the top of their scope.
Since only the actual declaration is hoisted, not the initialization, the value in the first example returns undefined
.
Keyword | Scope | Hoisting | Can Be Reassigned | Can Be Redeclared |
---|---|---|---|---|
var |
Function scope | Yes | Yes | Yes |
let |
Block scope | No | Yes | No |
const |
Block scope | No | No | No |
To demonstrate this concept more clearly, below is the code we wrote and how JavaScript actually interpreted it.
// The code we wrote
console.log(x);
var x = 100; // How JavaScript interpreted it
var x;
console.log(x);
x = 100;
JavaScript saved x
to memory as a variable before the execution of the script. Since it was still called before it was defined, the result is undefined
and not 100
. However, it does not cause a ReferenceError
and halt the script. Although the var
keyword did not actually change location of the var
, this is a helpful representation of how hoisting works.
This behavior can cause issues, though, because the programmer who wrote this code likely expects the output of x
to be true
, when it is instead undefined
.
We can also see how hoisting can lead to unpredictable results in the next example:
// Initialize x in the global scope
var x = 100; function hoist() {
// A condition that should not affect the outcome of the code
if (false) {
var x = 200;
}
console.log(x);
} hoist();
# Output
undefined
In this example, we declared x
to be 100
globally. Depending on an if
statement, x
could change to 200
, but since the condition was false
it should not have affected the value of x
. Instead, x
was hoisted to the top of the hoist()
function, and the value became undefined
.
This type of unpredictable behavior can potentially cause bugs in a program. Since let
and const
are block-scoped, they will not hoist in this manner, as seen below.
// Initialize x in the global scope
let x = true; function hoist() {
// Initialize x in the function scope
if (3 === 4) {
let x = false;
}
console.log(x);
} hoist();
# Output
true
Duplicate declaration of variables, which is possible with var
, will throw an error with let
and const
.
// Attempt to overwrite a variable declared with var
var x = 1;
var x = 2; console.log(x); # Output
2 // Attempt to overwrite a variable declared with let
let y = 1;
let y = 2; console.log(y); # Output
Uncaught SyntaxError: Identifier 'y' has already been declared
To summarize, variables introduced with var
have the potential of being affected by hoisting, a mechanism in JavaScript in which variable declarations are saved to memory. This may result in undefined variables in one’s code.
The introduction of let
and const
resolves this issue by throwing an error when attempting to use a variable before declaring it or attempting to declare a variable more than once.
Constants
Many programming languages feature constants, which are values that cannot be modified or changed. In JavaScript, the const
identifier is modelled after constants, and the values assigned to a const
cannot be reassigned.
It is common convention to write all const
identifiers in uppercase. This marks them as readily distinguishable from other variable values.
In the example below, we initialize the variable SPECIES
as a constant with the const
keyword. Trying to reassign the variable will result in an error.
// Assign value to const
const SPECIES = "human"; // Attempt to reassign value
SPECIES = "werewolf"; console.log(SPECIES); # Output
Uncaught TypeError: Assignment to constant variable.
Since const
values cannot be reassigned, they need to be declared and initialized at the same time, or will also throw an error.
// Declare but do not initialize a const
const TODO; console.log(TODO); Output
Uncaught SyntaxError: Missing initializer in const declaration
Values that cannot change in programming are known as immutable, while values that can be changed are mutable.
Although const
values cannot be reassigned, they are mutable as it is possible to modify the properties of objects declared with const
.
// Create a CAR object with two properties
const CAR = {
color: "blue",
price: 15000
} // Modify a property of CAR
CAR.price = 20000; console.log(CAR); # Output
{ color: 'blue', price: 20000 }
Constants are useful for making it clear to your future self and other programmers working on a project with you that the intended variable should not be reassigned. If you expect that a variable may be modified in the future, you will likely want to use let
to declare the variable instead.
To compare how variables are used in other languages, you can read our tutorial on “How To Use Variables in Python 3.”
The let
statement declares a block scope local variable, optionally initializing it to a value.
let x = 8; if (x === 8) {
let x = 4; console.log(x);
// expected output: 2
} console.log(x);
// expected output: 1
let
allows you to declare variables that are limited to a scope of a block
statement, or expression on which it is used, unlike the var
keyword, which defines a variable globally, or locally to an entire function regardless of block scope.
The other difference between var
and let
is that the latter is initialized to value only when parser evaluates it (see below).
Just like const
the let
does not create properties of the window
object when declared globally (in the top-most scope).
An explanation of why the name "let" was chosen can be found here.
var
takes that name - it is variable,const
- it is a constant,- Let is a mathematical statement that was adopted by early programming languages like Scheme and Basic.
Variables are considered low level entities not suitable for higher levels of abstraction, thus the desire of many language designers to introduce similar but more powerful concepts like in Clojure, F#, Scala, where let
might mean a value, or a variable that can be assigned, but not changed, which in turn lets the compiler catch more programming errors and optimize code better.
JavaScript has had var
from the beginning, so they just needed another keyword, and just borrowed from dozens of other languages that use let
already as a traditional keyword as close to var
as possible, although in JavaScript let
creates block scope local variable instead.
使用 var 关键字声明的变量不具备块级作用域的特性,它在 {} 外依然能被访问到。
在 ES6 之前,是没有块级作用域的概念的。
ES6 可以使用 let 关键字来实现块级作用域。
let 声明的变量只在 let 命令所在的代码块 {} 内有效,在 {} 之外不能访问。
Scoping rules
Variables declared by let
have their scope in the block for which they are defined, as well as in any contained sub-blocks. In this way, let
works very much like var
. The main difference is that the scope of a var
variable is the entire enclosing function:
function varTest() {
var x = 1;
{
var x = 2; // same variable!
console.log(x); // 2
}
console.log(x); // 2
} function letTest() {
let x = 1;
{
let x = 2; // different variable
console.log(x); // 2
}
console.log(x); // 1
}
At the top level of programs and functions, let
, unlike var
, does not create a property on the global object. For example:
var x = 'global';
let y = 'global';
console.log(this.x); // "global"
console.log(this.y); // undefined
Emulating private members
In dealing with constructors it is possible to use the let
bindings to share one or more private members without using closures:
var Thing; {
let privateScope = new WeakMap();
let counter = 0; Thing = function() {
this.someProperty = 'foo'; privateScope.set(this, {
hidden: ++counter,
});
}; Thing.prototype.showPublic = function() {
return this.someProperty;
}; Thing.prototype.showPrivate = function() {
return privateScope.get(this).hidden;
};
} console.log(typeof privateScope);
// "undefined" var thing = new Thing(); console.log(thing);
// Thing {someProperty: "foo"} thing.showPublic();
// "foo" thing.showPrivate();
// 1
The same privacy pattern with closures over local variables can be created with var
, but those need a function scope (typically an IIFE in the module pattern) instead of just a block scope like in the example above.
Redeclarations
Redeclaring the same variable within the same function or block scope raises a SyntaxError
.
Keyword | Scope | Hoisting | Can Be Reassigned | Can Be Redeclared |
---|---|---|---|---|
var |
Function scope | Yes | Yes | Yes |
let |
Block scope | No | Yes | No |
const |
Block scope | No | No | No |
if (x) {
let foo;
let foo; // SyntaxError thrown.
}
You may encounter errors in switch
statements because there is only one block.
let x = 1;
switch(x) {
case 0:
let foo;
break; case 1:
let foo; // SyntaxError for redeclaration.
break;
}
However, it's important to point out that a block nested inside a case clause will create a new block scoped lexical environment, which will not produce the redeclaration errors shown above.
let x = 1; switch(x) {
case 0: {
let foo;
break;
}
case 1: {
let foo;
break;
}
}
Temporal dead zone
hoisting
Unlike variables declared with var
, which will start with the value undefined
, let
variables are not initialized until their definition is evaluated. Accessing the variable before the initialization results in a ReferenceError
. The variable is in a "temporal dead zone" from the start of the block until the initialization is processed.
function do_something() {
console.log(bar); // undefined
console.log(foo); // ReferenceError
var bar = 1;
let foo = 2;
}
The temporal dead zone and typeof
Unlike with simply undeclared variables and variables that hold a value of undefined
, using the typeof
operator to check for the type of a variable in that variable's temporal dead zone will throw a ReferenceError
:
// prints out 'undefined'
console.log(typeof undeclaredVariable);
// results in a 'ReferenceError'
console.log(typeof i);
let i = 10;
Another example of temporal dead zone combined with lexical scoping
Due to lexical scoping, the identifier foo
inside the expression (foo + 55)
evaluates to the if block's foo, and not the overlying variable foo with the value of 33.
In the same line, the if block's foo
has already been created in the lexical environment, but has not yet reached (and terminated) its initialization (which is part of the statement itself).
The if block's foo
is still in the temporal dead zone.
function test(){
var foo = 33;
{
let foo = (foo + 55); // ReferenceError
}
}
test();
This phenomenon may confuse you in a situation like the following. The instruction let n of n.a
is already inside the private scope of the for loop's block. So, the identifiern.a
is resolved to the property 'a' of the 'n' object located in the first part of the instruction itself ("let n").
This is still in the temporal dead zone as its declaration statement has not been reached and terminated.
function go(n) {
// n here is defined!
console.log(n); // Object {a: [1,2,3]} for (let n of n.a) { // ReferenceError
console.log(n);
}
} go({a: [1, 2, 3]});
Other situations
When used inside a block, let
limits the variable's scope to that block. Note the difference between var
whose scope is inside the function where it is declared.
var a = 1;
var b = 2; if (a === 1) {
var a = 11; // the scope is global
let b = 22; // the scope is inside the if-block console.log(a); // 11
console.log(b); // 22
} console.log(a); // 11
console.log(b); // 2
However, this combination of var
and let
declaration below is a SyntaxError
due to var
being hoisted to the top of the block. This results in an implicit re-declaration of the variable.(hoisting)
let x = 1; {
var x = 2; // SyntaxError for re-declaration
}
Update compatibility data on GitHub
const
Constants are block-scoped, much like variables defined using the let
statement. The value of a constant can't be changed through reassignment, and it can't be redeclared.
const number = 42; try {
number = 99;
} catch(err) {
console.log(err);
// expected output: TypeError: invalid assignment to const `number'
// Note - error messages will vary depending on browser
} console.log(number);
// expected output: 42
Syntax
const name1 = value1 [, name2 = value2 [, ... [, nameN = valueN]]];
nameN
- The constant's name, which can be any legal identifier.
valueN
- The constant's value; this can be any legal expression, including a function expression.
Description
This declaration creates a constant whose scope can be either global or local to the block in which it is declared.
Global constants do not become properties of the window
object, unlike var
variables.
An initializer for a constant is required; that is, you must specify its value in the same statement in which it's declared (which makes sense, given that it can't be changed later).
The const
declaration creates a read-only reference to a value. It does not mean the value it holds is immutable, just that the variable identifier cannot be reassigned. For instance, in the case where the content is an object, this means the object's contents (e.g., its properties) can be altered.
All the considerations about the "temporal dead zone" apply to both let
and const
.(hoisting)
A constant cannot share its name with a function or a variable in the same scope.
Examples
The following example demonstrates how constants behave. Try this in your browser console.
// NOTE: Constants can be declared with uppercase or lowercase, but a common
// convention is to use all-uppercase letters. // define MY_FAV as a constant and give it the value 7
const MY_FAV = 7; // this will throw an error - Uncaught TypeError: Assignment to constant variable.
MY_FAV = 20; // MY_FAV is 7
console.log('my favorite number is: ' + MY_FAV); // trying to redeclare a constant throws an error - Uncaught SyntaxError: Identifier 'MY_FAV' has already been declared
const MY_FAV = 20; // the name MY_FAV is reserved for constant above, so this will fail too
var MY_FAV = 20; // this throws an error too
let MY_FAV = 20; // it's important to note the nature of block scoping
if (MY_FAV === 7) {
// this is fine and creates a block scoped MY_FAV variable
// (works equally well with let to declare a block scoped non const variable)
let MY_FAV = 20; // MY_FAV is now 20
console.log('my favorite number is ' + MY_FAV); // this gets hoisted into the global context and throws an error
var MY_FAV = 20;
} // MY_FAV is still 7
console.log('my favorite number is ' + MY_FAV); // throws an error - Uncaught SyntaxError: Missing initializer in const declaration
const FOO; // const also works on objects
const MY_OBJECT = {'key': 'value'}; // Attempting to overwrite the object throws an error - Uncaught TypeError: Assignment to constant variable.
MY_OBJECT = {'OTHER_KEY': 'value'}; // However, object keys are not protected,
// so the following statement is executed without problem
MY_OBJECT.key = 'otherValue'; // Use Object.freeze() to make object immutable // The same applies to arrays
const MY_ARRAY = [];
// It's possible to push items into the array
MY_ARRAY.push('A'); // ["A"]
// However, assigning a new array to the variable throws an error - Uncaught TypeError: Assignment to constant variable.
MY_ARRAY = ['B'];
Update compatibility data on GitHub
Javescript——变量声明的区别的更多相关文章
- JavaScript:学习笔记(7)——VAR、LET、CONST三种变量声明的区别
JavaScript:学习笔记(7)——VAR.LET.CONST三种变量声明的区别 ES2015(ES6)带来了许多闪亮的新功能,自2017年以来,许多JavaScript开发人员已经熟悉并开始使用 ...
- ES6和ES5变量声明的区别(var let const)
// es5的语法与es6的语法区别 // var let const console.log(name);//undefine,不会报错,因为变量声明会提到作用域的最前面 var name=&quo ...
- JavaScript中变量声明有var和没var的区别
JavaScript中变量声明有var和没var的区别 JavaScript中有var和没var的区别 Js中的变量声明的作用域是以函数为单位,所以我们经常见到避免全局变量污染的方法是 (functi ...
- 详解变量声明加 var 和不加 var 的区别
在全局作用域中声明变量加 var 关键字和不加 var ,js 引擎都会将这个变量声明为全局变量,在实际运行时,两种声明方式的变量的行为也是几乎一致的.但是在全局作用域下是否声明一个变量的 时候加va ...
- js 变量声明 (var使用与不使用的区别)
js 变量声明 (var使用与不使用的区别) 一.总结 一句话总结:不使用var声明变量的时候,变量是全局对象(window对象)属性,在全局中使用var声明变量是全局变量 var 全局变量 局部变量 ...
- JavaScript 中定义变量时有无var声明的区别
关于JavaScript中定义变量时有无var声明的区别 var a=5; //正确 a=5; //正确 在javascript中,以上两种方法都是定义变量的正确方法.微软的Script56.CHM中 ...
- js中变量声明有var和没有var的区别
转js中var用与不用的区别 2015年07月13日 16:08:22 阅读数:3627 Javascript声明变量的时候,虽然用var关键字声明和不用关键字声明,很多时候运行并没有问题,但是这两种 ...
- PHP中变量声明和定义的区别
先记录一下(不知道PHP是不是一样,但是C语言是这样的):把建立空间的声明称之为“定义”,而把不需要建立存储空间的声明称之为“声明”.声明的最终目的是为了提前使用,即在定义之前使用,如果不需要提前使用 ...
- c++中变量声明和变量定义的区别。2016年12月6日
整个流程: 1.程序告诉cpu,程序将要使用一个变量.(暂时不一定用到,先说一下.) 2.程序告诉CPU,程序现在就要使用一个变量.(现在就用) 3.cpu按照这个变量的类型,把内存划分出几个单位(b ...
随机推荐
- iptables详解(5)iptables的icmp扩展
ICMP(Internet Control Message Protocol)Internet控制报文协议.它是TCP/IP协议簇的一个子协议,用于在IP主机.路由器之间传递控制消息.控制消息是指网络 ...
- 基于STM32L476开发板的USB音频设备
现代音频设备中有很多知识产权. 我想研究创建一个与手机交互的算法设备(运行non-trivial算法的嵌入式设备). 我发现创建一个Lightning设备比创建一个连接到Android手机的的USB设 ...
- 4.kafka生产者---向Kafka中写入数据(转)
转: https://www.cnblogs.com/sodawoods-blogs/p/8969513.html (1)生产者概览 (1)不同的应用场景对消息有不同的需求,即是否允许消息丢失.重复 ...
- Python下载图片并保存本地的两种方式
一:使用Python中的urllib类中的urlretrieve()函数,直接从网上下载资源到本地,具体代码: 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 ...
- C# Get请求携带body
C# get 请求携带body需要用到RestSharp,可以通过NuGet获取,但是只有.NetFramework 4.5+版本支持.通过Postman可以测试并生成C#代码 var client ...
- 1205 CSRF跨站请求与django中的auth模块使用
目录 今日内容 昨日回顾 基于配置文件的编程思想 importlib模块 简单代码实现 跨站请求伪造csrf 1. 钓鱼网站 如何实现 模拟该现象的产生 2. 解决问题 解决 {% csrf_toke ...
- machine learning(10) -- classification:logistic regression cost function 和 使用 gradient descent to minimize cost function
logistic regression cost function(single example) 图像分布 logistic regression cost function(m examples) ...
- CentOS7下rabbitmq的详细安装教程
一.安装前的准备工作:[rabbitmq下载] rabbitmq下载官网地址:http://www.rabbitmq.com/ 具体的安装包的下载[这里安装的版本是3.7.5]:https://git ...
- 「数据结构与算法之链表(Python)」(四)
什么是链表 顺序表的储存分为一体式结构和分离式结构,但总的来说存储数据的内存是一块连续的单元,每次申请前都要预估所需要的内存空间大小.这样就不能随意的增加我们需要的数据了.链接就是为了解决这个问题.它 ...
- 服务如何配置JVM
为了使JVM的资源利用更合理,往往需要手动设置JVM的初始值.下面将详细介绍不同环境下的JVM配置. 1.如果是应用程序,则:java -Xms800m -Xmx800m 你的类名 java -Xms ...