JS 事件循环

web:

1、
浏览器有多线程,GUI渲染线程,定时器线程,IO线程(ajax)线程,事件触发线程,js线程。
2、
对于js而言,是单线程,一条线走到底。其他线程都是异步任务,处理把回调推入任务队列。
3、
js开始运行,把同步任务一个个入栈执行,这其中由于同步任务产生的异步回调都进入
任务队列了。执行完毕后,栈空。开始查询异步队列,一个个压入栈再执行,直至清空队列,其中过程再产生异步任务再次推入异步队列。不断循环的检查任务队列,执行任务队列的任务,压入任务队列。
其中任务队列有细分有宏任务,微任务。
4、
优先执行微任务,并且微任务清一列,宏任务清一个。
5、
同步任务执行可以看做第一个宏任务执行完毕。接着清空微任务队列,清一个宏任务,接着再清空微任务队列,再继续清一个宏任务,不断循环,直到没有,也可以看成微任务只有一列但是一列有多个,宏任务有多列但是一列只有一个。这是js的事件循环机制。
6、
属于微任务(microtask)的事件有以下几种:
Promise.then
MutationObserver
Object.observe
process.nextTick
7、
属于宏任务(macrotask)的事件有以下几种:
setTimeout
setInterval
setImmediate
MessageChannel
requestAnimationFrame
I/O
UI交互事件

setTimeout(() => {
  console.log('宏任务--1')
  Promise.resolve()
  .then(() => {
    console.log('微任务2--1')
  })
  .then(() => {
    console.log('微任务2--2')
    setTimeout(() => {
      console.log('宏任务--3')
    })
  })
})
console.log('同步任务--1')

new Promise((resolve) => {
  console.log('同步任务--2')
  resolve()
}).then(() => {
  console.log('微任务1--1')
})

Promise.resolve().then(() => {
  console.log('微任务1--2')
})
setTimeout(() => {
  console.log('宏任务--2')
  Promise.resolve().then(() => {
    console.log('微任务3--1')
  })
  setTimeout(() => {
    console.log('宏任务--4')
  })
})
console.log('同步任务--3')
console.log('同步任务--4')


Node:

https://nodejs.org/zh-cn/docs/guides/event-loop-timers-and-nexttick/

1、
node的事件分阶段来循环,
timers -> IO callbacks -> idle, prepare -> poll(incoming) -> check -> close
2、
入口在poll阶段,每个阶段都有自己的队列
3、
__定时器:__本阶段执行已经被 setTimeout() 和 setInterval() 的调度回调函数。
__待定回调:__执行延迟到下一个循环迭代的 I/O 回调。
__idle, prepare:__仅系统内部使用。
__轮询:__检索新的 I/O 事件;执行与 I/O 相关的回调(几乎所有情况下,除了关闭的回调函数,那些由计时器和 setImmediate() 调度的之外),其余情况 node 将在适当的时候在此阻塞。
__检测:__setImmediate() 回调函数在这里执行。
__关闭的回调函数:__一些关闭的回调函数,如:socket.on(‘close’, …)。
4、
轮询(poll)阶段有两个重要的功能:
计算应该阻塞和轮询 I/O 的时间。
然后,处理 轮询 队列里的事件。
这里会循环到清楚队列任务,或者达到node限制
5、
每个阶段结束都会查询清空process.nextTick的回调队列

const fs = require('fs')
fs.readFile('lib.js', () => {
  setTimeout(() => {
    console.log('cb setTimeout--1')
  }, 0)
  setImmediate(() => {
    console.log('cb setImmediate--1')
  })
})

process.nextTick(() => {
  console.log('nextTick--1')
})
setImmediate(() => {
  console.log('setImmediate--1')
})
setTimeout(() => {
  console.log('setTimeout--1')
  process.nextTick(() => {
    console.log('nextTick--3')
  })
}, 0)
console.log('script--1')

Promise.resolve().then(() => {
  console.log('resolve then--1')
})
new Promise((resolve) => {
  console.log('Promise--1')
  resolve()
}).then(() => {
  console.log('then--1')
  process.nextTick(() => {
    console.log('nextTick--3')
  })
})

setTimeout(() => {
  console.log('setTimeout--2')
  Promise.resolve().then(() => {
    console.log('setTimeout -- resolve then--1')
  })
  setImmediate(() => {
    console.log('setImmediate--3')
  })
}, 0)
console.log('script--3')
console.log('script--4')

