ES6的兼容性
- IE10+
- Chrome
- Firefox
- 移动端
- Node.js
ES6的新特性
- 变量
- 箭头函数
- 函数的参数
- 数组
- 字符串
- 面向对象
- Promise
- generator
- 模块化
1.变量(新增let、const)
- let声明的是变量,const声明的是常量,const修改值会报错,const保存的是内存地址,可以给对象或数组添加属性或元素,但是不能重新复写
- let、const声明的变量仅在块级作用域内有效,var 声明变量是全局的,没有块级作用域功能
- let 、const 不存在变量提升 , var 存在变量提升,例如:
console.log(a)
var a = 1; //先执行var 10,并不会先赋值
- let 、const不能在同一块级作用域内重复申请
- let不会进行预处理,var会,例如:
console.log(a)
let a = 1; //运行之后会报错
console.log(a)
var a = 1; //运行之后不会报错,只会提示未定义
2.箭头函数(()=>)
例如:
let show = function(){
alert(123)
}
可以写成
let show=()=>{
alert(123)
}
(1)只有一个参数的时候,()可以省略。
let show=(a)=>{
alert(a)
}
show(2)
可以写成
let show=a=>{
alert(a)
}
show(2)
(2)只有一个return的时候,花括号可以省略。上面可以写成:
let show=a=>alert(a)
show(2)
3.函数的参数
(1)参数的扩展
- 收集剩余的参数
function show(a,b,...c){
alert("a="+a",b="+b",c="+c)
}
show(1,2,3,4,5) //输出a=1,b=2,c=3,4,5
值得一提的是,…c这种扩展只能放在最后面,放在中间不生效。
- 展开数组
let arr = [1,2,3];
function show(a,b,c){
alert("a="+a",b="+b",c="+c)
}
show(...arr) //输出a=1,b=2,c=3
展开后的效果,跟直接把数组的内容写在这一样。
(2)默认参数
function show(a,b=1,c=2){
alert("a="+a",b="+b",c="+c)
}
show(1,0) //输出a=1,b=0,c=2
4.解构赋值
- 左右了两边结构必须一样
- 右边必须是个东西
- 声明和赋值不能分开(必须在一句话里完成)
let [a,b,c] = [1,2,3]
5.数组
(1) map() 方法 —— 映射,一个对一个
- map()方法一定要有一个返回值,没有返回值的话就返回一个undefined
- map()方法的返回值是一个数组
- 应用场景:①要返回一个利用原数组经过运算后的数组
let arr1 = [1,2,3]
let arr2 = arr1.map(function(num){
return num * 2
})
console.log(arr2) //返回[2,4,6]
- ②或者在一个对象数组中拿到某一个属性,并返回一个新数组的情况
let persons = [
{name:"alex",age:18},
{name:"sky",age:20}
]
let names = persons.map(function(person){
return person.name
})
console.log(names) //返回["alex","sky"]
(2) reduce () 方法 —— 一堆出来一个
reduce ( function (a,b,index) { } ) 中有三个参数,a表示每次计算出的结果,再带进去计算的数值,b表示和a计算的数值,index为索引,例如:
计算总和
let arr1 = [1,2,3]
let arr2 = arr1.reduce(function(num,item,index){
return num + item
})
console.log(arr2) //输出6
计算平均数
let arr1 = [1,2,3]
let arr2 = arr1.reduce(function(num,item,index){
if(index != arr1.length - 1){
return num + item
}else{
return (num + item)/arr1.length
}
})
console.log(arr2)
将一个数组中的某些属性值抽出来,组成一个新的数组
此时输出的是一个数组[“alex”,“sky”,“rose”]
(3) filter () 方法 —— 过滤器
filter()可以返回一个新数组,也可以直接改变原数组
已知对象数组,要把对象数组中一些符合要求的对象重新组成一个数组,例如:
let num1 = [84,12,5,8]
let num2 = num1.filter(function(a){
if(a%3 == 0){
return a
}
})
console.log(num2) //输出[84,12]
也可以直接写成
let num1 = [84,12,5,8]
let num2 = num1.filter(function(a){
return a%3 == 0
})
console.log(num2) //输出[84,12]
(4) forEach () 方法 —— 循环
let num1 = [84,12,5,8]
let num2 = num1.forEach(function(item){
alert(item)
})
也可以加入索引参数:
let num1 = [84,12,5,8]
let num2 = num1.forEach(function(item,index){
alert(index+ ":" +item)
})
(5) find ()方法
- 只会找到第一个符合的,找到之后就会直接返回,就算下面还有符合要求的,也不会再找下去
应用场景:
① 在对象数组中,找到符合要求的对象,只能找到第一个符合要求的
let persons = [
{name:"alex",age:18},
{name:"sky",age:20}
]
let person = persons.find(function(p){
return p.name === "alex"
})
console.log(person) //输出[name:"alex",age:18]
② 两个对象数组,找A数组中有B数组的ID的元素
let coursesType = {id:1,name:"computer course"}
let courses = [
{id:1,name:"java"},
{id:1,name:"js"},
{id:2,name:"c++"},
]
let course = courses.find(function(c){
return c.id === coursesType.id
})
console.log(course) //输出[id:1,name:"java"]
(6) some() 和 every() 方法
- some()方法只要有一个满足就行,every()方法要所有满足才可以,即some():一真即真,every():一假即假,跟逻辑运算符差不多
① some只要有一个大于,它就不会再往下找,直接返回true,因为相当于 || (逻辑或运算符)
let computers = [
{name:"apple",ram:4},
{name:"IBM",ram:16},
{name:"Acer",ram:32},
{name:"ASUS",ram:64},
]
let some = computers.some(function(computer){
return computer.ram > 16
})
console.log(some) //输出true
② every()相当于&&,找到第一个错的就直接返回false,不再往下找
let computers = [
{name:"apple",ram:4},
{name:"IBM",ram:16},
{name:"Acer",ram:32},
{name:"ASUS",ram:64},
]
let every = computers.every(function(computer){
return computer.ram > 16
})
console.log(every) //输出false
6.字符串
(1)新增两个方法
① startsWith()
let str = "http://www.baidu.com"
if(str.startsWith("http://")){
alert("普通网址")
}else{
alert("其他网址")
}
② endsWith()
let str = "1.txt"
if(str.endsWith(".txt")){
alert("文本文件")
}else{
alert("其他")
}
(2) 模板字符串
- 反单引号(``):连接字符串(${内容}),可以折行
let str1 = "明月"
let str2 = `床前${str1}光`
alert(str2) //输出床前明月光
7.面向对象
(1) 构造函数
在ES6之前,创建一个实例对象是通过构造函数来实现的
//定义构造函数 Person
function Person(name, age) {
this.name = name
this.age = age
}
//在构造函数原型上定义方法 show
Person.prototype.show = function() {
console.log('姓名:' + this.name)
console.log('年龄:' + this.age)
}
//创建了一个Person类的实例
var person = new Person('Jack', 18)
console.log(person.name) // Jack
console.log(person.age) // 18
person.show() /* 姓名:Jack
年龄:18 */
我们通过 new
关键字调用构造函数,即可生成一个实例对象。不妨我们再来回顾一下 new
关键字的作用过程,即 var person = new Person('Jack', 18)
等价于以下代码
var person = function (name='Jack', age = 18) {
// 1.创建一个新的空对象赋值给this
var this = {}
// 2.执行构造函数里的所有代码
this.name = name
this.age = age
// 3.返回this
return this
}
通过以上代码我们可以得知,构造函数中的 this
指向的是新生成的实例对象
(2) class 语法
使用 class
语法来更改以上实例,看看有什么区别
//用class定义一个类
class Person {
constructor(name, age) {
this.name = name
this.age = age
}
show() {
console.log('姓名:' + this.name)
console.log('年龄:' + this.age)
}
}
//生成Person类的一个实例对象person
var person = new Person('Jack', 18)
console.log(person.name) // Jack
console.log(person.age) // 18
person.show() /* 姓名:Jack
年龄:18 */
通过调用实例对象的属性 name
、age
以及方法 show
,我们可以看到,跟构造函数没有任何的区别,所以说 class
语法就是构造函数的一个语法糖,即构造函数的另一种写法,这两者并无本质区别
其实我们还可以通过 typeof
来验证一下 class
定义的类的类型
class Person {}
console.log(typeof Person) // function
① constructor
当我们用 class 定义了一个类,然后用关键字 new
调用该类,则会自动调用该类中的 constructor
函数,最后生成一个实例对象。constructor
函数内部的 this
指向的也是新生成的实例对象。
如果要生成一个不需要任何属性的实例对象,则我们不需要在 constructor
函数里写任何代码,此时可以省略它,例如
class Person {
//不写constructor函数
say() {
console.log('hello world')
}
}
上述代码省略了 constructor
函数,此时JavaScript
会默认生成一个空的 constructor
函数,例如
class Person {
constructor() {
}
say() {
console.log('hello world')
}
}
以上两段代码是等价的
也正是因为 constructor
函数的存在,class
定义的类必须通过 new
来创建实例对象,否则就会报错
class Person {
}
var person = Person()
/*
报错
var person = Person()
^
TypeError: Class constructor Person cannot be invoked without 'new'
*/
而传统的构造函数就可以不通过 new
来调用,因为其本身就是一个函数,若不加关键字 new
,则相当于直接执行该函数
② 类方法的定义
在传统的构造函数中,为了使每个实例对象都拥有共同的方法,在构造函数的原型上进行方法的定义,例如
function Person() {}
Person.prototype.show = function () {
console.log('hello world')
}
因此,class
语法定义的方法也是在原型上的,不过这里称之为类的原型上,同时省略了大量的代码,直接将方法写在 class
内即可
class Person {
//在Person类的原型上定义了方法 show
show() {
console.log('hello world')
}
//在Person类的原型上定义了方法 hide
hide() {
console.log('bye world')
}
}
③ get 函数和 set 函数
在 class
类中,可以使用两个内部定义的函数,即 get
和 set
,语法为 get/set 属性名() {}
,分别表示读取属性/设置属性时调用此函数,其中 set
函数接收一个参数,表示所设置的值
class Person {
get number() {
return 18
}
set number(value) {
console.log('现在的number值为:' + value)
}
}
var person = new Person()
//访问属性number
person.number // 18
//设置属性number为20
person.number = 20 // 打印:现在的number值为:20
当我们访问属性 number
时,会调用 get number() {}
函数,故返回 18;当设置属性 number
的值为 20时,会调用 set number() {}
函数,故打印了 现在的number
值为:20
④ 静态方法(static)
在 class
类中的方法都是写在原型上的,因此生成的实例对象可以直接调用。现在有一个关键字 static
,若写在方法的前面,则表示此方法不会被写在原型上,而只作为该类的一个方法,这样的方法叫做静态方法;相反,若没加关键字 static 的方法就叫做非静态方法
class Person {
show() {
console.log('我是非静态方法show')
}
static show() {
console.log('我是静态方法show')
}
static hide() {
console.log('我是静态方法hide')
}
}
Person.show() // 我是静态方法show
var person = new Person()
person.show() // 我是非静态方法show
person.hide() /* person.hide()
^
TypeError: person.hide is not a function
*/
首先我们直接调用 Person
类的 show
方法,实际调用的就是有关键字 static
的 show
方法;
然后我们生成了一个实例对象 person
,然后调用 person
实例对象上的 show
方法,实际调用的就是没有关键字 static
的 show
方法,从这我们可以看出,静态方法和非静态方法可以重名;
最后我们调用了 person
实例对象上的 hide
方法,但报错了,因为在 class
类中,我们定义的是静态方法,即有关键字 static
的 hide
方法,也就是此方法没有被写进类的原型中,因而实例对象 person
无法调用此方法。
我们都知道,类中定义的方法内的 this
指向的是实例对象,但在静态方法中的 this
指向的是类对象
class Person {
constructor() {
this.name = 'Lpyexplore'
}
show() {
console.log(this.name)
}
static cite() {
this.show()
}
static show() {
console.log('我是非静态方法show')
}
}
Person.cite() // 我是非静态方法show
var person = new Person()
person.show() // Lpyexplore
首先我们直接调用 Person
类的静态方法 cite
,执行代码 this.show()
,因为静态方法中的 this
指向 Person
类,所以其实调用的就是静态方法 show
,所以打印了 我是非静态方法show
然后我们生成了一个实例对象 person
,调用 person
的 show
方法,因为在非静态方法 show
中,this
指向的是实例对象 person
,因此打印了 Lpyexplore
⑤ 实例属性的简易写法
原先我们为实例对象定义的属性都是写在 constructor
函数中的
class Person {
constructor() {
this.name = 'Lpyexplore'
this.age = 18
}
show() {
console.log('hello world')
}
}
var person = new Person()
console.log(person.name) // Lpyexplore
console.log(person.age) // 18
现在我们用实例对象的属性新写法来改写以上代码
class Person {
name = 'Lpyexplore'
age = 18
show() {
console.log('hello world')
}
}
var person = new Person()
console.log(person.name) // Lpyexplore
console.log(person.age) // 18
这种写法就是将 constructor
函数中的属性定义放到了外部,同时不需要写 this
,因为此时的属性定义与其他方法也处于同一个层级。
虽然这样的写法比较简便,但也有一定的缺点,那就是用这种写法定义的属性是写死的。
⑥ 静态属性
静态属性就只属于 class
类的属性,而不会被实例对象访问到的属性。
class Person {
name = '我是实例对象的name属性'
static name = '我是Person类的name属性'
static age = 18
}
console.log(Person.name) // 我是Person类的name属性
var person = new Person()
console.log(person.name) // 我是实例对象的name属性
console.log(person.age) // undefined
(3) class 的继承
① 继承的概念
继承就是使一个类获得另一个类的属性
和方法
。
② ES5 中原型链实现继承
通过原型链实现继承
// 创建构造函数 Parent
function Parent() {
// 定义了实例对象属性 name1
this.name1 = 'parent'
}
// 为 Parent原型定义方法 show1
Parent.prototype.show1 = function() {
console.log('我是Parent的show1方法')
}
// 创建构造函数 Child
function Child() {
this.name2 = 'child'
}
// 将构造函数 Child的原型设置成 Parent的实例对象
Child.prototype = new Parent()
// 为Child原型定义方法 show2
Child.prototype.show2 = function() {
console.log('我是Child的show2方法')
}
// 生成实例对象 child
var child = new Child()
console.log(child.name1) // parent
console.log(child.name2) // child
child.show1() // 我是Parent的show1方法
child.show2() // 我是Child的show2方法
我们可以看到,我们通过改变构造函数 Child
的原型 prototype
为构造函数 Parent
生成的实例对象,实现了继承,即通过构造函数 Child
生成的实例对象具有 Parent
中定义的属性name1
和方法show1
,同时也具有属于自己的属性name2
和方法show2
③ ES6 中 class 实现继承
ES5中实现继承的写法显然有些麻烦,所以在 class
类中,我们可以通过关键字 extends
来实现继承
class Parent{
constructor() {
this.name1 = 'parent'
}
show1() {
console.log('我是Parent的show1方法')
}
}
// Child类 继承 Parent类
class Child extends Parent{
constructor() {
super();
this.name2 = 'child'
}
show2() {
console.log('我是Child的show2方法')
}
}
var child = new Child()
console.log(child.name1) // parent
console.log(child.name2) // child
child.show1() // 我是Parent的show1方法
child.show2() // 我是Child的show2方法
接下来我们详细讲一下super
关键字
④ super
在ES6中规定了,在子类继承了父类以后,必须先在子类的 constructor
函数中调用 super
函数,其表示的就是父级的 constructor
函数,作用就是为子类生成 this
对象,将父类的属性和方法赋值到子类的 this
上。因此,若没有调用 super
函数,则子类无法获取到 this
对象,紧接着就会报错
class A{
constructor() {
this.name1 = 'A'
}
}
class B extends A{
constructor() {
this.name2 = 'B'
}
}
var b = new B()
/*
this.name2 = 'B'
^
ReferenceError: Must call super constructor in derived class before accessing 'this' or returning from derived constructor
*/
再看一个例子
class Father{
constructor(name,age){
this.name = name
this.age = age
}
}
//继承
class Son extends Father{
constructor(name,age,sex){
super(name,age,sex) //相当于先调用的父类的构造函数一遍
this.sex = sex
this.color = "黄色"
}
}
//实例化
let father = new Father("sky",38)
let son = new Son("alex",18,"男")
console.log(father)
console.log(son)
输出结果
super()
代表的是父类的构造函数,其实 super
还可以作为对象使用,即不作为函数调用。当 super
在子类的普通方法内时,指向的是父类的原型对象;在子类的静态方法内时,指向的是父类
class A{
show1() {
console.log('我是A类的show1方法')
}
}
class B extends A{
constructor() {
super()
}
show2() {
super.show1()
}
}
var b = new B()
b.show2() // 我是A类的show1方法
上述代码,B
类继承 A
类,其中 A
类有一个 show1
方法,是写在其原型 A.prototype
上的,而在 B
类的 show2
方法中调用了 super.show1()
,我们说过 super
在普通的方法中指向的是父类的原型对象,所以 super.show1()
相当于 A.prototype.show1()
我们再来看一个 super
在子类的静态方法中的例子
class A{
static hide1() {
console.log('我是A类的hide1方法')
}
}
class B extends A{
constructor() {
super()
}
static hide2() {
super.hide1()
}
}
B.hide2() // 我是A类的hide1方法
上述代码,B
类继承 A
类,B
类在其静态方法 hide2
中调用了 super.hide1()
,因为 super
在静态方法中指向的是父类,所以 super.hide1()
就相当于 A.hide1()
说到静态方法,其实类的继承,也是可以继承静态方法的
class A{
static show() {
console.log('我是A类的show方法')
}
}
class B extends A{}
B.show() // 我是A类的show方法
还需要注意的是,当我们在子类的普通方法中通过 super
调用父类的方法时,方法中的 this
指向的是当前子类的实例对象
class A {
constructor() {
this.name = 'Jack'
}
show1() {
console.log(this.name)
}
}
class B extends A{
constructor() {
super();
this.name = 'Lpyexplore'
}
show2() {
super.show1()
}
}
var b = new B()
b.show2() // Lpyexplore
那么,当我们在子类的静态方法中通过 super
调用父类的方法时,方法中的 this
指向的是子类,而不是子类的实例对象
class A {
constructor() {
this.x = 1
}
static show1() {
console.log(this.x)
}
}
class B extends A{
constructor() {
super();
this.x = 2
}
static show2() {
super.show1()
}
}
B.show2() // undefined
B.x = 3
B.show2() // 3
上述代码中,我们在 B
类的静态方法 show2
中通过 super
调用了 A
类的静态方法 show1
,执行代码 console.log(this.x)
,此时的 this
指向的是 B
类,但因为 B
类的 constructor
函数中定义的属性 x
是定义在 B
类的实例对象上的,所以 this.x
返回的是 undefined
。
所以我们在 B
类上定义一个属性 x
并且值为 3
,此时再此调用 B.show2()
,返回的就是 3
了。
原文
8. json
(1) json 对象
① JSON.parse()
从一个字符串中解析出json对象
let str = '{"name":"alex","age":18}'
let obj = JSON.parse(str)
console.log(obj) //输出{name:"alex",age:18}
注意:单引号写在{}外,每个属性名都必须用双引号,否则会抛出异常。
② JSON.stringify()
于从一个对象解析出字符串
let obj = {name:"alex",age:18}
let str = JSON.stringify(obj)
console.log(str) //输出'{"name":"alex","age":18}'
(2) json 的简写
① 属性和属性值一样
以前的写法
let a = 1
let b = 2
let json = {a:a,b:b,c:3}
console.log(json) //输出{a:1,b:2,c:3}
现在可以只写一个
let a = 1
let b = 2
let json = {a,b,c:3}
console.log(json) //输出{a:1,b:2,c:3}
② 函数
以前的写法
let json = {
a:1,
show:function(){
console.log(this.a)
}
}
json.show()
现在的写法
let json = {
a:1,
show(){
console.log(this.a)
}
}
json.show()