image frame

脑洞大开の乱乱想

放下碎碎念,重新出发

互联网运营基本概念

不同层次的运营:

  • p1 运营生涯的入门。相关工作的执行和落实能力
  • p2 称为没有短板的运营。文案,数据分析,sop,第三方推广等
  • p3 找到长板。内容,推广投放,事件和活动,增长黑客

随时整理方法论。

说说你的学习计划

学习计划,先过一遍,先做笔记,再整理删除,形成自己的说话方式。

认识运营

从维护群开始

作为从业者,能讲清楚运营到底是什么吗?
如何运营好一个微信群,需要做哪些事?

有的群只发广告,有的群会活跃,会围绕某些主题讨论。这是现状。
要维护好微信群,有一些方法论可作为指导:

  1. 引入种子用户
  2. 组织群内话题
  3. 找到热情用户,加强沟通
  4. 用线下线上活动缔结关系
  5. 发展成群管理员,sop工作文档
  6. 群的良性发展

所以,运营就是通过一系列有目的的行为和资源投入,让事情持续良性运转。

##我对维护群的思考
这让我想起以前玩手机游戏建立的帮派群,这个群从火热,到最后凉凉解散。我都做了什么?
首先是【引入种子用户】在游戏里宣传qq群,鼓励大家加入群,方便交流心得。必要的话,催促就剩你了,群里有攻略和游戏物资兑换码
用户入群,我会主动欢迎和引导认识群管理,看公告。尽量让新人迅速找到话题参与,而不是第一时间屏蔽。【组织话题】
慢慢的,就有了核心用户,副会长或者氪金大佬,通过私聊优先送兑换码之类的,加深印象和联系,乐意维护群氛围,避免冷群。【寻找核心用户】
积极讨论游戏和群外交。鼓励贡献心得。也有意识鼓励分享生活,避免游戏无聊冷群。持续发展了一段时间,很多人愿意聊天胜过游戏。这里都是对的,良性的。
后来问题出在哪里?是人和人的矛盾激化,观点尖锐,游戏进入后期,游戏无聊,话题开始减少。最后大家弃坑退游。

现在来看,很多东西,是摸索出来的,也能对的上。

你如何理解互联网运营

研发负责生孩子,运营负责养孩子,运营统筹既有资源开展各种形式的方案,寻找新用户,维系老用户,让用户乐意投入时间和金钱。

不同的运营岗位完全不同

淘宝运营,地推,作图,找文章洗稿等等。
这是因为面向的主体不同,手段也就不同,比如淘宝用户,app用户,线下用户等。

一个业务的组成: 用户 产品 运营。 三个元素互相牵扯。具体不解释了。

思考:

  1. 互联网运营工作流程都有哪些
  2. 指定运营策略环节要做哪些事情
  3. 拉新 推广 转化的手段有哪些
  4. 用户维系的手段有哪些

    互联网运营工作流程

    工作流程:
  • 制定策略。评估产品阶段、形态、当前数据,有啥问题和趋势
  • 分解指标,规划工作。目标拆分。资源争取、扩展和分配。工作任务规划。
  • 执行落地,达成目标。文档,活动,传播,销售等
  • 检验数据,调整方向。数据分析、用户反馈、潜在发展方向和需求

运营策略:

  • 拉新 推广 推广
    • 第三方渠道推广
    • 病毒营销
    • 活动
    • 新媒体
    • 广告投放
    • 品牌传播投放
  • 用户维系
    • 客服
    • 用户体系建设
    • 用户激励
    • 用户召回
    • 运营机制设计
    • 活动

浏览器相关知识点

加载页面

