i12n / i12n.github.io

3 stars 0 forks source link

ES6 #4

Open i12n opened 9 years ago

i12n commented 9 years ago

运行环境

安装 node,并运行以下命令查看已经实现的ES6特性

$ node --v8-options | grep harmony

运行js文件

$ node --harmony --use-strict yourfilename.js

let

  1. let 声明的变量只在其所在的代码块中起作用。
  2. 不存在变量提升,必须先声明后使用,不能重复声明。
  3. 暂时性死区,在区块内形成封闭作用域,在let声明之前,外部同名变量也不能使用。
  4. 支持块级作用域
{
  {
    /* 不存在变量提升,必须先声明后使用,不能重复声明  */
    console.log(a); // ReferenceError

    let a = 3;
    console.log(a); // 3
  }
 /* 支持块级作用域,let 声明的变量只在其所在的代码块中起作用*/
  console.log(a); // ReferenceError
}
{
  var b = 1;
  {
   /* 暂时性死区,在区块内形成封闭作用域,在let声明之前,外部同名变量也不能使用 */
    console.log(b); // ReferenceError

    console.log(c); // undefined
    let b = 2;
    var c = 3;
    console.log(b); // 2
  }
  console.log(b);  // 1
  console.log(c);  // 3
}
i12n commented 9 years ago

运行环境

  1. 在线环境 ES6 Fiddle
  2. Babel 可以将 ES6 转码为 ES5
# 安装babel
$ npm install --global babel

#运行支持ES6的REPL环境
$ babel-node

#运行ES6脚本
$ babel-node yourfilename.js

数组解构

按一定模式,从数组中取值

  // 数组解构
  let [a, b, c] = [3, 2, 1];
  console.log(a); // 3
  console.log(b); // 2
  console.log(c); // 1

  let [x, , y] = [1, 2, 3];
  console.log(x); // 1
  console.log(y); // 3

  let [m, [n], [p]] = [1, [2], [3]];
  console.log(m); // 1
  console.log(n); // 2
  console.log(p); // 3

  let [header, ...trail] = [1, 2, 3, 4, 5, 6];
  console.log(header); // 1
  console.log(trail); //[2, 3, 4, 5, 6]

  // 默认值,数组成员为 undefined 时默认值生效
  let [ref1 = 1, ref2 = 2, ref3 = 3, ref4 = 4, ref5] = [0, null, undefined];
  console.log(ref1); // 0
  console.log(ref2); // null
  console.log(ref3); // 3
  console.log(ref4); // 4
  console.log(ref5); // undefined

对象解构

按一定模式从对象中取出属性和方法

//支持默认值,当对象为 undefined 时,默认值生效
let {a, b, c = 2, d} = {a: 1, b: "one"};
console.log(a); // 1
console.log(b); // "one"
console.log(c); // 2
console.log(d); // undefinded

let x;
({x} = {x:1}); // 避免将 {} 解析为 代码块,要加 (),{x} = {x: 1} 会报错
console.log(x); // 1
i12n commented 9 years ago

运行环境

Firefox 代码草稿纸

Proxy

Proxy 可以理解为给对象增加了代理,代理将对象进行包装,在外界使用代理访问对象的同时,可以做一些额外的事情。

// new Proxy(target, handler);
var p = new Proxy( {name: 'Jim'}, {
  get: function(target, name) {
    console.log(name);
    // 返回属性值,以正常访问到对象
    return target.name;
  },
  set: function(target, prop, value) {
    if (prop === 'age') {
      if (value > 200) {
        throw new RangError('The age seem invalid');
      }
    }
    // 设置属性值 
    target[prop] = value;
  }
});

p.name; // name
p.age; // age

p.name = 'Tom';
console.log(p.name); // Tom

p.age = 2001; // ReferenceError

Proxy - MDN 代理Poxies - infoQ

i12n commented 9 years ago

运行环境

Firefox 代码草稿纸

