个人前端知识梳理-ES6篇

本篇记录部分es6相关内容和我的理解。javascript

======================相关文章和开源库=======================前端

系列文章vue

1.前端知识梳理-HTML,CSS篇
java

2.前端知识梳理-ES5篇
node

3.前端知识梳理-ES6篇
es6

4.前端知识梳理-VUE篇
sql

我的维护的开源组件库编程

1.bin-ui,一个基于vue的pc端组件库json

2.树形组织结构组件
数组

3.bin-admin ,基于bin-ui的后台管理系统集成方案

4.bin-data ,基于bin-ui和echarts的数据可视化框架

5.其他生态连接

========================================================

一、let,const

var 会变量提高;let 声明的变量只在它所在的代码块有效;

它们是继 var 以后, 新的变量定义方法。与 let 相比,const 更容易被理解,用于定义常量,即不可变量

二、块级做用域

没有块级做用域。这个问题之因此为人所熟知,是由于它引起了诸如历遍监听事件须要使用闭包解决等问题。

<button>一</button>
<button>二</button>
<button>三</button>
<button>四</button>
<div id="output"></div>
<script>
  var buttons = document.querySelectorAll('button')
  var output = document.querySelector('#output')
  for (var i = 0; i < buttons.length; i++) {
    buttons[i].addEventListener('click', function() {
      output.innerText = buttons[i].innerText
    })
  }
</script>
复制代码

从直观的角度看这段代码并无语义上的错误,可是当咱们点击任意一个按钮时,就会报出这样的错误信息:

出现这个错误的缘由是由于 buttons[i] 不存在,即为 undefined。

为何会出现按钮不存在结果呢?经过排查,咱们能够发现,每次咱们点击按钮时,事件监听回调函数中获得的变量 i 都会等于 buttons.length, 也就是这里的 4。而 buttons[4] 偏偏不存在,因此致使了错误的发生。

再而致使 i 获得的值都是 buttons.length 的缘由就是由于 JavaScript 中没有块级做用域,而使对 i 的变量引用(Reference)一直保持在上一层做用域(循环语句所在层)上, 而当循环结束时 i 则正好是buttons.length。

而在 ES6 中,咱们只需作出一个小小的改动,即可以解决该问题(假设所使用的浏览器已经支持所须要的特性):

for (/* var */ let i = 0; i < buttons.length; i++) {
  // ...
}
复制代码

经过把 for 语句中对计数器 i 的定义语句从 var 换成 let,便可。由于 let 语句会使该变量处于一个块级做用域中, 从而让事件监听回调函数中的变量引用获得保持。咱们不妨看看改进后的代码通过 babel 的编译会变成什么样子:

// ...
var _loop = function (i) {
  buttons[i].addEventListener('click', function () {
    output.innerText = buttons[i].innerText
  })
}

for (var i = 0; i < buttons.length; i++) {
  _loop(i)
}
// ...
复制代码

三、箭头函数(Arrow Function)

继 let 和 const 以后,箭头函数就是使用率最高的新特性了。

箭头函数,顾名思义即是使用箭头(=>)进行定义的函数,属于匿名函数(Lambda)一类。

箭头函数有好几种使用语法:

// 1.means return `foo + ' world'`
foo => foo + ' world' 
// 2.  
(foo, bar) => foo + bar
// 3. 
foo => {
  return foo + ' world'
}
// 4.
(foo, bar) => {
  return foo + bar
}
复制代码

箭头函数与上下文绑定

事实上,箭头函数在 ES2015 标准中,并不仅是做为一种新的语法出现。用于对函数内部的上下文 (this)绑定为定义函数所在的做用域的上下文。

let obj = {
  hello: 'world',
  foo() {
    let bar = () => {
      return this.hello
    }
    return bar
  }
}
window.hello = 'ES6'
window.bar = obj.foo()
window.bar() //=> 'world'
复制代码

上面代码中的 obj.foo 等价于:

// ...
foo(){
  let bar = (function() {
    return this.hello
  }).bind(this)

  return bar
}
// ...
复制代码

