版本历史
ES5 浏览器可用性
https://www.caniuse.com/#search=es5
ES6 浏览器可用性
https://www.caniuse.com/#search=es6
JS
包含三个部分:ECMAScript(核心)
,DOM(文档对象模型)
,BOM(浏览器对象模型)
。ECMAScript
是JS
语言的基础。ECMAScript
的最新版是第六版ECMAScript 6
,于2015年6月17日发布,截止发布日期,JavaScript
的官方名称是ECMAScript 2015
,是当前最新的正式规范。
ECMAScript
的各个版本:(从第三版开始说)
第三版
ECMAScript3
新增了对正则表达式、新控制语句、try-catch异常处理的支持,修改了字符处理、错误定义和数值输出等内容。标志着ECMAScript成为了一门真正的编程语言。
第四版于2008年7月发布前被废弃。
第五版
ECMAScript5
力求澄清第3版中的歧义,并添加了新的功能。新功能包括:原生JSON对象、继承的方法、高级属性的定义以及引入严格模式。
第六版
ECMAScript6
是继ES5之后的一次主要改进。增添了许多必要的特性,例如:模块和类以及一些实用特性,Maps、Sets、Promises、生成器(Generators)等。
ES5
支持IE9及以上
最常用的就是JSON对象了,早期的浏览器就要加载js插件来实现。
严格模式
为整个脚本文件开启严格模式,需要在所有语句之前放一个特定语句 "use strict";
(或 'use strict';
)
1 | // 整个脚本都开启严格模式的语法 |
严格模式,严格模式,重要其实就是es5中弃用了很多以前版本的语法,你再用就提示错误。
一句话说明了这个的用途,我们只要知道哪些语法被弃用了(es3,es2,es1),就知道哪些会在使用这个模式下报错了。
简单罗列弃用限制和es5不符合语法定义限制:
- 不使用var声明变量严格模式中将不通过
- 任何使用
eval
的操作都会被禁止 - eval作用域
- with被禁用
- caller/callee 被禁用
- 对禁止扩展的对象添加新属性会报错
- 删除系统内置的属性会报错
- delete使用var声明的变量或挂在window上的变量报错
- delete不可删除属性(isSealed或isFrozen)的对象时报错
- 对一个对象的只读属性进行赋值将报错
- 对象有重名的属性将报错
- 函数有重名的参数将报错
- 八进制表示法被禁用
- arguments严格定义为参数,不再与形参绑定
- 函数必须声明在顶层
- ES5里新增的关键字不能当做变量标示符使用,如implements, interface, let, package, private, protected, public, static, yield
- call/apply的第一个参数直接传入不包装为对象
- call/apply的第一个参数为null/undefined时,this为null/undefined
- bind的第一个参数为null/undefined时,this为null/undefined
JSON对象
1 | // 声明一个“JSON格式”的String |
Object对象
方法有很多,但常用的是最后一个
Object.getPrototypeOf
返回对象的原型Object.getOwnPropertyDescriptor
返回对象自有属性的属性描述符Object.getOwnPropertyNames
返回一个数组,包括对象所有自有属性名称集合(包括不可枚举的属性)Object.create
创建一个拥有置顶原型和若干个指定属性的对象Object.defineProperty
给对象定义一个新属性,或者修改已有的属性,并返回Object.defineProperties
在一个对象上添加或修改一个或者多个自有属性,并返回该对象Object.seal
锁定对象。阻止修改现有属性的特性,并阻止添加新属性。但是可以修改已有属性的值Object.freeze
冻结对象,阻止对对象的一切操作。冻结对象将永远不可变。Object.preventExtensions
让一个对象变的不可扩展,也就是永远不能再添加新的属性Object.isSealed
判断对象是否被锁定Object.isFrozen
判断对象是否被冻结Object.isExtensible
判断对象是否可以被扩展Object.keys
返回一个由给定对象的所有可枚举自身属性的属性名组成的数组
举个例子怎么用的,这样就可以简单遍历对象了,又贴代码了
1 | var obj = { |
bind/apply/call
都能起到改变this的效果
1 |
|
Array对象
indexOf/lastIndexOf
indexOf
返回根据给定元素找到的第一个索引值,否则返回-1
lastIndexOf
方法返回指定元素在数组中的最后一个的索引,如果不存在则返回 -1
isArray
判断是否为数组
1 |
|
forEach
使用foreach遍历数组的话,使用break不能中断循环,使用return也不能返回到外层函数。
遍历循环数组
1 | //这样就方便计算数组中数据了 |
map
处理原数组,返回处理后的值。
1 | var arrObj = [ |
filter
过滤器,根据自己的条件进行过滤,filter和上面的map还是有点小区别的,自己发现吧。
1 |
|
some
判断数组中是否有满足需要的值,返回true/false
1 |
|
every
判断数组中是否所有值都满足需要,返回true/false
1 |
|
ES6
IE压根不支持,Edge支持
箭头操作符
方便写回调,不改变this的引用。
1 | var array = [1, 2, 3]; |
变量定义let和const
let声明变量(块级作用域),let是更完美的var,它声明的全局变量不是全局属性widow的变量,这便解决了for循环中变量覆盖的问题
const声明常量(块级作用域)
字符串新增方法
1 | let str = 'happy'; |
字符串模版
ES6之前
通过\
和+
来构建模板
1 | $("body").html("This demonstrates the output of HTML \ |
ES6之后
- 基本的字符串格式化。将表达式嵌入字符串中进行拼接。用
${}
来界定; - ES6反引号直接搞定;
1 | $("body").html(`This demonstrates the output of HTML content to the page, |
函数参数默认值
1 | // ES6之前,当未传入参数时要实现:text = 'default'; |
对象合并Object.assign()
1 | const obj1 = { |
键值对重名简写
1 | function people(name, age) { |
对象字面量简写
1 | const people = { |
解构(…)
Spread / Rest 操作符指的是
...
,具体是 Spread 还是 Rest 需要看上下文语境。
当被用于迭代器中时,它是一个 Spread 操作符:
1 | function foo(x,y,z) { |
当被用于函数传参时,是一个 Rest 操作符:当被用于函数传参时,是一个 Rest 操作符:
1 | function foo(...args) { |
对象解构中的 rest 操作符
在对象解构模式下,rest 操作符会将解构源的除了已经在对象字面量中指明的属性之外的,所有可枚举自有属性拷贝到它的运算对象中。
1 | const obj = {foo: 1, bar: 2, baz: 3}; |
如果你正在使用对象解构来处理命名参数,rest 操作符让你可以收集所有剩余参数:
1 | function func({param1, param2, ...rest}) { // rest operator |
对象字面量中的 spread 操作符
对象字面量内部,spread 操作符将自身运算对象的所有可枚举的自有属性,插入到通过字面量创建的对象中:
1 | const obj = {foo: 1, bar: 2, baz: 3}; |
要注意的是顺序问题,即使属性 key 并不冲突,因为对象会记录插入顺序:
1 | {qux: 4, ...obj} |
如果 key 出现了冲突,后面的会覆盖前面的属性:
1 | const obj = {foo: 1, bar: 2, baz: 3}; |
对象 spread 操作符
这一节,我们会看看 spread 操作符的使用场景。
我也会用 Object.assign() 实现一遍,它和 spread 操作符很相似(之后我们会更详细地比较它们)。
拷贝对象
拷贝对象 obj 的可枚举自有属性:
1 | const clone1 = {...obj}; |
clone 对象们的原型都是 Object.prototype,它是所有通过对象字面量创建的对象的默认原型:
1 | Object.getPrototypeOf(clone1) === Object.prototype |
拷贝一个对象 obj,包括它的原型:
1 | const clone1 = {__proto__: Object.getPrototypeOf(obj), ...obj}; |
注意,一般来说,对象字面量内部的 proto 只是浏览器内置的特性,并非 JavaScript 引擎所有。
对象的真拷贝
有时候,你需要老老实实地拷贝对象的所有自有属性(properties)和特性(writable, enumerable, …),包括 getters 和 setters。这时候 Object.assign()
和 spread
操作符就回天乏术了。你需要使用属性描述符(property descriptors):
1 | const clone1 = Object |
如果还希望保留 obj 的原型,可以用 Object.create()
:
1 | const clone2 = Object.create( |
对象和数组解构和Rest操作符
1 | // 对象解构 |
Promise
非常好用
Set
Set作为ES6新的数据解构(类数组),它的成员都是唯一的,因为最直接的使用场景便是去重、并、差、交集的使用。它使用的算法叫做“Same-value-zero equality”,类似精确运算的===,主要是NaN,这里它将两个视为相等。
1 | // Set实例的常用方法和属性add,delete,clear,has、size |
同时Set实例配合常用遍历方法,实现并、交、差集。
1 | const a =[1, 2, 3] |
二进制和八进制字面量
ES6 支持二进制和八进制的字面量,通过在数字前面添加 0o 或者0O 即可将其转换为八进制值:
1 | let oValue = 0o10; |
for…of 和 for…in
for in遍历的是数组的索引(即键名),而for of遍历的是数组元素值。
for…of:
1 | let letters = ['a', 'b', 'c']; |
for…in:
1 | let letters = ['a', 'b', 'c']; |
对象超类
ES6 允许在对象中使用 super 方法:
1 | var parent = { |
类
ES6 中支持 class 语法,不过,ES6的class不是新的对象继承模型,它只是原型链的语法糖表现形式。
函数中使用 static 关键词定义构造函数的的方法和属性:
1 | class Student { |
类中的继承和超集:
1 | class Phone { |
extends 允许一个子类继承父类,需要注意的是,子类的constructor 函数中需要执行 super() 函数。
当然,你也可以在子类方法中调用父类的方法,如super.parentMethodName()。
有几点值得注意的是:
- 类的声明不会提升(hoisting),如果你要使用某个 Class,那你必须在使用之前定义它,否则会抛出一个 ReferenceError 的错误
- 在类中定义函数不需要使用 function 关键词
Generator
实际应用中大都用ES7的async/await来替代
yield
由于 Generator 函数返回的遍历器对象,只有调用next方法才会遍历下一个内部状态,所以其实提供了一种可以暂停执行的函数。yield表达式就是暂停标志。
- 遇到yield表达式,就暂停执行后面的操作,并将紧跟在yield后面的那个表达式的值,作为返回的对象的value属性值。
- 下一次调用next方法时,再继续往下执行,直到遇到下一个yield表达式。
- 如果没有再遇到新的yield表达式,就一直运行到函数结束,直到return语句为止,并将return语句后面的表达式的值,作为返回的对象的value属性值。
- 如果该函数没有return语句或者执行完return之后再运行next的时候,则返回的对象的value属性值为undefined,done为true。
需要注意
- yield表达式只能用在 Generator 函数里面,用在其他地方都会报错。
- yield表达式如果用在另一个表达式之中,必须放在圆括号里面
- yield表达式用作函数参数或放在赋值表达式的右边,可以不加括号。
- yield放在表达式中的时候,
let s = (yield 1+2)
,s其值将会是undefined,
而1+2这个等于3的值将会作为next返回对象的value的值
next
yield表达式本身没有返回值,或者说总是返回undefined。
next方法可以带一个参数,该参数就会被当作上一个yield表达式的返回值。
由于next方法的参数表示上一个yield表达式的返回值,所以在第一次使用next方法时,传递参数是无效的。
V8 引擎直接忽略第一次使用next方法时的参数,只有从第二次使用next方法开始,参数才是有效的。从语义上讲,第一个next方法用来启动遍历器对象,所以不用带有参数。
yield*
如果在 Generator 函数内部,调用另一个 Generator 函数,默认情况下是没有效果的。
这个就需要用到yield*表达式,用来在一个 Generator 函数里面执行另一个 Generator 函数。
1 | function* inner() { |
示例1
不传值
1 | function* test() { |
传值
1 | function* test() { |
上面的两个例子可以发现yield修饰的表达式的返回值为undefined,所以我们一定要把之前的结果传进去。
示例2
1 | function* test01(x) { |
第一个 next 方法的 value 属性,返回表达式 x + 1 的值(2)。
第二个 next 方法带有参数3,这个参数可以传入 Generator 函数,作为上个阶段异步任务的返回结果,被函数体内的变量 y 接收。因此,这一步的 value 属性,返回的就是3(变量 y 的值)。
示例3
Generator 函数内部还可以部署错误处理代码,捕获函数体外抛出的错误。
1 | function* test02(x) { |
上面代码的最后一行,Generator 函数体外,使用指针对象的 throw 方法抛出的错误,可以被函数体内的 try ... catch
代码块捕获。
这意味着,出错的代码与处理错误的代码,实现了时间和空间上的分离,这对于异步编程无疑是很重要的。
示例4
下面看看如何使用 Generator 函数,执行一个真实的异步任务。
1 | function loaddata() { |
输出的结果
{value: “{“code”: 0, “msg”: “success”}”, done: false}
{ “code”: 0, “msg”: “success”}
{value: undefined, done: true}
这三行的输出分别对应上面的最后三行代码
上面代码中,Generator 函数封装了一个异步操作,该操作先读取一个远程接口,然后从 JSON 格式的数据解析信息。
就像前面说过的,这段代码非常像同步操作,除了加上了 yield 命令。
ES7
async/await
非常好用
原来的写法
1 | update_click:function(){ |
新写法
1 | async update_click:function(){ |
简单封装
1 | const fetchData = () => { |
Promise.all()
Promise.all(promiseArray)方法是Promise
对象上的静态方法,该方法的作用是将多个Promise
对象实例包装,生成并返回一个新的Promise
实例。
参数:promiseArray
,是一个Promise
实例数组
1 | var p1 = Promise.resolve(1), |
求幂运算符
1 | Math.pow(3, 2) === 3 ** 2 // 9 |
Array.prototype.includes()
数组原型的方法,查找一个数值是否在数组中,只能判断一些简单类型的数据,对于复杂类型的数据无法判断。该方法接受两个参数,分别是查询的数据和初始的查询索引值。
1 | [1, 2, 3].indexOf(3) > -1 // true |
两者的优缺点和使用场景
简便性
includes方法略胜一筹,直接返回bool。indexOf需要返回数组下标,我们需要对下标值在进行操作,进而判断是否在数组中。
精确性
两者这都是通过===进行数据处理,但是对NaN数值的处理行为不同。includes对NaN的处理不会遵循严格模式去处理,所以返回true。indexOf会按照严格模式去处理,返回-1。
1
2[1, 2, NaN].includes(NaN) // true
[1, 2, NaN].indexOf(NaN) // -1使用场景
如果仅仅查找数据是否在数组中,建议使用includes,如果是查找数据的索引位置,建议使用indexOf更好一些
ES8
Object.entries()
该方法会将某个对象的可枚举属性与值按照二维数组的方式返回。(如果目标对象是数组,则会将数组的下标作为键值返回)
1 | Object.entries({ one: 1, two: 2 }) //[['one', 1], ['two', 2]] |
Object.values()
它的工作原理和Object.entries()方法很像,但是它只返回键值对中的值,结果是一维数组
1 | Object.values({one: 1, two: 2}) // [1, 2] |
字符串填充
ES8提供了新的字符串填充方法,该方法可以使得字符串达到固定长度。它有两个参数,字符串目标长度和填充内容。
1 | console.info('vue'.padStart(6, 'm')); // mmmvue |