process.nextTick(() => {
  console.log('nextTick--2')
  setImmediate(() => {
    console.log('setImmediate--2')
  })
})


补充

宿主环境提供的叫宏任务,由语言标准提供的叫微任务。

宏任务每次执行一个的原因:浏览器为了能够使得JS内部task与DOM任务能够有序的执行,会在一个task执行结束后,在下一个 task 执行开始前,对页面进行重新渲染 (task->渲染->task->…)

参考:https://segmentfault.com/a/1190000014940904?utm_source=tag-newest

JS new 一个对象干了什么事


function Person(name, age) {
  this.name = name
  this.age = age
}
Person.prototype.attr = 'human'
const man1 = new Person('123', 18)
console.log(man1.name)
console.log(man1.age)
console.log(man1.attr)

function _new(Constructor, ...param) {
  // 新建一个对象
  const obj = {} // Object.create(null)
  // 建立继承关系,__proto__指向构造函数原型,获得原型属性与方法
  // 这里获得 attr 属性
  obj.__proto__ = Constructor.prototype
  // 开始执行这个构造函数,就是这个构造函数的this方法属性挂到这个新对象上
  // 这里就是把 name 和 age 挂上去
  const ret = Constructor.call(obj, ...param)
  // const ret = Constructor.apply(obj, param)
  // 若构造函数中没有返回值或返回值是基本类型(Number、String、Boolean)的值,则返回新实例对象;
  // 若返回值是引用类型的值,则实际返回值为这个引用类型。
  return typeof ret === 'object' ? ret : obj
}
const man2 = _new(Person, '456', 20)
console.log(man2.name)
console.log(man2.age)
console.log(man2.attr)

JS的继承方式

1、原型链继承

function SuperType(type) {
  this.property = true
  this.colors = ['red', 'blue']
  this.type = type
}
SuperType.prototype.getSuperValue = function() {
  console.log(this.property)
}

function SubType(type) {
  this.subproperty = false
  this.subtype = type
}

// 如果这里是 SubType.prototype = SuperType.prototype
// 则 this.property 值为 undefined
SubType.prototype = new SuperType('super')

SubType.prototype.getSubValue = function() {
  console.log(this.subproperty)
}

const instance = new SubType('one')

instance.getSuperValue()
instance.getSubValue()

console.log(instance instanceof SubType, instance.constructor)
console.log(instance instanceof SuperType)

instance.colors.push('black')
console.log(instance.colors, instance.type, instance.subtype)

const instance2 = new SubType('two')
console.log(instance2.colors, instance2.type, instance2.subtype)

// 所有实例的 colors 属性指向同一个对象,一改全改
// 如果父类有参数,没法带进去

2、构造函数继承

function SuperType(type) {
  this.property = true
  this.colors = ['red', 'blue']
  this.type = type
}
SuperType.prototype.getSuperValue = function() {
  console.log(this.property)
}

function SubType(type) {
  SuperType.call(this, type)
  // 为了确保不被父级的属性覆盖,可以在这之后再赋值子类的属性
  this.subproperty = true
}

const instance1 = new SubType('one')

console.log(instance1 instanceof SubType, instance1.constructor)
console.log(instance1 instanceof SuperType, instance1.type)

// 没有用到原型的方法属性
// instance1.getSuperValue()

instance1.colors.push('black')
console.log(instance1.colors)

const instance2 = new SubType('two')
console.log(instance2.colors, instance2.type)

3、组合继承(原型链继承 + 构造函数继承)

function SuperType(type) {
  this.property = 'true111'
  this.colors = ['red', 'blue']
  this.type = type
}
SuperType.prototype.getSuperValue = function() {
  console.log(this.property)
}

function SubType(type) {
  SuperType.call(this, type) // 调用一次
  // 为了确保不被父级的属性覆盖,可以在这之后再赋值子类的属性
  this.subproperty = true
}

SubType.prototype = new SuperType() // 调用两次

// 如果没有这一步,最终顺着父类的原型链指向父类SuperType
SubType.prototype.constructor = SubType

const instance1 = new SubType('one')

console.log(instance1 instanceof SubType, instance1.constructor)
console.log(instance1 instanceof SuperType, instance1.type)

