《ES6标准入门(阮一峰) 第3版 》读书笔记

一个数据结构只要部署了Symbol.iterator属性,就被视为具有 iterator 接口,就可以用for...of循环遍历它的成员。也就是说,for...of循环内部调用的是数据结构的Symbol.iterator方法。

// yield语句遍历完全二叉树
// 下面是二叉树的构造函数,
// 3个参数分别是左树、当前节点和右树
function Tree(left, label, right){
	this.left = left;
	this.label = label;
	this.right = right;
}
// 下面是中序(inorder)遍历函数。
// 由于返回的是一个便利器,所以要用generator函数。
// 函数体内采用递归算法,所以左树和右树要用yield*遍历
function* inorder(t){
	if(t){
		yield* inorder(t.left);
		yield t.label;
		yield* inorder(t.right);
	}
}
// 下面生成二叉树
function make(array){
	// 判断是否为叶节点
	if(array.length == 1) return new Tree(null,array[0], null);
	return new Tree(make(array[0]), array[1], make(array[2]));
}
let tree = make([[['a'], 'b', ['c']], 'd', [['e'], 'f', ['g']]]);
// 遍历二叉树
var result = [];
for(let node of inorder(tree)){
	result.push(node);
}
console.log(result);
var clock = function* (){
	while(true){
		console.log('Tick!');
		yield;
		console.log('Tock!');
		yield;
	}
}
clock();

// 由于JavaScript是单线程语言,只能保持一个调用栈。引入协程以后,
// 每个任务可以保持自己的调用栈。这样做的最大好书,就是抛出错误的时候,
// 可以找到原始的调用栈。不至于像异步操作的回调函数那样,
// 一旦出错原始的调用栈早就结束。Generator函数是对协程的实现,但属于不完全实现。
// Generator函数可以改善代码运行流程
function* longRunningTask(value){
	try{
		var value2 = yield step1(value);
		var value3 = yield step2(value2);
		var value4 = yield step3(value3);
		var value5 = yield step4(value4);
		// Do something with value4
	} catch(e) {
		// handle any error from step1 through step4
	}
}
// 使用一个函数按次序自动执行所有步骤
scheduler(longRunningTask(initalValue));
function scheduler(task){
	var taskobj = task.next(task.value);
	// 如果Generator函数未结束,就继续调用
	if(!taskObj.done){
		task.value = taskObj.value;
		scheduler(task);
	}
}
// Promise的最大问题是冗余,原来的任务被Promise包装之后,
// 无论什么操作,一眼看去都是很多then的堆积,原来的语义变得很不清楚。	协程:多个线程互相协作,完成异步任务。
// next返回值的value属性石Generate函数向外输出数据;
// next方法还可以接受参数,向Generator函数体内输入数据
function* gen(x){
	var y = yield x + 2;
	return y;
}
var g = gen(1);
g.next() //{value:3, done: false}
g.next(2) //{value:2, done: true}
// 传名调用
function f(m){
	return m*2;
}
f(x+5);
// JavaScript语言是传值调用,
// 手动执行其实就是用then方法层层添加回调函数 手写自动执行器
function run(gen){
	var g = gen();
	function next(data){
		var result = g.next(data);
		if(result.done){
			return result.value;
		}
		result.value.then(function(data){
			next(data);
		});
	}
	next();
}
// Node提供Stream模式读写数据,特点是一次只处理数据的一部分,
// 数据被分成一块一块依次处理,就好像“数据流”一样。
// async函数用一句话来说,它就是Generator函数的语法糖。
// async函数对Generator函数的改进体现在4点:1、自带执行器;2、更好的语义;3
// 更广的适用性:async函数的await命令后面,
// 可以是Promise对象和原始类型的值(数值、字符串和布尔值,但这时等同于同步操作);
// 4、返回值是Promise:async函数完全可以看作由多个异步操作包装成的一个Promise对象,
// 而await命令就是内部then命令的语法糖。
// 
// async指定多少毫秒后输出一个值
function timeout(ms){
	return new Promise((resolve) =>{
		setTimeout(resolve, ms);
	});
}
async function aysncPrice(value, ms){
	await timeout(ms);
	console.log(value);
}
asyncPrint('hello world', 50);
// 如果函数返回多个值,优先使用对象的解构赋值,而不是数组的解构赋值。
// 这样便于以后添加返回值,以及更改返回值的顺序
// bad
function processInput(input){
	return [left, right, top, bottom];
}
// good
function processInput(input){
	return {left, right, top, bottom};
}
const {left, right} = processInput(input);
// bad
[1,2,3].map(function(x){
	return x*x;
});
// good
[1,2,3].map((x)=>{
	return x*x;
});
// best
[1,2,3].map(x=>x*x);
// 箭头函数取代Function.prototype.bind,不再用self/_this/that绑定this
// bad
// const self = this;
const boundMethod = function(...params){
	return method.apply(self,params);
}
// acceptable
const boundMethod = method.bind(this);
// best
const boundMethod = (...params) => method.apply(this, params);