Set

Set 是一个数据集合,不允许重复的数据

// 使用数组初始化 set
var s = new Set([6, 5]);

// 添加值
s.add(4);
s.add(5); // 出现重复,被忽略

console.log(s.size); // 3

// 判断一个值是否在 set 中,使用 has 方法
console.log(s.has(4)); // true

// 遍历set 使用 for... of
for ( v of s) {
  console.log(v); // 6 5 4
}

// 数组去重
var a = [1, 1 ,3, 3];
a = [...(new Set(a))]; // [1, 3]

for...of 遍历属性值 for...in 遍历属性名称

Map

Map 是键值对的集合,虽然对象本质上也是键值对的集合,但是对象只能用字符串当做键。

// 初始化化
var map = new Map([['one', 1], ['two', 2]]);

console.log(map.size); // 2
console.log(map.has('one')); // true
console.log(map.get('one')); // 1

// 遍历 key
for (let key of map.keys()) {
  console.log(key); // one two
}

// 遍历 value
for (let value of map.values()) {
  console.log(value); // 1 2
}

for (let [key, value] of map) {
  console.log([key, value]); // ["one", 1] ["two", 2]
}

[...map.entries()]; // [["one", 1], ["two", 2]]
i12n commented 9 years ago

Iterator

与 C++ 的迭代器类似,ES6 也提供了 Iterator 接口,用来遍历一些数据结构。

定义一个 Iterator 的方法如下:

  // TypeScript  

  interface IteratorResult {
      done: boolean;
      value: any;
  }
  interface Iterator {
      next(): IteratorResult;
  }

Iterator 是一个对象,定义了用于遍历的 next 方法;next 返回的是一个对象,其中value是遍历的返回值,done表示遍历是否结束。

function myIterator() {
  var id = 0;
  // 返回 Iterator
  return ({
    // 定义 next 方法
    next() {  
       if (id < 4) {
         return {value: id++, done: false};
       } else {
         return {done: true};
       }
    }
  });
};

var it = myIterator();
console.log(it.next()); // {value = 0, done = false }

Iterator 的作用是遍历数据结构,为数据结构的访问提供统一的接口,那么如何遍历数据结构呢?首先要将 Iterator 部署到数据结构,部署方法如下:

interface Iterable {
  [Symbol.iterator](): Iterator
}

将 Iterator 配置给数据结构的 Symbol.iterator 属性,Symbol.iterator 是一个方法,调用这个方法就会得到 Iterator。


var array = [5,5,5,5,5];

// 为array 配置 Iterator
array[Symbol.iterator] = myIterator;

配置了 Symbol.iterator ,就可以使用 for...of 进行遍历。

for(var i of array) {
  console.log(i); // 0 1 2 3
}

一些原生的数据结构(例如Array,Set,Map)已经具备了Iterator接口,因为这些数据结构已经部署了 Symbol.iterator属性。在这里重新部署了 array 的 Symbol.iterator,因此for...of遍历array时没有打印它的元素。

Iterator 是为数据遍历服务的,更确切的说,是为for...of提供支持的;只有配置了Symbol.iterator,才能使用for...of遍历;另外,Object 是不支持 for...of 遍历的,可以使用for...in 。

一个遍历斐波那契数列的代码

var fib = {
  [Symbol.iterator]() {
    var pre = 0,
        cur = 1;
    return {
      next(){
        var tmp = cur;
        cur = pre + cur;
        pre = tmp;
        return {done: false, value: cur};
      }
    }
  }

};

for (var n of fib) {
  if (n > 100)
    break;
  console.log(n);
}

Iterator + for..of ES6 In Depth: Iterators and the for-of loop

i12n commented 9 years ago

Generator函数

function *run(signal) {
  var i = 0;
  console.log(i);
  while(++i){
    console.log(i);
    if(yield signal) break;
  }
}

var worker = run();

