JavaScript —— Symbol数据类型的延伸之(const关键字)

Symbol数据类型的延伸

Hello大家好好久不见!最近真是隔了很久都没有产出了!过完年回来整个人的状态都不是很好,最近才调整回来,所以现在让我们继续学习。

导语:Symbol是一种基本数据类型,每个从Symbol()返回的值都是唯一的。一个symbol的值能作为对象属性的标识符,这是该数据类型仅有的目的。 ---- 《摘自MDN》

敲黑板:唯一的、标识符

这句话值得我们细细咀嚼,我们由上面这句话来展开思考。说到唯一性,我们可以联想到的数据类型有什么?

  1. const常量
  2. TypeScript枚举enum
  3. TypeScript Symbol

下面我们就这三种数据类型展开对比:

一、叭叭 const 常量

常量,我们在JavaScript当中经常会用到,通常使用const声明一个常量。使用const进行常量的定义一般定义为大写,并且一定要赋予初始值,定义好之后,不能被修改,也不能被重复声明。

延伸:letvar
我们知道,使用var关键字声明一个变量,存在着‘‘变量提升’’这种隐晦的问题,该关键字作用于全局作用域,不受块级作用域影响。
而变量提升就是当流程执行之前,JavaScript会先收集声明语句代码,遇到var声明语句时,会将变量提升到作用域顶部,使得你在执行变量声明语句之前,可以使用输出这个变量,他并不会报错,虽然变量的值是undefined(仅限var关键字声明)。
而这片事先收集和声明的缓存区域,官方术语为 ---- Temporal Dead Zone,简称TDZ,也就是我们经常会忽略的,临时性死区(下面会延伸细说)。

我们看一下一个非常经典的例子:

<div class="item"></div>
<div class="item"></div>
<div class="item"></div>
const items = document.querySelectorAll('.item')
for (var i = 0; i < items.length; i++) {
  items[i].onclick = function() {
    // 如果你在这里选择操作DOM,那么就会报错
	items[i].style.background = 'red'
  }
}
console.log(i) // 3

为每一个div注册完点击事件之后,再进行点击操作,此时执行onclick的回调函数items[i].style.background = 'red'
因为var i = 0不受块级作用域影响,尽管是在for循环的块级作用域里面,i也因为变量提升的原因,早就被提升到全局作用域顶部去了,所以执行完for循环,页面加载完成之后,onclick回调函数所能获取到的i永远都是3,继而造成找不到这个DOM,因此报错。相当于下面这段代码:

var i;
var i = 0;
var i = 1;
var i = 2;
// for循环完之后,i还会自增一次
var i = 3; 
{
  items[i].onclick = function() {
    // 这里会顺着作用域链去找 i 这个变量,最终找到全局作用域中的 i,所以 i 永远都是 3
	items[i].style.background = 'red'
  }
}
{
  items[i].onclick = function() {
	items[i].style.background = 'red'
  }
}
{
  items[i].onclick = function() {
	items[i].style.background = 'red'
  }
}

而如果我们使用let来针对块级作用域去声明局部变量,那么循环语句会受到块级作用域的影响,代码就会变成这样:

// let受块级作用域影响,不存在变量提升(const也是)
{
  let i = 0;
  items[i].onclick = function() {
    // 这里会顺着作用域链去找 i 这个变量,最终找到当前局部作用域中的 i
    // 所以i永远都是当前作用域里面的数值,即当前块级作用域的 i 为 0
	items[i].style.background = 'red'
  }
}
{
  let i = 1;
  items[i].onclick = function() {
	items[i].style.background = 'red'
  }
}
{
  let i = 2;
  items[i].onclick = function() {
	items[i].style.background = 'red'
  }
}
// for循环完之后,i还会自增一次,但仅限于此块级作用域之内访问
{
  let i = 3
}

但聪明的同学可能会使用this(狗头),虽然可以解决问题,但是为了更加符合我们常规的思维逻辑,还是建议大家还是尽量对块级作用域使用let去声明变量:

const items = document.querySelectorAll('.item')
for (var i = 0; i < items.length; i++) {
  items[i].onclick = function() {
    // 使用 this 奏效
	this.style.background = 'red'
  }
}
console.log(i) // 3

延伸:临时性死区
letconst声明的变量不存在变量提升,不会被提升到作用域的顶部。难道就是因为这点才跟var不同的吗?其实里面的猫腻并非如此。
var语句——JavaScript在流程开始执行之前,会扫描收集声明语句代码。如果遇到var声明语句,这个变量会直接被拎到作用域顶部,相当于你是爸爸,搁这扮猪吃老虎。但未执行变量声明语句之前,它被声明为undefined,执行之后,才有赋值,注意:该变量并没有被放进TDZ里面。
let、const语句——一样,收集声明代码的时候,如果遇到这两种声明语句,那么这个变量会被JavaScript放入到TDZ(临时性死区)里面,在执行到该声明语句之前不可访问,此时你使用输出该变量,那么得到的结果将是Error: xxx is not defined。但执行声明语句之后,该变量就被释放出TDZ区域,这时候就可以访问这个变量了。

我们还是用刚才这个循环输出的例子来做演示:

// let受块级作用域影响,不存在变量提升(const也是),
// 但它会在循环语句开始之前,被收集到 TDZ 里面

// ===========临时性死区==========

let i; // ============> 第一个for循环,收集到 i
let i = 0; // ========> 流程执行到 let 声明语句了,声明并赋值
// done===============> 释放 i
let i; // ============> 第二个for循环,收集到 i
let i = 0; // ========> 流程执行到 let 声明语句了,声明并赋值
// done===============> 释放 i
let i; // ============> 第三个for循环,收集到 i
let i = 0; // ========> 流程执行到 let 声明语句了,声明并赋值
// done===============> 释放 i

// ==============================

{
  let i = 0; // let 声明语句受块级作用域影响, i 不会被提升到块级作用域以外的作用域去
  items[i].onclick = function() {
    // 这里会顺着作用域链去找 i 这个变量,最终找到当前局部作用域中的 i
    // 所以i永远都是当前作用域里面的数值,即当前块级作用域的 i 为 0
	items[i].style.background = 'red'
  }
}
{
  let i = 1;
  items[i].onclick = function() {
	items[i].style.background = 'red'
  }
}
{
  let i = 2;
  items[i].onclick = function() {
	items[i].style.background = 'red'
  }
}
// for循环完之后, i 还会自增一次,但仅限于此块级作用域之内访问
{
  let i = 3
}

二、与 Symbol 分析对比

回归正题,刨去上面两点延伸不讲,我们来仔细对比一下const声明的常量和symbol数据类型的异同和差异点:

  1. symbol是一种基本数据类型,const声明的常量也是;
  2. symbol返回的值都是唯一的,const声明的常量也是唯一的,不可被修改、重复声明;
  3. symbol声明之后得到的是一个可选的字符串作为变量描述(相当于字符串标识),而const声明的常量则是一定要被赋予初始值,且可以是字符串常量、数值常量或者布尔常量(撇开数组和对象不讲);
  4. 介于第三点的结论,symbol作为标识符的时候,细粒度比较小且唯一,适合作为对象属性的标识符。而const声明的常量维度比较大,它是有条件作为对象属性的标识符的,但实际上和逻辑思维上并不推崇。

三、最后

结论: Symbol数据类型和const声明的常量,两者都可以成为全局唯一的标识符,但经过我们的分析,我们发现,Symbol会更加精准,专一,从这个层面上来讲,Symbol确实是更胜一筹。

以上就是本篇文章的所有内容,都是本菜鸟的个人理解,有哪里不对的欢迎评论区交流指正~
通篇阅读下来,我想要分享的不是如何解决问题,而是面对问题时自己思考问题的思维。我喜欢发散衍生,举一反三,希望大家会喜欢!
下一篇继续学习TypeScript的枚举类型。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值