Maison > interface Web > js tutoriel > Explication détaillée de l'utilisation de la commande let dans js ES6

Explication détaillée de l'utilisation de la commande let dans js ES6

零下一度
Libérer: 2017-07-09 09:34:56
original
1706 Les gens l'ont consulté

ES6 ajoute une nouvelle commande let pour déclarer les variables. Son utilisation est similaire à var, mais la variable déclarée n'est valide que dans le bloc de code où se trouve la commande let

letCommand

Utilisation de base

ES6 new La commande let est utilisée pour déclarer des variables. Son utilisation est similaire à var, mais la variable déclarée n'est valide que dans le bloc de code où se trouve la commande let.

{
 let a = 10;
 var b = 1;
}

a // ReferenceError: a is not defined.
b // 1
Copier après la connexion

Le code ci-dessus se trouve dans le bloc de code et déclare deux variables en utilisant respectivement let et var. Ensuite, ces deux variables sont appelées en dehors du bloc de code. En conséquence, la variable déclarée par let signale une erreur et la variable déclarée par var renvoie la valeur correcte. Cela montre que la variable déclarée par let n'est valide que dans le bloc de code dans lequel elle se trouve.

Pour le compteur de boucles, il est très approprié d'utiliser la commande let.

for (let i = 0; i < 10; i++) {}

console.log(i);
//ReferenceError: i is not defined
Copier après la connexion

Dans le code ci-dessus, le compteur i n'est valide que dans le corps de la boucle for, et une erreur sera signalée s'il est référencé en dehors de la boucle.

Si le code suivant utilise var, le résultat final est 10.

var a = [];
for (var i = 0; i < 10; i++) {
 a[i] = function () {
  console.log(i);
 };
}
a[6](); // 10
Copier après la connexion

Dans le code ci-dessus, la variable i est déclarée par var et est valide dans la portée globale. Ainsi, à chaque boucle, la nouvelle valeur i écrasera l'ancienne valeur, ce qui fera que la sortie finale sera la valeur de i lors du dernier tour.

Si let est utilisé, la variable déclarée n'est valide que dans la portée au niveau du bloc et le résultat final est 6.

var a = [];
for (let i = 0; i < 10; i++) {
 a[i] = function () {
  console.log(i);
 };
}
a[6](); // 6
Copier après la connexion

Dans le code ci-dessus, la variable i est déclarée par let Le courant i n'est valide que dans ce cycle, donc i dans chaque cycle est en fait une nouvelle variable, donc la sortie finale est 6 .

Pas de promotion variable

ne souffrons pas de "promotion variable" comme la var. Par conséquent, les variables doivent être utilisées après avoir été déclarées, sinon une erreur sera signalée.

console.log(foo); // 输出undefined
console.log(bar); // 报错ReferenceError

var foo = 2;
let bar = 2;
Copier après la connexion

Dans le code ci-dessus, la variable foo est déclarée avec la commande var et la promotion de la variable se produira. Autrement dit, lorsque le script démarre, la variable foo existe déjà, mais n'a aucune valeur, donc indéfini sera affiché. La variable bar est déclarée avec la commande let et aucune promotion de variable n'aura lieu. Cela signifie que la variable bar n'existe pas avant de la déclarer, et si elle est utilisée, une erreur sera générée.

Zone morte temporaire

Tant que la commande let existe dans la portée au niveau du bloc, les variables qu'elle déclare seront "liantes" dans cette zone et ne seront pas ne sera plus affecté par la commande let.

var tmp = 123;

if (true) {
 tmp = &#39;abc&#39;; // ReferenceError
 let tmp;
}
Copier après la connexion

Dans le code ci-dessus, il y a une variable globale tmp, mais let déclare une variable locale tmp dans la portée au niveau du bloc, ce qui amène cette dernière à lier cette portée au niveau du bloc, donc avant, let déclare la variable, attribuer une valeur à tmp signalera une erreur.

ES6 stipule clairement que s'il y a des commandes let et const dans un bloc, les variables déclarées par ces commandes dans ce bloc formeront dès le début une portée fermée. Toute utilisation de ces variables avant déclaration entraînera une erreur.