worker.next(); // 打印 0 1
worker.next(); // 打印 2
worker.next(true);
worker.next();

Generator 函数主要特点是:

  1. 执行Generator函数会返回一个 Iterator 对象,for...of 可以遍历
  2. 执行next时,碰到 yield 暂停,并将 yield 之后的值返回
  3. next如果带有参数,则参数作为上一个yield的返回值
i12n commented 9 years ago

ES6 相关的 gulp 插件

gulp-babel

gulp-babel 可以将 es6 转换为 es5 ,这样可以避免浏览器不支持 es6 而带来的问题。

例如以下es6代码:

let a = 3;
alert(a);  

被转换为

"use strict";

var a = 3;
alert(a);

gulp-eslint

gulp-eslint 可用对代码进行格式检测和语法错误检测等。 与 gulp-jshint 相比,gulp-eslint 对 es6 提供支持。由于 gulp-eslint 是可配置的,可以根据需求配置相关规则,并且提供了详细的配置文档。 为了支持 es6 特性,在 package.json 中添加如下配置:

 "eslintConfig": {
    "env": {
      "browser": true,
      "node": true,
      "es6": true
    }
  }

如果不添加,在对如下代码进行检测时,将报错:error Parsing error: Unexpected token let

let a = 3;
alert(a); 
i12n commented 9 years ago

Class

ES6 class 并没有引入新的面向对象模型,它只是一个方便使用的语法糖,并没有改变已有的基于原型链的面向对象方式。 class 的定义和使用,如下代码:

class Fruit {
  constructor(name) {
    this.name = name;
  }
  sayName() {
    console.log(this.name);
  }
}

var apple = new Fruit('apple');
apple.sayName(); // apple

class Orange extends Fruit {
  constructor(price, weight) {
    super('Orange');
    this.weight = weight;
    this.price = price;
  }

  sayWeight() {
    console.log(this.weight);
  }
  get total() {
    return this.price * this.weight;
  }
}

var orange = new Orange(3.14, 2);
orange.sayWeight(); // 2
console.log(orange.total); // 6.28

首先,constructor 在 new 一个实例的过程中会被执行,如果没有实现 constructor 方法,则会生成一个空的constructor方法。

其次,extends 可以实现类继承,super 用于调用父类的 constructor. ES5 和 ES6   在实现继承是存在差别的,ES5 是先创建子类的实例对象,然后将父类的方法添加到 this 上;而 ES6 是先创建父类的实例对象 this 然后子类的构造函数来修改 this.

i12n commented 9 years ago

Decorators

由Yehuda Katz 提出的 Decorators ,是ES7的一个提案。

Decorators make it possible to annotate and modify classes and properties at design time. A decorator is:

  • an expression
  • that evaluates to a function
  • that takes the target, name, and decorator descriptor as arguments
  • and optionally returns a decorator descriptor to install on the target object

Decorators 作为一种语法糖,可以修饰类和类的属性,但是要依赖于 ES5 的 Object.defineProperty。Decorators 修饰属性的时候, 传递的参数 target、 name、 decorator 与 Object.defineProperty 的参数是一致的,target 是类的 prototype。

function setLog(target, name, descriptor) {
  let funName = '_' + name;
  Object.defineProperty(target, funName, {
    value: function() {
        console.log('start run ' + name);
        target[name]();
        console.log(name + ' finish');
      }
  });
  return descriptor;
}
class Apple {
  @setLog
  showName() {
    console.log('apple');
  }
}

var apple = new Apple();
apple._showName();

Decorators 也可以修饰类,参数 target 为类的自身。

function isFruit(target) {
  target.isFruit = true;
}

@isFruit
class Apple {
}
console.log(Apple.isFruit); // true

目前 babel 支持 decorators,使用方法如下: 命令行:

$ babel --optional es7.decorators source.js > dest.js

脚本:

babel.transform("code", {optional: ["es7.decorators"]})

