Skip to content

Instantly share code, notes, and snippets.

@teal-front
Last active February 2, 2024 09:38
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save teal-front/5a0bfd8b0cbd17d30af51fa409fa5d5a to your computer and use it in GitHub Desktop.
Save teal-front/5a0bfd8b0cbd17d30af51fa409fa5d5a to your computer and use it in GitHub Desktop.
ES6

Array

http://2ality.com/2014/05/es6-array-methods.html

indexOf

使用===进行比较

[NaN].indexOf(NaN) // => -1

includes

使用sameValueZero比较,支持NaN为相同值

1.If Type(x) is different from Type(y), return false. 2.If Type(x) is Number, then a. If x is NaN and y is NaN, return true. b. If x is +0 and y is -0, return true. c. If x is -0 and y is +0, return true. d. If x is the same Number value as y, return true. e. Return false. 3.Return SameValueNonNumber(x, y).

[NaN].includes(NaN) // true

迭代器(Iterator)

https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Guide/Iterators_and_Generators

  1. Array、String、Map、Set内建有迭代器
  2. 对于Object,可以使用Symbol.iterator创建自定义的迭代器
// fib
/// raw iterator
let fibRaw = function () {
let prev= 1, cur = 1
return {
 next() {
   [prev, cur] = [cur, prev + cur]
   return { done: false, value: cur }
 }
}
}
let fibraw = fibRaw()
console.log(fibraw.next().value)
console.log(fibraw.next().value)
console.log(fibraw.next().done)

/// iterator
let fibIterator = {
[Symbol.iterator]() {
 let prev = 1, cur = 1
 return {
   next() {
     [prev, cur] = [cur, prev + cur]
     return { done: false, value: cur }
   }
 }
}
}
for (let fibItem of fibIterator) {
if (fibItem > 100) break
console.log(`fibItem: ${fibItem}`)
}

/// generator
//// 迭代器的语法糖,代替next方法
let fibGenerator = {
*[Symbol.iterator]() {
   let prev = 1, cur = 1
   while (true) {
     [prev, cur] = [cur, prev + cur]
     yield cur      
   }
}
}
for(let fibGenItem of fibGenerator) {
if (fibGenItem > 100) break
console.log(`fibGenItem: ${fibGenItem}`)
}

ArrayBuffer(二进制数组)

ArrayBuffer对象、(TypedArray视图和DataView视图)是 JavaScript 操作二进制数据的一个接口。它们都是以数组的语法处理二进制数据,所以统称为二进制数组。

很多浏览器操作的 API,用到了二进制数组操作二进制数据,比如XMLHttpRequestWebSocket

ArrayBuffer对象代表储存二进制数据的一段内存,它不能直接读写,只能通过视图(TypedArray视图和DataView视图)来读写,视图的作用是以指定格式解读二进制数据。

setTimeout

  1. 计时器存在精度问题,比如 windows 上,精度为 15ms,如果设置成 8ms,那可以实际是 0ms 或是 15ms,所以要确保至少延迟 15ms,那要把时间设置大于 15ms。
  2. 定时器的计算时间,从 setTimeout 执行的时候就开始计算了
setTimeout(function() {
  // 会在下面的loop运行完后立马执行,而不是再等25ms
  console.log("timer invoke");
}, 25);
let time = Date.now();
while (Date.now - time < 30) {
  console.log("looping");
}

callback