En bref, au sein du bloc de code, la variable n'est disponible que lorsqu'elle est déclarée à l'aide de la commande let. Grammaticalement, cela s'appelle la « zone morte temporaire » (TDZ).

if (true) {
 // TDZ开始
 tmp = &#39;abc&#39;; // ReferenceError
 console.log(tmp); // ReferenceError

 let tmp; // TDZ结束
 console.log(tmp); // undefined

 tmp = 123;
 console.log(tmp); // 123
}
Copier après la connexion

Dans le code ci-dessus, avant que la commande let ne déclare la variable tmp, elle appartient à la "zone morte" de la variable tmp.

La « zone morte temporaire » signifie également que typeof n'est plus une opération sûre à 100 %.

typeof x; // ReferenceError
let x;
Copier après la connexion

Dans le code ci-dessus, la variable x est déclarée à l'aide de la commande let, donc avant d'être déclarée, elle appartient à la "zone morte" de x Tant que la variable est utilisée, an. l'erreur sera signalée. Par conséquent, typeof lancera une ReferenceError lors de l’exécution.

À titre de comparaison, si une variable n'est pas déclarée du tout, l'utilisation de typeof ne signalera pas d'erreur.

typeof undeclared_variable // "undefined"
Copier après la connexion

Dans le code ci-dessus, undeclared_variable est un nom de variable qui n'existe pas, et le résultat est "indéfini". Par conséquent, avant de le laisser, le typeofoperator était sûr à 100 % et ne signalerait jamais d'erreur. Ce n'est plus vrai. Cette conception vise à aider chacun à développer de bonnes habitudes de programmation. Les variables doivent être utilisées après avoir été déclarées, sinon une erreur sera signalée.

Certaines "zones mortes" sont cachées et difficiles à trouver.

function bar(x = y, y = 2) {
 return [x, y];
}
bar(); // 报错
Copier après la connexion

Dans le code ci-dessus, la raison pour laquelle une erreur est signalée lors de l'appel de la fonction bar (certaines implémentations peuvent ne pas signaler d'erreur) est que la valeur par défaut du paramètre x est égale à un autre paramètre y, et y n'a pas été déclaré pour le moment et appartient à " Dead Zone ". Si la valeur par défaut de y est x, aucune erreur ne sera signalée car x a été déclaré à ce moment-là.

function bar(x = 2, y = x) {
 return [x, y];
}
bar(); // [2, 2]
Copier après la connexion

ES6 stipule que la promotion de variable ne se produit pas dans les zones mortes temporaires et dans les instructions let et const. Ceci vise principalement à réduire les erreurs d'exécution et à empêcher l'utilisation de la variable avant qu'elle ne soit déclarée, ce qui entraîne un comportement inattendu. . De telles erreurs sont très courantes dans ES5, et désormais, grâce à cette disposition, il est facile d'éviter de telles erreurs.

En bref, l'essence de la zone morte temporaire est que dès que vous entrez dans la portée actuelle, la variable que vous souhaitez utiliser existe déjà, mais elle n'est pas disponible. Vous ne pouvez l'obtenir et l'obtenir que lorsque. la ligne de code qui déclare la variable apparaît.

N'autorise pas les déclarations répétées

let n'autorise pas les déclarations répétées de la même variable dans la même portée.

// 报错
function () {
 let a = 10;
 var a = 1;
}

// 报错
function () {
 let a = 10;
 let a = 1;
}
Copier après la connexion

Par conséquent, les paramètres ne peuvent pas être redéclarés à l'intérieur d'une fonction.

function func(arg) {
 let arg; // 报错
}

function func(arg) {
 {
  let arg; // 不报错
 }
}
Copier après la connexion

Portée au niveau du bloc

Pourquoi avons-nous besoin d'une portée au niveau du bloc ?

ES5 n'a qu'une portée globale et une portée de fonction, mais pas de portée au niveau du bloc, ce qui entraîne de nombreux scénarios déraisonnables.

Dans le premier scénario, la variable interne peut écraser la variable externe.

var tmp = new Date();

function f() {
 console.log(tmp);
 if (false) {
  var tmp = "hello world";
 }
}

f(); // undefined
Copier après la connexion

上面代码中,函数f执行后,输出结果为undefined,原因在于变量提升,导致内层的tmp变量覆盖了外层的tmp变量。