//  多个await命令后面的异步操作如果不存在继发关系,最好让它们同时触发
// 写法一
let [foo, bar] =await Promise.all([getFoo(), getBar()]);
// 写法二
let fooPromise = getFoo();
let barPromise = getBar();
let foo = await fooPromise;
let bar = await barPromise;
// 上面两种写法中,getFoo和getBar都是同时触发,这样就会缩短程序的执行时间
// async函数的实现原理就是将Generator函数和自动执行器包装在一个函数里
async function fn(args){
  // ...
}
// 等同于
function fn(args){
  return spawn(function* (){
    // ...
  });
}
// spawn函数就是自动执行器
function spawn(genF){
  return new Promise(function(resolve, reject){
    var gen = genF();
    function step(nextF){
      try{
        var next = nextF();
      }catch(e){
        return reject(e);
      }
      if(next.done){
        return resolve(next.value);
      }
      Promise.resolve(next.value).then(function(v){
        step(function(){
          return gen.next(v);
        }),function(e){
          step(function(){
            return gen.throw(e);
          });
        }
      })
    }
    step(function(){
      return gen.next(undefined);
    })
  })
}
// aysnc实现:假如某个DOM元素上面,部署了一系列的动画,前一个动画结束,才能开始后一个。
// 如果当中与一个动画出错,就不再继续执行,而返回上一个成功执行的动画的返回值。
async function chainAnimationsAsync(elem, animations){
  var ret = null;
  try{
    for(var anim of animations){
      ret = await anim(elem);
    }
  }catch(e){
    /* 忽略错误,继续执行 */
  }
  return ret;
}
// for await...of循环用于遍历异步的Iterator接口
async function f(){
  for await (const x of createAsyncIterable(['a','b'])){
    console.log(x);
  }
}
// 基本上,ES6中的class可以看作只是一个语法糖,它的绝大部分功能,ES5都可以做到,新的class写法只是让对象原型的写法更加清晰,更像面向对象编程的语法而已。
// Symbol值,达到私有方法和私有属性的效果
const bar = Symbol('bar');
const snaf = Symbol('snaf');
export default class myClass{
  // 公有方法
  foo(baz){
    this[bar](baz);
  }
  // 私有方法
  [bar](baz){
    return this[snaf] = baz;
  }
}
// 类的方法内部如果含有this,它将默认指向类的实例。一旦单独使用该方法,很可能会报错。
// 父类的静态方法可以被子类继承
class Foo {
  static classMethod(){
    return 'hello';
  }
}
class Bar extends Foo{}
Bar.classMethod() // 'hello
// ES6可以自定义原生数据结构(比如Array、String等)的子类,这是ES5无法做到的。
class MyArray extends Array{
  constructor(...args){
    super(...args);
  }
}
var arr = new MyArray();
arr[0] = 12;
arr.length // 1
arr.length = 0;
arr[0] // undefined
// 装饰器改写React与Redux库结合代码
class MyReactComponent extends React.Component{}
export default connect(mapStateToProps, mapDispatchToProps)(MyReactComponent);
// 可改写成如下
@connect(mapStateToProps, mapDispatchToProps)
export defulat class MyReactComponent extends React.Component{}
// 由于存在函数提升,修饰器不能用于函数。类是不会提升的,所以就没有这方面的问题。
// 将Mixin写成一个修饰器
export function mixins(...list){
  return function(target){
    Object.assign(target.prototype, ...list);
  }
}
import {mixins} from './mixins';
const Foo = {
  foo(){
    console.log('foo')
  }
};
@mixins(Foo)
class MyClass{}
let obj = new MyClass();
obj.foo() // "foo"
// CommonJs模块就是对象,输入时必须查找对象属性
// 如下代码只有运行时才能得到这个对象,称为"运行时加载",导致完全没办法在编译时进行"静态优化"
let {stat,exists,readFile} = require('fs');
// ES6"编译时加载"或者叫静态加载
import {stat,exists,readFile} from 'fs';
import { Socket } from 'dgram';
// 严格模式不能使用前缀0表示八进制数,否则报错
// 接口改名
export {foo as myFoo} from "my_module";
// defer是“渲染完再执行”,async是“下载完就执行”。
// CommonJs模块输出的是值的肤质,也就是说,一旦输出一个值,模块内部的变化就影响不到这个值。
// ES6模块不会缓存结果,而是动态地去被加载的模块取值,并且斌梁总是绑定其所在的模块。
// 总是用Class取代需要prototype的操作。因为Class的写法更简洁,更易于理解。
// bad
function Queue(contents = []){
  const value = this._queue[0];
  this._queue.splice(0,1);
  return value;
}
// good
class Queue{
  constructor(contents = []){
    this._queue = [...contents];
  }
  pop(){
    const value = this._queue[0];
    this._queue.splice(0,1);
    return value;
  }
}
// .eslintrc
{
  "extends": "eslint-config-airbnb"
}
// WebGL,就是浏览器与显卡之间的通信接口,为了满足JavaScript与显卡之间大量、实时的数据交换。
// 它们自建的数据通信必须是二进制的,而不能是传统的文本格式。
// 二进制数组并不是真正的数组,而是类似数组的对象
// TypedArray视图与DataView视图的一个区别是,它不是一个构造函数,而是一组构造函数,代表不同的数据格式。TypedArray的数组成员都是同一个数据类型,而DataView的数组成员可以是不同的数据类型。
// 网页canvas元素输出的二进制像素数据就是TypedArray数组
var canvas = document.getElementById('myCanvas');
var ctx = canvas.getContext('2d');
var imageData = ctx.getImageData(0,0,canvas.clientWidth,canvas.height);
var unit8ClampedArray = imgaeData.data;
// Websocket可以通过ArrayBuffer发送或接收二级制数据
var websocket = new WebSocket('ws://127.0.0.1:8081');
socket.binaryType = 'arraybuffer';
// Wait until socket is open
socket.addEventListener('open', function(event){
  // send binary data
  var typedArray = new Uint8Array(4);
  socket.send(typedArray.buffer);
});
// Receive binary data
socket.addEventListener('message', function(event){
  var arrayBuffer = event.data;
  // ...
});

猜你喜欢

转载自blog.csdn.net/taozi550185271/article/details/106339165
今日推荐