再谈Js定义变量,你不得不踩的那些坑

前言

今天在Codewars刷题,碰到一个7kyu(这个等级,学过一点js的人,都会做)等级的题目,题目很简单:
再谈Js定义变量,你不得不踩的那些坑_第1张图片
求累加和,并对异常情况进行特殊处理。看着非常简单,事实上并不难。我的第一次代码:

// ?????
var SequenceSum = (function () {
    function SequenceSum () { }
	 var sum = 0
     var str = ''
    SequenceSum.showSequence = function (count) {
        for (let i = 0; i < count; i++) {
            str += `${i}+`
            sum += i
        }
        sum += count
        if (count < 0) return `${count}<0`;
        if (count == 0) return `${count}=0`;
        return `${str}${count} = ${sum}`
    };
    return SequenceSum;
})();

测试结果:
再谈Js定义变量,你不得不踩的那些坑_第2张图片
但是在自己本地跑代码, 整个结果是没有错误的。看了上面的结果,错误的输出都有0+1+2+3+4+5+这段,然而这段代码正好是我在循环中所定义的,看来在测试的时候,对于第一次循环时所产生的结果并没有进行CG。

引入正题

var,let,const 声明变量

JavaScript是弱类型语言,可以不需要声明变量而直接使用。这样虽然简单但不易发现变量名方面的错误,所以不建议这样做。通常的做法是在使用JavaScript变量前先声明变量。目前,JavaScript变量声明方式有3种,分别是使用var、let和const关键字声明。其中,使用var声明变量,是ECMAScript6版本以前一直使用的方式,由于这种方式声明的变量在某些情况下会导致一些问题,因而在ECMAScript6版本中增加了使用let和const两种方式声明变量。

如何声明变量

老生常谈,我就不多写了。

var

方式一:var 变量名;
方式二:var 变量名1,变量名2,…,变量名n;
方式三:var 变量名1 = 值1,变量名2 = 值2,…,变量名n = 值n;

let

方式一:let 变量名;
方式二:let 变量名1,变量名2,…,变量名n;
方式三:let 变量名1=值1,变量名2=值2,…,变量名n=值n;

const

const 变量名 = 值;

需要特别注意的是:使用const声明变量时,必须给变量赋初值,且该值在整个代码的运行过程中不能被修改。另外,变量也不能重复多次声明。这些要求任何一点没满足都会报错。

三者区别

  1. 变量初始化要求不同:var和let声明变量时可以不需要初始化,没有初始化的变量的值为“undefined”,在代码的运行过程中变量的值可以被修改。const声明变量时必须初始化,并且在代码的整个运行过程中不能修改初始化值,否则运行时会报错。
  2. 变量提升的支持不同:var声明支持变量提升,而const和let声明不支持变量提升。变量提升
  3. 级作用域的支持不同:var声明的变量,不支持块级作用域,let和const声明的变量支持块级作用域。凡是使用一对花括号“{}”括起来的代码都称为一个代码块。所谓块级作用域,指的是有效范围为某个代码块,离开了这个代码块,变量将失效。
if (true) { 
  let num = 3; 
  const msg = "How are you?";
} 
alert(num); //num为块级变量,离开判断块后无效,所以报:Uncaught ReferenceError  
alert(msg); //msg为块级变量,离开判断块后无效,所以报:Uncaught ReferenceError
for (let i = 0; i < 9; i ++ ) {      
  var j = i; 
} 
alert(i);  //i为块级变量,离开循环块后无效,所以报:Uncaught ReferenceError 
alert(j);  //j为全局变量,离开循环块后仍有效,所以运行正常,输出结果:8
  1. 重复声明:在同一个作用域中,var可以重复声明同一个变量,let和const不能重复声明同一个变量。
var gv1 = "JavaScript";
let gv2 = "JS";
const number = "10000";
var gv1 = "VBcript";   //correct
let gv2 = "JScript";   //Uncaught SyntaxError:Identifier'gv2'has already been declared
const number = "20000";//Uncaught SyntaxError:Identifier'number'has already been declared
alert(gv1);
alert(gv2);
alert(number);
  1. let和const存在暂时性死区:当块中存在let/const声明语句时,let/const声明的变量就绑定到这个当前块作用域,不会受外部变量的影响,也不会影响外部变量。这个特点导致了在块作用域中,块变量在块开始到块变量声明之间出现了一个称为“暂时性死区”的区域。在“暂时性死区”中使用块变量,将会导致ReferenceError。
var msg = "JavaScript";
var p = 123;
if(true){
     console.log("块内输出msg:" + msg);//Uncaught ReferenceError: Cannot access 'msg' before initialization
     console.log("块内输出p:" + p);//UVM79:5 Uncaught ReferenceError: Cannot access 'p' before initialization
     let msg = "JScript";
     const p = 456;
}
console.log("块外输出msg:" + msg); //③
console.log("块外输出p:" + p); //④

上面报的错误:VM79:5 Uncaught ReferenceError: Cannot access 'msg' before initialization,其实就是之前的:Uncaught ReferenceError:msg is not defined
这个错误原因是因为①和②处代码处在“暂时性死区”中,由此也可见,块变量不会受外部变量影响。

总结

上面写到的区别,其实注重于块级作用域的问题,而我做的题目就是因为块级作用域从而导致了内存重复读取或者没有清楚块内内存的问题。修改代码如下:

var SequenceSum = (function () {
    function SequenceSum () { }

    SequenceSum.showSequence = function (count) {
        let sum = 0
        let str = ''
        for (let i = 0; i < count; i++) {
            str += `${i}+`
            sum += i
        }
        sum += count
        if (count < 0) return `${count}<0`;
        if (count == 0) return `${count}=0`;
        return `${str}${count} = ${sum}`
    };
    return SequenceSum;
})();

将之前的var进行删除,修改成为let 进行块级作用域内部的定义。从而修复了之前出现的bug。
再谈Js定义变量,你不得不踩的那些坑_第3张图片

你可能感兴趣的:(Javascript学习)