四、模板字符串

它的出现可让很是多的字符串使用变得尤其轻松。

模板字符串要求使用 ` 代替本来的单/双引号来包裹字符串内容。它有两大特色:

  • 支持变量注入
  • 支持换行

支持变量注入

模板字符串之因此称之为“模板”,就是由于它容许咱们在字符串中引用外部变量,而不须要像以往须要不断地相加、相加、相加……

let name = 'Will Wen Gunn'
let title = 'Founder'
let company = 'LikMoon Creation'

let greet = `Hi, I'm ${name}, I am the ${title} at ${company}` 复制代码

支持换行

在 Node.js 中,若是咱们没有支持换行的模板字符串,若须要拼接一条SQL,则颇有多是这样的:

var sql =
  "SELECT * FROM Users " +
  "WHERE FirstName='Mike' " +
  "LIMIT 5;"
复制代码

或者是这样的:

var sql = [
  "SELECT * FROM Users",
  "WHERE FirstName='Mike'",
  "LIMIT 5;"
].join(' ')
复制代码

不管是上面的哪种,都会让咱们感到很不爽。但若使用模板字符串,仿佛打开了新世界的大门~

let sql = ` SELECT * FROM Users WHERE FirstName='Mike' LIMIT 5; `
复制代码

五、对象字面量扩展语法

方法属性省略 function

这个新特性能够算是比较有用但并非很显眼的一个。

let obj = {
  // before
  foo: function() {
    return 'foo'
  },
  // after
  bar() {
    return 'bar'
  }
}
复制代码

六、表达式解构

来了来了来了,至关有用的一个特性。

// Matching with object
function search(query) {
  // ...
  // let users = [ ... ]
  // let posts = [ ... ]
  // ...
  return {
    users: users,
    posts: posts
  }
}
let { users, posts } = search('iwillwen')
// Matching with array
let [ x, y ] = [ 1, 2 ]
// missing one
[ x, ,y ] = [ 1, 2, 3 ]
function g({name: x}) {
  console.log(x)
}
g({name: 5})
复制代码

七、函数参数表达、传参

这个特性有很是高的使用频率,一个简单的语法糖解决了从前须要一两行代码才能实现的功能。

默认参数值

这个特性在类库开发中至关有用,好比实现一些可选参数:

import fs from 'fs'
import readline from 'readline'
import path from 'path'
function readLineInFile(filename, callback = noop, complete = noop) {
  let rl = readline.createInterface({
    input: fs.createReadStream(path.resolve(__dirname, filename))
  })
  rl.on('line', line => {
    //... do something with the current line
    callback(line)
  })
  rl.on('close', complete)
  return rl
}
function noop() { return false }
readLineInFile('big_file.txt', line => {
  // ...
})
复制代码

后续参数

咱们知道,函数的 call 和 apply 在使用上的最大差别即是一个在首参数后传入各个参数,一个是在首参数后传入一个包含全部参数的数组。 若是咱们在实现某些函数或方法时,也但愿实现像 call 同样的使用方法,在 ES2015 以前,咱们可能须要这样作:

function fetchSomethings() {
  var args = [].slice.apply(arguments)
  // ...
}
function doSomeOthers(name) {
  var args = [].slice.apply(arguments, 1)
  // ...
}
复制代码

而在 ES2015 中,咱们能够很简单的使用 … 语法糖来实现:

function fetchSomethings(...args) {
  // ...
}
function doSomeOthers(name, ...args) {
  // ...
}
复制代码

要注意的是,...args 后不可再添加

虽然从语言角度看,arguments ...args 是能够同时使用 ,但有一个特殊状况则不可:arguments 在箭头函数中, 会跟随上下文绑定到上层,因此在不肯定上下文绑定结果的状况下,尽量不要再箭头函数中再使用 arguments,而使用 ...args

虽然 ECMA 委员会和各种编译器都无强制性要求用 ...args 代替 arguments,但从实践经验看来,...args 确实能够在绝大部份场景下能够代替 arguments 使用, 除非你有很特殊的场景须要使用到 arguments.calleearguments.caller。因此我推荐都使用 ...args 而非 arguments

