全面掌握ECMAScript新特性——ES6声明与数据类型(一)


从2015年正式发布的ES6成为JavaScript的下一代标准后,标准委员会(TC39)在每年都发布了一个ES的新版本,在每个版本里都引入了很多新特性,下面让我们全面的掌握这些ES的新特性。

Let 和Const


let用来声明变量,const用来声明常量。

如何使用

const TAG = "我是常量";

let a;
a = 2;

console.log(TAG, "a=" + a); //我是常量   a=2

四个特点

一、只在块级作用域内有效

let和const为JavaScript新增了块级作用域,通常情况下,{}包裹的代码拥有的作用域就是块级作用域,声明的变量或常量只在块级作用域内有效,外部不能访问。

if (true) {	//外层块级作用域
  let a = 1;
  const A = 1;
  if (true) {	//内层块级作用域
    let a = 2;
  }
  console.log(a,A);	//(1)输出:1 , 1
}

console.log(a); //(2)Uncaught ReferenceError: a is not defined

上面有两个块级作用域,都声明了变量a,但外层块级作用域与内层块级作用域无关,所以(1)处输出的是外层的变量值1,(2)处访问了不在一个块级作用域定义的变量,所以会报错。


另外一个理解块级作用域的示例。

//for循环体内的定时器
//在ES6之前,是没有块级作用域的,变量用var声明,直接挂载在全局作用域上
for (var i = 0; i < 3; i++) {
  setTimeout(function () {
    console.log(i); //3、3、3
  }, 100);
}
//使用var声明,for同步操作优先于setTimeout异步操作,在开始执行setTimeout的时候,for循环已经执行完,i为3,则后续每次setTimeout输出的i都是3
//使用let声明的话,则会在循环体内部形成闭包,每次for循环都会给闭包提供每次循环i的值,并且不被释放,最终setTimeout里会分别输出0、1、2
for (let i = 0; i < 3; i++) {
  setTimeout(function () {
    console.log(i); //0 1 2
  }, 100);
}

二、暂时性死区

不能在变量和常量声明之前使用。
let和const命令会使区块形成封闭作用域,若在声明之前使用,就会报错,这个在语法上,称为“暂时性死区”(简称TDZ)。

if (true) {
  tmp = "abc"; // ReferenceError
  let tmp; 
}

三、不能重复声明
let a = 1;
let a = 2;
//报错  SyntaxError: Identifier 'a' has already been declared 

const B=1;
const B=2;
//报错  SyntaxError: Identifier 'B' has already been declared 

四、不属于顶层对象

let声明的变量,全局对象(window,global,self)不能访问到

let a = 10;
console.log(window.a);	//undefined

String

ES6对字符串进行了一些扩展,如下:

模板字符串

ES6新增了模板字符串(字符串)的方式定义字符串,用来解决之前字符串很长要换行、字符串中有变量或者表达式遇到的问题,下面是具体的使用场景

//一、打印长字符串且换行,直接在模板字符串中保留格式即可
let welcome=`
  你好
    欢迎来到ES6
      ——谢谢
`

console.log(welcome);
/*
	输出结果为:
  你好
    欢迎来到ES6
      ——谢谢
*/

//二、字符串中有变量或者表达式,直接在模板字符串中使用${变量/表达式}即可
let type = "ES6";
let name1 = "mango";
let name2 = "和goman";

let welcome = `欢迎${name1 + name2}来到${type}世界`;	

console.log(welcome);	//learn1.js?c1a0:7 欢迎mango和goman来到ES6世界

方法

String.prototype.includes()

判断字符串是否包含一个指定字符串,返回boolean类型。

const str = "ECMAScript"
console.log(str.includes("EC")); //true 找不到返回false  

startsWith()和endsWith()

startsWith()用来判断字符串是否以指定字符串作为开头,返回boolean类型。
endsWith()用来判断字符串是否以指定字符串作为结尾,返回boolean类型。

const str = "ECMAScript"
console.log(str.startsWith("ECM")); //true 

console.log(str.endsWith("Script")); //true 

String.prototype.repeat()

将原有字符串重复n遍,得到一个新的字符串

const str = "ECMAScript";

console.log(str.repeat(3)); //ECMAScriptECMAScriptECMAScript

Number

ES6开始逐步减少全局性方法,使得语言逐步模块化,所以把一些处理数值的方法转移到了Number对象上,功能行为保持不变。

//将目标转换为整数
//ES5
parseInt("5.6")	//5
//ES6
Number.parseInt("5.6")	//5

//将目标转换为浮点数
//ES5
parseFloat("12.45str")	//12.45
//ES6
Number.parseFloat("12.45str")	//12.45

另外,为了便于开发,Number还增加了一些方法和属性

一、判断一个数值是否是整数
Number.isInteger(25) // true
Number.isInteger(25.1) // false

二、获取JavaScript最大安全值和最小安全值
Number.MAX_SAFE_INTEGER=9007199254740991
Number.MIN_SAFE_INTEGER=-9007199254740991

三、判断一个数值是否是在安全范围
Number.isSafeInteger(9007199254740992)	//false

Symbol

新引入原始数据类型,用来表示独一无二的值。

声明方式
let sym = Symbol();
let sym2 = Symbol();

console.log(sym == sym2); //false	生成的值是独一无二的,所以不相等

console.log(typeof sym);  //symbol	typeof查看值的类型为symbol

let symWithDesc = Symbol("name"); //Symbol()括号内可以添加描述
console.log(symWithDesc.toString()); //输出:Symbol(name)   打印描述需要转换成字符串

项目中应用


一、消除魔术字符串
假如我们需要做一个点击菜单,做不同处理的功能,我们通常会这样实现。

const clickMenu = function (menu) {
  switch (menu) {
    case "home":
      break;
    case "me":
      break;
  }
};

clickMenu("home")

"home"这种可能会多次出现,与代码形成强耦合的字符串就是魔术字符串,在项目中我们应该尽量消除魔术字符串,下面使用Symbol消除魔术字符串

const MENU_TYPE = {
  home: Symbol(),
  me: Symbol(),
};

const clickMenu = function () {
  switch (menu) {
    case MENU_TYPE.home:
      break;
    case MENU_TYPE.me:
      break;
  }
};

clickMenu(MENU_TYPE.home);


二、作为对象独一无二的属性值
假如我们想生成一个公司人名对象,并以每个人名为key值,这时候如果有人名重名便会有问题,而Symbol能解决这个问题

const scores = {
  [Symbol("张三")]: {
    age: 22,
  },
  [Symbol("李四")]: {
    age: 21,
  },
  [Symbol("张三")]: {
    age: 20,
  },
};

注意,通过Symbol定义的属性,只能通过下面两种方式进行遍历,否则无法获取属性。

for (let key of Object.getOwnPropertySymbols(scores)) {
  console.log(key, key);
}

for (let key of Reflect.ownKeys(scores)) {
  console.log(key, scores[key]);
}

Set和Map


为了更方便地实现数据操作,ES6新增了Set和Map两种数据结构。

Set

Se是类似于数组,但成员的值都是唯一的数据结构。

新建

新建一个存储月份的Set数据结构,可以定义一个空的Set实例,也可以是带有数组形式的默认数据。

let monthSets = new Set();
let monthSets2 = new Set(["一月","二月","三月"]);

基本使用
//添加数据
monthSets.add("一月");
monthSets.add("二月").add("三月");

console.log(monthSets); //Set(3) {"一月", "二月", "三月"}

//遍历集合Set
//forEach():使用回调函数遍历每个成员
monthSets.forEach((item) => console.log(item));	//一月 二月  三月

//for...of:直接遍历每个成员
for (const item of monthSets) {
  console.log(item);	//一月 二月  三月
}

