1、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 <b>' + basket.count + '</b> ' +
'items in your basket, ' +
'<em>' + basket.onSale +
'</em> 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中改进的对象表达式
5)ES6中的箭头函数
6)ES6中的Promise
7)块级作用域的let和const
-
let、const和var的差别
- var
var声明的变量可进行声明提升,let和const不会
var可以重复声明
var在非函数作用域中定义是挂在到window上的 - let
let声明的变量只在局部起作用,块作用域
let防止变量污染,不可再声明 - const
具有let的所有特征
不可被改变
如果使用const声明的是对象的话,是可以修改对象里面的值的
//我们用{}来定义块,但是在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
-
声明提升
函数内部会在开始前先定义需要使用的值,函数定义比变量早
注意:这里指声明提升,是函数里有声明到变量或者函数才会这样,如果函数内部没有声明对应的变量就会往外层找
//内部没有声明变量,往外层找变量值 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);
8)模块化
Module 的语法
JS ES6中export和import详解
-
概述
1)历史上,JavaScript 一直没有模块(module)体系,无法将一个大程序拆分成互相依赖的小文件,再用简单的方法拼装起来。2)在 ES6 之前,社区制定了一些模块加载方案,最主要的有 CommonJS 和 AMD 两种。前者用于服务器,后者用于浏览器。
3)ES6 模块的设计思想是尽量的静态化,使得
编译时
就能确定模块的依赖关系,以及输入和输出的变量。CommonJS 和 AMD 模块,都只能在运行时
确定这些东西。比如,CommonJS 模块就是对象,输入时必须查找对象属性。而ES6 模块不是对象。CommonJS只有运行时才能得到这个对象,从而获取到对象里的属性
4)ES6 模块还有以下好处:
- 引入宏(macro)和类型检验(type system)这些只能靠静态分析实现的功能
- 不再需要UMD模块格式了,将来服务器和浏览器都会支持 ES6 模块格式。目前,通过各种工具库,其实已经做到了这一点。
- 将来浏览器的新 API 就能用模块格式提供,不再必须做成全局变量或者navigator对象的属性。
- 不再需要对象作为命名空间(比如Math对象),未来这些功能可以通过模块提供。
-
严格模式(严格模式是 ES5 引入的,不属于 ES6)
ES6 的模块自动采用严格模式,不管你有没有在模块头部加上"use strict";。
严格模式主要有以下限制。
- 变量必须声明后再使用
- 函数的参数不能有同名属性,否则报错
- 不能使用with语句
- 不能对只读属性赋值,否则报错
- 不能使用前缀 0 表示八进制数,否则报错
- 不能删除不可删除的属性,否则报错
- 不能删除变量delete prop,会报错,只能删除属性delete global[prop]
- eval不会在它的外层作用域引入变量
- eval和arguments不能被重新赋值
- arguments不会自动反映函数参数的变化
- 不能使用arguments.callee
- 不能使用arguments.caller
- 禁止this指向全局对象
- 不能使用fn.caller和fn.arguments获取函数调用的堆栈
- 增加了保留字(比如protected、static和interface)
-
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()
- Import
//写法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结构。在静态分析阶段,这些语法都是没法得到值的。
- 模块的整体加载
1)除了指定加载某个输出值,还可以用(*)指定一个对象,所有的变量都会加载在这个对象上
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与export default均可用于导出常量、函数、文件、模块等
- 在一个文件或模块中,export、import可以有多个,export default仅有一个
- 通过export方式导出,在导入时要加{ },export default则不需要
- 输出区别:
- 输出单个值,使用export default
- 输出多个值,使用export
- 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()函数:
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没有重复的值
- 数据去重
其他数组去重:loadsh.cloneDeep,includes()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]
2、数据类型
-
JS数据类型
8种。Number、String、Boolean、Null、undefined、object、symbol、bigInt。null表示空对象
undefined表示已在作用域中声明但未赋值的变量 -
基本类型
String、Number、boolean、null、undefined。 -
引用类型:
object:包含 function、Array、Date。RegExp,Error
基本数据类型的数据直接存储在栈中;而引用数据类型的数据存储在堆中,每个对象在堆中有一个引用地址。引用类型在栈中会保存他的引用地址,以便快速查找到堆内存中的对象。
顺便提一句,栈内存是自动分配内存的。而堆内存是动态分配内存的,不会自动释放。所以每次使用完对象的时候都要把它设置为null,从而减少无用内存的消耗