ECMA-262
97年提出的脚本语言标准规范
JavaScript和ECMAScript不同
JavaScript的实现有三个部分:DOM(文档对象模型)、BOM(浏览器对象模型)、ECMAScript(核心)三部分组成。
- DOM:将整个网页映射为一个多节点的结构,通过API操作可以动态的删除、添加、替换和修改任何节点
- BOM:提供与浏览器交互的方法和接口,已经正式纳入HTML5标准
- ECMAScript:在设计实现JavaScript脚本语言的语法和基本对象的核心内容时所遵循的标准规范
let命令
声明变量,只在let命令所在的代码块内有效
{
var name='lb';
let age = 25;
}
// 输出lg
console.log(name);
// 输出:age is not define
console.log(age);
相对于var命令
- 变量提升
- var定义的变量在声明之前可以使用,值为undefined,但是正常思维是先声明变量,而后使用否则报错,let可以实现
- 变量不允许重复声明
- let命令不允许相同作用域内重复声明同一个变量参数
- 声明的参数也不能与形参同名
- for循环下,var声明的变量全局范围内生效,所以全局只有一个变量,每次循环使用的都是同一个变量;而let声明的变量只在块级作用域内有效,每次循环的变量都会重新声明
const命令
声明一个只读类型的变量,一旦声明就不能改变,也意味着一旦声明常量必须立即进行初始化,错误示例:
// 正确
const PI=3.14;
// 报错
PI = 3.1415926;
// 报错
const foo;
作用域和let相同,也是块级作用域,且不可重复声明。
const实际上是指变量指向的那个内存的地址不能变动。对于简单类型,值保存在变量指向的内存地址中,等同于常量;而符合类型的数据类型,变量指向的内存地址保存的只是一个指针,const只能保证整个指针是固定的,对于指向的数据结构是不是可变的完全不能控制,所以讲一个数组或者对象声明为常量时必须小心。
变量的解析赋值
解析数据:逐个拆分现有的队形或者数组来提取所需要的数据。
解构:ES6允许按照一定的模式从数组和对象中提取值再对变量赋值。
数组的解析赋值
语法:
let [var1, var2, ...] = array
1.模式匹配
要求等号两遍的模式相同
// a=1,b=2,c=3
let [a, b, c] = [1, 2, 3]
2.嵌套匹配
支持任意深度的嵌套
// foo=1, bar=2, baz=3
let [foo, [bar], baz] = [1, [2], 3]
3.不完全匹配
等号左边只匹配等号右边的数组的一部分
// x=1,y=3
let [x, , y] = [1, 2, 3]
4.省略号匹配
有格式要求,省略号修饰符的变量必须放到最后;如果解析不成功,那么变量的值是undefined
// head=1, tail=[1, 2, 3, 4]
let [head, ...tail] = [1, 2, 3, 4]
// 解析失败
let [x, y] = ['a'];
5.含有默认值的匹配
等号左边可以给默认值,右侧中是undefined或者是没有匹配的值的时候,使用默认值
// a=1,b=1,c=2
let [a=0, b=1, c=2]=[1, undefined]
6.字符串解析匹配
右侧可以是字符串,直接按照类似字符数组进行解析
// a='h', b='e', c='l'
var [a, b, c] = 'hello';
对象的解析赋值
在ES6中,同样对象也是按照的等号左边和右边进行匹配的。
**区别于数组:**数组是按照位置次序进行匹配的,而对象是按照属性的名称进行匹配的,不一定按照属性的先后顺序。
1.基本形式
变量左边只有key的形式
// foo='aaa', bar='bbb'
// 如果把等号左边的foo和bar顺序替换解析后的结果是相同的
let {foo, bar} = {foo: 'aaa', bar: 'bbb'};
2.左边左边是kv形式
// baz:'aaa'
let {foo: baz} = {foo: 'aaa', bar: 'bbb'};
let obj = {first: 'hello', last: 'world'};
// f:'hello', l:'world'
let {first: f, last: l} = obj;
3.解构的正常情况
let {foo:foo, bar:bar} = {foo: 'aaa', bar: 'bbb'};
// 简化之后
let {foo, bar} = {foo: 'aaa', bar: 'bbb'};
总结:解构赋值的内部机制是,先找到同名的属性,然后再赋值给对应的变量;被赋值的是后者,而不是前者。
解构赋值的用途
1.从函数中返回多个值
function example(){
return [1, 2, 3];
}
let [a, b, c] = example();
function example1(){
return {
foo: 1,
bar: 2
};
}
let {foo, bar} = example1();
2.函数参数的定义
可以方便的将一组参数和变量名对应起来
function arraySum([x, y, z]){
return x+y+z;
}
arraySum([1, 2, 3]);
function objectSum({x, y, z}){
return x+y+z;
}
objectSum({z:4, y:3, x:1});
3. 解析JSON
let jsonData = {
name: '刘兵',
age : 25,
like: ['羽毛球', '', '足球']
};
// 这里解构之后,myLike=['羽毛球', '', '足球']
let {name, age, like: myLike} = jsonData;
4.遍历Map
map结构支持原生的Iterator接口
const map=new Map();
map.set('name', '刘兵');
map.set('age', 25);
for(let [key, value] of map){
console.log("key:" + key +",value="+ value);
}
for(let [key] of map){
console.log("key:" + key);
}
for(let [, value] of map){
console.log("value="+ value);
}
箭头函数
1.定义
// 普通函数定义
function func_name(parm1 [, param2]){
// do something
}
// ES6中将function关键字和函数名都删除掉,并使用箭头来连接参数列表和函数体
(param1 [,param2])=> {
// do something
}
// 箭头函数的简化形式:
// 1.当函数只有一个参数的时候,小括号可以省略,但是没有参数的时候不可以省略;
// 2.函数体只有一个return语句的时候,可以省略大括号和return关键字,但是都能够函数体包含多个语句的时候不能省略;
2.箭头函数和解构赋值
// 定义
arrow_remainder = ([i, j]) => i%j;
// 调用
arrow_remainder(8, 3)l
数组方法
map方法
遍历数组中的每个元素,作为参数执行一个指定的函数,然后将返回值形成一个新的数组
let arr = [1, 2, 3];
let newArr = arr.map(item => item*2);
let newArr2 = arr.map(item => item > 60 ? '及格': '不及格');
forEach方法
遍历数组,将改变原本数组本身,并灰顶调用函数的参数依次是:数组元素,元素的索引,数组本身。
let arr = [1, 2, 3, 4];
arr.forEach(function(element, index, arr){
arr[index] = element+1;
});
console.log(arr);
filter方法
执行特定的函数后返回数组的一个子集,称为过滤函数
let arr = [60, 70, 80, 90];
let result = arr.filter(item => item % 3 == 0);
every方法和some方法
两者返回值都是true或者false,同时也是将数组的每一个元素作为入口指定函数的参数,不同在于every要求每个元素运行结果都为true,而some则是元素的运行结果有一个为true即可。
var people = [
{name: 'lb', sex: 'male'},
{name: 'wq', sex: 'female'},
{name: 'lyq', sex: 'female'},
];
var result = people.every(function(p){
return p.sex === 'female';
});
// 简化形式
var result = people.every(p => p.sex === 'female');
var some = people.some(p => p.sex === 'female');
reduce方法
接收一个函数作为累加器,使用每个元素依次执行,不包括数组中的被删除或者从未被赋值的元素,回调函数有四个参数。
语法格式如下:
reduct((pre, cur, index, arr) => {
// do something
}, init);
参数说明:
- arr原数组
- prev表示上一次回调时的返回值,或者初始值init
- cur表示当前正在处理的数组元素
- index表示当前正在处理的元素的索引
- init表示初始值
如果提供init,那么索引为0;反之为1;
const arr = [1, 2, 3, 4, 5];
// 求和
const sum = arr.reduce((pre, item) => {
return pre + item;
}, 0);
// 最大值,没有指定初始值,所以索引从1开始,也就是prev第一轮就是数组的第一个元素
var max = arr.reduce((prev, cur)=>{
return Math.max(prev, cur);
});
// 去重
var newArr = arr.reduce((prev, cur) =>{
prev.indexOf(cur) === -1 && prev.push(cur);
return prev;
}, []);
字符串扩展
模板字符串
定义
传统字符串输出的时候如果有变量,那么需要使用大量的加号和双引号或者单引号进行拼接,繁琐且不方便。ES6使用反引号(`)来解决这个问题。既可以当做是普通字符使用,也可以定义多个字符串。
<!-- 传统做法 -->
"姓名" + name +"<br>";
<!-- ES6 -->
`姓名 ${name} <br>`;
好处:
- 可以保留所有的空格和缩进
- 也可以嵌套任意的JavaScript表达式
- 也可以是调用函数,如果函数的结果不是字符串,则按照一般的规则转化为字符串
新增方法
includes(String, index)
返回布尔值,表示从index开始查找String,找到返回true,反之false;
startsWith(String, index)
返回布尔值,表示从index开始查找String是否在源字符串的头部,找到返回true,反之false;
endsWith(String, index)
返回布尔值,表示从index开始查找String是否在源字符串的尾部,找到返回true,反之false;
repeat()
可以将源字符串重复几次,并返回一个新串;注意如果输入的是小数,那么会被向下取整;如果输入的是NaN,会被当做是0.
padStart()和padEnd()
字串符补全长度的方法
参数:
- 都有两个
- 第一个是补全后的字符串的最大长度
- 第二个是要补的字符串
Module语法
ES6之前,JavaScript没有该体系。如果想实现,那么可以通过社区指定的模块加载方案,最主要有CommonJa和AMD两种,前者用于服务器,后者永远忽浏览器。
ES6规范可以完全取代上面两种规范。设计思想上是尽量静态化。静态化的意思就是在静态分析阶段就能确定模块之间的依赖关系。静态化的好处是可以在编译的时候进行优化,当然缺点也很明显就是不能进行条件加载。ES6的模块不是对象,而是通过export命令显示指定输出的代码,然后通过import再导入。
export命令
模块功能主要有两个命令:export和import命令。
- export用来规定模块的对外接口
- import命令用于输入其他模块提供的功能。
一个模块就是一个独立的文件,该文件内部的所有变量都无法从外部获取。如果想从外面进行获取就必须使用export输出该变量,也可以使用大括号指定要输出的一组变量;
export命令输出的变量就是本来的名字,也可以通过使用as关键字进行重命名。
var n = 1;
export {n as m};
export命令可以出现在模块顶层的任何位置。如果处于块级作用域内,就会报错。因为如果处于条件代码块中就没法做静态化处理。
import命令
加载某个模块,因为该命令是静态执行,所以不能使用表达式或者变量;并且即使多次执行,那么也不会被执行多次,只会被执行一次。
import {firstName, lastName} from "./profile";
该命令接收一个对象,其中指定要从其他模块导入的变量名必须和被导入的模块对外接口的名称相同。
from后面指定模块文件的位置,可以是相对路径,也可以是绝对路径;
export default命令
import命令的缺点是用户必须知道要导入的模块位置以及变量名或者函数名称,因此为了提供方便,可以使用export default命令;可以用来执行模块的默认输出,因此该命令只能使用一次。
// export-default
export default function(){
console.log('foo');
}
// 其他文件引入的时候,可以为该匿名函数指定任意的名字
// 注意:import命令后面不可以使用大括号
import customName from './export-default';
JSON与Map
JSON(JavaScript Object Notation,JavaScript对象表示法),一种轻量级的数据交换格式。
有对象和数组两种组织方式。
// 数组方式
[1, 2, 3]
// 对象方式
{
"name": "Wang",
"age": 18
}
注意事项:
- 数组或者对象中字符串必须使用双引号,不能使用单引号
- 对象的成员名称必须使用双引号
- 数组或者对象最后一个成员后面不能有逗号
- 数组或者对象的每个成员都可以是简单值或者复合值
- 简单值:字符串、数值(必须是十进制)、布尔值或者null(NaN、Infinity、-Infinity和undefined都会被转换成null)
- 符合值:符合JSON格式的对象或者是数组
JSON使用
语法格式:
- JSON对象.键名
- JSON对象[“键名”]
- 数组对象[索引]
从JavaScript转换成JSON对象可以使用JSON.stringify()
方法;从JSON对象转换成JavaScript对象可以使用JSON.parse()
方法
Map数据结构
JavaScript的Object本质上是键值对的集合,只能用字符串作为键,未解决这个问题,ES6提供Map数据结构,其键的范围不限于字符串,而是各种类型的值,是一种字典数据结构。使用如下:
const myMap = new Map();
myMap.set('age', 18);
常用属性和方法
- size属性:返回Map结构的成员总数
- set(key, value):设置key对应的值为value,然后返回该Map结构
- get(key):读取key对应的值
- has(key):返回一个布尔值,如果找不到key,返回undefined
- delete(key):删除某个键,如果删除成功,返回true;否则返回false
- clear方法:清空数据,没有返回值
- 循环遍历
- keys():返回键名的遍历器
- values():返回键值的遍历器
- entries():返回所有成员的遍历器
- forEach():遍历Map的所有成员
JSON相互转换
Map-》JSON
function mapToJson(map){
return JSON.stringify([...map]);
}
JSON-》Map
function jsonToMap(jsonStr){
return new Map(JSON.parse(jsonStr));
}
Promise对象
异步编程实现的一种方案。Promise是一个对象,可以异步获取操作的消息。用于一个异步操作的最终完成(失败)以及结果值的标识。处理成功执行成功的操作;处理失败捕获错误或停止后续操作。
new Promise(
/*executor*/
function(resolve, reject){
// 为真,此时状态是fulfilled
if(条件){
resolve();
}else{
// 此时状态是rejected
reject();
}
}
);
Promise对象一共有三种状态:
- pending:初始状态,标识初始化,调用executor执行器函数之后的状态;
- fulfilled:完成状态,意味着成功;
- rejected:失败状态,表示操作失败;
Promise有两种状态可以转化:
- 操作成功:从pending转化为fulfilled状态
- 操作失败:从pending状态转化为rejected状态
并且状态转换是单向,不可逆的。
Promise对象的方法
Promise.prototype.then()
返回一个Promise对象,意味着实例化之后可以进行链式调用,可以接受两个函数,第一个是处理成功之后的函数,第二个是错误结果的函数;
var promise1 = new Promise(function(resolve, reject){
setTimeout(function(){
resolve('success');
}, 2000);
});
promise1.then(function(data){
console.log(data);
}, function(err){
console.log(err);
}).then(function(data){
// ...
});
Promise.prototype.catch()
返回一个Promise对象,主要用于捕获异步操作时出现的异常。所以通常省略then的第二个参数,把错误处理控制权交给后面的catch方法进行处理
var promise1 = new Promise(function(resolve, reject){
setTimeout(function(){
resolve('success');
}, 2000);
});
promise1.then(function(data){
console.log(data);
}).catch(function(err){
console.log(err);
});