
认识 ES6
什么是 ES6
ECAMScript是JavaScript的标准,JS是ES的实现;主流实现只有JS,所以很多时候JS就等同于ECMA- 正式名称:
ES2015,是ECMA标准的第 6 版
为什么使用 ES6
- 语言都在更新换代,加入新特性
- 支持更多语法,使用更便利
- 增强前端开发的工程性
ES6 语法特性
变量和函数
声明方法——let
使用 var 来声明变量存在问题:
- 可以重复声明,可能会导致在程序中变量被不小心再次声明导致值被修改
- 不能声明常量,只能通过大写来进行,那么就意味着常量也可能被不小心的修改。
- 作用域是函数级的
let 和 const 作用域是块级的,不能重复声明,前者是变量,后者是常量。
关于let和var的更详细的解析见:
const 声明常量
const声明一个只读的常量。一旦声明,常量的值就不能改变。
const实际上保证的,并不是变量的值不得改动,而是变量指向的那个内存地址所保存的数据不得改动。所以,只要内存地址不变动就不会报错。
const PI = 3.1415;
PI // 3.1415
PI = 3;
// TypeError: Assignment to constant variable.
const foo = {};//foo是一个对象,所以改变属性不会报错
// 为 foo 添加一个属性,可以成功
foo.prop = 123;
foo.prop // 123
// 将 foo 指向另一个对象,就会报错
foo = {}; // TypeError: "foo" is read-only
解构赋值
解构赋值语法是一种 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';//为导入的变量重命名