解构传参

在 ES2015 中,... 语法还有另一个功能:无上下文绑定的 apply。什么意思?看看代码你就知道了。

function sum(...args) {
  return args.map(Number)
    .reduce((a, b) => a + b)
}
console.log(sum(...[1, 2, 3])) //=> 6
复制代码

八、新的数据结构

ES2015 以前,JavaScript 中有哪些基本的数据结构。

  • String 字符串
  • Number 数字(包含整型和浮点型)
  • Boolean 布尔值
  • Object 对象
  • Array 数组

其中又分为值类型引用类型,Array 实际上是 Object 的一种子类。

Set 和 WeakSet

在 ES2015 中,ECMA 委员会为 ECMAScript 增添了集(Set)和“弱”集(WeakSet)。它们都具备元素惟一性,若添加了已存在的元素,会被自动忽略。

let s = new Set()
s.add('hello').add('world').add('hello')
console.log(s.size) //=> 2
console.log(s.has('hello')) //=> true
复制代码

在实际开发中,咱们有不少须要用到集的场景,如搜索、索引创建等。

WeakSet 在 JavaScript 底层做出调整(在非降级兼容的状况下),检查元素的变量引用状况。若是元素的引用已被所有解除,则该元素就会被删除, 以节省内存空间。这意味著没法直接加入数字或者字符串。另外 WeakSet 对元素有严格要求,必须是 Object,固然了,你也能够用 new String('...') 等形式处理元素。

let weaks = new WeakSet()
weaks.add("hello") //=> Error
weaks.add(3.1415) //=> Error

let foo = new String("bar")
let pi = new Number(3.1415)
weaks.add(foo)
weaks.add(pi)
weaks.has(foo) //=> true
foo = null
weaks.has(foo) //=> false
复制代码

Map 和 WeakMap

从数据结构的角度来讲,映射(Map)跟本来的 Object 很是类似,都是 Key/Value 的键值对结构。 可是 Object 有一个让人很是不爽的限制:key 必须是字符串或数字。在通常状况下,咱们并不会赶上这一限制,但若咱们须要创建一个对象映射表时,这一限制显得尤其棘手。

而 Map 则解决了这一问题,可使用任何对象做为其 key,这能够实现从前不能实现或难以实现的功能,如在项目逻辑层实现数据索引等。

map.set(object, 'hello')
map.set('hello', 'world')
map.has(object) //=> true
map.get(object) //=> hello
复制代码

而 WeakMap 和 WeakSet 很相似,只不过 WeakMap 的键和值都会检查变量引用,只要其一的引用全被解除,该键值对就会被删除。

let weakm = new WeakMap()
let keyObject = { id: 1 }
let valObject = { score: 100 }

weakm.set(keyObject, valObject)
weakm.get(keyObject) //=> { score: 100 }
keyObject = null
weakm.has(keyObject) //=> false
复制代码

九、类(Classes)

类,做为自 JavaScript 诞生以来最大的痛点之一,终于在 ES2015 中获得了官方的妥协,“实现”了 ECMAScript 中的标准类机制。 为何是带有双引号的呢?由于咱们不难发现这样一个现象:

// $ node
> class Foo {}
// [Function: Foo]
复制代码

回想一下在 ES2015 之前的时代中,咱们是怎么在 JavaScript 中实现类的?

function Foo() {}
var foo = new Foo()
复制代码

是的,ES6 中的类只是一种语法糖,用于定义原型(Prototype)的。

语法

与大多数人所期待的同样,ES2015 所带来的类语法确实与不少 C 语言家族的语法类似。

class Person {
  constructor(name, gender, age) {
    this.name = name
    this.gender = gender
    this.age = age
  }
  isAdult() {
    return this.age >= 18
  }
}
let me = new Person('iwillwen', 'man', 19)
console.log(me.isAdult()) //=> true
复制代码

与 JavaScript 中的对象字面量不同的是,类的属性后不能加逗号,而对象字面量则必需要加逗号。