Generator

  1. 生成器,是迭代器的语法糖。返回了一个{next(){return {done: true, value: ''}}的一个实现
  2. 与迭代器的使用,可以参考./Array.md 上的迭代器部分
  3. [Symbol.iterator] {next(){}} 可实现对象迭代器
  4. 还有$gen.return()、 $gen.throw()方法,都是可以终止迭代的, 相比原生迭代器,多了throw方法,还有return方法?
// 实现函数迭代器range迭代
function* range(start, end) {
  for (let i = start; i <= end; i++) {
    yield i;
  }
}
for (let i of range(3, 7)) {
  console.log(i);
}

function* gen() {
  let i = 0;
  while (true) {
    let ans = yield i++;
    if (ans === true) {
      i = 0;
    }
  }
}

let g = gen();
console.log(g.next().value);
console.log(g.next().value);
console.log(g.next().value);
console.log(g.next(true).value); // next中的参数被当作上一个yield的返回值
console.log(g.next().value);

Thunk

http://www.ruanyifeng.com/blog/2015/05/thunk.html

co

async

  1. async 函数总是返回 promise 对象
  2. generator/yield的升级版,自带执行器,免去了调用co/bluebird执行函数库的引用使用asyncpromise进行重构:
  3. async的兼容性没有generator和promise的好,async到chrome55才支持
来自babel上对async/await的pollfill

之前的:

async function abc() {
    await fn()
}

改好后的:

_asyncToGenerator(function *() {
  yield fn()  // 把await换成yield
})
function _asyncToGenerator(fn) {
  return function() {
    var gen = fn.apply(this, arguments);
    return new Promise(function(resolve, reject) {
      function step(key, arg) {
        try {
          var info = gen[key](arg);
          var value = info.value;
        } catch (error) {
          reject(error);
          return;
        }
        if (info.done) {
          resolve(value);
        } else {
          return Promise.resolve(value).then(
            function(value) {
              step("next", value);
            },
            function(err) {
              step("throw", err);
            }
          );
        }
      }
      return step("next");
    });
  };
}
var fetchCity = async function(cityName) {
  return $.get("/m/user/get-city-info", { cityName: cityName });
};

var citys = ["深圳市", "广州市", "北京市"];

var cityCodes = async function() {
  var ret = [];

  // method 1
  return Promise.all(citys.map(city => fetchCity(city)));

  // method 2
  return Promise.all(
    citys.map(async function(city) {
      var result = await fetchCity(city);

      return result;
    })
  );
};
cityCodes().then(d => console.log(d));

/// async polling
async function query() {
  let res = await polling();

  // 直到polling返回{done:true,value:"done"},才会执行到这里
  console.log(Date.now(), res);
}
let pend = () =>
  new Promise((resolve, reject) => {
    setTimeout(function() {
      let status = true;
      if (i++ > max) {
        status = false;
      }
      resolve({ status });
    }, 10);
  });
var max = 3,
  i = 1;
async function polling() {
  let res = await pend();

  if (i++ < 3) {
    console.log(Date.now, i);
    // 立即返回
    // 下面加不加await都好像是一样的
    //return await polling();
    // 等待1秒后再轮询
    return new Promise((resolve, reject) => {
      setTimeout(function() {
        resolve(polling());
      }, 1000);
    });
  } else {
    return {
      done: true,
      value: "done"
    };
  }
}

query();
调用时序

立即 resolved 的 Promise 是在本轮事件循环的末尾执行,总是晚于本轮循环的同步任务。

new Promise((resolve, reject) => {
  resolve(1);
  console.log(2);
}).then(r => {
  console.log(r);
});
// 2
// 1
reduce & Promise.resolve()组合使用

串行处理异步操作,各个异步操作之间不依赖返回值

function processAllUsers() {
  const sql = "SELECT id FROM users";
  return db.query(sql, []).then(users =>
    users.reduce((lastPromise, user) => {
      return lastPromise.then(_ => processUser(user.id));
    }, Promise.resolve())
  );
}

对错误的处理

async 里 promise 里的同步错误会立即错误,异步错误会被自身 promise 的 catch 捕获,或 async 返回的 promise 的 catch 捕获

(async function errorAsync() {
  // 这里的两种会立即报错
  await new Promise((resolve, reject) => {
    throw new Error("throw err");
    Promise.reject("reject err");
  });

  // setTimoue+ reject会在async返回 的promise's cache函数里捕获
  await new Promise((resolve, reject) => {
    setTimeout(function() {
      reject("err");
    }, 1000);
  });
})().catch(e => {
  console.log(e);
});

异步生成器

async + generator

async function* g() {}

async promise setTimeout 调用时序问题

async function fn1() {
  console.log(1);
  await fn2();
  console.log(7);
}
async function fn2() {
  console.log(2);
}
fn1();
console.log(3);
setTimeout(function() {
  console.log(8);
}, 0);
new Promise(resolve => {
  console.log(4);
  resolve();
}).then(_ => {
  // 竟然比async里异步要先执行,先进先出?
  console.log(6);
});
console.log(5);

Class

  1. Class不能定义在函数作用域内 ?

箭头函数

  1. 函数体内的this对象,就是定义时所在的对象,而不是使用时所在的对象。
window.a = 'window';
var obj = {
    a: 'obj',
    // 作为对比,这样是可以的正常使用this的
    fn () {
        return this.a
    },
    arrayFn: () => {
        return this.a
    }
}
obj.fn()  // => 'obj'
obj.arrayFn() // => 'window'
  1. 不可以使用yield命令,因此箭头函数不能用作 Generator 函数。
  2. 不可以当作构造函数,也就是说,不可以使用new命令,否则会抛出一个错误
  3. 不可以使用arguments对象,该对象在函数体内不存在。如果要用,可以用 rest 参数代替。

标签函数

String.raw: 输出模板字符串的原始值,同python的r String.raw\this line \n not break``

let & const 共同点

  1. 作用于块级作用域
  2. 没有变量提升
  3. 存在暂时性死区
typeof x            // => 'undefined'

typeof y;let y = 0; // => ReferenceError: y is not defined

var z = 0
{
    z            // ReferenceError: z is not defined
    let z = 0
}

for循环里保存变量,可替换闭包的效果 条件判断里的作用域是{}里的父作用域

for(let i = 0; i < 3;) {
    let i = i++   // 因为与上一行括号里的作用域不是同一个,所以这里let i不报错
    setTimeout(function () {
        console.log(i) // 1, 2
    }, 1000)
}

const

  1. const指向的引用的是内存地址,内存地址不变就行了,内存储存的内容是可以变的
const obj = {}
obj.foo = 'bar'  // 引用地址并没有变

obj = {}        // Error,引用地址改变了

http://es6.ruanyifeng.com/#docs/module-loader#ES6-模块与-CommonJS-模块的差异 https://auth0.com/blog/javascript-module-systems-showdown/

与commonJs的差异

  • CommonJS 模块是运行时加载,ES6 模块是编译时输出接口
  • ES6的编译时加载,决定了:esmodule的import&export不能不在块级作用域里;import $url的$url值不能是动态生成
  • �ESModule默认是严格模式滴
  • ESModule支持静态分析
  • esmodule: Circular dependencies supported
  • CommonJS 模块输出的是一个值的拷贝,ES6 模块输出的是值的引用

Issues

  1. nodejs默认使用commonJS,支持es6模块,nodejs > 8.5.0,得这样node --experimental-modules,extension: .mjs

模块的继承

export xx from xx

// 输出foo.js里面,除了default之外的属性
export * from './foo.js'
export default let foo = 'bar'
export function () {}

import & export

import * as service from './lib/service'
import default, {foo, bar} from 'module'

export

export var foo = 'bar'

export function foo () {
    
}

export const FOO = 'bar';

Object.assign

// https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/assign

Object.assign(target, source1, source2)

// Warn: 对于对象的复制,是引用,并不是deep copy
let obj1 = { a: 0 , b: { c: 0}};
let obj2 = Object.assign({}, obj1);
obj2.b.c = 3;
console.log(JSON.stringify(obj1)); // { a: 1, b: { c: 3}}
console.log(JSON.stringify(obj2)); // { a: 2, b: { c: 3}}

// deep clone
JSON.parse(JSON.stringify(deepObject))

__proto__ property

let obj = {
    __proto__: baseObj,
    toString: () => {
        return super.toString()
    }
}

getter & setter

getset函数名可以同名,但不能与变量a同名,不然循环引用

// object var
let o = {
    get getA() {
        return this.a
    },
    set getA(str) {
        this.a = str + 'lll'
    },
    a: 1
}
o.getA = '24343'
console.log(o.getA)

// Object.create
let childOfO = Object.create(o, {
    bar: {
        get: () => {
            return ''
        },
        set: (str) => {
            this.foo = str
        }
    }
})

// Object.defineProperty
Object.defineProperty(o, "b", {
        get: () => {
            return this.a;
        },
        set: (val) => {
            this.a = val;
        },
        configurable : true,
        enumerable : true
    });
    
// Object.prototype.__defineGetter__ && Object.prototype.__defineSetter__
o.__defineGetter__("giveMeA", function () {
        return this.a;
    });
o.__defineSetter__("setMeNew", function (val) {
    this.a  = val;
})

Reflect.apply与fn()的区别

You can compare the definition of Function.prototype.apply and Reflect.apply in the spec.

Basically they are equivalent, but there is a difference: if the arguments list is null or undefined, Function.prototype.apply will call the function with no arguments, and Reflect.apply will throw.

function func() {
  return arguments.length;
}
func.apply(void 0, null); // 0
Reflect.apply(func, void 0, null); // TypeError: null is not a non-null object

Another difference is that, when you use func.apply, you assume

func is a Function instance, i.e. it inherits from Function.prototype func has no apply own property which would shadow Function.prototype.apply But Reflect.apply doesn't require that. For example,

var obj = document.createElement('object');
typeof obj; // "function" -- can be called
obj.apply; // undefined -- does not inherit from Function.prototype
Reflect.apply(obj, thisArg, argList); // -- works properly
var func = a => a;
func.apply = a => 0;
func.apply(void 0, [123]); // 0 -- Function.prototype.apply is shadowed by an own property
Reflect.apply(func, void 0, [123]); // 123 -- works properly

Reflect与metadata

Reflect对象中与元数据相关的方法包括:

Reflect.defineMetadata():定义元数据,将元数据关联到指定的目标对象上。 Reflect.hasMetadata():检查目标对象是否具有指定的元数据。 Reflect.getMetadata():获取目标对象的指定元数据。 Reflect.getMetadataKeys():获取目标对象所有的元数据键。 Reflect.deleteMetadata():删除目标对象的指定元数据。

Reflect metadata与decorate的结合使用

import "reflect-metadata";

// 定义一个装饰器,用于添加元数据
function MyDecorator(metadata: string) {
  return function(target: any, propertyKey: string) {
    Reflect.defineMetadata("my-decorator", metadata, target, propertyKey);
  };
}
Reflect.metadata = MyDecorator;

// 定义一个类
class MyClass {
  @Reflect.metadata("some metadata")
  myMethod() {}
}

// 获取装饰器添加的元数据
const metadata = Reflect.getMetadata("my-decorator", MyClass.prototype, "myMethod");
console.log(metadata); // 输出: "some metadata"

http://es6.ruanyifeng.com/#docs/set-map

Set & Map

Set: 成员唯一,没有重复值(NaN不能重复,只能添加一次),故可以用来数组去重,

// 数组去重
function uniq (arr) {
    return [...new Set(arr)]
}

var s = new Set([1, 3, 4, 4])
var s = new Set(document.querySelectAll('div'))  // iterable param

for (var i of s) {
    console.log(i)
}
Array.from(s)

// 错误的用法 
s[0]

s.forEach(v => console.log(v))
s.keys() // SetIterator, not array
s.add(v)
s.delete(v)
s.has(v)
s.size

WeakSet

WeakSet 的成员只能是对象,而不能是其他类型的值,对象的引用方式为弱引用,在垃圾回收里不计数,也就是没有其他引用就可以回收了,不管WeakSet里的引用。

var ws = new WeakSet([
    [1, 3], [3, 4]
])


ws.add(obj)
ws.delete(obj)
ws.has(obj)

// WeakSet 不能遍历,是因为成员都是弱引用,随时可能消失,遍历机制无法保证成员的存在
ws.size // => undefined
// WeakSet没有迭代器接口
ws.forEach // => undefined

Map

Map键值对的数据结构,但键可以是除了字符串的其他类型(包括undefinednullNaN),Map 的键实际上是跟内存地址绑定的

var map = new Map([
    [global, 3],
    [window, 4]
])
var map2 = new Map(new Set([
    [global, 3]
])))

map.get(window) // => 3
map.set('foo', 'bar')
map.has(window) // => true
map.size // => 2
map.clear()

[...map] // => [[global, 3], [window, 4]]
for (var k of map.keys())
for (var [k, v] of map.entries())

WeakMap

只接受对象(null除外)作为键名 它的键名所引用的对象都是弱引用,不能使用values keys enties枚举,也不能使用clear

var wm = new WeakMap()
wm.set(obj, 2)

wm.size // undefined
// WeakMap没有迭代器接口
wm.forEach // undefined

WeakMap Use

  1. once an object is garbage-collected, its listeners or cache will be garbage-collected, too. In other words: there won’t be any memory leaks.
//cache
let cache = new WeakMap()
cache.set(obj, '')

// add listener
let listeners = new WeakMap()
function addListener (obj, fn) {
    if (!listeners.has(obj)) {
        listeners.set(obj, new Set())
    }
    listeners.get(obj).add(fn)
}
function trigger(obj) {
    let listeners = listeners.get(obj) || new Set()
    for (let action of listeners.keys) {
        action()
    }
}

protect data

Object&&OO.js里的private property/method

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment