都 2021 年了还不会连 ES6/ES2015 更新了什么都不知道吧
Posted GoldenaArcher
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了都 2021 年了还不会连 ES6/ES2015 更新了什么都不知道吧相关的知识,希望对你有一定的参考价值。
都 2021 年了还不会连 ES6/ES2015 更新了什么都不知道吧
javascript 的历史也算是非常久远了,最早能追溯回 90 年代,网景公司和微软为了能够抢占市场大打出手,分别推出了只有自家产品才能够使用的 JavaScript 版本。这就让开发们非常头疼了——写一份代码已经很难了,为了适配多端兼容还得再整一个一样,又不完全一样的版本出来。
为了解决这个问题,最终由 ECMA International(前身为欧洲计算机制造商协会)统一规范,并且将该规范命名为 ECMAScript,作为 JavaScript 的统一标准。
自此,神仙打架的日子结束了,开发们的日子也稍微好过一些了。
事件继续推进到了 2008 年后,移动端的兴起对 JavaScript 的影响是巨大的。也因此,社区中的大神对于那些功能应该被放入当时的 ES5 而产生了极大的分歧,最终导致了 ES6 的标准的限定也陷入了滞后。
从现在的角度看来,ES6 的变化真的可以说是翻天覆地的变化,也许,这样的变化对于当时的开发来说还是太早了。
时间又向后推进几年,到了 2015 年,ECMAScript6 终于作为正式版本发布,官方名称为 ECMAScript 2015,旨在更频繁的进行少量的迭代。并且,从 ECMAScript6(ES6/ES2015)之后,所有的版本迭代将会以 ECMAScript + 年份
作为正式的官方名称。
历史讲完了,下面就肩带概述一下从 ES2015-ES2021 分别都有什么新特性。
es6 / es2015
最主要的版本迭代,推出了很多对于 JavaScript 来说革命性的新特性。
let & const
在 ES6 之前,JavaScript 想要申明变量只能使用关键字 var
。JavaScript 特有的作用域提升让 var
具有一些特殊的行为,如:
for (var i = 0; i < 5; i++) {}
console.log(i); // 5
使用 let 和 const 可以很好地规避这个问题,上面同样的代码,以 let 为例:
for (let i = 0; i < 5; i++) {}
console.log(i); // Uncaught ReferenceError: i is not defined
const 与 let 最大的区别有两点:
- let 的值是可变的,而 const 是不可变的,如:
let b = 10; // 10
b = 20; // 20
const c = 10; // 10
c = 20; // Uncaught TypeError: Assignment to constant variable.
- let 初始化可以不用给值,const 不给值会报错
let b; // undefined
const c; // Uncaught SyntaxError: Missing initializer in const declaration
所以,目前的推荐用法是:
- 规避使用 var 去声明变量
更多关于作用域提升的内容,可以看之前记得笔记:var, let, const 的区别与相同
块级作用域
即 花括号{}
,这也是 ES6 新增特性之一,在花括号之中的内容独立作为一个作用域,外部无法访问。
在块级作用域搭配 let 和 const 可以有效地避免变量名污染的问题,如:
for (let i = 0; i < 3; i++) {
for (let i = 0; i < 3; i++) {
console.log(i);
}
}
// 0
// 1
// 2
// 0
// 1
// 2
// 0
// 1
// 2
因为外部作用域无法访问到内部的变量,而内部的 i 会就近寻找作用域上的变量。为了方便理解,还是建议将内外循环体重的变量取不同的名字。
解构
快速提取变量的方法,可以用于数组和对象。
数组解构
数组解构与根据 index 取值有些像:
const testArr = [1, 2, 3];
const [val1, val2, val3] = testArr;
console.log(val1, val2, val3); // 1, 2, 3
// 等同于
const val1 = testArr[0],
val2 = testArr[1],
val3 = testArr[2];
console.log(val1, val2, val3); // 1, 2, 3
在学习算法的过程中就会听经常的用到数组解构:
// 用数组解构
function swap(arr, i, j) {
[arr[i], arr[j]] = [arr[j], arr[i]];
}
// 不用数组解构
function swap2(arr, i, j) {
const temp = arr[i];
arr[i] = arr[j];
arr[j] = temp;
}
在工作中,在拆解固定值也挺方便的,例如说经常会出现有些字符串使用 -
进行分割:
const [base, str2] = someStr.split('-');
// 不用数组解构
const splittedStr = someStr.split('-');
const base = splittedStr[0],
str2 = splittedStr[1];
对象解构
根据属性名进行解构,解构中的变量名一定是匹配类中的属性名的,使用方法如下:
const getUserAddress = (metaData) => {
return {
zipcode: 123456,
addr1: 'some addr',
addr2: 'some addr2',
state: 'some state',
// 其他属性...
};
};
const { zipcode, addr1, addr2, state } = getUserAddress('dummy data');
// 或提取常用的函数出来,假设做数学运算,经常会用到一些数学常量和函数,就可以先提取出来
const { PI, abs, sin } = Math;
const { log } = console;
log(sin(PI));
对于解决变量名重复,ES6 也已经有了自己的解决方法,依旧以上面的函数为例:
const state = 'placeholder';
const getUserAddress = (metaData) => {
return {
zipcode: 123456,
addr1: 'some addr',
addr2: 'some addr2',
state: 'some state',
// 其他属性...
};
};
const {
zipcode,
addr1,
addr2,
state: currentState,
} = getUserAddress('dummy data');
模板字符串
非常好用的特性之一,它最大的特点就在于能够拼接变量以及保证原有的格式。如果说有的时候需要写一点 html 的东西,就非常的有用了:
// 拼接字符串
const fullName = `${firstName} ${lastName}`;
// 保证原有格式增强可读性
const tempHtml = `
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
</body>
</html>
`;
console.log(tempHtml);
/*
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
</body>
</html>
*/
效果图如下:
二者混用其实是最能够体现它效果强大的地方:
// 准备要发送出去的html
const tempHtml = `
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
${bodyEl}
</body>
</html>
`;
Math + Number + String + Object 扩展方法
-
Math
增加了一些常见的变量,如 Number.EPSILON,最后用的就是增加了两个非常有用的静态方法:
和一些数学中常用的计算方法,如 acosh,sin 等,除非特定项目,否则用的比较少。
-
Number
新增了对 NaN 和无穷大的处理的静态函数。
Number.isInteger(Infinity); // false Number.isNaN('NaN'); // false
-
String
-
includes,是否包含指定字符串
-
startsWith,是否以该字符串开头
-
endsWith,是否以该字符串结尾
-
repeat,重复字符串
使用方法:
const msg = 'Error: some error message'; msg.includes('Error'); // true msg.startsWith('Error'); // true msg.endsWith('Error'); // false '6'.repeat(3); // '666s'
-
-
Object
Object.assign(target, ...sources)
,将所有可枚举属性的值从衣蛾或多个源对象分配到目标对象。是一个浅复制的好方法。
默认参数
现在 JavaScript 可以为形参提供默认值。这样的好处就在于可以省略掉一些空值的判断,如:
function func(id, name, age, interests = []) {
// 进行操作
}
// 以前可能会写
function func(id, name, age, interests) {
if (interest && interest.length > 0) {
// 进行操作
}
}
现在就算再调用函数时没有提供参数,也可以直接进行循环操作了。
注:需要提供默认值的参数一定是放在最后的参数,所以才能做到在没有传值的时候设置默认值。
剩余 与 展开 操作符
即 ...
操作符,可以用来传递剩余的参数,也可以用来展开数组或是对象。
-
当用来传递剩余参数时
可以用来展开所有的参数,并且以数组的形式在函数内被调用。
这要哪个的优点在于可以避免使用
arguments
来获取所有的参数,同时,因为...
会获取剩余所有的参数,因此只能放置在参数的最后,并且只能用 1 次。function func(...args) { console.log(args); // [1,2,3,4,] } func(1, 2, 3, 4);
-
当用来展开时
可以用来展开数据,同样也可以用来做浅拷贝:
const test = [1, 2, 3, 4, 5]; const copy = [...test]; console.log(copy); // [1, 2, 3, 4, 5] const test2 = { name: 'test', age: 18 }; const copy2 = { ...test2 }; console.log(copy2); // { name: 'test', age: 18 } // 当需要输出一些值,就不需要用 test[0] test[1] 这样扣值 console.log(...test); // 1 2 3 4 5
箭头函数
现在比较推荐的写法,可以简化定义函数的代码,语法为:
const/let/var 函数名 = (参数) => {}
// 使用传统函数
function foo(val) {
return val + 1;
}
// 使用箭头函数
// 当只有一行时,可以忽略花括号直接在 => 后写要返回的值
const foo = (val) => val + 1;
// 返回多行时,可以用圆括号包起来
const foo = (val) => (
<div>
<div>paragraph1</div>
<div>paragraph2</div>
</div>
);
// 当操作有多行时,需要拥花括号包起来,并在 return 后申明返回值
const foo = (val) => {
console.log(val);
return val + 1;
};
与普通的函数相比,并且没有自己的 this
,arguments
,super
或 new.target
。
箭头函数可以用来取代以前使用匿名函数的地方。
对象字面量
有了以下几个有用的语法糖
-
当变量名一致时,可以省略变量名
const data = { name: 'name', age: 18 }; const testData = { 'Content-Type': 'text/plain', data, }; console.log(testData); // { 'Content-Type': 'text/plain', data: { name: 'name', age: 18 } }
-
计算属性名,也就是用
[]
动态传递属性名这个操作在循环调用时,或者需要动态传递参数时非常有用
const data = { name: 'name', age: 18 }; for (const key in data) { console.log(data[key]); } // name // 18
代理 Proxy
ES6 新推出的代理功能,Proxy 是对于目标对象的抽象,在实际操作目标对象时,可以通过 Proxy 实现对基本操作的拦截和自定义。
语法为:
const p = new Proxy(target, handler)
我觉得 MDN 中,通过 Proxy 绑定验证的案例挺好的:
let validator = {
set: function (obj, prop, value) {
if (prop === 'age') {
if (!Number.isInteger(value)) {
throw new TypeError('The age is not an integer');
}
if (value > 200) {
throw new RangeError('The age seems invalid');
}
}
// The default behavior to store the value
obj[prop] = value;
// 表示成功
return true;
},
};
let person = new Proxy({}, validator);
person.age = 100;
console.log(person.age);
// 100
person.age = 'young';
// 抛出异常: Uncaught TypeError: The age is not an integer
person.age = 300;
// 抛出异常: Uncaught RangeError: The age seems invalid
通过将验证抽离出来,能够对验证方法进行有效复用,在做数据验证时,也能够省去很多功夫。
反射 Reflect
Reflect 是一个内置的静态对象,它提供拦截 JavaScript 操作的方法。它的方法与 proxy handlers 的方法相同。
mdn 上的用法也很全:
const duck = {
name: 'Maurice',
color: 'white',
greeting: function () {
console.log(`Quaaaack! My name is ${this.name}`);
},
};
Reflect.has(duck, 'color');
// true
Reflect.has(duck, 'haircut');
// false
可能是说 Reflect 主要的作用还是为了能够提供一个标准的操作方法,如:
// 判断是否有某个属性
if (property in obj) {
}
// 删除某个属性
delete obj.proprety;
// 获取所有的属性名
Object.keys(obj);
// 使用 Reflect 进行同样的操作
Reflect.has(obj, property);
Reflect.deleteProperty(obj, property);
Reflect.ownKesy(obj);
相比较而言,Reflect 能够提供一个更加统一的接口调用标准,对于新手而言,查文档也会方便一些。
期约 Promises
提供了一种更好地解决异步编程的方案,通过链式调用扁平化函数调用,解决回调地狱的问题。
传统的写法可能会这么写:
function fetchData(callback) {
fetchData2((err, data) => {
if (err) return handleRejected2;
fetchData3((err, data) => {
if (err) return handleRejected3;
fetchData4((err, data) => {
// ...
});
});
});
}
但是使用了 Promiise 之后,就可以使用 then 去链式调用:
const promise = new Promise(resolve, reject);
promise
.then(fetchData, handleRejected1)
.then(fetchData2, handleRejected2)
.then(fetchData3, handleRejected3)
.then(fetchData4, handleRejected4);
从结构上来说也会更加的清晰一些。
类
以前是采用 function 的原型链继承法去实现的继承,如:
function Person(name) {
this.name = name;
}
// 添加新的原型链继承方法
Person.prototype.work = function () {
console.log('996是福报……?');
};
而使用 class 的结构会更加的清晰:
class Person {
constructor(name) {
this.name = name;
}
work() {
console.log('996是福报……?');
}
}
其实之前有其他的语言基础的话,使用 class 理解起来应该非常的轻松——和其他的语言非常的相似。
类的继承
而类的继承的方法,也与其他的语言看起来非常相似,下面是来自 MDN 的例子:
class Polygon {
constructor(height, width) {
this.name = 'Polygon';
this.height = height;
this.width = width;
}
sayName() {
ChromeSamples.log('Hi, I am a ', this.name + '.');
}
sayHistory() {
ChromeSamples.log(
'"Polygon" is derived from the Greek polus (many) ' + 'and gonia (angle).'
);
}
}
class Square extends Polygon {
constructor(length) {
super(length, length);
this.name = 'Square';
}
getArea() {
return this.height * this.width;
}
}
内置对象可被继承
内置对象,如 Array, Date 等可被用来继承:
// User code of Array subclass
class MyArray extends Array {
constructor(...args) {
super(...args);
}
}
var arr = new MyArray();
arr[1] = 12;
arr.length == 2;
类的静态方法
注意,静态方法是直接挂载在类上,而不是类的实例上。
目前感觉主要的调用方法就是返回一个新的对象:
class Square extends Polygon {
constructor(length) {
super(length, length);
this.name = 'Square';
}
static create(name) {
return new Square(name);
}
}
const square = Square.都2021年了,不会还有人连深度学习都不了解吧- 卷积篇
都2021年了,不会还有人连深度学习都不了解吧- 损失函数篇
都2021年了,不会还有人连深度学习都不了解吧- 损失函数篇
都2021年了,不会还有人连深度学习都不了解吧-- 下采样篇