Symbol/Generator/Thunk

今天又看了阮一峰的ES6知识点,对一些我平时不太常用的语法研究了一下
首先是Symbol这种数据类型,Symbol是除undefined, Object, Function, Number, String, Boolean的第七种类型,代表唯一性

我们可以用来定义唯一的值
const SYM = Symbol()    // 不加new关键字

const obj = {
   [SYM]: 'SYMBOL'
}
console.log(obj[SYM])

// 输出 SYMBOL
// 或者
const obj = {
  ATTACK: Symbol(),
  DEFENT: Symbol()
}

function progress(type){
  switch(type){
    case obj['ATTACK'] : console.log('攻击') ; break;
    case obj['DEFENT'] : console.log('防御') ; break;
    default: null ;
  }
}

progress(obj['ATTACK'])
progress(obj['DEFENT'])
Generator生成器,实际上是生成一个iterator的方法,写法上用一个*来和普通函数进行区分,内部可以使用yield来暂停到某一语句执行,通过使用.next()方法来触发下一个yield语句
function *f(){
  yield 5
}

const _f = f()
console.log(_f.next())

// 输出  { value: 5, done: false }  value是拿到的值也就是yield后面的,有可能是各种类型,有可能是个待执行的函数。  done代表是否执行完成
function *f(){
  yield 5
  return 'DONE'
}

const _f = f()
console.log(_f.next())
console.log(_f.next())

// 输出 { value: 5, done: false }
//      { value: 'DONE', done: true }
.next支持传入参数,传入的参数会代表上一次yield xxx的值,可以用一个变量来接收
注意是yield xxx都会被替换
function* foo(x) {
  var y = 2 * (yield (x + 1));
  var z = yield (y / 3);
  return (x + y + z);
}

var a = foo(5);
a.next() // Object{value:6, done:false}
a.next() // Object{value:NaN, done:false}
a.next() // Object{value:NaN, done:true}

var b = foo(5);
b.next() // { value:6, done:false }
b.next(12) // { value:8, done:false }
b.next(13) // { value:42, done:true }
如果想第一次调用.next就传入值,那么可以再包一层
function wrapper(generatorFunction) {
  return function (...args) {
    let generatorObject = generatorFunction(...args);
    generatorObject.next();
    return generatorObject;
  };
}

const wrapped = wrapper(function* () {
  console.log(`First input: ${yield}`);
  return 'DONE';
});

wrapped().next('hello!')
// First input: hello!
generator可以用来生成iterator迭代器类型的数据或者指定成某个迭代器
function *f(){
  yield 1
  yield 2
  yield 3
  yield 4
}

const _f = f()
for(let i of _f){
  console.log(i)
}
// 输出1 2 3 4
也可以将Object类型改造成具有iterator属性的可以用for of遍历的数据
const obj1 = {
  name: 'xiaowang',
  age: 18,
  sex: '男',
  like: 'football'
}

function *f(){
  const keys = Object.keys(this)
  for(let k of keys){
    yield [k, this[k]]
  }
}

obj1[Symbol.iterator] = f

for(let [k, v] of obj1){
  console.log(`${k}-${v}`)
}

// 输出
// name-xiaowang
// age-18
// sex-男
// like-football
如果有一个函数接收多个参数以及一个回调函数参数,那么它可以被Thunk,比如如下
function f(a, b, callback){
  let c
  setTimeout(() => {c = a + b; callback(c)}, 3000)
}
// 3秒之后做计算并调用回调方法callback去打印c
f(1,2,(res)=>console.log(res))
如果这时又有一个计算3+4的异步操作,需要在1秒后返回,但是要在这个3秒求1+2之后执行
function f2(a, b, callback){
  let c
  setTimeout(() => {c = a + b; callback(c)}, 1000)
}
f(3,4,(res)=>console.log(res)
// 如果执行
f(1,2,(res) => console.log(res))
f(3,4,(res) => console.log(res))
那么执行顺序是有问题的,会先输出7再输出3  而我们想要先输出3再输出7
可以
f(1,2,(res1) => {
  console.log(res1)
  f(3,4,(res2) => {
    console.log(res2)
  })
})
// 也可以定义一个generater生成器
function *f(){
    yield getSum(1,2)
    yield getSum(3,4)
    return 'DONE'
}

const _f = f()
// 想通过_f.next().value调用回调,也就是说getSum(,)是回调方法需要被执行
getSum(,)(callback)
_f.next().value((res1) =>{
  console.log(res1)
  _f.next().value((res2) => {
    console.log(res2)
  })
})
需要好好想想getSum(,)(callback)怎么来实现了
答案如下:
function f(a, b, time, callback){
let c
setTimeout(() => {c = a + b; callback(c)}, time)
}

var Thunk = function(fn){
return function (){ // getSum 执行
var args = Array.prototype.slice.call(arguments);
return function (callback){
args.push(callback);
return fn.apply(this, args);
}
};
};
const getSum = Thunk(f)
完整代码:
function f(a, b, time, callback){
let c
setTimeout(() => {c = a + b; callback(c)}, time)
}
var Thunk = function(fn){
return function (){
var args = Array.prototype.slice.call(arguments);
return function (callback){
args.push(callback);
return fn.apply(this, args);
}
};
};
const getData = Thunk(f)
function *g(){
let x = yield getData(1,2,3000)
let y = yield getData(3,4,1000)
}
const _g = g()


_g.next().value((res) => {
console.log(res)
_g.next().value((res) => {
console.log(res)
})
})
// 
function f(a, b, time, callback){
  let c
  setTimeout(() => {c = a + b; callback(c)}, time)
}

var Thunk = function(fn){
  return function (){
    var args = Array.prototype.slice.call(arguments);
    return function (callback){
      args.push(callback);
      return fn.apply(this, args);
    }
  };
};

const getData = Thunk(f)


function *g(){
  let x = yield getData(1,2,3000)
  let y = yield getData(3,4,1000)
}

function run(fn) {
  const _g = fn()

  function next(res) {
    let result = _g.next()
    res && console.log(res)
    if(result.done) return
    result.value(next)
  }

  next();
}


run(g)