然而,让人很不爽的是,ES2015 中对类的定义依然不支持默认属性的语法:

// 理想型 可是没实现
class Person {
  name: String
  gender = 'man'
  // .
}复制代码

继承

ES2015 的类继承总算是为 JavaScript 类继承之争抛下了一根定海神针了。

class Animal {
  yell() {
    console.log('yell')
  }
}
class Person extends Animal {
  constructor(name, gender, age) {
    super() // must call `super` before using `this` if this class has a superclass

    this.name = name
    this.gender = gender
    this.age = age
  }

  isAdult() {
    return this.age >= 18
  }
}

class Man extends Person {
  constructor(name, age) {
    super(name, 'man', age)
  }
}
let me = new Man('iwillwen', 19)
console.log(me.isAdult()) //=> true
me.yell()
复制代码

一样的,继承的语法跟许多语言中的很相似,ES2015 中若要是一个类继承于另一个类而做为其子类,只须要在子类的名字后面加上 extends {SuperClass} 便可。

ES2015 中的类机制支持 static 类型的方法定义,好比说 Man 是一个类,而我但愿为其定义一个 Man.isMan() 方法以用于类型检查,咱们能够这样作:

class Man {
  // ...
  static isMan(obj) {
    return obj instanceof Man
  }
}
let me = new Man()
console.log(Man.isMan(me)) //=> true
复制代码

就目前来讲,ES2015 的类机制依然很鸡肋:

1.不支持私有属性(private)

2.不支持前置属性定义,但可用 get 语句和 set 语句实现

3.不支持多重继承

4.没有相似于协议(Protocl)或接口(Interface)等的概念

十、原生的模块化

在 JavaScript 的发展历史上,曾出现过多种模块加载库,如 RequireJS、SeaJS、FIS 等,而由它们衍生出来的 JavaScript 模块化标准有 CommonJS、AMD、CMD 和 UMD 等。

其中最为典型的是 Node.js 所遵循的 CommonJS 和 RequireJS 的 AMD。

基本用法

import name from "module-name"
import * as name from "module-name"
import { member } from "module-name"
import { member as alias } from "module-name"
import { member1 , member2 } from "module-name"
import { member1 , member2 as alias2 , [...] } from "module-name"
import defaultMember, { member [ , [...] ] } from "module-name"
import defaultMember, * as alias from "module-name"
复制代码

如上所示,ES2015 中有不少种模块引入方式,咱们能够根据实际须要选择一种使用。

全局引入

全局引入是最基本的引入方式,这跟 CommonJS、AMD 等模块化标准并没有两样,都是把目标模块的全部暴露的接口引入到一个命名空间中。

import name from 'module-name'
import * as name from 'module-name'
复制代码

这跟 Node.js 所用的 CommonJS 相似: var name = require('module-name')

局部引入

与 CommonJS 等标准不一样的是,ES2015 的模块引入机制支持引入模块的部份暴露接口,这在大型的组件开发中显得尤其方便,如 React 的组件引入即是使用了该特性。

import { A, B, C } from 'module-name'
A()
B()
C()复制代码

接口暴露

ES2015 的接口暴露方式比 CommonJS 等标准都要丰富和健壮,可见 ECMA 委员会对这一部份的重视程度之高。

ES2015 的接口暴露有几种用法:

暴露单独接口

// module.js
export function method() { /* ... */ }复制代码

基本的 export 语句能够调用屡次,单独使用可暴露一个对象到该模块外。

暴露复盖模块

若须要实现像 CommonJS 中的 module.exports = {} 以覆盖整个模块的暴露对象,则须要在 export 语句后加上 default。

// module.js
export default {
  method1,
  method2
}
// main.js
import M from './module'
M.method1()复制代码

十一、Promise

Promise的含义

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

所谓Promise,简单说就是一个容器,里面保存着某个将来才会结束的事件 (一般是一个异步操做)的结果。从语法上说,Promise是一个对象,从它能够获取异步操做的消息。

Promise对象有如下2个特色:

1.对象的状态不受外界影响。Promise对象表明一个异步操做,有三种状态:Pending(进行中)、 Resolved(已完成)和Rejected(已失败)。只有异步操做的结果,能够决定当前是哪种状态, 任何其余操做都没法改变这个状态。这也是Promise这个名字的由来,它的英语意思就是“承诺”, 表示其余手段没法改变。

2.一旦状态改变,就不会再变,任什么时候候均可以获得这个结果。Promise对象的状态改变,只有两种可能: 从Pending变为Resolved;从Pending变为Rejected。只要这两种状况发生,状态就凝固了,不会再变了, 会一直保持这个结果。就算改变已经发生了,你再对Promise对象田静回调函数,也会当即获得这个结果。 这与事件(Event)彻底不一样,事件的特色是,若是你错过了它,再去监听,是得不到结果的。

有了Promise对象,就能够把异步操做以同步操做的流程表达出来,避免了层层嵌套的回调函数。此外, Promise对象提供了统一的接口,使得控制异步操做更加容易。

基本用法

ES6规定,Promise对象是一个构造函数,用来生成Promise实例

var promise = new Promise(function(resolve,reject){
  // ... some code
  if(/* 异步操做成功 */){
    resolve(value);
  }else{
    reject(error);
  }
});
复制代码

1 Promise构造函数接受一个函数做为参数,该函数的两个参数分别是resolvereject。它们是两个函数,由JavaScript引擎提供,不是本身部署。

2 resolve函数的做用,将Promise对象的状态从“未完成”变成“成功”(即从Pending变为Resolved),在异步操 做成功时调用,并将异步操做的结果,做为参数传递出去;

3 reject函数的做用是,在异步操做失败时调用,并将异步操做报出的错误,做为参数传递出去。

Promise实例生成之后,能够用then方法分别制定Resolved状态和Rejected状态的回调函数:

promise.then(function(value){
        // sucess
    },function(error){
        // failure
    });
复制代码

then方法能够接受2个回调函数做为参数,第二个函数是可选的,不必定要提供。这两个函数都接受Promise对象传出的值做为参数。

下面是一个Promise对象的简单例子:

function timeout(ms){
  return new Promise((resolve,reject)=>{
    setTimeout(resolve,ms,'done');
  });
}

timeout(100).then((value)=>{
  console.log(value);
});
复制代码

上面代码中,timeout方法返回一个Promise实例,表示一段事件之后才会发生的结果。过了指定的时间(ms参数)之后,Promise实例的状态变为Resolved, 就会触发then方法绑定的回调函数。

Promise新建后就会当即执行

let promise = new Promise(function(resolve,rejeact){
  console.log('Promise');
  resolve();
});

promise.then(function(){
  console.log('Resolved');
});

console.log('Hi');

// Promise
// Hi
// Resolved
复制代码

上面代码中,Promise新建后当即执行,因此首先输出的是”Promise”,而后then方法指定的回调函数,将在当前脚本全部同步任务执行完才会执行,因此”Resolved”最后输出。

下面是异步加载图片的例子:

function loadImageAsync(url){
  return new Promise(function(resolve,reject){
    var image = new Image();
    image.onload = function(){
      resolve(image);
    };
    image.onerror = function(){
      reject(new Error('Could not load image at' + url));
    };
    image.src = url;
  });
}复制代码

下面是一个用Promise对象实现Ajax操做的例子:

var getJSON = function(url){
  var promise = new Promise(function(resolve,reject){
    var client = new XMLHttpRequest();
    client.open('GET',url);
    client.onreadystatechange = handler;
    client.responseType = 'json';
    client.setRequestHeader('Accept','application/json');
    client.send();

    function handler(){
      if(this.readyState !== 4){
        return;
      }
      if(this.status === 200){
        resolve(this.response);
      }else{
        reject(new Error(this.statusText));
      }
    }
  });

  return promise;
};
getJSON('/posts.jons').then(function(json){
  consoloe.log(json);
},function(error){
  console.log('出错了');
});
复制代码
相关文章
相关标签/搜索