javascript 速记-ES6

2020-01-18  本文已影响0人  9吧和9说9话

ES6规范

let & const ☆☆☆☆☆

  1. let 是对局部变量的补充设计
  2. 并且向着大型应用语言的方向去要求,
    (1)没有类似var的变量声明提升(可以在声明之前引用这个变量),必须先声明,后使用
// 被称为暂时性死区 还是遵循 先声明后引用的条件
var tmp = 123;

if (true) {
tmp = 'abc'; // ReferenceError
let tmp;
}

(2)不允许多次声明
(3)在if swtich for等代码块中也起作用

  1. const 弥补了javascript中没有常量定义的设计。

字符串 ☆☆☆☆☆

  1. 字符串模板语法 ,目前已经非常常用了
  2. 字符串新增方法
    //实例方法
    xxx.trimStart // 删除掉字符串头部的空格
    xxx.trimEnd // 删除掉字符串尾部的空格
    xxx.padStart
    xxx.padEnd(maxLength, fillingString) //第一个参数指定 最终的字符串的最大长度  第二个参数 指定重复添加的字符串 
    xxx.includes // 检测字符串中是否包含 指定的字符串
    xxx.startsWith 
    xxx.endsWith
    xxx.repeat // 重复当前字符串 n次
    

解构赋值: ☆☆☆☆☆

  1. 数组解构赋值
  2. 对象解构赋值
  3. 函数参数的解构
function say({name, age}) {
   console.log(`${name}, ${age}`)
}
let o = {name: "ngnice", age: 10}
say(o);

数值扩展 ☆☆

  1. 二进制和八进制的表示法
    (1)二进制 以 0B 或者 0b开头
0b101 === 5 // true

(2)以0o 开头表示八进制

0o101 === 65 // true
  1. Number增加静态方法
// 是否有限, 对于非数字和Infinity 都返回false
Number.isFinite(1) // true
Number.isFinite(Infinity) // false
Number.isFinite("1") //false

// Number.isNaN  判断是否是一个非数字
// 如果参数类型不是NaN,Number.isNaN一律返回false。
Number.isNaN(NaN) // true
Number.isNaN(9)// false
Number.isNaN(null) //false

// 将parseInt 和 parseFloat 移植到了Number类上
Number.parseInt(90.2) //90
  1. Math对象新增了一些数学函数方法
Math.trunc() 

正则扩展 ☆

数组扩展 ☆☆☆☆☆

  1. 扩展运算符 更像 rest的逆运算
const arr = [1,3,4,4];
console.log(...arr);
// 可以用于重新组合数组
let newArr = [1,22,...arr];
  1. Array.from将其他类数组对象转换成数组
Array.from(arguments)
Array.from(document.querySelectorAll('xxx'))
Array.from(new Set([1,2,3,34]))

注意点:
(1)和扩展运算符区别:Array.from 能将任何类似数组的结构转换成数组(即用于length属性)
(2)扩展运算符本质上是调用被转换对象的iterator接口,所以只有部署了这个接口的对象才能使用。

  1. Array.of 将一组值转换成数组
Array.of(1,2,3,43) // [1, 2, 3, 43]
  1. 其他的一些方法
arr.find(callback) // 返回符合条件的第一个元素
arr.findIndex  // 类似 arr.indexOf

xxx.includes()// 是否包含某个值
xxx.keys xxx.values  xxx.entries
arr.fill(value, start, end) // 使用指定的值 填充指定的范围,范围不包含结束位置
  1. flat 拍平数组 参数拍平多少层:1,2,3, ...Infinity
[1, [2, [3]]].flat(Infinity) //[1, 2, 3]

对象扩展 ☆☆☆☆☆

  1. 属性简写和方法简写
// es6 之前
var name = "ngnce";
const obj = {
  name: name,
  log: function () {
      console.log(this.name)
  }
}

// es6之后
var name = "ngnce";
const obj = {
  name,
  log () {
      console.log(this.name)
  }
}
  1. 属性名表达式
// 对象属性声明的两种方式
var obj = {};
obj.name = "aaa";
// or
var property = "name"
obj["last"+property] = "ngnice";

es6之前,针对对象字面量这种声明方式 无法使用第二种,现在我们就可以了

