JavaScript-6


ES6

Symbol

  • Symbol,新增数据类型,表示独一无二的值

  • Symbol 值通过Symbol函数生成。这就是说,对象的属性名现在可以有两种类型,一种是原来就有的字符串,另一种就是新增的 Symbol 类型。凡是属性名属于 Symbol 类型,就都是独一无二的,可以保证不会与其他属性名产生冲突。

  • Symbol 函数前不能使用 new 命令,否则会报错。因为生成的 Symbol 是一个原始类型的值,不是对象。也就是说,由于 Symbol 值不是对象,所以不能添加属性。基本上,它是一种类似于字符串的数据类型。

    let s = Symbol();

    Symbol()参数

  • 字符串: Symbol函数可以接受一个字符串作为参数,表示对 Symbol 实例的描述,主要是为了在控制台显示,或者转为字符串时,比较容易区分

    let s1 = Symbol('foo');
    let s2 = Symbol('bar');
    
    s1 // Symbol(foo)
    s2 // Symbol(bar)
    
    s1.toString() // "Symbol(foo)"
    s2.toString() // "Symbol(bar)"
  • 对象:参数是一个对象,就会调用该对象的toString方法,将其转为字符串,然后才生成一个 Symbol 值。

    const obj = {
      toString() {
          return 'abc';
      }
    };
    const sym = Symbol(obj);
    sym // Symbol(abc)

    注意,Symbol()函数的参数只是表示对当前 Symbol 值的描述,因此相同参数的Symbol函数的返回值是不相等的。

Symbol运算

Symbol 值不能与其他类型的值进行运算,会报错。

let sym = Symbol('My symbol');

"your symbol is " + sym
// TypeError: can't convert symbol to string
`your symbol is ${sym}`
// TypeError: can't convert symbol to string

Symbol 类型转换

  • Symbol 值可以显式转为字符串。

    let sym = Symbol('My symbol');
    
    String(sym) // 'Symbol(My symbol)'
    sym.toString() // 'Symbol(My symbol)'
  • Symbol 值可以转为布尔值,但是不能转为数值。

    let sym = Symbol();
    Boolean(sym) // true
    !sym  // false
    
    if (sym) {
    // ...
    }
    
    Number(sym) // TypeError
    sym + 2 // TypeError

    Symbol操作

  • 在创建 Symbol 的时候,参数是为 Symbol 数据的描述。

    • Symbol.prototype.description 返回 Symbol 的描述。
    const sym = Symbol('foo');
    sym.description // "foo"
  • 作为属性名的 Symbol

    let mySymbol = Symbol();
    
    // 第一种写法
    let a = {};
    a[mySymbol] = 'Hello!';
    
    // 第二种写法
    let a = {
    };
    
    // 第三种写法
    let a = {};
    Object.defineProperty(a, mySymbol, { value: 'Hello!' });
    
    // 以上写法都得到同样结果
    a[mySymbol] // "Hello!"

    Symbol 值作为对象属性名时,不能用点运算符。

    • Symbol 作为属性名,遍历对象的时候,该属性不会出现在for…in、for…of循环中,也不会被Object.keys()、Object.getOwnPropertyNames()、JSON.stringify()返回。
      但是,它也不是私有属性,有一个Object.getOwnPropertySymbols()方法,可以获取指定对象的所有 Symbol 属性名。该方法返回一个数组,成员是当前对象的所有用作属性名的 Symbol 值。
    const obj = {};
    let a = Symbol('a');
    let b = Symbol('b');
    
    obj[a] = 'Hello';
    obj[b] = 'World';
    
    const objectSymbols = Object.getOwnPropertySymbols(obj);
    
    objectSymbols
    // [Symbol(a), Symbol(b)]

解构

ES6 允许按照一定模式,从数组和对象中提取值,对变量进行赋值,这被称为解构

/**********************数组解构************************/ 
let [a, b, c] = [1, 2, 3];
a // 1
b // 2
c // 3
let [, , third] = ["foo", "bar", "baz"];
third // "baz"
//  如果解构不成功,变量的值就等于undefined。
let [foo] = [];
let [bar, too] = [1];
foo //undefined
too //undefined