第二种场景,用来计数的循环变量泄露为全局变量。

var s = &#39;hello&#39;;

for (var i = 0; i < s.length; i++) {
 console.log(s[i]);
}

console.log(i); // 5
Copier après la connexion

上面代码中,变量i只用来控制循环,但是循环结束后,它并没有消失,泄露成了全局变量。

ES6的块级作用域

let实际上为JavaScript新增了块级作用域。

function f1() {
 let n = 5;
 if (true) {
  let n = 10;
 }
 console.log(n); // 5
}
Copier après la connexion

上面的函数有两个代码块,都声明了变量n,运行后输出5。这表示外层代码块不受内层代码块的影响。如果使用var定义变量n,最后输出的值就是10。

ES6允许块级作用域的任意嵌套。

{{{{{let insane = &#39;Hello World&#39;}}}}};
Copier après la connexion

上面代码使用了一个五层的块级作用域。外层作用域无法读取内层作用域的变量。

{{{{
 {let insane = &#39;Hello World&#39;}
 console.log(insane); // 报错
}}}};
Copier après la connexion

内层作用域可以定义外层作用域的同名变量。

{{{{
 let insane = &#39;Hello World&#39;;
 {let insane = &#39;Hello World&#39;}
}}}};
Copier après la connexion

块级作用域的出现,实际上使得获得广泛应用的立即执行函数表达式(IIFE)不再必要了。

// IIFE 写法
(function () {
 var tmp = ...;
 ...
}());

// 块级作用域写法
{
 let tmp = ...;
 ...
}
Copier après la connexion

块级作用域与函数声明

函数能不能在块级作用域之中声明,是一个相当令人混淆的问题。

ES5规定,函数只能在顶层作用域和函数作用域之中声明,不能在块级作用域声明。

// 情况一
if (true) {
 function f() {}
}

// 情况二
try {
 function f() {}
} catch(e) {
}
Copier après la connexion

上面代码的两种函数声明,根据ES5的规定都是非法的。

但是,浏览器没有遵守这个规定,为了兼容以前的旧代码,还是支持在块级作用域之中声明函数,因此上面两种情况实际都能运行,不会报错。不过,“严格模式”下还是会报错。

// ES5严格模式
&#39;use strict&#39;;
if (true) {
 function f() {}
}
// 报错
Copier après la connexion

ES6 引入了块级作用域,明确允许在块级作用域之中声明函数。


// ES6严格模式
&#39;use strict&#39;;
if (true) {
 function f() {}
}
// 不报错
Copier après la connexion

ES6 规定,块级作用域之中,函数声明语句的行为类似于let,在块级作用域之外不可引用。

function f() { console.log(&#39;I am outside!&#39;); }
(function () {
 if (false) {
  // 重复声明一次函数f
  function f() { console.log(&#39;I am inside!&#39;); }
 }

 f();
}());
Copier après la connexion

上面代码在 ES5 中运行,会得到“I am inside!”,因为在if内声明的函数f会被提升到函数头部,实际运行的代码如下。

// ES5版本
function f() { console.log(&#39;I am outside!&#39;); }
(function () {
 function f() { console.log(&#39;I am inside!&#39;); }
 if (false) {
 }
 f();
}());
Copier après la connexion

ES6 的运行结果就完全不一样了,会得到“I am outside!”。因为块级作用域内声明的函数类似于let,对作用域之外没有影响,实际运行的代码如下。

// ES6版本
function f() { console.log(&#39;I am outside!&#39;); }
(function () {
 f();
}());
Copier après la connexion

很显然,这种行为差异会对老代码产生很大影响。为了减轻因此产生的不兼容问题,ES6在附录B里面规定,浏览器的实现可以不遵守上面的规定,有自己的行为方式。

允许在块级作用域内声明函数。
函数声明类似于var,即会提升到全局作用域或函数作用域的头部。
同时,函数声明还会提升到所在的块级作用域的头部。
注意,上面三条规则只对ES6的浏览器实现有效,其他环境的实现不用遵守,还是将块级作用域的函数声明当作let处理。

前面那段代码,在 Chrome 环境下运行会报错。

// ES6的浏览器环境
function f() { console.log(&#39;I am outside!&#39;); }
(function () {
 if (false) {
  // 重复声明一次函数f
  function f() { console.log(&#39;I am inside!&#39;); }
 }

 f();
}());
// Uncaught TypeError: f is not a function
Copier après la connexion

上面的代码报错,是因为实际运行的是下面的代码。

// ES6的浏览器环境
function f() { console.log(&#39;I am outside!&#39;); }
(function () {
 var f = undefined;
 if (false) {
  function f() { console.log(&#39;I am inside!&#39;); }
 }

 f();
}());
// Uncaught TypeError: f is not a function
Copier après la connexion

考虑到环境导致的行为差异太大,应该避免在块级作用域内声明函数。如果确实需要,也应该写成函数表达式,而不是函数声明语句。

// 函数声明语句
{
 let a = &#39;secret&#39;;
 function f() {
  return a;
 }
}

// 函数表达式
{
 let a = &#39;secret&#39;;
 let f = function () {
  return a;
 };
}
Copier après la connexion

另外,还有一个需要注意的地方。ES6的块级作用域允许声明函数的规则,只在使用大括号的情况下成立,如果没有使用大括号,就会报错。

// 不报错
&#39;use strict&#39;;
if (true) {
 function f() {}
}

// 报错
&#39;use strict&#39;;
if (true)
 function f() {}
Copier après la connexion

do 表达式

本质上,块级作用域是一个语句,将多个操作封装在一起,没有返回值。

{
 let t = f();
 t = t * t + 1;
}
Copier après la connexion

上面代码中,块级作用域将两个语句封装在一起。但是,在块级作用域以外,没有办法得到t的值,因为块级作用域不返回值,除非t是全局变量。

现在有一个提案,使得块级作用域可以变为表达式,也就是说可以返回值,办法就是在块级作用域之前加上do,使它变为do表达式。


let x = do {
 let t = f();
 t * t + 1;
};
Copier après la connexion

上面代码中,变量x会得到整个块级作用域的返回值。

JavaScript ES6 的 let 和 var 的比较

在javascript 1.7中, let 关键词被添加进来, 我听说它声明之后类似于”本地变量“, 但是我仍然不确定它和 关键词 var 的具体区别。

回答:
不同点在于作用域, var关键词的作用域是最近的函数作用域(如果在函数体的外部就是全局作用域), let 关键词的作用域是最接近的块作用域(如果在任何块意外就是全局作用域),这将会比函数作用域更小。
同样, 像var 一样, 使用let 声明的变量也会在其被声明的地方之前可见。

下面是Demo 例子。

全局(Global)

当在函数体之外它们是平等的。

let me = &#39;go&#39;; //globally scoped 
var i = &#39;able&#39;; //globally scoped
Copier après la connexion

函数(Function)
当瞎下面这种, 也是平等的。

function ingWithinEstablishedParameters() { 
  let terOfRecommendation = &#39;awesome worker!&#39;; //function block scoped 
  var sityCheerleading = &#39;go!&#39;; //function block scoped 
};
Copier après la connexion

块(Block)
这是不同点, let 只是在 for 循环中, var 却是在整个函数都是可见的。

function allyIlliterate() { 
  //tuce is *not* visible out here 
 
  for( let tuce = 0; tuce < 5; tuce++ ) { 
    //tuce is only visible in here (and in the for() parentheses) 
  }; 
 
  //tuce is *not* visible out here 
}; 
 
function byE40() { 
  //nish *is* visible out here 
 
  for( var nish = 0; nish < 5; nish++ ) { 
    //nish is visible to the whole function 
  }; 
 
  //nish *is* visible out here 
};
Copier après la connexion

Ce qui précède est le contenu détaillé de. pour plus d'informations, suivez d'autres articles connexes sur le site Web de PHP en chinois!

Étiquettes associées:
source:php.cn
Déclaration de ce site Web
Le contenu de cet article est volontairement contribué par les internautes et les droits d'auteur appartiennent à l'auteur original. Ce site n'assume aucune responsabilité légale correspondante. Si vous trouvez un contenu suspecté de plagiat ou de contrefaçon, veuillez contacter admin@php.cn
Tutoriels populaires
Plus>
Derniers téléchargements
Plus>
effets Web
Code source du site Web
Matériel du site Web
Modèle frontal