es6 新特性

127 阅读5分钟

模板字符串

let a = "1"
let str = `${a}` 

可以放
变量、算术计算、三目、对象属性、创建对象、调用函数、访问数组元素 ——有返回值的合法的js表达式

不可以放
没有返回值的js表达式, 也不能放分支/判断、循环等程序结构。 比如: if else for while...等

let

var的问题

  • a. 会被声明提前:打乱程序正常的执行顺序
  • b. 没有块级作用域:代码块内的变量会超出代码块范围,影响外部的变量

什么是块级作用域:

  • 在其他语言里,指除了对象{}和function的{}之外,其余if else、for等分支和循环结构的{}范围程序块{}内的变量,出了所在的程序块{},就不能使用,但是js没有块级作用域

例子

var t = 0; //全局变量t
function fun1() {
    //这个为局部变量t 
    // var t; //导致下面的  t += 0.8; 不能访问全局变量
    t += 0.8; //将fun1的执行时间累加到全局变量t上
    if (false) { //不是作用域 + 加上js 是词法作用域,所以t 会被执行前,分析阶段做声明提前
        var t = new Date(); //这里会导致 局部变量t 声明提前到fun1(){} 下的第一行,
    }
}

function fun2() {
    t += 0.3; //将fun2的执行时间累加到全局变量t上
}
fun1();
fun2();
console.log(`t:${t}`)

let 特点

  • 因为不会声明提前,所以不能在声明变量之前,提前使用该变量。
//使用var
var a;//undefined
console.log(a);//undefined
a=10; //var a;被声明提前
console.log(a);//10

//使用let
console.log(b); //ReferenceError: Cannot access 'b' before initialization
let b=10;//初始化: 第一次声明变量并赋值
console.log(b);
  • 在相同作用域内,禁止声明两个同名的变量!

//使用let
var a = 10;
var a = 100;
console.log(a); //100
let a = 100;// Uncaught SyntaxError: Identifier 'a' has already been declared
console.log(a);
  • 因为let底层相当于匿名函数自调,所以,即使在全局创建的let变量,在window 中也找不到!
//使用var
var a=10;
console.log(a); //10
console.log(window.a);//10

//使用let
let b=10;
console.log(b); //10
console.log(window.b);//undefined
//等价于
function () {
    let b = 10;
    var b = 10; //局部变量
    console.log(b); //10 局部变量
    console.log(window.b); //undefined
})()

const 的逻辑跟let 一直,只是不能修改而已。

循环遍历 for

普通for

  • 可遍历索引数组,
  • 可以遍历类数组对象(arguments)——只要下标是数字
var arr = ["11", "22", "33"];
for(var i = 0; i<arr.length; i++ ) 
      console.log(arr[i]) 
}
//输出 1  2  3

function test() { 
for(var i = 0; i<arguments.length; i++ ) 
      console.log(arguments[i]) 
}
test(1,2,3)
//输出 1  2  3

forEach /map

只能遍历数字下标的索引数组,伪类或对象会失败

function test() { 
    arguments.forEach(e => {//这里报错,不支持伪数组 
        console.log(e)
    });
    arguments.map(e => {//这里报错,不支持伪数组
        console.log(e)
    });
}
test(1,2,3)

for of

for of会依次取出数组或类数组对象中每个属性值

支持遍历伪数组,因为伪数组也有有序的下标

缺点无法获取key值

function test() { 
    for(var o  of arguments) {
          console.log(o) 
    } 
}
test(1,2,3)

let arr = {0:"0" , 1:"1", length:2 } 
function test() { 
    for(var o  of arr) //这里会报错 class.js:13 Uncaught TypeError: arr is not iterable 不支持自己定义的类数组对象
          console.log(o)  
    } 
} 
test()

参数增强

参数默认值

function test(a = 10) {
    console.log(a)//10
}
test()

方法参数arguments

function test() { 
    for(let o of arguments) {
        console.log(a)//1,2,3
    }
}
test(1,2,3)

缺点 当test是箭头函数 就不能使用 argumnets

剩余参数rest (声明时的形参)

参数收集
适合箭头函数参数不确定场景

let test = (...arr) => { 
    for(let a of arr) {
        console.log(a)//1,2,3
    }
}
test(1,2,3)

可以只收集后半部分


let test = (p1,p2,...arr) => { 
    console.log(p1) //1
    console.log(p2) //2
    for(let a of arr) {
        console.log(a)//3,4,5
    }
}
test(1,2,3,4,5)

不能使用箭头函数的场景

  • 构造函数不能用
  • 对象的方法不能用
  • 原型对象方法不能用
  • DOM中事件处理函数不能用
  • 箭头函数无法用call,apply,bind改变this
  • 箭头函数不支持arguments
  • 箭头函数没有prototype

展开运算符(spread) (调用时的实参)

展开时,会复制值

展开数组,把每个数组元素都复制一下

Math.max(...[1,2,3]) //是的Math.max支持数组
//等价于
Math.max(1,2,3)
  1. 复制数组
let arr = [1,2,3]
let arr2 = [...arr]
  1. 合并数组
let arr = [1,2,3]
let arr2 = [4,5,6]
let arr3 = [...arr,...arr2] //[1,2,3,4,5,6]
  1. 复制对象