instance1.getSuperValue()

instance1.colors.push('black')
console.log(instance1.colors)

const instance2 = new SubType('two')
console.log(instance2.colors, instance2.type)

4、原型式继承

// 缺点:引用类型属性依然是共享的
function object(o) {
  function F(){}
  F.prototype = o
  return new F()
}

const person = {
  name: 'xiaoming',
  colors: ['1', '2']
}

const p1 = object(person)
const p2 = object(person)

console.log(p1.colors, p2.colors)

p2.colors.push('3')

console.log(p1.colors, p2.colors)

// 等同于
const p3 = Object.create(person, {
  name1: {
    value: 'xiaohong'
  }
})

console.log(p1, p2, p3, p3.name)

5、寄生式继承

function object(o) {
  function F(){}
  F.prototype = o
  return new F()
}

// 包了一层,定义了个方法增强对象,
// 缺点是函数没法复用,引用类型属性依然是共享的
function createObject(o) {
  const clone = object(o)
  clone.say = function() {
    console.log(this.colors)
  }
  return clone
}

const person = {
  name: 'xiaoming',
  colors: ['1', '2']
}

const p1 = createObject(person)
const p2 = createObject(person)

console.log(p1.colors, p2.colors)

p2.colors.push('3')

console.log(p1.colors, p2.colors)

p2.say()

6、寄生式组合继承

function SuperType(type) {
  this.name = 'true111'
  this.colors = ['red', 'blue']
}
SuperType.prototype.getSuperValue = function() {
  console.log(this.colors)
}

function SubType(name, age) {
  SuperType.call(this, name) // 调用一次
  // 为了确保不被父级的属性覆盖,可以在这之后再赋值子类的属性
  this.age = age
}

SubType.prototype = new SuperType() // 调用两次

// 如果没有这一步,最终顺着父类的原型链指向父类SuperType
SubType.prototype.constructor = SubType

const s1 = new SubType('saaa', 18)
const s2 = new SubType('sbbb', 19)

console.log('~1~', s1, s2)

s2.colors.push('yellow')

console.log('~2~', s1, s2)

// 这样每个 SubType 实例都有两组 name 和 colors,
// 一组是自身属性下,一组是原型链指向父类实例的

function inherit(newType, superType) {
  newType.prototype = Object.create(superType.prototype)
  newType.prototype.constructor = newType
}

function NewType(name, age) {
  SuperType.call(this, name) // 调用一次
  // 为了确保不被父级的属性覆盖,可以在这之后再赋值子类的属性
  this.age = age
}

inherit(NewType, SuperType)

const n1 = new NewType('nbbb', 20)
const n2 = new NewType('nccc', 21)

console.log('~3~', n1, n2, n1.constructor)

n1.colors.push('yellow')

console.log('~4~', n1, n2)




7、ES6 Class 继承(略)

总结

  寄生式组合继承是 ES5 最完善的继承方式。细细一看,不就是跟 new 一个实例所做的操作差不多吗。不同之处在于复制的对象原型不一样。当然具体还是有差别,一个是原型,一个实例化。

空间起源

  好记性不如烂笔头。从小听到大,真的很有道理。但其实小时候仗着记忆力好,就懒得动笔。无论是记作业还是上课记笔记。当然所谓的好记忆其实那一瞬间,犹如内存,断电就没了。随着年纪增大,可能记忆力变差,可能经历的事多,确实遗忘的东西变多了。所以琢磨着找个地方给记下来。当然这种地方很多,自己搭建一个纯属好(第四声)玩。记录一些心情随想,所见所闻,所接触到的开发技术,烹饪煮食。
  另外一个就是照片,以前老是觉得在于精不在于多,明明身处随时随地可照相的时代。后来在看一些地方旅游照片的时候,发现自己喜欢的街头巷尾照片太少了,就感觉缺失了那一环一样。以后也多给拍下来。
  放两张偶然拍的自己觉得好的照片。




“bu”鱼

5.1那天家里寄了些生鲜小鱼,用传统的“bu”法,我也不知这个是什么做法,用普通话怎么念。

做法

取一铁盆,加水没过鱼,倒入酱油,撒上盐,量全凭自己把握。滚 15~20 分钟即可。期间注意翻身,以免粘锅和受热不均匀。