Es2017 将会给我们带来什么?

本文作者:ivweb biliou

pipeline-operator

此前,如果我们需要实现函数1的返回值域给函数2调用 最简单的方式是


 A(B(C()))

面向对象的话可以

let obj = {
    value: void 0,
      A() {
          this.value = 1;
          return this;
    },
    B() {
        this.value += 2;
        return this;
    },
    C() {
        this.value *=3;
        return this;
    }
}

obj
  .A()
  .B()
  .C()

如果在node端我们还可以使用.pipe

.pipe(A())
.pipe(B())
.pipe(C())

基本使用

而在Es2017中,TC39也为我们提供了管道运算符,它的基本用法是,将上一个函数执行,且将返回值作为入参,传递给下个函数的形参。并执行下一个函数,直到全部函数执行完成,返回最后一个函数返回的结果。 例如如下三个函数

function doubleSay (str) {
  return str + ", " + str;
}
function capitalize (str) {
  return str[0].toUpperCase() + str.substring(1);
}
function exclaim (str) {
  return str + '!';
}
// 原来的方式
let result = exclaim(capitalize(doubleSay("hello")));

>>> Hello, hello!
// 使用管道函数重构
let result = "hello"
  |> doubleSay
  |> capitalize
  |> exclaim;

>>> Hello, hello!

同时,我们可以借助箭头函数在流的中间截获中间值 例如另外一个自定义一个函数

function bilibliousStringFunction(s, b = ', BiLi') {
    return s.toUpperCase() + b;
}

然后我们改写一下我们的函数

let result = 'hello'
    |> doubleSay
    |> data => bilibliousStringFunction(data)
    |> exclaim;

>>> HELLO, HELLO, BiLi!

柯里化

我们还可以通过pipeline-operator实现函数柯里化 首先我们来复习一下柯里化

function curry(fn) {
  var arg = [].slice.call(arguments, 1);
  return function () {
    var newarg = [].concat.apply(arg, arguments);
    return fn.apply(this, newarg);
    }
}


var addCurry = curry(function (x,y,z) {
  return x+y+z;
},1);

addCurry(2,3);

ok, 接下来我们可以使用pipeline-operator重写上述逻辑

function add(...params1) {
  return function (...params2) {
    let payload = params1.concat(params2);
    return payload.length > 1 ? payload.reduce((b, n) => b + n) : payload[0] || 0;
  }
}

let result = 1
  |> add(2, 3);

result >>> 6

也是比较优雅的

继承

我们也可以在对象继承方面做一些文章

// 基础对象
function Person (name, age) {
  return { name, age };
}

// 功能函数
function Walk (Person) {
  Person.walk = function () {
    console.log('I can walk now!');
  }
  return Person;
}

function Talk (Person) {
  Person.talk = function () {
    console.log('I can talk now!');
  }
  return Person;
}

function Eat (Person) {
  Person.eat = function () {
    console.log('I can eat now!');
  }
  return Person;
}

function Ride (Person) {
 Person.ride = function () {
    console.log('I can ride now!');
  }
  return Person; 
}

// 具体对象
function Father (name, age) {
  return Person(name, age) |> Walk |> Talk |> Eat |> Ride;
}

function Son (name, age) {
  return Person(name, age) |> Walk |> Talk |> Eat;
}

这样我们就能很轻松的搭配我们所需的功能函数,拼装为具体能实现的实例

数据检测

同时,关于数据检验,我们现在可以这样玩

function bounded (prop, min, max) {
  return function (obj) {
    if ( obj[prop] < min || obj[prop] > max ) throw Error('out of bounds');
    return obj;
  };
}

function format (prop, regex) {
  return function (obj) {
    if ( ! regex.test(obj[prop]) ) throw Error('invalid format');
    return obj;
  };
}

function Xss(obj) {
  return testXssInObj(obj); // 伪代码
}

function createPerson (attrs) {
  attrs
    |> bounded('age', 1, 100)
    |> format('name', /^[a-z]$/i)
    |> Xss
    |> Person.insertIntoDatabase;
}

try {
  createPerson({age: 20, name: "__alert('Xss')__"})
} catch(err) {
  alert('Your Name or Age was illegal!')
}

bind-operator

在此之前,如果要绑定函数的作用域,我们一般用的是 bind, call, apply 如今,es2017 为我们提供了一个语法糖(Syntactic sugar) 栗子如下:

const Owen = {
    year: 18,
    getValue() {
        return this.year
    }
}

const bilibiliou = {
    year: 21
}


bilibiliou::Owen.getValue();  // >>> 21
// 等效于 Owen.getValue.call(bilibiliou)

如果不指定左值,则绑定默认的上下文

::console.log
// console.log.bind(console);

我们可以发现,对于函数,如果 有执行符 () 则被编译为call,如果没有则会编译为bind 如果我们传递了多个参数,则会被编译为apply

foo::bar(...[1,2,3]);
// bar.apply(foo, 1,2,3);

bind-operator 为我们带来了很多便利,首先就是React 中需要绑定this域的场景

this.somethingHandle = this.somethingHandle.bind(this); 我们完全可以使用bind-operator 进行改写 this.somethingHandle = ::this.somethingHandle 同时对于一些类数组,我们的操作也可以变得更加优雅

let oBtns = document.querySelectorAll('.button');

oBtns::map(v => {
    // to do something
});

// 等价于

[].map.call(oBthns, v => {
    // to do something
});

当我们对数据需要进行一系列处理的时候,我们还可以这么玩

function (data) {
  function extentionFunction1 (param) {
    return data.blabla(param)
  }

  function extentionFunction2 (param) {
    return data.foofoo(param);
  }


  return data
      .dataHandle1()
      ::extentionFunction1(123)
      .dataHandle2()
      ::extentionFunction2(456)
}

等价于

 data = data
        .dataHandle1()
        .dataHandle2()

     data = extentionFunction1(123);
     data = extentionFunction2(456);

     return data;
Object.entries 和 Object.values

在此之前我们就能通过 Object.keys 将对象的key转为数组,如今,TC39为我们扩展了这类的方法

const obj = {
    retcode: 0,
    msg: 'Success',
    data: 'blabla'
}
const result = Object.values(obj);
console.log(result); // [0, 'Success', 'blabla']

如上,我们可以使用values输入一个对象内的所有values 我们还可以使用 entries 输出每一个键值对

const obj = {
    retcode: 0,
    msg: 'Success',
    data: 'blabla'
}
const result = Object.entries(obj);
console.log(result); // [['retcode', 0], ['msg', 'Success'], ['data', 'blabla']]

String Padding

string 扩展了两个方法

// padStart
'abc'.padStart(10);         // "       abc"
'abc'.padStart(10, "foo");  // "foofoofabc"
'abc'.padStart(6,"123465"); // "123abc"
'abc'.padStart(8, "0");     // "00000abc"
'abc'.padStart(1);          // "abc"
// padEnd
'abc'.padEnd(10);          // "abc       "
'abc'.padEnd(10, "foo");   // "abcfoofoof"
'abc'.padEnd(6, "123456"); // "abc123"
'abc'.padEnd(1);           // "abc"

未来我们可以更好的处理字符串了

function sayHi (userFirstName) {
  let Hello = ' 大爷,您可来了,小的这就给您斟茶倒水,招呼菊仙姑娘';
  return userFirstName.padEnd(userFirstName.length + Hello.length, Hello);
}

sayHi('bilibiliou')

逗号不会抛错

有些时候(复制多个对象)可能后面不小心会留下一个小逗号

var obj = {
  1,
  2,
};

又或者 写函数入参的时候

function hi(a,b,) {
  console.log(`${a} ${b}`)
}

hi('Hello', 'World',);

还有可能是数组


[1,2,3,4,]

当代码复杂且代码被混淆|压缩|编译|转义的时候,可能一个逗号的Error也要查半天,断点调 在Es8的规范中,能够忽视这种错误,让程序正常的跑起来 当然,虽然规范给了我们这样的便利,还是最好不要犯这种低级的错误比较好。

共享内存

现在共享经济日渐火爆,有共享单车、共享充电宝、共享女友 blabla 当然也少不了共享内存了?! (Shared Memory and Atomics) 我们可以通过new Worker('calc.js') 来开辟一个子进程


let w = new Worker("calc.js");

如果主线程和子线程需要通讯的话,主要使用 postMessage 和 onmessage 主线程

w.postMessage("hi");
w.onmessage = function (ev) {
  console.log(ev.data);
}

子线程

function (ev) {
  console.log(ev.data);
  postMessage("ho");
}

