JS 开发者必须知道的十个 ES6 新特性
1)ES6中的默认参数
/* ES6 之前,不能直接为函数的参数指定默认值,只能采用变通的方法。当传递参数的值为0时,因为0在JavaScript中算是false值,它会直接变成后面硬编码的值而不是0本身 */
function log(x, y) {
y = y || 'World';
console.log(x, y);
}
log('Hello') // Hello World
/* ES6 允许为函数的参数设置默认值,即直接写在参数定义的后面 */
function log(x, y = 'World') {
console.log(x, y);
}
log('Hello') // Hello World
2)ES6中的模版表达式
//传统的 JavaScript 语言,输出模板通常是这样写的
$('#result').append(
'There are ' + basket.count + ' ' +
'items in your basket, ' +
'' + basket.onSale +
' are on sale!'
);
//ES6写法:
/* 模板字符串(template string)是增强版的字符串,用反引号(`)标识。它可以当作普通字符串使用,也可以用来定义多行字符串,或者在字符串中嵌入变量 */
// 普通字符串
`In JavaScript '\n' is a line-feed.`
// 多行字符串
`In JavaScript this is
not legal.`
console.log(`string text line 1
string text line 2`);
// 字符串中嵌入变量
let name = "Bob", time = "today";
`Hello ${name}, how are you ${time}?`
//可以调用函数
const fn=()=>{ return '我是fn函数' }
let html = `我是模板字符串${fn()}`
console.log(html) // 我是模板字符串 我是fn函数
3)ES6中的拆包表达式/解构赋值
/* ES6之前 */
var data = $('body').data(), // 假设data中有mouse和house的值
house = data.house,
mouse = data.mouse
/* ES6 */
var { house, mouse} = $('body').data() // 我们会拿到house和mouse的值的
//ES6交换两个变量的值
let a=5;
let b=3;
[a,b]=[b,a]
let { foo, bar } = { foo: 'aaa', bar: 'bbb' };
// foo = 'aaa'
// bar = 'bbb'
let { baz : foo } = { baz : 'ddd' };
// foo = 'ddd'
4)ES6中改进的对象表达式
7)块级作用域的let和const
let、const和var的差别
//我们用{}来定义块,但是在ES5中这些花括号起不到任何作用
function calculateTotalAmount (vip) {
var amount = 0
if (vip) {
var amount = 1
}
{ // 让块来的更疯狂
var amount = 100
{
var amount = 1000
//这里的变量定义并不在return层,但也能改变amount的值
}
}
return amount
}
console.log(calculateTotalAmount(true)) //输出1000
console.log(amount) //报错 可见,通过var定义的变量不能跨函数作用域访问到
//改成用let
function calculateTotalAmount (vip) {
var amount = 0 // 或许应该用let, 但你可以混用
if (vip) {
let amount = 1 // 第一个数量为 0
}
{ // 更多的块
let amount = 100 // 第一个数量为 0
{
let amount = 1000 // 第一个数量为 0
}
}
return amount
}
console.log(calculateTotalAmount(true)) //输出0
var a = [];
//变量i是let声明的,当前的i只在本轮循环有效,所以每一次循环的i其实都是一个新的变量
/*
那它怎么知道上一轮循环的值,从而计算出本轮循环的值?这是因为 JavaScript 引擎内部会记住上一轮循环的值,初始化本轮的变量i时,就在上一轮循环的基础上进行计算。
*/
for (let i = 0; i < 10; i++) {
a[i] = function () {
console.log(i);
};
}
a[6](); // 6
//如果for里使用var定义,则a[6]输出为10
声明提升
函数内部会在开始前先定义需要使用的值,函数定义比变量早
注意:这里指声明提升,是函数里有声明到变量或者函数才会这样,如果函数内部没有声明对应的变量就会往外层找
//内部没有声明变量,往外层找变量值
var a = 10;
function test() {
console.log("22", a);//10
}
test();
console.log("11", a);//10
//内部声明了变量
var a = 10;
function test() {
console.log("22", a); //undefined
var a = 20;
console.log("33", a); //20
}
test();
console.log("11", a); //10
var flag = true;
if (flag) {
var a = "123";
}
function fn() {
//在这个'b'前面没有var, 会往父层作用域找,一直往上找
//找不到就报错
//程序执行到这个就报错了,比下面的console.log更早
b = 20;
}
fn();
console.log(a);
//b这个变量在全局/局部都没有声明,所以console会报错
console.log(b);
Module 的语法
JS ES6中export和import详解
概述
1)历史上,JavaScript 一直没有模块(module)体系,无法将一个大程序拆分成互相依赖的小文件,再用简单的方法拼装起来。
2)在 ES6 之前,社区制定了一些模块加载方案,最主要的有 CommonJS 和 AMD 两种。前者用于服务器,后者用于浏览器。
3)ES6 模块的设计思想是尽量的静态化,使得编译时
就能确定模块的依赖关系,以及输入和输出的变量。CommonJS 和 AMD 模块,都只能在运行时
确定这些东西。比如,CommonJS 模块就是对象,输入时必须查找对象属性。而ES6 模块不是对象。
CommonJS只有运行时才能得到这个对象,从而获取到对象里的属性
4)ES6 模块还有以下好处:
严格模式(严格模式是 ES5 引入的,不属于 ES6)
ES6 的模块自动采用严格模式,不管你有没有在模块头部加上"use strict";。
严格模式主要有以下限制。
ES6的模块化无法在Node.js中执行,需要用Babel编辑成ES5后再执行
Export:(变量/函数/class)
// 写法一
export var m = 1;
// 写法二
var m = 1;
export {m};
// 写法三
var n = 1;
export {n as m};
// 报错
export 1;
// 报错
var m = 1;
export m;
// 报错
function f() {}
export f;
// 正确
export function f() {};
// 正确
function f() {}
export {f};
位置:export模块可以位于模块中的任何位置,但是必须是在模块顶层,如果在其他作用域内,会报错。
function foo() {
export default 'bar' // SyntaxError
}
foo()
//写法1
import { firstName, lastName, year } from './profile.js';
function setName(element) {
element.textContent = firstName + ' ' + lastName;
}
//import命令输入的变量都是只读的,因为它的本质是输入接口。
import {a} from './xxx.js'
a = {}; // Syntax Error : 'a' is read-only;
//但是,如果a是一个对象,改写a的属性是允许的
import {a} from './xxx.js'
a.foo = 'hello'; // 合法操作
//写法2:别名
import { lastName as surname } from './profile.js';
//写法3:import语句会执行所加载的模块
//如果多次重复执行同一句import语句,那么只会执行一次,而不会执行多次。
import 'lodash';
import 'lodash';
import { foo } from 'my_module';
import { bar } from 'my_module';
// 等同于
import { foo, bar } from 'my_module';
1)注意,import命令具有提升效果,会提升到整个模块的头部,首先执行。
上面的代码不会报错,因为import的执行早于foo的调用
。这种行为的本质是,import命令是编译阶段执行的,在代码运行之前。
foo();
import { foo } from 'my_module';
2)由于import是静态执行,所以不能使用表达式和变量,这些只有在运行时才能得到结果的语法结构。
// 报错
import { 'f' + 'oo' } from 'my_module';
// 报错
let module = 'my_module';
import { foo } from module;
// 报错
if (x === 1) {
import { foo } from 'module1';
} else {
import { foo } from 'module2';
}
上面三种写法都会报错,因为它们用到了表达式、变量和if结构。在静态分析阶段,这些语法都是没法得到值的。
import * as circle from './circle';
2)注意,模块整体加载所在的那个对象(上例是circle),应该是可以静态分析的,所以不允许运行时改变
。
import * as circle from './circle';
// 下面两行都是不允许的
circle.foo = 'hello';
circle.area = function () {};
export default
// 第一组
export default function crc32() { // 输出
// ...
}
import crc32 from 'crc32'; // 输入
// 第二组
export function crc32() { // 输出
// ...
};
import {crc32} from 'crc32'; // 输入
export default 和 export 区别:
模块的继承:模块之间也可以继承
export * from 'circle'; //export *,表示再输出circle模块的所有属性和方法
export var e = 2.71828182846;
export default function(x) {
return Math.exp(x);
}
//只输出circle模块的area方法,且将其改名为circleArea。
export { area as circleArea } from 'circle';
import和export命令只能在模块的顶层,不能在代码块之中。否则会语法报错
这样的设计,可以提高编译器效率,但是没有办法实现运行时加载
。
因为require是运行时加载,所以import命令没有办法代替require的动态加载功能。
所以引入了import()函数。完成动态加载
。
import()返回一个Promise对象, import()也可以用在 async 函数之中。
const main = document.querySelector('main');
import(`./section-modules/${someVariable}.js`)
.then(module => {
module.loadPageInto(main);
})
.catch(err => {
main.textContent = err.message;
});
1)import()函数适用场合
//1.按需加载:
button.addEventListener('click', event => {
import('./dialogBox.js')
.then(dialogBox => {
dialogBox.open();
})
.catch(error => {
/* Error handling */
})
});
//2.条件加载
if (condition) {
import('moduleA').then(...);
} else {
import('moduleB').then(...);
}
//3.动态的模块路径
//import()允许模块路径动态生成。下面代码中,根据函数f的返回结果,加载不同的模块。
import(f())
.then(...);
//JavaScript 模块的循环加载
// a.js
var b = require('b');
// b.js
var a = require('a');
什么要使用模块化?都有哪几种方式可以实现模块化,各有什么特点
ES6 模块与 CommonJS 模块有三个重大差异。
CommonJS 模块输出的是一个值的拷贝,ES6 模块输出的是值的引用。
CommonJS 模块是运行时加载,ES6 模块是编译时输出接口。
CommonJS 模块的require()是同步加载模块,ES6 模块的import命令是异步加载,有一个独立的模块依赖的解析阶段。
9)(…)展开运算符
function test(...params){
console.log(params)//[1, 2, 3, 4]
}
test(1,2,3,4)
let [arg1,arg2,...arg3] = [1, 2, 3, 4];
const [arg, ...arg1, arg2] = ["a", "b", "c", "d"]; //报错
//注意:结构赋值中展开运算符只能用在最后。
10)字符串新增方法
确定一个字符串是否包含在另一个字符串中
es5:indexOf
es6:ncludes()
repeat方法返回一个新字符串,表示将原字符串重复n次。
字符串补全长度的功能
padStart()用于头部补全
padEnd()用于尾部补全
padStart()和padEnd()一共接受两个参数,第一个参数是字符串补全生效的最大长度,第二个参数是用来补全的字符串。
消除字符串头部的空格、消除尾部的空格
es5 :trim()
es6:trimStart(),trimEnd()
11)set数据结构,set没有重复的值
let arr = [1,2,3,4,6,3,3,1,3,4];
let removeRepeat = [...new Set(arr)];
console.log(removeRepeat); //[1, 2, 3, 4, 6]
其他数组去重:loadsh.cloneDeep,includes()JS数据类型
8种。Number、String、Boolean、Null、undefined、object、symbol、bigInt。
null表示空对象
undefined表示已在作用域中声明但未赋值的变量
基本类型
String、Number、boolean、null、undefined。
引用类型:
object:包含 function、Array、Date。RegExp,Error
基本数据类型的数据直接存储在栈中;而引用数据类型的数据存储在堆中,每个对象在堆中有一个引用地址。引用类型在栈中会保存他的引用地址,以便快速查找到堆内存中的对象。
顺便提一句,栈内存是自动分配内存的。而堆内存是动态分配内存的,不会自动释放。所以每次使用完对象的时候都要把它设置为null,从而减少无用内存的消耗