/**********************对象解构************************/ 
let { foo, bar } = { foo: 'aaa', bar: 'bbb' };
foo // "aaa"
bar // "bbb"

对象解构时等号左边的变量名称必须是要对象的属性值否解构不成功

解构默认值

// 对象默认值
var { x = 3 } = {};
x // 3
var { x, y = 5 } = { x: 1 };
x // 1
y // 5
// 数组默认值
var [foo = true] = [];
foo // true
var [x, y = 'b'] = ['a']; // x='a', y='b'

let/const

let关键字,用来声明变量。它的用法类似于var,但是所声明的变量,只在let关键字所在的代码块内有效。

不存在变量提升

var命令会发生“变量提升”现象,即变量可以在声明之前使用,值为undefined。这种现象多多少少是有些奇怪的,按照一般的逻辑,变量应该在声明语句之后才可以使用。

为了纠正这种现象,let命令改变了语法行为,它所声明的变量一定要在声明后使用,否则报错。

var 声明的变量挂载在 window 对象下,而 let则没有。
let 不允许重复声明

作用域

ES5 只有全局作用域和函数作用域,没有块级作用域,

使用 let 关键字可以支持块级作用域。

const

const声明一个只读的常量。一旦声明,常量的值就不能改变。

模板字符串

模板字符串(template string)是增强版的字符串,用反引号(`)标识。它可以当作普通字符串使用,也可以用来定义多行字符串,或者在字符串中嵌入变量。

// 字符串中嵌入变量
let name = "Bob", time = "today";
`Hello ${name}, how are you ${time}?`

rest 参数

ES6 引入 rest 参数(形式为…变量名),用于获取函数的多余参数,这样就不需要使用arguments对象了。rest 参数搭配的变量是一个数组,该变量将多余的参数放入数组中。

function add(...values) {
  let sum = 0;

  for (var val of values) {
    sum += val;
  }

  return sum;
}

add(2, 5, 3) // 10

… 在数组中的使用

… 在数组中时作为展开运算符使用。

箭头函数

ES6 允许使用“箭头”(=>)定义函数。

let f = (v)=>{
    return v
}

圆特号中定义形参列表。花括号为函数体。

  • 箭头函数只有一个参数时 圆括号可以省略

    let f = v=>{
      return v
    }
  • 箭头函数,函数体中只有一条有效语句时,花括号可以省略。

    let f = v=>
      console.log( v )
  • 箭头函数,省略花括号时如果要返回数据给调用者时不可以使用 return 关键字。

    let f = v=>
      v  // 返回数据给调用者
    1. 函数体内的this对象,就是定义时所在的对象,而不是使用时所在的对象。
    2. 不可以当作构造函数,也就是说,不可以使用new命令,否则会抛出一个错误。
    3. 不可以使用arguments对象,该对象在函数体内不存在。如果要用,可以用 rest 参数代替。

函数默认值

ES6 允许为函数的参数指定默认值。

function f(num=19){
    num // 19
}
f()
let f2 = (num=99)=>{
    num //99
}
f2()

当调用函数时没有给函数传递实际参数时就会使用参数的默认值。

Set

ES6 提供了新的数据结构 Set。它类似于数组,但是成员的值都是唯一的,没有重复的值。

// 实例化set对象
let set = new Set()
  • set.size: 返回Set实例的成员总数。
  • set..add(value): 添加某个值,返回 Set 结构本身。
  • set.delete(value): 删除某个值,返回一个布尔值,表示删除是否成功。
  • set.has(value): 返回一个布尔值,表示该值是否为Set的成员。
  • set.clear(): 清除所有成员,没有返回值。
  • set.keys(): 返回键名的遍历器
  • set.values(): 返回键值的遍历器
  • set.entries(): 返回键值对的遍历器
  • set.forEach(): 使用回调函数遍历每个成员

    let set = new Set(['red', 'green', 'blue']);

for (let item of set.keys()) {
console.log(item);
}
// red
// green
// blue

for (let item of set.values()) {
console.log(item);
}
// red
// green
// blue

for (let item of set.entries()) {
console.log(item);
}
// ["red", "red"]
// ["green", "green"]
// ["blue", "blue"]

> set 也可以使用展开运算符  ... new Set([1,3,4,6]) 
## Map
ES6 提供了 Map 数据结构。它类似于对象,也是键值对的集合,但是“键”的范围不限于字符串,各种类型的值(包括对象)都可以当作键。也就是说,Object 结构提供了“字符串—值”的对应,Map 结构提供了“值—值”的对应,是一种更完善的 Hash 结构实现。如果你需要“键值对”的数据结构,Map 比 Object 更合适。
```js
const m = new Map();
const o = {p: 'Hello World'};

m.set(o, 'content')
m.get(o) // "content"

m.has(o) // true
m.delete(o) // true
m.has(o) // false</code></pre>
<ul>
<li><code>map.size</code>: 属性返回 Map 结构的成员总数。</li>
<li><code>map.set(key, value)</code>: set方法设置键名key对应的键值为value,然后返回整个 Map 结构。</li>
<li><code>map.get(key)</code>: get方法读取key对应的键值,如果找不到key,返回undefined。</li>
<li><code>map.has(key)</code>: has方法返回一个布尔值,表示某个键是否在当前 Map 对象之中。</li>
<li><code>map.delete(key)</code>: delete方法删除某个键,返回true。如果删除失败,返回false。</li>
<li><code>map.clear()</code>: clear方法清除所有成员,没有返回值。</li>
<li><code>map.keys()</code>: 返回键名的遍历器。</li>
<li><code>map.values()</code>: 返回键值的遍历器。</li>
<li><code>map.entries()</code>: 返回所有成员的遍历器。</li>
<li>
<p><code>map.forEach()</code>: 遍历 Map 的所有成员。</p>
<pre><code class="language-js">const map = new Map([
['F', 'no'],
['T',  'yes'],
]);</code></pre>
</li>
</ul>
<p>for (let key of map.keys()) {
console.log(key);
}
// "F"
// "T"</p>
<p>for (let value of map.values()) {
console.log(value);
}
// "no"
// "yes"</p>
<p>for (let item of map.entries()) {
console.log(item[0], item[1]);
}
// "F" "no"
// "T" "yes"</p>
<p>// 或者
for (let [key, value] of map.entries()) {
console.log(key, value);
}
// "F" "no"
// "T" "yes"</p>
<p>// 等同于使用map.entries()
for (let [key, value] of map) {
console.log(key, value);
}
// "F" "no"
// "T" "yes"</p>
<p>map.forEach((val,index,map)=>{
console.log(val,index,map)
})
// no F Map(2) {"F" => "no", "T" => "yes"}
//yes T Map(2) {"F" => "no", "T" => "yes"}</p>
<pre><code>## class
ES6 提供了更接近传统语言的写法,引入了 Class(类)这个概念,作为对象的模板。通过class关键字,可以定义类。

新的class写法只是让对象原型的写法更加清晰、更像面向对象编程的语法而已。
```js
class Human {
    constructor(name, age, sex) {
        this.name = name
        this.age = age
        this.sex = sex
    }
    toString() {
        console.log(this.name + " : " + this.age + " : " + this.sex)
    }
}

let h1 = new Human('xiaoMing', 19, 'man')
  • class 关键字表示定义一个类。

  • constructor 构造函数, 在实例化时第一个被调用的函数,成员要在 constructor 构造中定义。

    • 一个类只能有一个构造函数,如果没有定义则会自动生成一个空的 constructor
  • toString 成员方法。成员方法前面不能添加 function 并且成员之间不能使用 , 逗号分隔。

    static 静态成员

  • 静态方法:

    • 类相当于实例的原型,所有在类中定义的方法,都会被实例继承。如果在一个方法前,加上static关键字,就表示该方法不会被实例继承,而是直接通过类来调用,这就称为“静态方法”。
    class Human {
      constructor(name, age, sex) {
          this.name = name
          this.age = age
          this.sex = sex
      }
      // 对象成员
      toString() {
          console.log(this.name + " : " + this.age + " : " + this.sex)
      }
      // 静态方法,不属于对象
      static say(){
          console.log('静态方法')
      }
    }
    // 调用静态方法
    Human.say()

    父类的静态方法,可以被子类继承。Class 内部只有静态方法,没有静态属性。

    私有方法和私有属性

    私有方法和私有属性,是只能在类的内部访问的方法和属性,外部不能访问。ES6 不提供私有方法和私有属性。

extends

Class 可以通过extends关键字实现继承,这比 ES5 的通过修改原型链实现继承,要清晰和方便很多。

// 父类
class Human {
    constructor(name, age, sex) {
        this.name = name
        this.age = age
        this.sex = sex
    }
    // 对象成员
    toString() {
        console.log(this.name + " : " + this.age + " : " + this.sex)
    }
    // 静态方法,不属于对象
    static say(){
        console.log('静态方法')
    }
}
// 继承Human类
class Man extends Human{
    constructor(name,age,job){
        // 调用父类的构造方法
        super(name,age,'man')
        this.job = job
    }
}
  • super 表示父类对象。
  • super() 表示父类构造方法。
  • 子类中如果显示包含 constructor 那么必须在constructor最上方调用 super()父类构造方法。实例化子类时会先实例化父类。

Promise 对象

Promise 是异步编程的一种解决方案,比传统的解决方案——回调函数和事件——更合理和更强大。

ES6 将其写进了语言标准,统一了用法,原生提供了Promise对象。

Promise,简单说就是一个容器,里面保存着某个未来才会结束的事件(通常是一个异步操作)的结果。从语法上说,Promise 是一个对象,从它可以获取异步操作的消息。

Promise 对象三种状态

  1. pending(进行中)

  2. fulfilled(已成功)

  3. rejected(已失败)

    只有异步操作的结果,可以决定当前是哪一种状态,任何其他操作都无法改变这个状态。这也是Promise这个名字的由来,它的英语意思就是“承诺”,表示其他手段无法改变。

    Promise 对象两个特点。

  4. 对象的状态不受外界影响。

  5. 一旦状态改变,就不会再变,任何时候都可以得到这个结果。

Promise 对象创建

const promise = new Promise(function(resolve, reject) {
  // ... some code
  if (/* 异步操作成功 */){
    resolve(value); //触发成功状态
  } else {
    reject(error); //触发失败状态
  }
});
//监听状态发生变化
promise.then(function(value) {
  // success 成功
}, function(error) {
  // failure 失败
});

实例化 Promise 对象时参数为一个回调函数,该回调函数有两个参数分别是 resolvereject

  • resolve(value)函数的作用是,将Promise对象的状态从“未完成”变为“成功”(即从 pending 变为 resolved),在异步操作成功时调用,并将异步操作的结果,作为参数传递出去;
  • reject(value)函数的作用是,将Promise对象的状态从“未完成”变为“失败”(即从 pending 变为 rejected),在异步操作失败时调用,并将异步操作报出的错误,作为参数传递出去。
  • promise.then()Promise实例生成以后,可以用then方法分别指定resolved状态和rejected状态的回调函数。

    • 参数1:成功状态触发该的回调函数
    • 参数2:状态为失败触发的回调函数

    then

    then方法返回的是一个新的Promise实例(注意,不是原来那个Promise实例)。

    //监听状态发生变化
    promise
    .then(function (value) {
      // success 成功
      return 'success'
    }, function (error) {
      // failure 失败
      return 'error'
    })
    // 接收上一个then()的数据
    .then(function(value){
      console.log( value )
      return 'hello'
    })

    then方法可以返回新的promise实例,将会被下一个then所接收,注意then方法将只有一个回调,不再接收第二个回调函数。

catch

  • Promise.prototype.catch方法是.then(null, rejection)或.then(undefined, rejection)的别名,用于指定发生错误时的回调函数。
  • 使用catch方法就可以不再指定then中第二个参数,这样代码会更加易读

    promise
    .then(function (value) {
        console.log( value )
    })
    .catch(function(err){
        console.log( err )
    })

Promise.all(iterable)

Promise.all方法常被用于处理多个promise对象的状态集合。这个方法返回一个新的promise对象,该promise对象在iterable参数对象里所有的promise对象都成功的时候才会触发成功,一旦有任何一个iterable里面的promise对象失败则立即触发该promise对象的失败。这个新的promise对象在触发成功状态以后,会把一个包含iterable里所有promise返回值的数组作为成功回调的返回值,顺序跟iterable的顺序保持一致;如果这个新的promise对象触发了失败状态,它会把iterable里第一个触发失败的promise对象的错误信息作为它的失败错误信息。

function pTest(num) {
    return new Promise(function (resolve, reject) {
        if (10 > num) {
            resolve('成功')
        } else {
            reject('失败')
        }
    })
}
let nums = [1, 3, 3, 9]
//    获取 promise 实例集合
let ps = nums.map((item) => {
    return pTest(item)
})

/*所有实例状态成功最终状态才成功,否则就失败*/
Promise.all(ps)
    // ps 实例中所有状态都是 resolve时才触发then方法
    .then(res => {
        console.log(res)
    })
    // ps实例中只要有一个状态为 reject 就会触发 catch 方法
    .catch(err => {
        console.log(err)
    })

Promise.race(iterable)

Promise.race 处理多个promise对象的状态集合时任意一个子promise成功或失败时都会返回该promise的状态 .

function pTest(s) {
    return new Promise(function (resolve, reject) {
        if (0 == s) {
            reject(new Error('时间出错'))
        } else {
            setTimeout(() => {
                resolve(s)
            }, s);
        }

    })
}
/*会获取到第一个改变的状态 */
Promise.race([pTest(2), pTest(10), pTest(5)])
    .then(res => {
        console.log(res)
    })
    .catch(err => {
        console.log(err)
    })

async

ES2017 标准引入了 async 函数,使得异步操作变得更加方便。async 用来定义一个返回 AsyncFunction 对象的异步函数。异步函数是指通过事件循环异步执行的函数,它会通过一个隐式的 Promise 返回其结果。

基本语法

async function f(num) {
    if(10>num){
        // 抛出错误 会被 .catch()方法接收到
        throw new Error('数据错误')
    }
    // return 返回的数据 .then() 方法可以接收
    return 'hello world';
}

f(11).then(res=>{
    console.log( res )
}).catch(err=>{
    console.error( err )
})
  1. async 关键字声明异步函数。
  2. async 函数返回 Promise 对象。
  3. async 函数中 使用 throw 触发 reject (错误)状态,return 触发 resolve (成功)状态。

await

await 操作符用于等待一个Promise 对象。它只能在async异步函数 中使用。

async function f() {
    // await命令只能用在async函数之中
    // await命令会等待 异步操作执行完毕后再执行后续的操作
    return await new Promise((resolve, reject) => {
        setTimeout(() => {
            resolve('success')
        }, 1000);
    })
    // 去掉上面的return 就会在await等待结束后返回 'ok'
    return 'ok'

}

f().then(res => {
    console.log(res)
}).catch(err => {
    console.log(err)
})
  1. await关键字只在异步函数内有效。如果你在async异步函数外使用它,会抛出语法错误。

  2. await 只能等待的基于promise的异步操作。

  3. await 可以有多个。

Module

模块(module)体系,是将一个大程序拆分成互相依赖的小文件,再用简单的方法拼装起来

模块功能主要由两个命令构成:export和import。export命令用于规定模块的对外接口,import命令用于输入其他模块提供的功能。

  • <script> 标签中要使用模块功能需要设置 type="module"

    module示例:

  • m1.js

    //导出模块中数据
    export default { name: "小明" };
  • main.js

    //导入模块中数据
    import obj from './m1.js';
    console.log(obj)
  • index.html