而现在我们可以开辟一定大小的存储空间,进行线程间数据共享 let sab = new SharedArrayBuffer(1024); // 1KB 的共享内存 w.postMessage(sab) // 通过postMessage 将指针发送给子线程 子线程直接从全局获取到指针,并写入数据,完成内存共享

var sab;
onmessage = function (ev) {
   sab = ev.data;
}

下面是一个素数生成器的例子

var sab = new SharedArrayBuffer(Int32Array.BYTES_PER_ELEMENT * 100000); // 100000 个素数
var ia = new Int32Array(sab);  // ia.length == 100000
var primes = new PrimeGenerator(); // 伪代码,素数生成器网上有对应实现
for ( let i=0 ; i < ia.length ; i++ )
   ia[i] = primes.next();
w.postMessage(ia);
var ia;
onmessage = function (ev) {
  ia = ev.data;        // ia.length == 100000
  console.log(ia[37]); // 第38位素数 163
}

值得注意的是,在共享内存内两个线程都能够修改数据

console.log(ia[37]);  // 163
ia[37] = 123456; // 最后为 123456

所以,应该有合理的机制来保证共享内存的合理使用

Atomic

未来还会提供一个类似 Math 方法一样的全局对象 Atomic Atomic 下存在多个静态方法用来操作 SharedArrayBuffer 共享内存对象 具体可以看 https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/Atomics 关于 Atomic 的介绍

求幂运算符(**)

以前在写动画的时候,经常这么操作

const M = Math;
const { pow: Pow } = M;

现在 新规范为我们推出了(**) 求幂运算。 我们可以直接这样

5 ** 2 // >>> 25

// 等同于 Math.pow(5, 2)

let num = 5;
num **= 2;
console.log(num);
// >>> num 25

原文出处:IVWEB社区

原创声明,本文系作者授权云+社区发表,未经许可,不得转载。

如有侵权,请联系 yunjia_community@tencent.com 删除。

编辑于

我来说两句

0 条评论
登录 后参与评论

相关文章

来自专栏机器学习原理

图数据库neo4j介绍(3)——CypherCreateMatchSetDELETE REMOVE

Match (n:Person {id:'baba'}) set n.name='张三',n.age=50 return n

552
来自专栏大内老A

来源于WCF的设计模式:可扩展对象模式[上篇]

我一直很喜欢剖析微软一些产品、框架的底层实现。在我看来,这不但让我们可以更加深入地了解其运作的原理,同时也能提高设计/架构的技能。因为对于这些框架或者产品来说,...

1897
来自专栏跟着阿笨一起玩NET

算法~将文件夹下所有文件输出到日志文件中(包括所有子文件夹下的)

算法文章,总是带给我们无穷的思考和兴趣,一个问题,多种解决方法,看你如何去思考它,对于标题所引出的问题,我觉得,使用递归是比较有效的方法,当然递归还有很多使用场...

521
来自专栏禁心尽力

反射+自定义注解---实现Excel数据列属性和JavaBean属性的自动映射

简单粗暴,直奔主题。 需求:通过自定义注解和反射技术,将Excel文件中的数据自动映射到pojo类中,最终返回一个List<pojo>集合?   今天我只是通...

2719
来自专栏Phoenix的Android之旅

深入理解注解-类的常量池

但是留了个问题没有进一步说明,就是注解所设定的数据是存在什么地方的? 明白这个问题需要引入一个新东西,类的常量池。

564
来自专栏张善友的专栏

如何结合IbatisNet的LIST遍历实现模糊查询

我仿照Java的Spring+Ibatis+Struct用Castle+IBatisNet+Asp.net的开发框架的DAO的基类:BaseSqlMapDao内...

1839
来自专栏蓝天

算术运算指令

算术运算指令是反映CPU计算能力的一组指令,也是编程时经常使用的一组指令。它包括:加、减、乘、除及其相关的辅助指令。

694
来自专栏iOS技术

透彻理解 KVO 观察者模式(附基于runtime实现代码)

推荐另一篇文章:透彻理解 NSNotificationCenter 通知(含实现代码)

4008
来自专栏Java Edge

遨游springmvc之HandlerExceptionResolver1.前言2.原理4.总结

3725
来自专栏jeremy的技术点滴

JVM的Finalization Delay引起的OOM

3378

扫码关注云+社区