2017.8.14补充 Decorators 中的参数 target 是 class 的 constructor 或 property,并不是该 class 定义的具体的 object。在下面的代码中,discount 将用于装饰 Apple 类中的 getPrice 方法(discount 将原有的 descriptor.value 重写,也就是 getPrice 函数被重新定义),注意以下几点:

function discount(target, name, descriptor) {   // 在 class 的 constructor 执行之前就已经被执行,this 指向全局 window
  let method = descriptor.value;  // 原有的方法,原有方法中的 this 指向的是class
 定义的 object 对象,但是此时 constructor 还没有被执行,所以 这时的 object 为 undefined ,也就是this 为 undefined
  descriptor.value = function(...args) {  // 重新定义方法, this 指向 class 定义的具体 object,如果这里用箭头函数则 this 为 外部作用域里的 this 也就是 window
    let ret = method.apply(this, ...args);   // 执行原有方法,需要将原有方法中的 this 指向 具体的 object
    return ret * 0.95
  }
  return descriptor;
}

class Apple {
  constructor(price) {
    this.price = price;
  }

  @discount
  getPrice() {
    return this.price;
  }
}

const apple = new Apple(6);
console.log(apple.getPrice());

https://jsfiddle.net/kxokqLh7/ http://www.darul.io/post/2016-02-11_es7-decorators-in-depth-or-not


refer:

Javascript Decorators Decorators in ES7 ES7 Decorator 装饰者模式

i12n commented 9 years ago

资源

ECMAScript Proposals ECMAScript Compat

i12n commented 8 years ago

String.prototype.includes 与 Array.prototype.includes

String.prototype.includes 判断字符串是否包含某个子串 用法:str.includes(searchString[, position]) , position 为起始位置 返回值: true false 大小写敏感

console.log('String'.includes('St')); // true
console.log('String'.includes('st')); // false
console.log('String'.includes('st', 1)); // false

Array.prototype.includes 判断数组是否包含某个元素, 更详细的介绍 用法 array.includes(searchElement[, fromIndex]), fromIndex 为起始位置 返回值:true false 可以判断 NaN

console.log(['a', 'b'].includes('a')); // true
console.log(['a', 'b'].includes('a', 1)); // false
console.log([NaN].includes(NaN)); // true
i12n commented 8 years ago

Promise 捕获异常

Promise  在出现异常的时候,会自动将异常抛给下一级的 reject 函数,而不是本级的 reject 函数,例如下面的代码,并不会将错误信息打印:

var promise = new Promise((resolve, reject) => resolve());
promise
    .then(
        () => { throw new Error('Resolve Error!!!') },
        e => console.log(e)
    );

而这段代码则会将错误信息打印:

var promise = new Promise((resolve, reject) => resolve());
promise
    .then(
        () => { throw new Error('Resolve Error!!!') },
        e => console.log(e)
    )
    .then(
        () => {},
        e => console.log(e) // 'Resolve Error!!!'
    );

为了方便,可以在最后一级的后面加一个 catch 来输出错误信息:

var promise = new Promise((resolve, reject) => resolve());
promise
    .then(
        () => { throw new Error('Resolve Error!!!') }, 
        e => console.log(e)
    )
    .catch(
        e => console.log(e) // 'Resolve Error!!!'
    );

每一层只能捕获上一层的异常,而异常被处理之后,下一层将会恢复正常不会手影响。也就是说,无论异常与否,每一层都会进入直至结束。

var promise = new Promise((resolve, reject) => resolve());
promise
    .then(
        () => { throw new Error('Resolve Error!!!') },
        e => console.log(e)
    )
    .then(
        () => {},
        e => console.log(e)   // 'Resolve Error!!!'
    )
    .then(
        () => console.log('end'), // 'end'
        e => console.log(e) 
    );
i12n commented 8 years ago

箭头函数 (=>) 与普通函数的区别

箭头函数没有自己的 this,它的 this 继承自外围作用域。