简单版:

  • 浏览器根据DNS服务器得到ip
  • 向ip发送http请求
  • 服务器收到处理返回http请求
  • 浏览器获得内容
    具体细节在 browser-render 里

    渲染过程

    简单版:
  • 根据html生成 DOM 树
  • 根据css生成 CSSOM
  • 将DOM和CSSOM整合成 RenderTree
  • 根据 RenderTree 开始渲染和展示
  • 遇到 script 执行并阻塞渲染
    更多细节在 browser-render 里

    性能优化

    优化原则和方向

  • 多使用内存 缓存 和其他方法
  • 减少 cpu gpu计算
    因此,方向: 减少页面体积,提升网络加载;优化页面渲染

    减少页面体积,提升网络加载

  • 压缩合并 css js 图片
  • 缓存,md5文件名
  • cdn

    优化网页渲染

  • css前,js后
  • 懒加载
  • 减少dom查询
  • 减少dom操作,多个操作合并 documentFragment
  • 事件节流
  • 尽早执行 DOMContentLoaded
  • ssr
  • 后台ssr渲染

    优化图片

  • 压缩图片
  • 使用css替代图片
  • 拿空间换时间,图片多裁切,js先计算宽度,再发请求对应的尺寸
  • 小图片用base64
  • 合并图片
  • 正确选择格式
  • 懒加载

    节流

    throttle 滚动触发的次数太多
    其实就是不断 setInterval 计算距离上次的时间,够时间了才触发

    防抖

    多次点击按钮,百度输入按键智能搜索
    核心是 setTimeout 不断清计时器,然后添加新的。
    时间不到永远不触发。

    dns预解析

    1
    link rel=dns-prefetch href=xxx.cn

    预加载

    1
    link rel=preload href=a.html
    现在暂时不用,但希望尽快获取
    兼容性。

    预渲染

    1
    link rel=prerender href=a.html
    小说的下一页,极大概率会点击。

前端基础知识之css

选择器和权重

盒模型

*{box-sizing: border-box}
标准模型和ie模型的区别。默认content-box

js如何获取宽高?

dom.currentStyle.width
window.getComputedStyle(dom).width
dom.getBoundingClientReact().width 窗口位置。

BFC

边距重叠解决方案。margin合并
渲染规则:

  • BFC垂直边距会重叠
  • bfc区域不会和浮动重叠
  • 独立容器,外界 不会
  • 计算bfc高度,浮动不参与计算
    触发:
  • float 值不为none
  • position不是static relative
  • display table相关
  • overflow
    使用场景:
    比如清楚浮动。可以设置float overflow

    float

圣杯,双飞翼

Q:左侧100px,右侧200px,中间自适应。

  • 浮动。纯浮动,中间不需要指定宽度。left right center
  • 绝对定位。中间 left right 指定数值。
  • flex。 flex:1
  • 表格布局 table-cell
  • grid

定位

flex

重绘 回流

repaint reflow
重绘回流不可避免,我们优化的点是减少不必要的重绘回流
重绘,不脱离文档流,一些属性发生变化,比如颜色,背景等。
回流,DOM大小位置发生变化,浏览器需要重新渲染,或者全部重新渲染。

要进行优化,一般都是减少回流,比如先暂存DOM修改,一次性完成修改。

前端算法

预计,这是一篇需要长期更新的文章。想到啥就来补充吧。

合理使用更适合的算法可以提升代码执行效率,比如二分查找的效率更高。

前端常见的数据结构

  • 简单。
    • 有序数据结构。栈,队列,链表,有序数据结构省空间。
    • 无需数据结构。集合,字典,散列表。无需数据结构省时间。
  • 复杂。
    • 数,堆

简单数据结构对应js里就是 Object 和 Array

时间复杂度

  • 常数 o(1)
  • 对数 o(logN)
  • 线性 o(n)
  • 线性对数 o(nlogN)
  • 平方 立方 o(n^2) o(n^3)
  • 指数 o(2^n)
    小技巧,二重循环,就是 n^2。 有二分 logN 。 保留最高项。

举例分析算法复杂度

1
2
let i =0 //1
while(i<n){} //n

代码 1+n ,最终 O(n)

1
2
3
4
5
let num =1
while(num < n){
num*=2 // 增长速度 2^n 代码 logN
}
// 1+2*logN = O(logN)
1
2
3
4
for(){
for(){}
}
// n^2

基础算法

枚举和递归。

递归经典案例,js对象深拷贝。
递归容易造成爆栈,尾部调用可以解决递归问题。递归的爆栈问题可以通过把递归改成枚举,也就是 for while 代替递归

1
2
3
4
5
6
7
8
9
10
11
12
13
function deepClone(_empty, _target) {
for (let k in _target) {
if (typeof _target[k] === "object") {
_empty[k] = {};
deepClone(_empty[k], _target[k]);
} else {
_empty[k] = _target[k];
}
}
}

let tmp = Object.create(null)
deepClone(tmp,obj)

快排和二分查找

基于 分治思想。通过对数据分类处理,不断降低数量级,实现O(logN)

快速排序

随机选择数组a,以这个数为基准。
其他数组和这个数字比较,数字小的放左边,数字大的放右边
一次循环后,a左边是小于a的数,a右边是大于a的数
对左边和右边的数据再递归

二分查找

解决在一堆有序的数中找到指定的数。

  • 数组中排在中间的数字a,与要找的数字比大小
  • 因为数据是有序的,觉得从前后部分查找

二分查找logN

做题思路,找关键词: 有序的数列查找,要求算法复杂度 logN 一般就是二分思想

思路:

  1. 先降低数量级,拿可以计算出来的情况做解题步骤
  2. 有限把特殊情况处理
  3. 检验正确性
  4. 是否可以优化

    正则匹配

    比如一个字符出现的次数。可以for循环里使用 正则

1234567变成 1,234,567
算法可以,正则也可以。正则零宽断言

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
function exchange(num) {
num += ''; //转成字符串
if (num.length <= 3) {
return num;
}
num = num.replace(/\d{1,3}(?=(\d{3})+$)/g, (v) => {
console.log(v)
return v + ',';
});
return num;
}
console.log(exchange(1234567));


---

### 排序

https://segmentfault.com/a/1190000009426421

https://segmentfault.com/img/bVNIpc?w=554&h=337



数据结构与算法

https://juejin.im/entry/58759e79128fe1006b48cdfd

js递归

https://segmentfault.com/a/1190000009857470

波兰式,逆波兰式

和浏览器有关的js-web-api

  • BOM操作
  • DOM操作
  • 事件绑定
  • ajax
  • 存储

BOM

  • navigator
  • screen
  • location
  • history

DOM

property

获取和修改,是改变的js,js获取DOM节点等。

attribute

改变的是html的属性。

事件

事件绑定

普通事件绑定

1
2
3
4
5
6
var btn = document.getElementById('btn1')
btn.addEventListener('click', function (event) {
// event.preventDefault() // 阻止默认行为
// event.stopPropagation() // 阻止冒泡
console.log('clicked')
})

事件冒泡

事件会向上冒泡到body绑定的事件上。

事件代理

对 父类或者body添加事件监听,冒泡到body时候 e.target表示触发元素,如果触发元素的nodeName classlist 等是一样的,那就继续触发。

自己实现一个通用事件监听方法。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
function bindEvent(elem, type, selector, fn) {
if (fn == null) {
fn = selector;
selector = null;
}
elem.addEventListener(type, function(e) {
var target;
if (selector) {
// 事件委托
target = e.target;
if (target.matches(selector)) {
fn.call(traget, e);
}
} else {
fn(e);
}
});
}

网络请求

ajax

手写一个

1
2
3
4
5
6
7
8
9
10
11
var xhr = new XMLHttpRequest()
xhr.onreadystatechange =function(){
if(xhr.readyState===4){
if(xhr.state===200){
// do
xhr.responseText
}
}
}
xhr.open('GET','/api', false)
xhr.send(null)

fetch

比较新的底层技术

1
2
3
4
5
6
7
8
fetch('some/api/data.json', {
method:'POST', //请求类型 GET、POST
headers:{}, // 请求的头信息,形式为 Headers 对象或 ByteString
body:{}, //请求发送的数据 blob、BufferSource、FormData、URLSearchParams(get 或head 方法中不能包含 body)
mode:'', //请求的模式,是否跨域等,如 cors、 no-cors 或 same-origin
credentials:'', //cookie 的跨域策略,如 omit、same-origin 或 include
cache:'', //请求的 cache 模式: default、no-store、reload、no-cache、 force-cache 或 only-if-cached
}).then(function(response) { ... });

控制的更加底层。

跨域

浏览器为了数据安全
为了解决跨域问题

jsonp

先定义一个function

1
2
3
window.callback=function(data){
// 展示 data
}

然后script发送一个请求,返回 callback({}),这样就触发定义的function,实现了跨域

后端修改http头

1
2
3
4
5
6
response.setHeader("Access-Control-Allow-Origin", "http://m.juejin.com/");  // 第二个参数填写允许跨域的域名称,不建议直接写 "*"
response.setHeader("Access-Control-Allow-Headers", "X-Requested-With");
response.setHeader("Access-Control-Allow-Methods", "PUT,POST,GET,DELETE,OPTIONS");

// 接收跨域的cookie
response.setHeader("Access-Control-Allow-Credentials", "true");

补充,为了实现允许多个域跨域,可以建一个白名单。

存储

cookie

localstorage

es基础之变量类型

变量类型

数组类型分类和判断

定义了6中原始类型:Boolean String Number Null Undefined Symbol

如何判断原始类型?通过typeof就能得到以下值:

1
undefined boolean number string object function symbol

注意:
typeof null === object 这是一个bug,单独记忆
typeof [] === object 不会出现array
typeof Symbol() === symbol新增知识
可以把 undefined 翻译为 未定义。

通过使用 obj.a == null 隐式包含 null 和 undefined 的两层意思。

如何判断实例对应的构造函数,如何判断是不是数组,可以使用 instanceof

1
2
[] instanceof Array // true
new Foo() instaceof Foo // true

值类型 引用类型

值类型: Boolean String Number Undefined Null
引用类型:Date Array Function RegExp 等

引用类型使用同一块内存区域,所以a修改会影响b

深浅拷贝

既然是引用类型,完整拷贝一个对象?
浅拷贝。 Object.assign
如果对象里没有引用类型,都是简单类型

1
2
3
4
5
6
let a = {
age: 1
}
let b = Object.assign({}, a)
a.age = 2
console.log(b.age) // 1

es6 有了展开运算符,也干掉了 assign

1
2
3
4
5
6
let a = {
age: 1
}
let b = { ...a }
a.age = 2
console.log(b.age) // 1

如果对象里有嵌套。那不行了,还是得深拷贝
首先是JSON.stringify 这样有问题:

  • 忽略undefined
  • 忽略 symbol
  • 不能序列号函数
  • 不能解决循环引用的对象
    一般情况下可以干。

deepClone
lodash 的深拷贝函数https://link.juejin.im/?target=https%3A%2F%2Flodash.com%2Fdocs%23cloneDeep

类型转换

== 隐式转换。

1
2
’1‘ == 1
’1‘ == true

es基础之原型

原型和原型链

如何理解js原型?

引用类型: 数组 对象 函数

  • 引用类型都具有对象特性,都可以自由拓展属性,null除外
  • 引用类型都有一个 __proto__属性,值是对象
  • 所有函数都有prototype属性,值是对象
  • 引用类型,__proto__属性值 是 它构造函数的prototype的属性值

__proto__ 是非标准的。是一个对象,指向原型。es6里有了 Object.getPrototypeOf的形式。

注意 Function__proto__===Function.prototype

原型链

举例。

1
2
3
4
5
function Foo(){}
Foo.prototype.nn=function(){}
var f = new Foo()
f.nnn()
f.toString()

实例中,f本身没有nnn属性,它会到构造函数的prototype中寻找,一层层向上查找,直到找到。再比如都没有定义的.toString() 一直找到 Object 上找到,这就叫原型链。找不到就是 undefined

如何判断 f.nnn 是不是 f自身的属性?hasOwnProperty

1
2
3
4
var item
for(item in f){
if(f.hasOwnProperty(item){}
}

for..in 高级浏览器已经屏蔽了原型中的属性,以防万一还是得使用hasOwnProperty

我们通过instanceof可以获得继承的原型,但是不精准。精准的方法,利用原型链:

1
m.__proto__.constructor

new 运算符

new背后的 技术原理:

1
2
3
4
5
6
7
8
9
var new2 = function(func){
var o = object.create(func.prototype)
var k = func.call(o)
if(typeof k === 'object'){
return k
}else{
return o
}
}

第一条线,new一个实例。
var f1=new Foo()
f1.proto === Foo.prototype
Foo.prototype.constructor === function Foo(){}
顺着 Foo继续向上找。Function.prototype, Object.prototype, null

面向对象

类和继承

1
2
3
4
5
6
7
8
9
10
function Animal(){
this.name = 'name'
}
class Animal2{
constructor(){
this.name = 'name'
}
}
new Animal()
new Animal2()

实现继承

组合继承。核心是 Dog.prototype=new Animal()

1
2
3
4
5
6
7
8
9
10
function Parent(value){this.val = value}
Parent.prototype.getValue=function(){
console.log(this.val)
}
function Child(value){Parent.call(this, value)}
Child.protype=new Parent()

const child = new Parent()
child.getValue() //1
child instaceof Parent // true

核心。子类构造函数通过call继承了父类属性。然后设置子类原型是父类的实例实现继承

优点。可以传参,传给父类。
缺点,原型继承了父类实例,多了不许需要的属性,浪费。

既然,原型有问题,那就优化掉。

1
2
C5.prototype = Object.create(P5.prototype)
C5.prototype.consturctor = C5

断开 prototype ,设定constructor

class

class只是个语法糖,本质还是函数。
更贴合面向对象的写法,更容易阅读和理解。

1
2
3
4
class Person{}
typeof Person // 'function'
Person.prototype.constructor === Person
Person instanceof Function

class继承就简单了

1
2
3
4
5
6
7
8
9
10
11
12
13
class Parent{
constructor(value){this.val=value}
getValue(){this.value}
}
class Child extends Parent{
constructor(value){
super(value)
this.value =value
}
}
let child = new Child(1)
child.getValue //1
child instanceof Parent // true

原型的实际应用

jquery,zepto

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
//定义对象
var zepto = {
init:function(sel){
var dom = Array.from(document.qall(selector))
return zepto.Z(dom,selector)
}
}
// $(".a") 返回的是dom节点
var $=function(sel){return zepto.init(sel)}
// 构造函数
function Z(dom, selector){
var i,len=dom?dom.length:0
for i len i++{this[i]=dom[i]}
this.length=len
this.selector=selector||''
}
zepto.Z=function(dom,selector){return new Z(dom, selector)}
// 定义插件,定义方法
$.fn={
constructor:zepto.z,
css:function(){}
}
zepto.Z.prototype=Z.prototype=$.fn

jquery

1
jQuery.fn=jQuery.prototype

es基础之作用域和闭包

作用域和闭包

执行上下文

变量提升

1
2
3
4
console.log(a) // undefined var a 提升
var a =1
fn(1) //1 function提升
function fn(v){}

js在执行之前,会先预编译,把即将执行的变量和函数声明都拿出来:变量先给undefined,函数先声明等待使用。预编译完了才开始执行。

this

定义时候无法确认this,只有执行时候才能确认。

我们可以通过 call apply bind 来改变this的指向

关于this:

  • 直接调用foo,不管函数放到什么地方,this是Window
  • 对于,被调用的方式,谁调用指向谁
  • 通过 new 的方式,this始终绑定到 实例上,不会被改变

作用域和作用域链

内部能访问外部,外部不能访问内部,这就是作用域。
内部使用了一个属性,内部找不到就一层层向上查找,这就是作用域链。

闭包

1
2
3
4
5
6
7
8
9
function F1(){
var a =1
return function(){
console.log(a)
}
}
var f1 = F1()
var a = 200
f1()

闭包有两个应用场景:

  • 函数作为返回值
  • 函数作为参数传递

给大家开个眼

1
2
3
4
5
6
7
8
function a(){
let a = 1
window.b = function(){
console.log(a)
}
}
a()
b()

什么是闭包,函数a内部有函数b,b可以访问到a的变量,那么函数b就是闭包。

注意,很多人误认为闭包就是,函数嵌套函数,然后返回函数,这是有误解。

闭包的作用,让我们间接访问函数内部的变量。

用闭包解决问题

1
2
3
for(var i =0;i<=5;i++){
setTimeout(function timer(){console.log(i)},i*1000)
}

运行会返回一堆6,很明显异步函数执行的太晚了。

第一种用闭包来解决

1
2
3
4
5
6
7
for (var i = 1; i <= 5; i++) {
;(function(j) {
setTimeout(function timer() {
console.log(j)
}, j * 1000)
})(i)
}

第二种。setTimeout 传第三个参数。

1
2
3
4
5
6
7
8
9
for (var i = 1; i <= 5; i++) {
setTimeout(
function timer(j) {
console.log(j)
},
i * 1000,
i
)
}

第三种,就是用let了

1
2
3
4
5
for (let i = 1; i <= 5; i++) {
setTimeout(function timer() {
console.log(i)
}, i * 1000)
}

call apply bind

手写一个

call

1
2
3
4
5
6
7
8
9
10
11
Function.prototype.myCall = function(ctx) {
if (typeof this !== "function") {
throw new TypeError("error");
}
ctx = ctx || window;// 不传参数就是 window
ctx.fn = this; // 创建fn
const args = [...arguments].slice(1);// 多参数
const result = ctx.fn(...args);
delete ctx.fn;
return result;
};

apply

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
Function.prototype.myApply = function(context) {
if (typeof this !== 'function') {
throw new TypeError('Error')
}
context = context || window
context.fn = this
let result
// 处理参数和 call 有区别
if (arguments[1]) {
result = context.fn(...arguments[1])
} else {
result = context.fn()
}
delete context.fn
return result
}

bind

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
Function.prototype.myBind = function (context) {
if (typeof this !== 'function') {
throw new TypeError('Error')
}
const _this = this
const args = [...arguments].slice(1)
// 返回一个函数
return function F() {
// 因为返回了一个函数,我们可以 new F(),所以需要判断
if (this instanceof F) {
return new _this(...args, ...arguments)
}
return _this.apply(context, args.concat(...arguments))
}
}

new

自己写一个new?

  • 新生成对象
  • 链接到原型
  • 绑定this
  • 返回新对象
    1
    2
    3
    4
    5
    6
    7
    function create(){
    let obj={} // 1 新对象
    let Con=[].shift.call(arguments) // 获取传入参数,构造函数
    obj.__proto__=Con.prototype // 绑定空对象原型
    let result = Con.apply(obj,arguments) //绑定this并执行构造函数
    return result instanceof Object?result:obj
    }

    instanceof

    自己实现一个 instanceof
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    function myInstanceof(left,right){
    let prototype=right.prototype
    left = left.__proto__
    while(true){
    if(left===null || left===undefined)
    return false
    if(prototype===left) return true
    left=left.__proto__
    }
    }
    获取右边的原型。获取左边的原型。循环判断原型,直到null

js基础之es6+

es6

const let 存在的意义

function会提升,var会提升
let不会挂载到window

因为存在暂时性死区,如果先使用再声明,会报错。
也有提升,在编译阶段可以访问作用域,但访问时有限制的。

箭头函数

一个是简洁,另一个是不绑定this

如果函数中,是简单 return,不包含 this 递归等
如果函数依赖bind

Set

发现用来计数去重很容易

1
2
var a=Array.from(query()*)).map(i=>i.tagName)
new Set(a).size

模块化

es6的模块化:

  • import..from 导入
  • export导出

如何使用,如何打包?
babel来转义,这里webpack都写过了。

rollup 比较高级,适合库的打包。

1
yarn add rollup rollup-plugin-node-resolve rollup-plugin-babel babel-plugin-external-helpers babel-preset-latest

Class

1
2
3
4
5
6
7
8
9
class MathHandle {
constructor(x,y){
this.x=x
this.y=y
}
add(){}
}
const m = new MathHandle(1,2)
console.log(m.add()

实现继承更简单了。class Dog extends Animal

和普通构造函数有何区别?

Set Map

新增的数组结构。
Set类似数组,但不允许重复。
Map类似对象,key可以是任意数据类型

1
2
3
4
5
6
7
8
//Set 去重
new Set([1,1,1,2,3])
// 方法属性
// size add() delete() has() clear()
// 遍历
set.keys() == set.values()
set.entries()
set.forEach()

Map

1
2
3
4
const map = new Map()
const obj = {p: 1}
map.set(obj, 'ok') // 设置
map.get(obj) // 获取ok

展开运算符

展开运算符作用:

  • 两个数组进行合并,干掉了 concat
  • 自动封装 arguments 剩余参数合并

循环

for...in key循环。 for...of 值循环。

正则表达式

可以通过 reg.flags 输出,按照 gimuy

Symbol

原始值不能通过instanceof判断类型,可以改造

1
2
3
4
5
6
class PrimitiveString{
static [Symbol.hasInstance](x){
return typeOf x === 'string'
}
}
'aaa' instanceof PrimitiveString

Promise

单独开一篇。

es基础之异步

异步

setTimeout 会放到代码结束之后执行。
异步不会阻塞后面程序执行

异步和单线程

1
2
3
var a = true
setTimeout(function(){a=false},100)
while(true){console.log)('x')}

