认识ES6
什么是ES6
ECAMScript
是JavaScript
的标准,JS
是ES
的实现;主流实现只有JS
,所以很多时候JS
就等同于ECMA
- 正式名称:
ES2015
,是ECMA
标准的第6版
为什么使用ES6
- 语言都在更新换代,加入新特性
- 支持更多语法,使用更便利
- 增强前端开发的工程性
ES6语法特性
变量和函数
声明方法——let
使用var来声明变量存在问题:
- 可以重复声明,可能会导致在程序中变量被不小心再次声明导致值被修改
- 不能声明常量,只能通过大写来进行,那么就意味着常量也可能被不小心的修改。
- 作用域是函数级的
let
和 const
作用域是块级的,不能重复声明,前者是变量,后者是常量。
关于let
和var
的更详细的解析见:
解构赋值
解构赋值语法是一种 Javascript 表达式。通过解构赋值, 可以将属性/值从对象/数组中取出,赋值给其他变量。
-
解构数组
let foo = ["one", "two", "three"]; let [one, two, three] = foo;//也可以分声明,后解构赋值 console.log(one); // "one" console.log(two); // "two" console.log(three); // "three" //设置默认值 let a, b; [a=5, b=7] = [1]; console.log(a); // 1 console.log(b); // 7 //解析从一个函数返回的数组 function f() { return [1, 2]; } let a, b; [a, b] = f(); console.log(a); // 1 console.log(b); // 2 //将剩余数组赋值给一个人 let [a, ...b] = [1, 2, 3]; console.log(a); // 1 console.log(b); // [2, 3]
-
解构对象
解构不仅可以用于数组,还可以用于对象。
对象的解构与数组有一个重要的不同。数组的元素是按次序排列的,变量的取值由它的位置决定;而对象的属性没有次序,变量必须与属性同名,才能取到正确的值。
let { foo, bar } = { foo: 'aaa', bar: 'bbb' }; foo // "aaa" bar // "bbb" //默认值 let {a = 10, b = 5} = {a: 3}; console.log(a); // 3 console.log(b); // 5 //给新变量命名 let o = {p: 42, q: true}; let {p: foo, q: bar} = o; console.log(foo); // 42 console.log(bar); // true
箭头函数
function(){
...
}
简写为()=>{}
-
如果有且仅有一个参数,()圆括号可以不写
-
如果有且仅有一个语句并且是
return
,那么大括号也可以不写var f = v => v; // 等同于 var f = function (v) { return v; };
-
修正
this
,this
会固定于当前的环境。函数体内的this
对象,就是定义时所在的对象,而不是使用时所在的对象。(普通的函数可以使用bind绑定)
扩展
扩展运算符
扩展运算符(spread)是三个点(
...
)。它好比 rest 参数的逆运算,将一个数组转为用逗号分隔的参数序列。
- 收集
function show(a,b,...c){
//第一个值给a
//第二个值给b
//剩余的参数给c,c为数组
}
- 展开
console.log(...[1, 2, 3])
// 1 2 3
console.log(1, ...[2, 3, 4], 5)
// 1 2 3 4 5
[...document.querySelectorAll('div')]
// [<div>, <div>, <div>]
注意,只有函数调用时,扩展运算符才可以放在圆括号中,否则会报错。
- 处理
json
数据
let json={a:12,b:5};
//...json 相当于 a:12,b:5,但是展开后并不能构成json数据
let json2={
...json,
c:8
};//形成新json数据{a:12,b:5,c:8}
对象的扩展
-
属性的简洁表示
ES6 允许在大括号里面,直接写入变量和函数,作为对象的属性和方法。
const foo = 'bar'; const baz = {foo}; baz // {foo: "bar"} // 等同于 const baz = {foo: foo};//此处属性名就是变量名, 属性值就是变量值。 //函数也可以简写为 const o = { method() { return "Hello!"; } }; //下面是一个实例 const Person = { name: '张三', //等同于birth: birth birth, // 等同于hello: function ()... hello() { console.log('我的名字是', this.name); } };
-
Null判断运算符
读取对象属性的时候,如果某个属性的值是
null
或undefined
,有时候需要为它们指定默认值。常见做法是通过||
运算符指定默认值。//想要实现的效果是当属性的值为`null`或`undefined`,默认值就会生效 //但是属性的值如果为空字符串或`false`或`0`,默认值也会生效。 const headerText = response.settings.headerText || 'Hello, world!';
为了避免这种情况,ES2020 引入了一个新的 Null 判断运算符
??
。它的行为类似||
,但是只有运算符左侧的值为null
或undefined
时,才会返回右侧的值。//这样可以就满足需求 const headerText = response.settings.headerText ?? 'Hello, world!'; //??符很适合判断函数参数是否赋值 function Component(props) { const enable = props.enabled ?? true; // … }
原生array扩展
array
扩展:map
、reduce
、filter
、foreach
-
map
:映射。map()
方法创建一个新数组,其结果是该数组中的每个元素都调用一个提供的函数后返回的结果。var numbers = [1, 4, 9]; var doubles = numbers.map(function(num) { return num * 2; }); // doubles数组的值为: [2, 8, 18] // numbers数组未被修改: [1, 4, 9]
map()
方法按照原数组顺序调用函数处理元素,并返回一个新数组,其中的元素为原始数组元素经过函数处理后的值。 -
reduce
:n=>1。reduce()
方法对数组中的每个元素执行一个由reducer函数(升序执行),将其结果汇总为单个返回值。
接收4个参数:-
Accumulator
(acc) (累计器) -
Current Value
(cur) (当前值) -
Current Index
(idx) (当前索引) -
Source Array
(src) (源数组)const array1 = [1, 2, 3, 4]; const reducer = (accumulator, currentValue) => accumulator + currentValue; // 1 + 2 + 3 + 4 console.log(array1.reduce(reducer)); // expected output: 10
-
-
filter
:过滤filter()
方法创建一个新数组, 其包含通过所提供函数实现的测试的所有元素。filter
中回调函数用来测试数组的每个元素。返回true
表示该元素通过测试,保留该元素,false
则不保留。它接受以下三个参数:-
element
数组中当前正在处理的元素。
-
index
(可选)正在处理的元素在数组中的索引。
-
array
(可选)调用了
filter
的数组本身。
const words = ['spray', 'limit', 'elite', 'exuberant', 'destruction', 'present']; const result = words.filter(word => word.length > 6); //如果没有任何数组元素通过测试,则返回空数组 console.log(result); // expected output: Array ["exuberant", "destruction", "present"]
-
-
foreach
: 遍历和
Python
中用法基本相同。const array1 = ['a', 'b', 'c']; array1.forEach(element => console.log(element)); // expected output: "a" // expected output: "b" // expected output: "c"
模板字符串
模板字符串相当于加强版的字符串,用反引号 `,除了作为普通字符串,还可以用来定义多行字符串,还可以在字符串中加入变量和表达式。
-
字符串插入变量和表达式。
-
变量名写在 中 , {} 中, 中,{} 中可以放入
JavaScript
表达式。
// 普通字符串
`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}?`
Json数据的转换
JSON.stringify
:将字符串数据转换为Json
数据。
``JSON.parse::将
Json`数据解析为字符串数据。
Babel
是一个JavaScript
编译器
异步操作
Promise
Promise 对象用于表示一个异步操作的最终完成 (或失败), 及其结果值.
一个 Promise
有以下几种状态:
- pending: 初始状态,既不是成功,也不是失败状态。
- fulfilled: 意味着操作成功完成。
- rejected: 意味着操作失败。
Promise
对象是由关键字 new
及其构造函数来创建的。该构造函数会把一个叫做“处理器函数”(executor function
)的函数作为它的参数。这个“处理器函数”接受两个函数——resolve
和 reject
——作为其参数。当异步任务顺利完成且返回结果值时,会调用 resolve
函数;而当异步任务失败且返回失败原因(通常是一个错误对象)时,会调用reject
函数。
reject
方法:返回一个状态为失败的Promise
对象,并将给定的失败信息传递给对应的处理方法。resolve
方法:返回一个状态由给定value
决定的Promise
对象。如果该值是thenable
(即,带有then
方法的对象),返回的Promise
对象的最终状态由then
方法执行决定;否则的话(该value
为空,基本类型或者不带then
方法的对象),返回的Promise
对象状态为fulfilled
,并且将该value
传递给对应的then
方法。
let myFirstPromise = new Promise(function(resolve, reject){
//当异步代码执行成功时,我们才会调用resolve(...), 当异步代码失败时就会调用reject(...)
setTimeout(function(){
resolve("成功!"); //代码正常执行!
}, 250);
});
myFirstPromise.then(function(successMessage){
//successMessage的值是上面调用resolve(...)方法传入的值.
//successMessage参数不一定非要是字符串类型,这里只是举个例子
console.log("Yay! " + successMessage);
});
如上所示:
then
方法:添加解决(fulfillment)
和拒绝(rejection)
回调到当前promise
, 返回一个新的promise
, 将以回调的返回值来resolve
.catch
方法:添加一个拒绝(rejection)
回调到当前promise
, 返回一个新的promise
。当这个回调函数被调用,新promise
将以它的返回值来resolve
,否则如果当前promise
进入fulfilled
状态,则以当前promise
的完成结果作为新promise
的完成结果.finally
方法:添加一个事件处理回调于当前promise
对象,并且在原promise
对象解析完毕后,返回一个新的promise
对象。回调会在当前promise
运行完毕后被调用,无论当前promise
的状态是完成(fulfilled
)还是失败(rejected
)。
async/await
async
函数返回一个 Promise 对象,可以使用then
方法添加回调函数。当函数执行的时候,一旦遇到await
就会先返回,等到异步操作完成,再接着执行函数体内后面的语句。
面向对象
面向对象的这一块功能和java
的类似
- 通过
class
进行类声明 - 通过
constructor
方法进行构造 extends
来继承父类super
来使用父类的方法- 取值函数
(getter)
和存值函数(setter)
- 私有方法在命名上加以区别,方法前面加下划线表示这是一个只限于内部使用的私有方法(实际上外部还是可以调用)。
//class作为类声明,extends表示继承了Point类
class ColorPoint extends Point {
constructor(x, y, color) { //constructor表示构造函数
super(x, y); // 调用父类的constructor(x, y)
this.color = color;
}
get prop() { //取值函数
return 'getter';
}
set prop(value) { //存值函数
console.log('setter: '+value);
}
toString() {
return this.color + ' ' + super.toString(); // 调用父类的toString()
}
// 公有方法
foo (baz) {
this._bar(baz);
}
// 私有方法
_bar(baz) {
return this.snaf = baz;
}
}
let inst = new ColorPoint(10,10,blue); //使用new,来创建一个实例
inst.prop = 123; //prop属性有对应的存值函数和取值函数,因此赋值和读取行为都被自定义了。
// setter: 123
inst.prop
// 'getter'
模块系统
JS模块系统演化简史
没有模块->CMD (Common Module Definition)
->AMD(Asynchronous Module Definition)
->语言提供模块支持
模块功能主要由两个命令构成:
export
和import
。export
命令用于规定模块的对外接口,import
命令用于输入其他模块提供的功能。
export命令
基本用法
一个模块就是一个独立的文件。export
关键字可以将文件中的变量输出,使得外部模块也能读取该变量。
var firstName = 'Michael';
var lastName = 'Jackson'; //可以导出变量
function v1() { ... } //也可以导出函数或者类
export { firstName, lastName, year ,v1};
需要特别注意的是,export
命令规定的是对外的接口(而不是数值),必须与模块内部的变量建立一一对应关系。
实质是,在接口名与模块内部变量之间,建立了一一对应的关系。
export var foo = 'bar';
setTimeout(() => foo = 'baz', 500);//代码输出变量foo,值为bar,500毫秒后变成baz
// 报错
var m = 1;
export m; //这里直接输出了数值1
export {m};//正确
这里表示,导出的是foo
这个接口,通过这个接口来使用该模块的foo
变量,当模块中的变量值改变了,那么导出的接口的值也改变了。
export default命令
export default
命令可以为模块指定默认输出。
本质上,
export default
就是输出一个叫做default
的变量或方法,然后系统允许你为它取任意名字。
// export-default.js
export default function () {
console.log('foo');
}//此处的默认输出是一个函数
// import-default.js
import customName from './export-default';
customName(); // 'foo'
//import命令为该匿名函数指定任意名字,这时就不需要知道原模块输出的函数名。
export default
命令只能使用一次,import
命令后面不用加大括号(因为只可能唯一对应export default
命令)。
import命令
使用export
命令定义了模块的对外接口以后,其他 JS 文件就可以通过import
命令加载这个模块。
import { firstName, year } from './profile.js';
import { lastName as surname } from './profile.js';//为导入的变量重命名