var obj = {
  // 属性可以使用第二种方法声明
  [["last"+property]: "ngnice",
  log() {
  }
}
  1. 扩展运算符,解构赋值
  2. ES2020 新增的链判断运算符,类似dart中的语法
var a = obj?.other?.age||'default';
  1. ===== 以及 Object.is
    (1)== 非严格相等 判断两个值相等: 会进行类型自动转换
    (2)=== 严格相等: 不会进行类型转换,对于基础类型只判断其值是否相等,对于引用类型 判断其内存地址是否相等,了NaN=== NaN 不成立
    (3)Object.js 在=== 基础上修正了两点:
    Object.is(NaN, NaN)//true
    Object.is(+0, -0)//false
    
  2. Object.assign 对象合并
    (1) 浅拷贝
    (2)使用对象的扩展运算符 能达到类似的效果
var a = {name: "ngnice"};
var b = {age: 10}
var c =  Object.assign({}, a,b);
// or
var c =  {...a, ...b}

函数扩展 ☆☆☆☆☆

  1. 函数参数默认值
// es6 之前
function log(msg) {
  msg = msg || "默认值";
  console.log(msg)
}
// es6 之后 更加清晰 简洁
function log(msg = "默认值") {
  console.log(msg)
}

注意点:
(1)参数变量是默认声明的,所以不能用let或const再次声明。
(2)参数默认值是惰性求值的。
(3)参数默认值 一般设置 最后一个参数 否则 无法省略

  1. rest参数
    可以用来替换 arguments , 而且 arguments是一个类数组对象,不能直接使用数组的实例方法, 但是 rest参数就是一个数组
function add(...values) {
  let sum = 0;
  for (var val of values) {
    sum += val;
  }
  return sum;
}
add(1,2,3)// 6
  1. 箭头函数
var log = (msg="aaa")=>console.log(msg);

箭头函数有几个使用注意点。
(1)函数体内的this对象,就是定义时所在的对象,而不是使用时所在的对象。
(2)不可以当作构造函数,也就是说,不可以使用new命令,否则会抛出一个错误。
(3)不可以使用arguments对象,该对象在函数体内不存在。如果要用,可以用 rest 参数代替。
(4)不可以使用yield命令,因此箭头函数不能用作 Generator 函数。

错误使用

const cat = {
  lives: 9,
  jumps: () => {
    this.lives--;
  }
}
cat.jumps();
  1. 函数的name属性 标准化
    注意点:
    (1)bind返回的函数,name属性值会加上bound前缀。
function foo() {};
foo.bind({}).name // "bound foo"

(function(){}).bind({}).name // "bound "
  1. 尾调用优化

新数据结构 Set Map ☆☆☆☆☆

  1. Set 集合,是一种值不重复的数组

  2. WeakSet
    (1)只能存储引用类型的值 不能存储基础数据类型
    (2)弱引用 无法遍历 可以防止内存泄露

  3. MapObject类似 区别在于 Map的键 可以是对象类型

  4. WeakMap 的键只能是对象类型,并且不计入垃圾回收机制

类 ☆☆☆☆☆

  1. 定义对象
    es6之前一直使用构造函数来定义类, 看起来和普通的函数无任何的区别
function Person() {
}

(1) 必须先定义 后使用,不存在声明提升

es6提供了class关键字来定义一个类,和传统的OOP 语言 更加接近。

  1. 属性和方法声明
    (1)属性声明
 class foo {
  // 写在类中的顶层
  bar = 'hello';
  baz = 'world';

  constructor(age) {
    // 可以放到这里
    this.name = "ngnice";
    this.age = age;
  }
}

(2)静态方法 static,静态属性 Person.staticProp, 新提案中提出用static关键字
(3)私有属性和方法:使用_命名方式(没有代码实际效果,只是约定编码规则),使用Symble实现

  1. 构造函数
    (1)必须和new一起使用
  2. super关键字
    (1)super函数 一般用于子类继承父类,在子类中的构造函数中调用;ES6 规定 必须在子类中的构造方法中调用super方法 否则就会在new的时候 报错。
    (2)在子类的方法中调用 指向父类的原型对象, 所以无法直接通过super.prop 访问父类的属性,调用的父类方法中的this 指向当前子类实例对象。
class N {
    name = "ngnice";
    age = 10;
    say() {
        console.log(this.name);
    }
}
class NN extends N {
   name = "new name";
   log() {
     console.log(super.age)
   }
   log1() {
    super.say();
  }
}
var n = new NN();
n.log();// 输出undefined
// log1中的this 指向当前子类实例对象 所以输出的是 new name
n.log1();// 输出 new name  

(3)在子类的静态方法中调用 指向父类对象

  1. __proto__属性和 prototype属性
    (1)对象的__proto__属性 指向 构造函数的原型对象(初始化这个对象的构造函数的原型对象)
class Animal{
}
let cat = new Animal();
cat.__proto__ === Animal.prototype //true

// 普通字面量
var a = 2;
a.__proto__ == Number.prototype

(2)实例对象(通过new 关键字创建的对象)没有prototype属性, 只有函数 或者是 通过class关键字定义的类才有这个属性 指向当前类的原型对象。
(3)可以通过instanceof来判断是否是某个类的实例对象

总结来说:
针对实例对象来说(不论是通过new 命令创建的还是通过字面量创建)__proto__都是指向构造类的原型对象prototype
对于类来说 如果是通过extends关键字实现继承的 子类的 __proto__指向的是父类,如果是通过ES5方式实现继承的话,还是指向 构造函数的原型 Function.prototype

  1. ES6中的继承
    (1) ES6中的继承 通过关键字extends实现。
    (2)和ES5中的继承对比,ES5中的继承本质上是先创建子类实例对象this,然后将父类的属性和方法添加到新的子类上
function People(name, age) {
   this.age = age;
   this.name = name;
}
People.prototype.say  =function () {
  console.log(`${this.name} is ${this.age}`);
}

// 定义子类 继承 父类
function Xiaoming(name, age, sex) {
   // this 已经存在
   this.sex = 1;
  // 实例属性通过下面的方法 添加到子类实例对象上
   People.apply(this, [name, age]);
}
// 通过原型拷贝的方式 给子类的原型上添加父类的方法 实现继承
Xiaoming.prototype = Object.create(People.prototype)
// 对子类对象的构造函数属性进行修正
Xiaoming.prototype.constructor = Xiaoming

可以参考:Object.create实现继承

es6中实现继承就比较简单了(代码摘自阮一峰老师的教程)

class Point {
  constructor(x, y) {
    this.x = x;
    this.y = y;
  }
}

class ColorPoint extends Point {
  // 子类中如果显式的声明 constructor 构造函数 
  //必须在构造函数中添加 super调用,并且在此调用之前无法获取到`this`的引用。
  constructor(x, y, color) {
    this.color = color; // ReferenceError
    super(x, y);
    this.color = color; // 正确
  }
}

(3)通过Object.getPrototype可以判断是否继承了另外一个类
对于ES6 class关键字声明的类

Object.getPrototype(ColorPoint) 

对于ES5语法 声明的类 需要判断的是其prototype

Object.getPrototype(Xiaoming.prototype)=== People.prototype 

本质上是 原型对象上的__proto__指向的原型对象

ColorPoint.__proto__ === Point
Xiaoming.prototype.__proto__ === People.prototype 

模块 ☆☆☆☆☆

模块化的意义: 针对中大型编程中而言,我们实际上有两个述求,
(1) 能使用第三方模块提供通用的功能实现(不需要重复造轮子)可能有同学说重复造轮子难道不好吗,如果针对业务需求开发,你会发现如果通用的功能模块都有优秀的第三方实现,我们只需要开发业务逻辑部分的开发是多么的幸福;
(2) 则是针对业务逻辑通用代码或者是没有开源的第三方模块,需要我们自己去实现,在组织我们大量的业务逻辑代码的时候,模块化的机制是多么的重要,方便协作的同事和自己后续维护。

(1)封装、解耦、分治
(2)防止全局污染
(3)代码组织方式

总结
到最后还是能总结到软件编程中的两个永恒的话题:效率和质量。

  1. javascript中的模块化之路
    (1)commonjs
    (2)amd/cmd
    (3)es6中的模块化

ES6的模块 和 commonjs模块
(1)ES6 模块是引用
(2)commonjs 模块是拷贝

promise ☆☆☆☆☆

前端异步编程的解决方案之一,改写了callback模式

实现自己的promise

Iterator和 for of

遍历器概念

javascript中目前存在的数据集合 Set Map Array Object

  1. 为了给数据集合提供统一的访问接口,ES6提供了Iterator接口,和for of
  2. ES6规定Iterator接口部署在数据集合的Symbol.iterator属性上,Iterator是一个函数 返回一个对象,包含一个next的的方法,用来移动内部指针,遍历数据集合。
  3. ES6中的有些数据结构原生部署了Iterator接口
Array
Map
Set
String
TypedArray
函数的 arguments 对象
NodeList 对象

以数组做例子:

var arr = [1,2,2];
var iterator = arr[Symbol.iterator]()
iterator.next();// {value: 1, done: false}
iterator.next();//{value: 2, done: false}
iterator.next();//{value: 2, done: false}
iterator.next();//{value: undefined, done: false}
  1. 有些场合默认会调用Iterator接口
    (1)解构赋值
    (2)扩展运算符
    (3)yield* 跟上部署了遍历器接口的数据结构
    (4)其他一些应用
for...of
Array.from()
Map(), Set(), WeakMap(), WeakSet()(比如new Map([['a',1],['b',2]]))
Promise.all()
Promise.race()
  1. Iterator接口除了必须设置实现的next方法,还可以部署return throw方法
var obj = {
   num: 10,
   [Symbol.iterator]() {
       return {
            next() {
                if(obj.num -- == 0) {
                    return {done: true, value: obj.num}
                } else {
                    return {done: false, value: obj.num}
                }
                
            },
            return() {
                // logic code
                console.log('return function')
                // 返回值 必须是一个对象
                return {
                    done: true,
                    value: "ngnice"
                }
            }
       }
   }

}
// break可以触发 return
for(let a of obj) {
    console.log(a);
    if(a < 4) {
        break;
    }
}
// throw error也可
for(let a of obj) {
    console.log(a);
    if(a < 4) {
        throw  new Error('ngnice');
    }
}

for of 就是用来消费部署了Iterator接口的数据集合。

var set = new Set([1,2,3,4]);
for( let item of set ) {
  console.log(item)
}
// 注意map
var m = new Map()
m.set('name', 'ngnice')
m.set('age', 10)

for(let item of m) {
    console.log(item)
}
// ["name", "ngnice"]
//  ["age", 10]

generator ☆☆☆☆☆

generator 函数

  1. generator书写上和普通函数不同的地方就是 函数名和function关键字之间需要添加一个*号, 并且函数体内部可以通过 yield定义多个状态(或者是任务)。
  2. 在执行效果上,和普通普通函数不同的地方就是可以暂停当前函数的执行。
  3. ES6中提供解决javascript中异步编程解决方案的方案之一。

示例代码:

function *gen(x) {
  var a = yield x+1;
  yield a+x;
  return 11;
}
// 调用之后 返回的是一个部署了iterator 接口的对象 而不是函数的返回值
var g = gen(10);
// 需要调用遍历器的next方法
g.next();// {value: 11, done: false}
g.next(10);// {value: 20, done: false}
g.next(); // {value: 11, done: true}
g.next(); // {value: undefined, done: true}

注意点:

  1. generator函数执行之后 不会返回函数的返回值,而是返回一个部署了可迭代协议的对象,因此可以使用for of遍历。
  2. 通过调用next方法执行yield后面的表达式,而yield并不会有返回值,或者说是默认返回undefined,但是可以通过 next方法传入参数,将作为上一个yield表达式的返回值。
  3. 每次执行next方法,都会依次执行yield后面的表达式,并且暂停在哪里,直到下一个next的调用,
  4. 如果后面没有yield语句,就将函数的返回值作为value值 返回 done属性为true.
  5. yield命令只能使用在generator方法中。

generator的异常处理

我们在进行异常捕获的时候 使用try catch语句块,但是只能捕获try语句块中的代码异常。
但是 Generator因为它可以暂停执行,交出控制权,所以允许我们捕获代码块之外的异常。

function *gen() {
  try {
    yield a;
    console.log('10');
    yield 11;
    console.log('11')
  } catch(err) {
    console.log(err);
    
  }
}

var g = gen();
g.next();
g.throw('outer error');

Generatorreturn方法

类似普通函数的return 作用,它可以结束Generator的执行,提前将done属性改成 true,如果return提供了入参的话,将作为value值。
如果存在try finally则会直接跳到finally中执行完成。

function *gen() {
  yield "ngnice";
  yield console.log("a");
  try {
    console.log(yield 'nice');
  } finally{
    console.log('finally');  
    yield 1;
    yield 2;
  }
}

var g = gen();
g.next();
g.return("return value");

对比:

function *gen() {
  yield "ngnice";
  yield console.log("a");
  try {
    console.log(yield 'nice');
  } finally{
  // 如果finally代码块中还存在yield语句 将不会结束
    console.log('finally');  
    yield 1;
    yield 2;
  }
}

var g = gen();
g.next();
g.next();
g.next();
g.return("return value");

yield * 表达式 可以获取generator函数生成对象的内部值。

function* gena () {
  yield 1;
  yield 0;
  yield 2;
  yield 3;
}

function *genB() {
  yield 'a';
  yield* gena();
  yield 'last';

}
var g = genB()
g.next();
g.next();
g.next();

generator的应用

异步操作的同步化表达

  1. javascriptthunk函数将多参数函数转换成 接受一个回调函数的参数形态。
  2. 通过在回调函数中 递归的调用generator生成的对象的next方法实现了 自动化运行。
  3. 那么出了回调函数这种方式,也可以通过 promise的这种方案实现(本质上还是回调实现)
  4. 通过next方法 可以将数据从回调中 传递出来

代码具体实现, 我们可以参考TJ大神的co函数库

async

一句话 就是 async 就是 generator在异步编程应用的语法糖

proxy reflect ☆☆☆☆

上一篇 下一篇

猜你喜欢

热点阅读