let obj = {name:"jason",age:18}
let obj2 = {...obj}
  1. 合并多个对象属性
let obj = {name:"jason",age:18}
let obj2 = {weight:120,height:178}
let obj3 = {...obj,...obj2,gender:"男"}

解构赋值

  1. 对象解构 解决对象多层嵌套访问问题 xx.xx.xx
var user = {
    name: "jason11",
    age: 18,
    address: {
        street: "xxxxx街道",
        city: "shenzhen"
    }
}
console.log(user.address.city)

let {address:add} = user //解构同时重新命名
console.log(add.city)
let {address} = user //如果属性同名可以合并成一个
console.log(address.city)
  1. 数组解构赋值
var arr=[2021,6,7,9,33];
// 0 1 2 3 4
//仅提取出arr数组中年、月、日三个值单独使用
var [y,m,d]=arr; // 2021,6,7
var [,,d1]=arr; // 7
  1. 解构赋值失败 解构失败对应的值为undefined
var user = {
    name: "jason11",
    age: 18,
}
var {
    name: name,
    address: address, //这里右边的address解构失败为undefined
    gender, //这里右边的gender解构失败为undefined
} = user 
console.log(name) //jason1
console.log(address) //undefined
console.log(gender) //undefined

参数解构

形参和实参都使用对象的方式传入

//定义 参数使用对象定义
function $ajax({
    url: url,
    type: type,
    data: data,
    datatype: datatype
}){
console.log(url)
console.log(type)
console.log(data)
console.log(datatype)
}

//调用
$ajax({
    url: "https://www.baidu.com", 
  // data: data, //这里可以省略data参数   
    datatype: "json",
    type: "get", //并顺序不一定有序 
});

//当然 方法的参数如果形参和实参一致,可以合并
function $ajax({
    url,
    type,
    data,
    datatype
}){
console.log(url)
console.log(type)
console.log(data)
console.log(datatype)
}

class

es6的类 本质上还是 构造函数.

//es6的class 定义类创建实例
class User {
    constructor(name,age){
        this.name = name
        this.age = age
    }
    info(){
        console.log(this.name,this.age)
    }
}
let user = new User()
console.log(user)

//构造函数创建实例
function User2(name,age){
    this.name = name
    this.age = age 
}
User2.prototype.info = function () {
    console.log(this.name,this.age)
}
let user2 = new User2()
console.log(user2)

image.png

两者的底层是一致的。

在原型上都有constructor指向构造函数的定义

静态属性

class 通过定义static 变量实现。底层实现还是直接挂在构造函数上

//es6 class 定义静态变量
class User {
    constructor(){ 
    } 
    static a = "111"
}
console.log(User.a) 

//等价于 构造函数直接挂属性
function User2(){ 

}
User2.a = "111"
console.log(User.a) 

image.png

继承extends

为什么继承不用单词inherit,而用extends。 因为代码继承更多是为了扩展extends。

为什么用super表示父级,而不用father。 因为super表示的是数学中的超集合的意思,如猫科动物就是一个超集,老虎就是子集

  1. 使用extends 继承父类
  2. 在子类方法的构造函数调用super(),初始化父类的实例,不然会报错
class Person{
    constructor(name,age){
        this.name = name
        this.age = age
    }
    eating(){
        console.log(this.name + " eating~")
    }
} 
class User extends Person{
    constructor(name,age,address){
        super(name,age);//这里如果不调用super()会报错
        //Uncaught ReferenceError: Must call super constructor in derived class before accessing 'this' or 
        this.address = address;
    }
    running(){
        console.log(this.name + " runnig~")
    }
}
 
let user = new User("jason",18,1111)
user.running() //子类方法
user.eating() //父类方法

当没有主动写子类的constructor,es6会自动添加constructor和super的调用

class User extends Person { 
}
// 等同于
class User extends Person {
    constructor(...args) {
        super(...args);
    }
}

判断数组


let obj = {name:"xxx"}
let arr = [1,2,3]

//不能用typeof 判断数组
console.log(typeof obj) //true
console.log(typeof arr) //true 

//使用实例__proto__ 原型链判断 ok
console.log( obj.__proto__ === Array.prototype) //false
console.log( arr.__proto__ === Array.prototype) //true


//使用constructor 原型链判断
console.log( obj.__proto__.constructor === Array ) //false
console.log( arr.__proto__.constructor === Array  ) //true


//使用 Object.getPrototypeOf 原型链判断 ok
console.log( Object.getPrototypeOf(obj) === Array.prototype) //false
console.log( Object.getPrototypeOf(arr) === Array.prototype) //true


//使用 prototype.isPrototypeOf 原型链判断 ok
console.log( Array.prototype.isPrototypeOf(obj) ) //false
console.log( Array.prototype.isPrototypeOf(arr) ) //true


//使用 toString.call 原型链判断
console.log( Object.prototype.toString.call(obj) == "[object Array]" ) //false
console.log( Object.prototype.toString.call(arr) == "[object Array]"  ) //true

//使用Array.isArray
console.log( Array.isArray(obj) ) //false
console.log( Array.isArray(arr) ) //true

promise

promise使用

promise实现