因为js是单线程的,进入while之后会死循环,没有线程去跑定时器。

异步的场景

setTimeout setInterval 和 网络请求

promise

##async/await

要合理使用 async,要小心不必要的promise依赖

定时器

setTimeout(0) 不是马上执行,是单线程的js忙完了其他逻辑才运行,是异步的

可以考虑 requestAnimationFrame 这个函数自带节流 16.6ms

promise

单独开一篇吧。

浏览器中的 eventLoop

js遇到异步函数,会放到task里,等到当前执行队列空,开始执行task。

本质上,异步还是同步演变。

task分为两种:

  • 微观任务micro task 称为 job
  • 宏观任务 marco task 称为 task
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
console.log('script start') // 1

async function async1() {
await async2()// 去下一个 让出来
console.log('async1 end')//7
}
async function async2() {
console.log('async2 end')//2
}
async1()

setTimeout(function() {
console.log('setTimeout')//9
}, 0)

new Promise(resolve => {
console.log('Promise') //3
resolve()
})
.then(function() {
console.log('promise1')//5
})
.then(function() {
console.log('promise2')//6
})
console.log('script end') //4

观察这段代码。1-4 5-9
1-2牢记promise await是回调。
2-3-4 顺序执行 promise
然后继续执行 promise的回调 5-6
7 promise任务结束,继续回调
9 setTimeout

总结:

  • 先执行同步代码,这是宏观任务。
  • 同步代码结束,当前执行队列空,看异步代码
  • 执行微观任务
  • 渲染页面
  • 执行完微观任务,回到宏观任务执行setTimeout等

所以,promise和 setTimeout 比,promise是微观任务先执行

微观任务 process.nextTick promise MutationObserver
宏观任务 script setTimeout setInterval setImmediate I/O UI Rendering

node中的 event loop

和浏览器完全不同
node分为六个阶段,反复执行

timers – pending callbacks – idle,prepare – poll – check – close callbacks

timer

执行 setTimeout setInterval

I/O

处理上一轮循环中少数未执行的I/O 回调

poll

  • 回调timer阶段
  • 执行 io 回调

慢慢看吧。

请我喝杯咖啡吧~