📢 大家好,我是 【战神刘玉栋】,有10多年的研发经验,致力于前后端技术栈的知识沉淀和传播。 💗
🌻 近期刚转战 CSDN,会严格把控文章质量,绝不滥竽充数,如需交流,欢迎留言评论。👍
写在前面的话
前文《前端攻城狮 · 从 Nuxt 前端框架开篇》提到,前端技术栈正在发生翻天覆地的变化,各类新技术层出不穷,但回归基础依然是HTML+CSS+JavaScript
,其中负责逻辑交互的JavaScript
,我原称之为前端技术的灵魂。
后端程序猿如果想往全栈工程狮发展,那JavaScript
必须和Java
玩得一样明白,接下来系列文章,带你一起探索,无所不能的JavaScript
。本系列第一回,就先从ES6开始说起。
旁白:JavaScript一度被认为是一种玩具编程语言,它有很多缺陷,所以不被大多数后端开发人员所重视。但这是完全错误的理解。JavaScript确实很容易上手,学习性价比高,稍微掌握一点JS基础知识,外加HTML和CSS,就可以做一个简单网页或者拿来应聘一份可以糊口的工作。但其精髓却不为大多数开发人员所熟知,如果想精通JS,使用JS进行随心所欲地编程,编写高质量的JavaScript代码更是难上加难。
ES6 技术简介
【技术简介】
ECMAScript ,是由网景公司制定的一种脚本语言的标准化规范;最初命名为 Mocha ,后来改名为 LiveScript ,最后重命名为 JavaScript。
ECMAScript 2015(ES2015),第 6 版,最早被称作 ECMAScript 6(ES6),添加了新的特性。
Tips:很多ES6用法的浏览器兼容性不强,Vue等项目开发中,是借助 Babel,用来把 ES6 的代码转化为浏览器或者其它环境支持的代码。
【块级作用域 - let】
什么是作用域?作用域简单讲就是声明一个变量,这个变量的有效范围,在 let 没来之前。js 只有 var 的全局作用域和 函数作用域 ,ES6 为 js 带来了块级作用域。
Tips:一句话,能用let尽量用let,作用域就认准大括号即可。
Tips:let 还有其他知识点(变量提升、暂时性死区等),但用的场景不多,不需要深究。
【常量修饰符 - const】
const 声明一个只读的常量。一旦声明,常量的值就不能改变。
使用 const 关键字定义常量,const 限制的是给常量分配值的动作,并不限制常量中的属性值。
Tips:修改内部的东西不报错,仅有赋值报错。
Tips:const实际上保证的,并不是变量的值不得改动,而是变量指向的那个内存地址所保存的数据不得改动。对于简单类型的数据(数值、字符串、布尔值),值就保存在变量指向的那个内存地址,因此等同于常量。但对于复合类型的数据(主要是对象和数组),变量指向的内存地址,保存的只是一个指向实际数据的指针,const只能保证这个指针是固定的(即总是指向另一个固定的地址),至于它指向的数据结构是不是可变的,就完全不能控制了。因此,将一个对象声明为常量必须非常小心。
const app = ['☃️', '🌈'];
console.log(...app);
app.push('🤣');
console.log(...app);
app = 10;
//输出
☃️ 🌈
☃️ 🌈 🤣
TypeError: Assignment to constant variable.
//如果真的想将对象冻结,应该使用Object.freeze方法。
const foo = Object.freeze({});
// 常规模式时,下面一行不起作用;
// 严格模式时,该行会报错
foo.prop = 123;
【判断字符串里是否包含】
使用这些函数,可以轻松的完成是不是以什么开头的字符串,是不是以什么结尾的字符串,是不是包含了什么字符串等的操作。
let str = '你好,我是小周 ❤️';
console.log(str.startsWith('你好'));
console.log(str.endsWith('你好'));
console.log(str.includes(" "));
【函数的默认参数】
ES6里,可以使用默认参数,当调用函数时,没有给参数进行赋值时,就使用设置的默认参数执行,当给参数赋值时,就会使用新赋的值执行,覆盖默认值,使用如下:
function say(str) {
console.log(str);
}
function say1(str = '嘿嘿') {
console.log(str);
}
say();
say1();
say1('❤️');
【对象属性名】
使用点定义对象属性时,如果属性名中含有空格字符,是不合法的,语法通不过的,使用 [属性名] 可以完美解决,并且不仅可以直接写明属性名,还可以使用变量来指定,具体使用如下:
let obj = {};
let a = 'little name';
obj.name = '王子';
// 使用点定义属性中间有空格是不合法的
// obj.little name = '小王子';
obj[a] = '小王子';
console.log(obj);
//输出
{ name: '王子', 'little name': '小王子' }
【判断两个值是否相等】
一些特殊值使用 === 或 == 进行比较的结果,可能不满足你的需求,这是你可以使用Object.is(第一个值,第二个值) 来进行判断,可能你就开心的笑了。
Tips:类似于精确等于符号 ===
console.log(NaN == NaN); //false
console.log(+0 == -0); //true
console.log(Object.is(NaN, NaN)); //true
console.log(Object.is(+0, -0)); //false
ES6 对象&数组解构
描述:ES6 允许按照一定模式,从数组和对象中提取值,对变量进行赋值,这被称为解构(Destructuring)。
Tips:这点日常开发中比较常用,例如export和import的场景。
【数组解构】
如下所示,首先定义了一个函数,返回一个数组,在未使用解构数组前,调用数组并把返回值赋值给 temp ,然后打印 temp 数组,使用了解构数组后,直接定义一个数组变量,然后将函数返回值指向该变量,他会自动把第一项的值赋值给第一个数组变量,第二项赋值给第二个数组变量,以此类推,最后打印三个变量,看到没有问题。
function breakfast() {
return ['🍉', '🍔', '🍕'];
}
var temp = breakfast();
console.log(temp[0], temp[1], temp[2]);
let [a, b, c] = breakfast();
console.log(a, b, c);
//输出
🍉 🍔 🍕
🍉 🍔 🍕
//解构赋值允许指定默认值。
//注意,ES6 内部使用严格相等运算符(===),判断一个位置是否有值。
//所以,只有当一个数组成员严格等于undefined,默认值才会生效。
let [x, y = 'b'] = ['a']; // x='a', y='b'
let [x, y = 'b'] = ['a', undefined]; // x='a', y='b'
let [x = 1] = [null]; //x值为null
【对象解构 - 常用】
描述:对象的解构与数组有一个重要的不同。数组的元素是按次序排列的,变量的取值由它的位置决定;而对象的属性没有次序,变量必须与属性同名,才能取到正确的值。
示例:首先 breakfast 函数返回一个对象,使用解构对象,定义对象,键值对中键表示映射的实际对象的键名,值就是自定义变量,解构完成,会自动完成赋值,然后调用 breakfast 函数,返回对象,随后打印变量 a,b,c ,可以看到没问题。
function breakfast() {
return { a: '🍉', b: '🍕', c: '🍔' }
}
//let { a: a, b: b, c: c } = breakfast();
let { a, b, c } = breakfast();
console.log(a, b, c);
//输出
🍉 🍔 🍕
【对象简写】
使用ES6的对象表达式,如果对象属性和值一样,可以省略值,函数写法可以省去function,用法如下:
let a = '㊙️';
let b = '☃️';
const obj = {
a: a,
b: b,
say: function () {
}
}
const es6obj = {
a,
b,
say() {
}
}
ES6 模板字符串
**前言:**ES6引进的模板字符串功能主要是为了解决原来多行字符串拼接的麻烦,以及提供变量解析的功能。
**语义:**使用反引号 (`) 来代替普通字符串中的用双引号和单引号。模板字符串可以包含特定语法(${expression})的占位符。占位符中的表达式和周围的文本会一起传递给一个默认函数,该函数负责将所有的部分连接起来。
示例:
var name = '123';
var desc = 'ccc';
var html = `公司名:${name}
简介:${desc}`;
Tips:不同于普通字符串,模板字符串还可以多行书写,模板字符串中所有的空格,新行,缩进都会原样的输出在生成的字符串中。单纯的模板字符串还存在着很多的局限性,可以搭配标签模板使用。
ES6 箭头函数
提问:Vue中的 render: h => h(App) 具体是什么含义
=> 是ES6的箭头语法。
使用箭头函数可以让代码更简洁,但是也要注意箭头函数的局限性,以及箭头函数中自身没有 this,this指向父级。
let f1 = a => a;
let f2 = (a, b) => {
return a + b;
}
console.log(f1(10));
console.log(f2(10, 10));
//输出
10
20
ES6 对象拷贝
方法一: Object.assign()
// 对象浅拷贝, 复制所有可枚举属性
const obj1 = {a: 1};
const obj2 = {b: 2};
// copy obj1 and obj2 to a new obj;
Object.assign({}, obj1, obj2)
方法二 :Res参数
//等同于方法一, 属于对象浅拷贝
const obj1 = {a: 1, b: 2};
// obj2 equal obj1
const obj2 = {...obj1};
方法三:序列化
//在对象的拷贝方法中比较困扰的就是深层拷贝,此方法为深层拷贝;
function deepCopy (data) {
return JSON.parse(JSON.stringify(data));
}
ES6 展开&剩余操作
展开操作符
使用 … 可以展开元素,方便操作,使用如下:
Tips:主要用于数组元素,可以把数组拆分成多个子字符串。
let arr = ['❤️', '😊', '😍'];
console.log(arr);
console.log(...arr);
let brr = ['王子', ...arr];
console.log(brr);
console.log(...brr);
[ '❤️', '😊', '😍' ]
❤️ 😊 😍
[ '王子', '❤️', '😊', '😍' ]
王子 ❤️ 😊 😍
ES6 剩余操作符
… 操作符用在函数参数上,接收一个参数数组,使用如下:
Tips:类似Java的可变参数。
function f1(a, b, ...c) {
console.log(a, b, c);
console.log(a, b, ...c);
}
f1('🍎','🌈','☃️','㊙️');
🍎 🌈 [ '☃️', '㊙️' ]
🍎 🌈 ☃️ ㊙️
ES6 Map 和 Set
ES6 Map 用法
Map结合存储键值对,类似Java的Map,但方法名略有不同。
let food = new Map();
let a = {}, b = function () { }, c = "name";
food.set(a, '🍉');
food.set(b, '🥪');
food.set(b, '🥪');
food.set(c, '米饭');
console.log(food);
console.log(food.size);
console.log(food.get(a));
food.delete(c);
console.log(food);
console.log(food.has(a));
food.forEach((v, k) => {
console.log(`${k} + ${v}`);
});
food.clear();
console.log(food);
//输出
Map(3) { {} => '🍉', [Function: b] => '🥪', 'name' => '米饭' }
3
🍉
Map(2) { {} => '🍉', [Function: b] => '🥪' }
true
[object Object] + 🍉
function () { } + 🥪
Map(0) {}
ES6 Set 用法
Set 集合,与数组不同,Set 集合中不允许有重复元素,与Java的Set类似。
// 创建Set集合
let food = new Set('🍎🥪');
// 重复添加,只有一个能进去
food.add('🍉');
food.add('🍉');
console.log(food);
// 当前集合大小
console.log(food.size);
// 判断集合中是否存在某一元素
console.log(food.has('🍉'));
// 删除集合中某一元素
food.delete('🥪');
console.log(food);
// 循环遍历集合
food.forEach(f => {
console.log(f);
});
// 清空集合
food.clear();
console.log(food);
//输出
Set(3) { '🍎', '🥪', '🍉' }
3
true
Set(2) { '🍎', '🍉' }
🍎
🍉
Set(0) {}
ES6 类与实例
Tips:越来越像Java了哦。
ES6 static
使用static关键字修饰的方法,不用实例化对象也可以直接使用
class stu {
static say(str) {
console.log(str);
}
}
stu.say("~~");
ES6 extends
使用继承,可以减少代码冗余,比如:
class Person {
constructor(name, bir) {
this.name = name;
this.bir = bir;
}
showInfo() {
return '姓名:' + this.name + '生日:' + this.bir;
}
}
class A extends Person {
constructor(name, bir) {
super(name, bir);
}
}
let zhouql = new A("周棋洛", "2002-06-01");
// 周棋洛本身是没有showInfo方法的,是继承自Person的
console.log(zhouql.showInfo());
ES6 模块化
【简单说明】
使用模块化开发,ES6可以方便的导入和导出一些内容,还有默认导出等等细节。
Tips:其实就 export、export default、import的用法,也不难。
let a = '🍉';
let f1 = function (str = '你丫的写参数') {
console.log(str);
}
export { a, f1 };
import {a, f1} from './27模块测试.js';
console.log(a);
f1();
f1('知道了');
【实际测试】
/**
* export 命令能够对外输出的就是三种接口:函数(Functions), 类(Classes),var、let、const 声明的变量(Variables)。
*/
//方式一
export var firstName = 'Lin';
//方式二
const lastName = 'Wang';
export { lastName };
//导出函数
export function testSum(x, y) {
return x + y;
}
//导出并指定别名
function testConsole(x) {
console.log(x);
}
export {
testConsole as lwConsole
}
//导出默认
export default testConsole
/**
* 1、import命令具有提升效果,会提升到整个模块的头部,首先执行,因此代码位置不一定要在最上方
* 2、import语句会执行所加载的模块,因此可以有下面的写法,import 'lodash'
* 3、模块的整体加载可以使用*和as实现
*/
import {firstName, lastName, lwConsole, testSum as lwSum} from '../test/demo1'
import * as Demo1 from '../test/demo1'
import testConsole from "../test/demo1";
import '../test/demo3'
mounted() {
console.log('###ES6测试:普通导入导出###', firstName, lastName);
lwConsole('###ES6测试,导出指定别名###')
testConsole('###ES6测试,导出default方式###')
console.log('###ES6,导入指定别名测试###', lwSum(1, 2))
console.log('###ES6测试,导入使用*的方式###', Demo1.firstName)
},
总结陈词
上文简单介绍了ES6
的基础用法,算是一个JavaScript
系列入门,后续会更新更多内容。
关于ES6
的更多用法,也可以参考《阮一峰 - ES6》。