//删除数据
monthSets.delete("二月");
console.log(monthSets); // Set(2) {"一月", "三月"}
monthSets.clear(); //
console.log(monthSets); // Set(0) {}

常见应用

Set数据结构在实际项目中还有很多应用场景。

let monthSets = new Set(["一月", "二月", "三月"]);

//一、快速判断数据元素是否存在
monthSets.has("一月"); //true

//二、统计数据元素个数
monthSets.size; //3
console.log(monthSets.size); //3

//三、数组去重
let arr = [1, 2, 3, 2, 3, 4, 5];
let set = new Set(arr);
console.log(set); // {1, 2, 3, 4, 5}

//四、合并去重
let arr = [1, 2, 3];
let arr2 = [2, 3, 4];
let set = new Set([...arr, ...arr2]);
console.log(set); // {1, 2, 3, 4}

//五、取数组交集
let arr1 = [1, 2, 3];
let arr2 = [2, 3, 4];
let set1 = new Set(arr1);
let set2 = new Set(arr2);
let resultSet = new Set(arr1.filter((item) => set2.has(item)));
console.log(Array.from(resultSet)); // [2, 3]

//六、取数组差级
let arr1 = [1, 2, 3];
let arr2 = [2, 3, 4];
let set1 = new Set(arr1);
let set2 = new Set(arr2);
let arr3 = arr1.filter((item) => !set2.has(item));
let arr4 = arr2.filter((item) => !set1.has(item));

console.log([...arr3, ...arr4]);  //[1, 4]

WeakSet

WeakSet与Set类似,也是不重复的值的集合,但WeakSet的成员只能是对象。WeakSet引用的对象都是弱引用,如果其他对象不再引用该对象,那么垃圾回收机制就会自动回收这些对象所占用的内存,不考虑该对象还存在于WeakSet之中。
React源码中有很多地方使用到了WeakSet,例如在react-reconciler/src/ReactFiberHotReloading.new.js中。

export function markFailedErrorBoundaryForHotReloading(fiber: Fiber) {
  if (__DEV__) {
    if (resolveFamily === null) {
      // Hot reloading is disabled.
      return;
    }
    if (typeof WeakSet !== 'function') {
      return;
    }
    if (failedBoundaries === null) {
      failedBoundaries = new WeakSet();
    }
    failedBoundaries.add(fiber);
  }
}


Map

Map是一种键值对集合,与对象类似,但Object只支持“字符串:值”,而Map支持“各种类型的值:值”,map给我们提供了更合适的“键值对”数据结构。

基本使用
//定义
let map = new Map();

//添加数据
let address = { address: "江苏" };
map.set("name", "ES6");
map.set(27, "年龄信息");
map.set(address, "地址信息");

console.log(map); //{"name" => "ES6", 27 => "年龄信息", {…} => "地址信息"}

//获取数据
let name = map.get("name");
let age = map.get(27);
let addressObj = map.get(address);

console.log(name, age, addressObj);

//获取成员数量
console.log(map.size);  //3

//判断是否指定key成员
console.log(map.has("name")); //true

Map的遍历

map通常可以用forEach和for…of的方式进行遍历。

//定义
let map = new Map();

map.set("id", 1);
map.set("name", "mango");
map.set("address", {
  province: "江苏",
  city: "南京",
});


map.forEach((key, value) => console.log(key, value));


for (const [key, value] of map) {
  console.log(key, value);
}

//输出  id 1		name mango		address {province: "江苏", city: "南京"}

WeakMap

WeakMap与Map类似,也是用来生成键值对的集合。但WeakMap只接受对象作为键名,并且键名所指向的对象,属于弱引用对象。



更多文章内容,请关注公众号【方塘HCI】
在这里插入图片描述

参考资料
【1】ECMAScript简介
【2】MDN web docs
【3】ECMAScript2015~2020语法全解析
【4】阮一峰 ECMAScript6(ES6)标准入门教程 第三版
【5】JavaScript深入之词法作用域和动态作用域

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值