[ JavaScript ] - 一、什么是变量?

[ JavaScript ] - 一、什么是变量?

我们都知道声明一个变量,可以有很多种方式。

但是变量本身到底是什么意思呢?

事实上,在JavaScript里面的深浅拷贝能够给到我们一定的答案。

深浅拷贝

定义一个 a 变量,给予其赋值{age: 18},可以得知,JavaScript首先在内存中开辟了一个空间,用以存放该对象;并将其引用指向变量a。并且可知,在深拷贝之后,a变量和b变量的值并不相等,可以说此时a变量就代表了左侧内存当中的对象。

通俗的说,我们讨论一块豆腐的时候,可以称其为王粮、水欢、水判、水林、白物,当然肯定有豆腐(很抱歉,吃饭的时候和同事讨论过豆腐,就忍不住)。

我们在用这么多名字讨论的时候,豆腐,就是那块豆腐,客观存在的一块豆腐,你可以用很多名字 ---- 变量去称呼它。这就是变量,他的值等于这个对象,它本身不等于。

就像豆腐这个名字指的是一块豆腐,但是这个名字不等于一块豆腐。

变量,就像人名一样,你可以随意的更改,就好比变量一样,但是,他的值不仅指向这个对象,还包括路径(你家的地址,你是唯一的)。

是的,路径。深拷贝之所以称之为深拷贝,就在于b对象从a对象拷贝过来的时候,它在内存当中开辟了一个新的空间,它们所代表的路径已经发生改变。

内存/a ==> 内存/b,这是一个很粗略的比喻,但是当ab在比较的时候,会从两个不同的路径取出两个独一无二不同的对象,它们自然不可能相等。

声明变量

这个话题肯定不能避开这三兄弟,var、let、const

var let const
存在变量提升 不存在变量提升 不存在变量提升
代码块外部可调用 存在块级作用域 存在块级作用域
赋值后可更改 赋值后不可更改 赋值后不可更改

变量提升

console.log(age)
var age = 18;
==> undefined

如果这里使用let、const结果显而易见,会直接报错。但是var不会,JavaScript扫描到这里的时候,会将var声明提升到顶部。也就是说在打印该变量的时候,是已经声明了的,不过还没有赋值而已。

同时因为const不可修改值的原因,所以在一定程度上,let可以称之为var的完美进阶版。

作用域

在这三个声明当中,var不被块级作用域限制。比如:

{
  var a = 1;
}
console.log(a) // a => 1

在大量使用es6语法后,你会觉得不可思议,为什么能在括号外面能访问到块级代码里面的作用域。

{
  let b = 2;
}
console.log(b) // b => a is not defined

我们完全可以得知,var不会被块级代码限制。反之,letconst是有严格的代码作用域。

再来看这道经典面试题:

for (var i = 0; i < 5; i++) {
   setTimeout(function () {
      console.log(i)
   }, 1000);
}

不知道多少人倒在这道题上,按照常理,应该或者说我们希望它打印1,2,3,4,5。

但很明显,答案是5个5。

事实上,因为var不受块级作用域限制,该循环每次调取的i都是同一个,当循环结束时,i的值是5,当然会打印5个5了。

赋值

三个声明变量的方式里面,letvar的值都是可以直接修改的。但是const不行,区别在于用它声明变量时必须同时初始化变量,且尝试修改const声明的变量会导致运行时错误。

const a;
a = 1; 
// 报错
// ----------
const b = 1;
const b = 2;
// 报错

constlet声明的变量一样,不允许重复声明,作用域也是块。

const声明的限制只适用于它指向的变量的引用。换句话说,如果const变量引用的是一个对象,那么修改这个对象内部的属性并不违反const的限制。

属性和值均可以修改。

声明风格

不使用var

明显,var有各种各样的缺点,我们应当使用他的完美进阶版let

强制放弃使用var,会让我们的代码更健康,因为变量有了明确的作用域、声明位置、以及不变的值。

优先级

使用const声明可以让浏览器运行时强制保持变量不变,也可以让静态代码分析工具提前发现不合法的赋值操作。所以,const应该是开发者声明变量的首要选择。

当然,如果变量的预期不确定,或者在预定的未来会发生改变,你应该使用let。这样会让开发者更有信息的推断某些变量的值会不会发生改变,也能迅速发现因赋值导致的非预期行为。

关键字与保留字

同时我们要注意到,不是所有单词都是可以用来作变量的,为了和JavaScript内置的一些单词有所区别,避免出错,ECMA-262 第6版描述了所有保留的关键字:

关键字
break do in typeof
case else instanceof var
catch export new void
class extends return while
const finally super with
continue for switch yield
debugger function this default
if throw delete import
try

这些保留的关键字不能作为标识符或者属性使用。而且,ECMA-262 第6版还描述一组未来的保留字:

始终保留

enum

严格模式下保留

严格模式
implements package public interface
protected static let private

模块代码中保留

await

在未来的保留字中,你可以作为标识符和属性去使用,但是最好不要使用,以确保兼容过去和未来的 ECMAScript 版本。

评论

Your browser is out-of-date!

Update your browser to view this website correctly. Update my browser now

×