yujianAI/segmentfault_upvote5_clean huggingface dataset:
问题：http和https的区别？
我知道https更加安全，问题是怎么个安全法？
比如，我现在有一个纯的静态页面，就是做信息展示，就是我们说的marketing site,这里面也没有什么ajax请求啥的，是不是就http/https从安全角度就没有区别了？？？？
但是浏览器还是标记Http not secure呢？哪里不安全了？

回答：小明要给小红写信，但小明小红之间离的太远了，没法直接交换信件，只能是小明先给小强、小强再给小刚、小刚再给小红，小红收到了以后回信给小刚、小刚再给小强、小强才能给到小明。小强和小刚在这个过程中都可以拆开信看到内容、甚至模仿小明小红的字迹去篡改这封信。本来可能小明写的是情书，被小刚改成了战书，小红收到了不得气炸了。
你现在用浏览器访问了思否网站，但你的电脑跟思否网站服务器显然并不是一根网线直连的，中间会经过层层设备转发，有路由器、交换机、中继器、代理、网关等等等等。而 HTTP 是明文的，也就是这中间的每一个设备都可以监听、甚至篡改 HTTP 报文。即便你是纯静态的网站，但你怎么确保浏览器收到的一定就是你的服务器返回的那个 HTTP 报文、中间没经过其他设备的监听和篡改呢？本来你服务器返回的是一个企业宣传网站，结果被篡改成了一个黄赌毒网站。
这种监听和篡改在计算机中被称之为“中间人攻击” —— 顾名思义，就是因为通信两端中间的某些节点发生的攻击。而 HTTPS 要解决的就是这个问题。具体还可以细分为单向认证和双向认证。单向认证只能确保客户端收到的一定是服务器返回的内容，但无法保证服务器收到的一定是客户端发出的内容；双向认证则两端都可以确保不被篡改。
扩展思考：前面说因为客户端和服务器离得太远所以不得不需要很多“中间人”来转发，那么假如客户端和服务器用网线直接连接、去掉这些“中间人”，用 HTTP 就会安全吗？答案是依然不安全，至于为什么，您可以先自己思考一下。

标签: http, https
问题：为什么感觉redis队列不如mysql稳定，容易丢数据，原因何在？
目前的开发环境：php7.2 | thinkphp
1.用mysql实现的架构：
生产者：大量数据先存入mysql中间表（mysql中间表用唯一索引约束唯一性）
多个消费者：循环从mysql中间表中取数据，进行计算后存入mysql结果表
2.用redis实现的架构：
生产者：大量数据先进入redis集合（为了保证重复数据不入队列），确定能进入集合的数据再塞入redis队列
多个生产者：循环从redis队列中取数据，每取一条数据后将该数据从redis集合中删除，对数据进行计算，计算后的数据存入mysql结果表
两种架构在实际使用中发现，用纯mysql的方式实现，结果数据很稳定，没有出现丢失的情况。
而用redis的方式实现，会发现存在数据丢失的情况（即结果表里缺少数据），而且还找不到数据丢失的原因，trycatch和redis日志均无问题。用redis集合加redis队列作为中间过度的方式为什么会不稳定？原因在哪里？

回答：首先，Redis什么时候会出现`丢数据`？
Redis持久化有两种方式`RDB`和`AOF`，它只会在重启时可能丢失数据。正常运行的情况下，是不会丢失数据的。
找不到丢失的原因？，为什么不稳定？
MySQL有ACID，强一致性事务支持。而Redis分步操作，每步都可能出问题。
`而且还找不到数据丢失的原因`
分步骤来看：
- 大量数据先进入redis集合？ 是否正常
- 再塞入redis队列？ 是否正常

`循环从redis队列中取数据，每取一条数据后将该数据从redis集合中删除，对数据进行计算，计算后的数据存入mysql结果表`
这是一个很大的步骤。最简答的方法，加日志，看看是哪里丢的数据。比如取了数据从Redis删除了，计算时出现问题，没有写MySQL。
你抛出的问题，别人很难处理。没有代码不请求具体实现，而且你没有耐心的去找原因。每个步骤都加详细的日志，不可能存在找不到原因这种问题。

标签: 后端, php, java, mysql, redis
问题：JS 中通过apply实现数组中，查找最大值的方法原理是什么？
JS数组中无法直接通过`Math.max`查找数组中元素的最大值，但可以通过`apply`方法实现，底层实现逻辑及原理是什么样的呢？
```
Math.max.apply(null, [1,2,3]);
```

回答：我们这样来理解，
首先，
```
Math.max(value0, value1, /* … ,*/ valueN) 
```
这个函数的参数并不是数组，比如你有N个数字比大小，哪它就要输入N个参数，
对于在数组中的数字，你把整个数组当参数给它，Math.max就认为只有一个参数了。所以比不了
也就是说，你要把数组中每一位数字，都转成它的参数
```
const arr=[1,2,3]
//最笨的办法是
Math.max(arr[0],arr[1],arr[2])
//最新的办法
Math.max(...arr)
```
上面都是把数组的每一位，变成参数传个Math.max
哪为什么，Function.prototype.apply() 就可以呢，因为apply(thisArg, argsArray),第二个参数，就是接受一个数组对像，把它变成调用者的所有参数，

标签: javascript
问题：为什么mysql的依赖scope是runtime？
如题，runtime意味着打包时不包含mysql依赖，没有mysql驱动发布后的项目如何正常连接数据库呢？

回答：因为JDBC通常你在编码的时候依赖的只是JDBC接口，而不直接依赖mysql JDBC对吧？
想想你在连接mysql的时候是不是用的类似于这样的代码?
```
Class.forName("com.mysql.jdbc.Driver");
Connection conn = DriverManager.getConnection(url, username, password);
// ...
```
我们会发现我们会使用反射方法`Class.forName`在运行的时候从ClassPath中加载Mysql JDBC,后续调用的时候均没有使用任何MySQL JDBC的专有方法，而是用的JDBC接口，对吧？
所以JDBC Driver不是编译依赖(Compile Scope)，因为我们的代码中没有直接使用JDBC Driver的专有方法，所以编译的时候不需要JDBC Driver存在。JDBC接口才是编译依赖，JDBC Driver属于运行依赖(Runtime Scope)。
你声明Runtime依赖的时候在编译中是不会将依赖引入ClassPath的，但是打包的时候会，所以无需担心，JDBC Driver就应该是Runtime Scope依赖。
Compile Scope指的是编译的时候需要该依赖，通常编译依赖也是Runtime依赖，而Maven/Gradle对于compile依赖通常都是编译之后同时也包含在fat jar中的，相当于Compile就隐含Runtime。某些依赖也可能是CompileOnly依赖，比如注解处理器就是典型的CompileOnly依赖，只有编译的时候需要，编译过后在运行中是不需要的

标签: mysql, java
问题：chrome控制台调试：f9 和 f11 有何区别?
f10(跳过下一个函数调用)、f11(进入下一个函数调用)用的很熟，但f9(单步调试)没用过，经过测试感觉和f11一样，遇到函数就进入函数里边执行。那么f9 和 f11 有何区别？

回答：stackoverflow
You can spot the difference while running async code or multi-threaded code.
Step into: DevTools assumes that you want to pause in the asynchronous code that eventually runs
Step: DevTools pause in code as it chronologically ran
Consider this example:
```
setTimeout(() => {
    console.log('inside')
}, 3000);
console.log('outside')
```
After stopping on the breakpoint on the first line (`setTimeout(() => {`).
Step into: it waits 3 seconds and stops on the 2nd line (`console.log('inside')`)
Step: it pauses on the 4th line (`console.log('outside')`)
Link to the docs: https://developers.google.com...

标签: 前端, javascript, chrome, chrome-devtools, 调试
问题：函数传入的值，作为对象的 key 返回，TS 应该如何写？
返回的 `ts` 一定有 `a` 这个 `key`：
```
function test(a:string, b:object){
  return {
      [a]:b[a]
  }
}
```

回答：```
function test<T extends Record<string, any>, K extends keyof T>(a:K, b:T){
  return {
    [a]:b[a]
  } as Pick<T, K>
}

```

标签: typescript, 前端, javascript
问题：如果一次网页操作本身就只会触发一次回流/重绘，那么虚拟 dom 还有优势吗?
按照我目前的理解,虚拟dom的优势在于把当前的变化打包给浏览器,让浏览器一次性更新,而不是来一个更新一个,减少页面的回流和重绘.
但是如果只有一个按钮, 点一下文字变色或者改变某个元素的显隐性,那么这种操作浏览器应该本身就是只回流/重绘一次,这个时候虚拟dom还有优势吗?
#### 还有个小问题: 默认浏览器是怎么更新元素的?
如果我一次 display:none 了页面中的9个元素,那么在没有虚拟dom的情况下,浏览器会回流/重绘9次吗

回答：Q1：
> 但是如果只有一个按钮, 点一下文字变色或者改变某个元素的显隐性，那么这种操作浏览器应该本身就是只回流/重绘一次，这个时候虚拟 DOM 还有优势吗？那肯定是原生直接操作 DOM 更快。
但问题是这种简单页面的场景为啥你要用 MVVM 框架？
Q2：
> 如果我一次 display:none 了页面中的 9 个元素，那么在没有虚拟 DOM 的情况下，浏览器会回流/重绘 9 次吗。
这里的“一次”，我理解为是一段代码里连续控制 9 个元素隐藏，类似：
```
el1.style.display = 'none';
el2.style.display = 'none';
el3.style.display = 'none';
// 略...
```
那么答案是不一定。
老浏览器的话是回流 9 次（没错，就说你呢，明天就要退休的 IE）。现代浏览器会有渲染队列来对这种场景做优化，一段时间内的重复回流操作会被合并（但要注意这里的队列可能会被另一些操作打断并清空，比如 `getBoundingClientRect`、`getComputedStyle` 等等，这也很好理解，这些操作是要获取当前 DOM 状态的，自然要必须强制触发一次回流才可以拿到实时状态）。
而回流必然会重绘，重绘不一定回流。所以次数这里两者是一样的。

标签: 前端, javascript, vue.js, react.js, chrome
问题：来个 js 算法大牛看看这个题,有没有优雅点的代码?
`源数据`
```
const data = [
  {
    city:'浙江',
    children:[
      {
        city:'宁波',
        children:[{
          city:'鄞州区',
          children:[]
        },{
          city:'江北区',
          children:[]
        }]
      },
      {
        city:'杭州',
        children:[{
          city:'富阳',
          children:[]
        },{
          city:'上城区',
          children:[]
        }]
      }
    ]
  },
  {
    city:'上海',
    children:[
      {
        city:'黄浦区',
        children:[]
      }
    ]
  }
]
```
`目标结构`
```
[
  "浙江,宁波,鄞州区",
  "浙江,宁波,江北区",
  "浙江,杭州,富阳",
  "浙江,杭州,上城区",
  "上海,黄浦区"
]
```

回答：## solution 1
```
function flatTree(nodes, parents = []) {
    const pathes = nodes
        .filter(({ children }) => !children?.length)
        .map(({ city }) => [...parents, city]);
    const subPathes = nodes.filter(({ children }) => children?.length)
        .flatMap(({ city, children }) => flatTree(children, [...parents, city]));
    return pathes.concat(subPathes);
}
```
## solution 2
```
function flatTree(nodes, parents = []) {
    return nodes.flatMap(({ city, children }) => {
        return children?.length
            ? flatTree(children, [...parents, city])
            : [[...parents, city]];
    });
}
```
## using
```
console.log(flatTree(data).map(path => path.join(",")));
//                         ^^^^^^^^^^^^^^^^^^^^^^^^^^^
```

标签: javascript, 前端
问题：怎么让 a == 1 && a==2 && a==3  的结果为 true？
让 a == 1 && a==2 && a==3  的结果为 true

回答：```
function A() {
    this.value = 0;
    this.toString = function() {
        return ++this.value
    }
}
const a = new A();

a == 1 && a == 2 && a == 3
```
相等运算符 `==` 和 `!=` 使用 抽象相等比较算法 比较两个操作数。可以大致概括如下：
- 如果两个操作数都是对象，则仅当两个操作数都引用同一个对象时才返回 `true` 。
- 如果一个操作数是 `null`，另一个操作数是 `undefined`，则返回`true` 。
- 如果两个操作数是不同类型的，就会尝试在比较之前将它们转换为相同类型：当数字与字符串进行比较时，会尝试将字符串转换为数字值。如果操作数之一是 `Boolean`，则将布尔操作数转换为1或0。如果是 `true`，则转换为 `1`。如果是 `false`，则转换为 `0`。如果操作数之一是对象，另一个是数字或字符串，会尝试使用对象的 `valueOf()` 和 `toString()` 方法将对象转换为原始值。
- 如果操作数具有相同的类型，则将它们进行如下比较：`String`：`true` 仅当两个操作数具有相同顺序的相同字符时才返回。`Number`：`true` 仅当两个操作数具有相同的值时才返回。`+0` 并被 `-0` 视为相同的值。如果任一操作数为 `NaN`，则返回 `false`。`Boolean`： `true` 仅当操作数为两个 `true` 或两个 `false` 时才返回 `true`。

如果两个操作数是不同类型的，就会尝试在比较之前将它们转换为相同类型：
- 当数字与字符串进行比较时，会尝试将字符串转换为数字值。
- 如果操作数之一是 `Boolean`，则将布尔操作数转换为1或0。如果是 `true`，则转换为 `1`。如果是 `false`，则转换为 `0`。
- 如果操作数之一是对象，另一个是数字或字符串，会尝试使用对象的 `valueOf()` 和 `toString()` 方法将对象转换为原始值。

如果操作数之一是 `Boolean`，则将布尔操作数转换为1或0。
- 如果是 `true`，则转换为 `1`。
- 如果是 `false`，则转换为 `0`。

如果操作数具有相同的类型，则将它们进行如下比较：
- `String`：`true` 仅当两个操作数具有相同顺序的相同字符时才返回。
- `Number`：`true` 仅当两个操作数具有相同的值时才返回。`+0` 并被 `-0` 视为相同的值。如果任一操作数为 `NaN`，则返回 `false`。
- `Boolean`： `true` 仅当操作数为两个 `true` 或两个 `false` 时才返回 `true`。

来自 https://developer.mozilla.org...

标签: javascript
问题：js 函数 return false 跳出外部函数，怎么写？
```
function a(){
   if（true）
       return false;
}
```
这是没有任何问题的，如果我改成这种
```
function Test(){
   a();
   b();
   c();
}
```
js 函数a() return false 跳出外部函数Test()，怎么写？

回答：`return` 只能退出当前函数，不具备跨函数的功能。但是 `throw` 具有不限层次中断的功能，所以这个需要可能需要使用 throw 来完成。
一般情况下 `throw` 应该抛出一个 `Error` 对象。但是 JavaScript 并未对 `throw` 的对象有所有限制，所以也可以抛出其它东西，在确实不需要抛出错误的情况下，抛出一个状态对象，甚至值都是可以的。但不管抛出什么，要想外面的程序能正常运行，都是需要 `try ... catch` 的。
举例：
```
function a() {
    console.log("[Trace] a()");
    if (true) {
        throw "true-as-false";
    }
}

function b() {
    console.log("[Trace] b()");
}

function c() {
    console.log("[Trace] c()");
}

function test() {
    try {
        a();
        b();
        c();
    } catch (stat) {
        if (stat === "true-as-false") {
            return;
        }

        // TODO 其他情况正常处理可能发生的错误
    }
}

test();
```
只会输出 `[Trace] a()`，因为被 throw 给中断掉了。
如果是有一堆判断函数，只要有一个 flase 就退出，那就可以用数组来处理，比如
```
const fns = [
    () => { console.log(true); return true; },
    () => { console.log(true); return true; },
    () => { console.log(false); return false; },
    () => { console.log(true); return true; },
];

fns.every(fn => fn());
// true
// true
// false
```
只要遇到一个返回 `false` 的就结束了（注意，返回 `undefined`，也就是无返回值 …… 也表示 `false`)

标签: javascript
问题：判断一个字符串，A-Z对应输出0-25，a-z对应输出26-51，有什么简洁的写法吗？
比如：'AZaz' => '0 25 26 51'  
我目前的做法：
```
function Words(words){
    let strArray = []
    for (let s of words){
        if(/[A-Z]/.test(s)) strArray.push(s.charCodeAt(0)-65)
        else if(/[a-z]/.test(s)) strArray.push(s.charCodeAt(0)-71)
    }
    return(strArray.join(' ')) 
}

Words('AZaz')
```

回答：```
var Words=(s,a='ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz')=>s.split('').map(v=>a.indexOf(v)).join('');

Words('AZaz');    
```

标签: javascript, 前端, 算法
问题：来个 js 大牛,如何简洁的处理这种数组转换成对象?
有这样一个 Array,
`[{key:xxx},{value:xxx}]`
`key` `value`是固定的属性
```
[
    {key:'alan',value:12},
    {key:'mike',value:18}
]
```
期望值
```
{alan:12,mike:18}
```
希望不借助新的变量,简洁优雅一些~

回答：其实所谓简洁，无非就是找现成的功能函数替换掉一堆逻辑代码……所以，自己封个工具函数，一句调用是最简洁的！
```
const data = [
    { key: "alan", value: 12 },
    { key: "mike", value: 18 }
];

// 就这句话，换行主要是为了看清楚，可以不换
const result = Object.fromEntries(
    data.map(({ key, value }) => [key, value])
);

console.log(result);
```

标签: javascript, 前端
问题：Vue如何动态修改template?
先上代码：
```
<template>
    <div v-html="marked(content)"></div>
</template>
<script>
....
....
methods:{
   markdown (content) {
      var renderer = new marked.Renderer()
      renderer.image = (href, title, text) => {
        return '<a @click="showFullImage">' + text + '</a>'
      }
      marked.setOptions({
        renderer: renderer,
        gfm: true,
        tables: true,
        breaks: true,
        pedantic: false,
        sanitize: false,
        smartLists: true,
        smartypants: false,
        highlight: (code) => {
          return hljs.highlightAuto(code).value
        }
      })
      return marked(content)
   }
}
</script>
```
我在使用vue做一个工具，需要输入框中的markdown代码转为html，然后展示出来。
其中我想做的一个功能是，当用户输入：
```
![pic](www.example.com/img.jpg "title")
```
使用marked的自定义渲染函数，转换成html（已经实现）:
```
<a @click="showFullImage">title</a>'
```
现在的问题是，在展示的时候，使用v-html无法完成函数绑定。有没有好的方法？
简洁的说法是，我需要自己生成一段vue格式的html字符串，然后将这个字符串渲染出来，里面有各种vue指令需要绑定，该怎么做呢？

回答：我是在一个子组件中实现的，你可以动态的添加该子组件：
下面的`content`是markdown格式的数据，
`../common/markdown`文件是自己写好的基于marked的解析函数，它会将`Markdown`格式析为`Vue`格式的字符串:
```
![图片文字](url)
// 上面会解析为：
<img src="url" @click="showInfo('图片文字')">
```
用下面的方法即可以实现点击图片时，会输出信息。当然其他的vue处理方法同样支持。
```
<template >
  <div ref="markedContent"></div>
</template>

<script>
import Vue from 'vue'
import markdown from '../common/markdown'
export default {
  name: 'wf-marked-content',
  props: ['content'],
  mounted () {

    // 调用compile方法。你也可以将写在这里。
    // 但是代码太多，我个人不喜欢
    this.compile()
  },
  methods: {
    compile () {
      // 变量html是生成好的vue格式的HTML模板字符串，
      // 这个模板里面可以包含各种vue的指令，数据绑定等操作，
      // 比如 v-if, :bind, @click 等。
      const html = markdown(this.content)
      
      // Vue.extend是vue的组件构造器，专门用来构建自定义组件的，
      // 但是不会注册，类似于js中的createElement，
      // 创建但是不会添加。
      // 在这里创建出一个子组件对象构造器。
      const Component = Vue.extend({
      
        // 模板文件。由于Markdown解析之后可能会有多个根节点，
        // 因此需要包裹起来。
        // 实际的内容是：
        // `<div><img src="url" @click="showInfo(`图片文字')"></div>`
        template: `<div> ${html} </div>`,
        
        // 这里面写的就是这个动态生成的新组件中的方法了，
        // 当然你也可加上data、mounted、updated、watch、computed等等。
        methods: {
        
          // 上面模板中将点击事件绑定到了这里，因此点击了之后就会调用这个函数。
          // 你可以写多个函数在这里，但是这里的函数的作用域只限在这个子组件中。
          showInfo (title) {
            console.log(title)
          }
        }
      })
      
      // new Component()是将上面构建的组件对象给实例化，
      // $mount()是将实例化的组件进行手动挂载，
      // 将虚拟dom生成出实际渲染的dom，
      // 这里的markedComponent是完成挂载以后的子组件
      const markedComponent = new Component().$mount()
      
      // 将挂载以后的子组件dom插入到父组件中
      // markedComponent.$el就是挂载后生成的渲染dom
      this.$refs['markedContent'].appendChild(markedComponent.$el)
    }
  }
  // 本质上来讲，这个子组件不是任何组件的子组件，
  // 它是由vue直接在全局动态生成的一个匿名组件，然后将它插入到当前位置的。
  // 也正是因此，它才能够完成动态的生成和添加。
}
</script>
```

标签: vue.js, marked
问题：怎么判断一个数组里是否嵌套子数组
我怎么判断一个数组是不是双层呢，判断后返回true和false,比如[1,1,1,1]就是false，[1,[2,2,2],1,1,1]就是true,有啥方法么，如果是三层,怎么判断呢

回答：`isDeepThen()` 用来判断最大深度是否超出指定 level；`getMaxDeep()` 用来获取最大深度。都是递归实现。两个函数没有直接关系，只是用来干不同的事情。
```
const cases = [
    [1, [2, [3, [4], [5]]]],
    [1, [2, [3]]],
    [2, [2, 3]],
    [1, 2, 4],
    1
];

// 检查最大深度是否超过指定 level，如果 level 传 0 表示只要是数组就行
// 可以通过 getMaxDeep() 来判断，但是 getMaxDeep() 会遍历所有项，效率较低
// isDeepThen() 有快递中断机制
function isDeeperThen(arr, level = 0) {
    // 不是数数组，肯定是 false
    if (!Array.isArray(arr)) { return false; }
    // 如果是数组，层次肯定大于 0
    if (level === 0) { return true; }

    // 找到所有数组元素进行递归检查
    return arr.filter(el => Array.isArray(el)).some(el => isDeeperThen(el, level - 1));
}

// 获取最大深度（与上面那个 isDeepThen() 没关系）
function getMaxDeep(arr) {
    // 不是数组，深度为 0
    if (!Array.isArray(arr)) {
        return 0;
    }

    // 是数组，深度 + 1，具体是多深，还要递归判断元素中的数组
    return 1 + Math.max(...arr.map(el => getMaxDeep(el)));
}

cases.forEach(data => {
    console.log(
        isDeeperThen(data, 2).toString().padEnd(6, " "),
        getMaxDeep(data),
        JSON.stringify(data)
    );
});
```
```
true   4 [1,[2,[3,[4],[5]]]]
true   3 [1,[2,[3]]]
false  2 [2,[2,3]]
false  1 [1,2,4]
false  0 1
```

标签: javascript
问题：Mysql 为什么要有最左前缀的要求，优化器不能自己优化调整顺序吗
比如一个联合索引 `index('user_id', 'created_at')` ，我写了下面的 `SQL` 语句
```
select
    *
from
    tweet
where
    created_at < '2021-11-11'
    and user_id = 1
```
优化器会把上面的语句优化为下面的语句吗？
```
select
    *
from
    tweet
where
    user_id = 1
    and created_at < '2021-11-11'
```

回答：你对左前缀理解错了。
你这种情况是可以用到索引的。
左前缀是指这种情况走不了索引
```
select
    *
from
    tweet
where
    created_at < '2021-11-11'
```
而这种情况可以
```
select
    *
from
    tweet
where
    user_id = 1
```
因为 `user_id` 在联合索引的左边。
如果有索引 (a, b, c) 那么 `where a, b, c`、`where a, b`、`where a` （where 后的顺序无关，可以随意组合，但是跟出现的字段有关）都是可以走到索引的。但是 `where b, c` 就不走索引了，因为按照左前缀原则，这里必须要出现 `a` 才行。
使用如下。
上表中，第二行，`a, c` 虽然是联合索引`(a, b, c)` 中有两个字段，但是因为这里没有出现 `b`，按照做前缀原则就断开了，所以只能使用到 `a`
注意上文中 where 顺序可以随意组装，比如 `a, b, c` 你可以写成 `a, c, b`、 `c, a, b` 等，这些都不影响，但是出现的字段必须是上面写到的。
除了 where 外，还有 order。
`where a order c` 这个就只会用到 (a, b, c) 中的 `(a)` ，而如果是 `where a order b` 则能用到 `(a, b)`

标签: mysql
问题：请问大家，这个小算法问题，怎么改？
[
```
{
    "categoryId": "373",
    "parentId": "3",
    "categoryName": "张三",
    "sunCategorys": [
        {
            "categoryId": "374",
            "parentId": "373",
            "categoryName": "张三一",
        },
        {
            "categoryId": "375",
            "parentId": "373",
            "categoryName": "张三二",
        }
    ]
},
{
    "categoryId": "374",
    "parentId": "3",
    "categoryName": "李四",
    "sunCategorys": [
        {
            "categoryId": "375",
            "parentId": "374",
            "categoryName": "李四一",
        },
        {
            "categoryId": "376",
            "parentId": "374",
            "categoryName": "李四二",
        }
    ]
}
```
]
我想把上面数据的categoryName 和 sunCategorys里面的categoryName，categoryId的id的值取出来，组合成这样的形式，请问应该如何去写
[
```
{
    "text": "张三",
    "children": [
        {
            "text": "张三一",
            "id": 374
        },
        {
            "text": "张三二",
            "id": 375
        }
    ]
},
{
    "text": "李四",
    "children": [
        {
            "text": "李四一",
            "id": 375
        },
        {
            "text": "李四二",
            "id": 376
        }
    ]
}
```
]

回答：```
const result = data.map((item) => ({
    text: item.categoryName,
    children: item.sunCategorys.map((category) => ({
        text: category.categoryName,
        id: category.categoryId,
    })),
}));
```

标签: javascript, 前端, vue.js, react.js
问题：vue的nextTick为什么一定会比promise更先执行？
nextTick内部打比按照promise进行，那么是怎么做到比逻辑中的promise更早执行的？看源码写的是Promise.resolve().then(callback)执行回调代码callback
```
if (typeof Promise !== 'undefined' && isNative(Promise)) {
  const p = Promise.resolve()
  timerFunc = () => {
    p.then(flushCallbacks)
    // In problematic UIWebViews, Promise.then doesn't completely break, but
    // it can get stuck in a weird state where callbacks are pushed into the
    // microtask queue but the queue isn't being flushed, until the browser
    // needs to do some other work, e.g. handle a timer. Therefore we can
    // "force" the microtask queue to be flushed by adding an empty timer.
    if (isIOS) setTimeout(noop)
  }
  isUsingMicroTask = true
} 
```

回答：首先我不知道您是怎么得出一定这个结果的，nextTick并不一定比Promise快，它本来就是Promise就得遵循Promise的规范和顺序，但是nextTick是对Promise的封装，所以
```
nextTick(fn1) // Promise
nextTick(fn2)
nextTick(fn3)
```
实际上就初始化了一个Promise就在第一个nextTick执行的时候
后面调用的fn2,fn3可以理解为都是往第一个fn1函数里push的，所以下面的执行顺序就是fn1 fn2 fn3 fn4因为fn1 fn2 fn3合并为了一个函数，就造成了 fn2 和fn3比fn4快
```
nextTick(fn1) // Promise1
Promise.resolve().then(fn4) // Promise2
nextTick(fn2)
nextTick(fn3)
```
自始至终只有2个Promise 第一次执行nextTick的fn1和fn4，因为fn1声明在fn4之前所以fn1比fn4快
这只限于同一个事件循环，在下一次事件循环第一次执行nextTick依然会有一个新的Promise

标签: vue.js, 前端
问题：在 JavaScript 中，如何实现 const a = [...5] 就可以创建一个 [1, 2, 3, 4, 5] 数组
如何实现在编写 JavaScript 项目时，可以以下面这样的方式，创建一个新的数组：
```
const a = [...5];

// a = [1, 2, 3, 4, 5];
```

回答：```
Number.prototype[Symbol.iterator] = function () {
  const i = +this;
  let value = 0;
  return {
    next() {
      value += 1;
      return {done: value > i, value};
    }
  };
}
```

标签: javascript, 前端, 变态面试题
问题：请问 prettier 与 EditorConfig 功能是不是重复了？
prettier 与 EditorConfig 都是代码格式化工具，他们到底有什么区别？我只知道他们的配置文件不同，一个是.prettierrc文件，而另外一个是.editorconfig文件，但感觉他们功能一样，在网上看别人教程，为何都是两个一起使用的？不是功能重复了吗？求解答？

回答：EditorConfig 格式化的是比较基础的东西，基本上你用“Editor”（编辑器）本身能干的操作就可以用它来干。比如 Tab 变几个空格啊、换行符是 CR 还是 CRLF 啊、文件编码是不是 UTF-8 啊这种的。
而且它不只局限于格式化，名字也能看出来是“Config”（配置）而不是“Formatter”（格式化器）。你也可以用它来配置诸如让 IDE 忽略特定的编译警告错误之类的。
所以你会发现它跟编程语言本身没什么关系，各个语言的项目都能看到 `.editorconfig` 的身影，它更多地干的是当你用特定 IDE 时能配置的那些东西，好让那些不用这个 IDE 的、或者它 IDE 配置跟你不一样的开发者也能使用相同的编辑器方案。当然了它确实可以通过插件的形式去支持一些其他语言特有的格式化方案，不过并不常用。
而 Prettier 是 JS 特有的格式化工具，里面很多配置项是 JS 这门语言特有的规范。
总体来说二者有重叠的部分，但大部分并不相同。所以前端项目往往两者都有，非要只选一个的话选 Prettier 而不是 EditorConfig。

标签: 前端, prettier
问题：请求返回的是 字符串 格式的 js代码，我要怎么 import 到内部的 模块呢
手动@大佬回答 @边城 
如题 后端接口返回给我的是
字符串格式的
```
var a = 1
function setA(v){
    a = v
}
export function getA(){
    return a
}
```
我要怎么 `import` 到内部的 `getA` 模块呢
使用的 `vue`

回答：https://stackoverflow.com/que...
```
<script type="module">
const code = `
var a = 1
function setA(v){
    a = v
}
export function getA(){
    return a
}
`;
const objectURL = URL.createObjectURL(new Blob([code], { type: 'text/javascript' }));
const module = await import(objectURL);
console.log(module.getA());
</script>
```

标签: javascript, 前端, vue.js
问题：vue怎么监听数组项？
如下数组，其中ccc属性值根据bbb属性和ddd属性计算得到的，我需要监听数据项的变化，动态计算ccc属性值
```
 export default {
        data() {
            return {
                aaa: [{
                   bbb: "xxx",
                   ddd: "xxx",
                   ccc: ""
                }, {
                   bbb: "yyy",
                   ddd: "yyy",
                   ccc: ""
                }]
            }
        }
       
 }

```
于是我使用了watch监听
```
 watch: {
            aaa: {
                deep: true,
                handler(newValue, oldValue) {
                    for (let i = 0; i < newValue.length; i++) {
                        newValue[i].ccc = newValue[i].bbb + newValue[i].ddd;
                    }
                }
            }
        }
```
这种方法会导致每个数据项的变动，需要所有数据项都计算一次，有没有什么好的方式？
而且，还有个问题，监听数组并没法获取到旧值，也就是newValue和oldValue是一模一样的，都是新的值。
这个问题主要是讨论性能问题，比如每个数组有100个属性项，又或者ccc是通过耗性能的计算得到的，那么每次数据项的变动都导致所有数据项重新计算一次，这耗费的性能不可想象，所以大家可以想一下，看有没有好的实现方案?? 实现目标是只对变动的数据项进行计算

回答：```
<!DOCTYPE html>
<html>
<head>
  <meta charset="utf-8">
  <meta name="viewport" content="width=device-width">
  <title>JS Bin</title>
</head>
<body>
  <div id="app">
    <div v-for="v of list">
      <input type="number" v-model="v.a">
      <input type="number" v-model="v.b">
      <span>sum: {{v|sum}}</span>
      <span>和：{{v.c}}</span>
    </div>
  </div>
<script src="https://cdn.staticfile.org/vue/2.6.13/vue.min.js"></script>
  <script>
    new Vue({
      el: '#app',
      filters: {
        sum: item => +item.a + (+item.b)
      },
      data: {
        list: [{
          a: 1,
          b: 2,
          get c() {
            return +this.a + (+this.b)
          }
        }, {
          a: 3,
          b: 4,
          get c() {
            return +this.a + (+this.b)
                    }
        }]
      }
    })
  </script>
</body>
</html>
```
比较简单的方式——过滤器或者直接定义一个getter；
还有一个方式就是将动态计算显示的那个属性抽成组件，传入props通过vue的computed去计算，和直接在数据上定义getter原理上是一样的

标签: vue.js
问题：JS的onclick在Chrome和Safari中均不能被触发
写了一段很简单的源码，就是最基本的使用 <button> 触发事件：
HTML 源码如下：
```
<body>
    <button>Click this button to trigger an event</button>
</body>

<script src="Event.js"></script>
```
JS 源码如下：
```
var btn = document.getElementsByTagName("button")
btn.onclick = function () {
    alert("Successfully Triggered an Event !");
    console.log("Good");
}
```
在浏览器中解析后，调试时发现 btn 中选择到了 <button> 元素，而且也被赋值了。但是点击 <button> 按钮却没有反应，即没有出现 alert 对话框，控制台也没有显示 “Good”。这是为什么啊 ？

回答：因为getElementsByTagName获取的是元素集合，不是元素本身，所以你需要先从这个集合中取到元素，再进行绑定
```
var btns = document.getElementsByTagName("button")
// 或者使用遍历，需要注意它是一个为数组，不能直接当数组进行遍历 
btns[0].onclick = function () {
    alert("Successfully Triggered an Event !");
    console.log("Good");
}
```

标签: javascript, 前端, onclick, html
问题：JS排序问题(凑满减)
```
const list =[
  {title:'苹果',price:1},
  {title:'香蕉',price:4},
  {title:'鸭梨',price:3},
  {title:'西瓜',price:5},
  {title:'山竹',price:2}];
```
我想排序, 按照price大于3的正序, 然后再把小于3的倒序, 排在大于的数据后面, 求最优算法.
期望如下:
```
const list =[
 {title:'香蕉',price:4},
 {title:'西瓜',price:5},
 {title:'鸭梨',price:3},
 {title:'山竹',price:2},
 {title:'苹果',price:1}];
```

回答：```
list.sort((a, b) => {
    if(a.price > 3 && b.price > 3){
        return a.price - b.price
    }
    return b.price - a.price
})
```

标签: javascript, 前端
问题：if判断是否有值，但是这个值为0，它也判了不过请问怎么写呢
```
    // row保存
    saveRow (record) {
      const { num, id } = record 
      if (!num) { // 这里为0也走了，怎么判断单纯有没有值
        this.tableLoading = false
        this.$message.error('请填写完整信息。')
        return
      }
    },
```

回答：这里先要搞明白 `num` 允许是哪些值。如果只能是数字的话，可以这样
```
if (typeof num === "number") { ... }
```
不过这样的话，`num` 还有可能是 `NaN`，一般 `NaN` 也需要过滤掉（要不要过滤看业务），所以
```
if (typeof num === "number" && !isNaN(num)) { ... }
```
接下来，如果逻辑可以保证 `num` 只会是数字，或者空（含 `undefined`），那么可以这样判断
```
if (!(num === undefined || num === null || isNaN(num))) { ... }
```
如果用新语法 Nullish Coalescing，会简单一些
```
if (!isNaN(num ?? NaN)) { ... }
```
如果 `NaN` 算是合法的，那就得找个不应该出现的值（只能是一个非数字值了，幸好 JS 不绑定类型）
```
// if (num ?? false !== false) { ... }
// 修改一下，用 null 语义上更舒服一些
if (num ?? null !== null) { ... }
```
更复杂的情况，如果 `"1234"` （表示数的字符串）这种也算合法，还是老实的用 `typeof` 吧
```
// 代码没测试，小心使用
// 假设 NaN 不合法
function isValudNum(num) {
    switch (typeof num) {
        case "string":
            num = parseInt(num, 10);
            // 如果可能是浮点数用 parseFloat
            // 这里不加 break，需要穿透下去判断 isNaN
        case "number":
            return !isNaN(num);
        default:
            return false;
    }
}
```
如果 `NaN` 也合法，处理 `"string"` 类型的时候会更伤心，因为需要先判断是否数字，用正则表达式判断
```
case "string":
    return /^\d+$/.test(num);
    // 如果要判断浮点数
    // return /^\d+(?:\.\d+)?$/.test(num);
```
当然，如果只是做数字判断的话，`NaN` 不合法的情况下，用下面这个 `case "string"` 代替上面那个也是没问题的。

标签: javascript, 前端, vue.js
问题：定义一个数组，调用方法报错
通过不同方式定义一个数组，直接调用方法报错
感觉可能和js自动装包有点关系
但是每种情况报错文案还不太一样
为什么借用一个变量就ok了 
它们内部机制是什么，求大佬详细讲解下
```
const a = 1
const b = 2
[a,b].forEach(e => {
    console.log(e);
});

//VM409:3 Uncaught ReferenceError: Cannot access 'b' before initialization
```
```
let a = 1
let b = 2
[a,b].forEach(e => {
    console.log(e);
});

//ReferenceError: b is not defined
```
```
var a = 1
var b = 2
[a,b].forEach(e => {
    console.log(e);
});

//VM409:3 VM480:3 Uncaught TypeError: Cannot read property 'forEach' of undefined
```
```
const a = 1
const b = 2
const arr = [a,b]
arr.forEach(e => {
    console.log(e);
});

//1,2  执行成功
```
```
const a = 1
const b = 2
const fn = (e)=>{
     console.log(e);
}
[a,b].forEach(fn);

//1,2  执行成功
```

回答：少写了分号
每一行最后都加上分号
const a = 1;
const b = 2;
分号会和下一行的 [ 连在一起
相当于
```
const b = 2[a,b].forEach(e => {
    console.log(e);
});
```

标签: javascript, 数组
问题：问个java很白痴的问题
为什么`String`类型是大写的 别的类型都是小写的？

回答：小写的，应该是基本类型。Java 基本类型只有 8 种，这 8 种类型不是类类型，但有对应的类类型
- `byte` ⇔ `java.lang.Byte`
- `short` ⇔ `java.lang.Short`
- `int` ⇔ `java.lang.Integer`
- `long` ⇔ `java.lang.Long`
- `float` ⇔ `java.lang.Float`
- `double` ⇔ `java.lang.Double`
- `boolean` ⇔ `java.lang.Boolean`
- `char` ⇔ `java.lang.Character`

基本类型不是类，也不会在任何包里。其他类型，都是类或接口类型。
`String` 全名 `java.lang.String`，是一个类。
按 Java 的开发规范，类名是按 Pascal 风格命名，也就是组成类名的每个单词首字母大写，其他字符小写的命名风格。

标签: 后端, java
问题：for...of 循环含有辅助平面字符的字符串，输出让我感到困惑
## 代码
```
let count = 0,
    s = "𠮷a";
for(let i of s){
    count ++;
    console.log(i);
}
for...of 输出：𠮷 a
console.log(count);//2
console.log(s.length);//3
```
## 疑惑的地方
1、字符串s的length为3，循环次数是2次；我用for循环测试，循环次数是3
次。
2、for...of在循环中做了那些事情导致循环次数少了一次？

回答：Javascript 采用 Unicode字符集，在 UTF-16 之前使用的是 UCS-2 编码，该编码方法使用2个字节表示字符（基本平面字符）。
UTF-16 编码发布后，UCS-2 被整合进 UTF-16，基本平面字符仍用2个字节，辅助平面字符使用4个字节表示。
因此，Javascript 的字符操作函数在处理4个字节的字符时，会当成两个双字节的字符处理，从而无法返回正确的结果。
```
s.slice(0, 1) // �
s.substr(0, 1) // �
s.charAt(0) // \uD842
```
"𠮷" 的 UTF-16 编码是4个字节的 `0xD842 DFB7`，而4个字节的字符不属于 UCS-2编码，javascript 会将其识别为`U+D842`和`U+DFB7` 两个字符（在基本平面内，`U+D800` 到 `U+DFFF` 是一个空段，不对应任何字符）
ES6 增强了 Unicode 支持，可以识别4字节的字符。除了`for...of`，还有以下方法
```
s.codePointAt(0).toString(16) // 20bb7
s.at(0) // 𠮷
String.fromCodePoint("0x20BB7") // 𠮷
```
`length` 属性返回字符串中字符编码单元的数量，所以也可能与实际的字符数量不相同。
想要返回字符串的正确长度，可以使用 `Array.from(string).length`

标签: javascript, 前端, ecmascript-6
问题：CSRF攻击者是如何向网站注入欺诈链接的？
我明白CSRF的运作过程，但想不出来攻击者是如何在目标网站注入欺诈链接来骗用户点击的？

回答：比如这个 看似人畜无害的链接，就可以把人从思否诱导到一条不归之路[手动狗头]，这里利用的原理是只要构造出合适的 URL 就可以发起百度搜索，仅此而已，但是你跳过去之后，浏览器会把本地的 `Cookie` 给你带上，百度后台就会认为是你本人在搜这种奇怪的东西，换成字节搜索的话，没准明天你的抖音就会刷到震撼的短视频了。
这个例子只是一个不怀好意的诱导，还算不上攻击，因为百度搜索的这个特点不算什么漏洞。但是如果某个银行的转账接口也是在 URL 里拼参数的话，那可就危险了：如果用户恰好登过这个银行的网页，`Cookie` 还在，那么只要构造一条转账的 `URL`，找机会让用户去点，就能神不知鬼不觉地把用户的钱拿到手。
那么，我有没有在百度的网站注入任何东西？没有。所以对于要攻击的目标网站，我们是不需要注入任何东西的，只要找到他的 CSRF 漏洞就行。
- 我们真正需要“注入”的，是这条链接，我这里写回答其实就已经成功“注入”了，因此，假如你发现了某个网站有这种明显的 CSRF 漏洞的话，你只需要在这里提个问题，夹带私货，然后静候猎物上钩。
- 有些时候我们需要使用更高档的手段，比如构造表单，那么这里的跳转链接显然是不够用的，这时候如果思否有 XSS 漏洞可以注入长的 html 片段的话，那么可以在这个 html 片段里构造表单，由于 XSS 的存在，我们已经不需要用户点击来发起攻击了。（思否在几个月前貌似真的有 XSS ，参见这个问题，这里我说的是“貌似”，因为当我打算验证的时候已经无法复现了，不知道他们是不是恶搞）
- 除此之外，我们还可以给用户发带链接的钓鱼短信，或者在钓鱼邮件里放一个名为“船新艳照门.avi.html”的静态文件……

标签: 安全
问题：js简写，能不能用一行写出来
看注释，希望能用一句写出来，有没有这种简便写法？
```
const t = [
  {name: 'John', value: 111},
  {name: 'Mary', value: 222}
];

const param = t.reduce((p, n) => {
  // 这儿能不能用一行写出来，类似于这样 t.map(k => k.child);
  p[n.name] = n.value;
  return p;
}, {})
```

回答：```
const t = [
  {name: 'John', value: 111},
  {name: 'Mary', value: 222}
];
const param = t.reduce((p, n) => (p[n.name] = n.value,p), {})
```

标签: javascript, 前端
问题：TypeScript怎么实现接口的重载入？
### 题目描述
TS中是怎么实现接口的重载入？
### 相关代码
```
type ItemProps = {
  a: string;
  b?: boolean;
  c?: string; 
}

const arr: ItemProps[] = [
 {
   a: 'demo',
   b: true,
   c: 'test'
 },
 {
   a: 'demo2'
 }
]
```
在上面定义中，怎么定义`ItemProps`类型才能达到： 如果`b`属性存在，则`c`属性必需存在 的效果
### 期待结果
```
const arr: ItemProps[] = [
 {
   a: 'demo',
   b: true,
   c: 'test'
 },
 {
   a: 'demo2',
   b: true, // 类型报错，因为存在b属性，但是缺少c
 } 
]; 
```

回答：```
type ExcludeKeys<T> = { [P in keyof T]: never; }

type RequireOrExcludeKeys<T, Keys extends keyof T = keyof T> =
    Pick<T, Exclude<keyof T, Keys>>
    & (Required<Pick<T, Keys>> | ExcludeKeys<Pick<T, Keys>>)

type ItemProps = RequireOrExcludeKeys<{
    a: string;
    b?: boolean;
    c?: string;
}, 'b' | 'c'>
```
Playground
把条件改成：`如果b属性存在，则c必须存在，但是c属性存在，b可以不存在。`
总结一下，就是 c 必须存在，b 可选存在。
只要修改下`RequireOrExcludeKeys`的参数，以及`Required<Pick<T, Keys>>`。
```
type RequireOrExcludeKeys<T, RequireKeys extends keyof T = keyof T, PartialKeys extends keyof T = keyof T> =
    Pick<T, Exclude<keyof T, RequireKeys>>
    & ((Required<Pick<T, RequireKeys>> & Partial<Pick<T, PartialKeys>>)
        | ExcludeKeys<Pick<T, RequireKeys | PartialKeys>>)

type ItemProps = RequireOrExcludeKeys<{
    a: string;
    b?: boolean;
    c?: string;
}, 'c', 'b'>
```
Playground

标签: typescript, 前端, react.js, javascript
问题：请问如何将监听事件改为promise的形式
现在的代码结构是这样：
```
function receiver(type, callback) {
  document.addEventListener(type, (ev) => {
    callback(ev);
  });
}
```
然后通过回调函数来执行：
```
receiver('click', (ev) => {
  console.log(ev)
})
```
现在想改为promise的形式：
```
receiver('click').then(ev => {
  console.log(ev)
})
```
请问需要怎么修改？

回答：Promise就像一个状态机，而且是一个状态不可逆的状态机，这就意味它只会在到达Fullfiled或者Rejected状态时会执行你注册的函数，所以用Promise实现是不合适的
但是可以写成这样以达成相似效果
```
function receiver(type) {
  const listeners = [];

  const listen = {
    then(callback) {
      listeners.push(callback);

      return listen;
    }
  };
  document.addEventListener(type, async (ev) => {
    let acc = ev;
    for (let i = 0; i < listeners.length; ++i) {
      acc = await listeners[i](acc);
    }
  });

  return listen;
}

receiver("click")
  .then((e) => {
    console.log(e);

    return 1;
  })
  .then((v) => {
    console.log(v);
  });

```

标签: javascript, 前端, ecmascript-6
问题：$nextTick和setTimeout有什么区别？
①$nextTick和setTimeout有什么区别？
②在实际项目中使用两者哪个会好点？没有优缺点之类的，需要注意什么
在实际项目中遇到元素还没渲染出来就执行了事件，甚至是有些接口先调用了
除了这种延时调用的方法还有更好的解决方案嘛
大神们求解

回答：`nextTick`:在下次 DOM 更新循环结束之后执行延迟回调。在修改数据之后立即使用这个方法，获取更新后的 DOM。
`setTimeout`：只是延迟执行，在延迟执行的方法里，`DOM`有可能会更新也有可能没有更新。
常规做法就是延迟500ms或者1s
建议使用`nextTick`在有涉及`DOM`更新的场景

标签: vue.js, javascript
问题：在js中执行顺序？
在js中，假设执行五万条log(1)需要5秒钟，在这个js中的第一行写个setTimeout一秒后执行log(2)，这个log(2)是一秒后执行还是log(1)执行完以后再执行？

回答：- log(1)执行完以后再执行。这里涉及到浏览器端的js事件循环机制。js代码由上到下执行，在调用栈里面执行。
- 执行到setTimeout函数，是宏任务，放到回调队列里面。
- 然后继续执行调用栈里面的代码，五万条log(1)，是同步任务，在调用栈里面执行，一条一条先进先出。
- 等到过了1秒后，setTimeout函数执行完。但是此时的调用栈还没有空，继续等待，等到5秒后，调用栈空了，就会执行回调队列里面的任务。此时打印log(2)
- 附上js事件循环的链接：https://segmentfault.com/a/11...

标签: javascript
问题：js for循环以逗号拆分为新数组
原数据格式↓
```
const data = [{ name: 'apple', cut: 'a,b,c' }, { name: 'trigger', cut: 'a,g' }]
```
期望数据格式↓
```
const data1 = [{ name: 'apple', final: 'a' },
               { name: 'apple', final: 'b' },
               { name: 'apple', final: 'c' },
               { name: 'trigger', final: 'a' },
               { name: 'trigger', final: 'g' }]
```
在原数据格式data中的cut字段，用逗号来拆分为新字段final，name不变
请教该如何操作

回答：基本思路：把 `cut` 拆分之后进行一次映射，可以拿到每个原始对象对应的一个数组，组合起来就是一个二维数据；再把这个二维数据展开，就是你要的答案。
`flatMap` 就是先映射再展开
```
const data = [{ name: 'apple', cut: 'a,b,c' }, { name: 'trigger', cut: 'a,g' }];

const data1 = data.flatMap(it =>
    it.cut.split(",")
        .map(s => ({ name: it.name, final: s }))
);

console.log(data1);
```

标签: javascript, 前端, vue.js, react.js, ecmascript-6
问题：每日百万数据量的订单表如何导出到excel
需求是这样的：系统每日会产生上百万的成交订单，客户需要每天导出一份给他们，因此需要每天生成一份大数据量的excel。
首先想到的就是在定时任务中通过for循环分页访问数据库，然后分批导出到excel。但是for循环中访问数据库，如果测试不充分或者程序有bug会导致频繁访问数据库，会严重影响数据库的性能。其实在循环中访问数据库本来就是一种不好的习惯。
有什么好的方案或者建议吗？

回答：我貌似get到了您的想法
总得来说就是觉得数据已经存进去了，但由于导出`excel`，需要再循环查询数据读出来，感觉有点"多此一举"，这个多此一举导致了循环访问数据库，对此感到不安
那我提供一种思路，不一定能解决你的问题，但是或多或少是一种思路叭
即：能不能在插入订单数据的同时也能发个通知，也就是做个消息中间件，这样消费者就是`execl`导出服务，读取消息直接此时就写入到`excel`中
当然那肯定要准备一个消息中间件了，看你百万的数据量，肯定`kafka`没跑了，至于如何往`kafka`里扔消息，这里面有两种方案可以提供给你

1. 在插入订单的代码中新增一个消息发布
2. 做数据库的CDC（Change data capture）处理，也就是数据库的数据变更捕捉处理

方案1 那就是很简单啦，直接加代码，但是呢由于要在之前的逻辑里加东西，那就不好说了，毕竟开闭原则嘛，修改以前的逻辑势必会带来一些风险
方案2 这就基本不用改以前的代码了，毕竟做的是`CDC`，当然不知道你们用的什么数据库，那`CDC`的具体方案就不一样了，如果以`mysql`举例，当然是基于它的`binlog`来做处理咯，随便搜一搜一大堆，你可以自己造轮子，也可以用别人早已经造好的，当然造好的确实需要花一些代价去学习啦。不过呢不用硬生生修改之前的逻辑，主要就是去解析增量的数据库日志就可以了
当然也许你会问，主动去解析日志文件跟去循环查询数据库不是还是差不太多么？
no no no，有些数据库是支持响应式的操作的，就是你主动查和数据库主动推送给你的区别，比如解析`mysql binlog`的工具`Maxwell`，好家伙，它就是伪装成`Slave`，这不就可以从`Master`节点获取到`binlog`了嘛，从而再发出来，如果发给`kafka`，你再做一个消费者服务，不就可以了么
总之`CDC`是一种数据集成方法吧，不同数据库实现`CDC`的方法并不相同，当然其实早已经有一个库整合了不少主流数据库，然后集成了自己一套`CDC`的接口，你可以去看看debezium，不过这玩意儿，我自己装起来很头大，后面公司最终的方案也没有用`debezium`，还是找到了我们自己使用的数据库的官方响应`API`完成的
仅做参考，希望对您有帮助，不过最后说一下，您提到："频繁访问数据库，会严重影响数据库的性能"
影响性能根本原因就是读写操作是一个库嘛，所以你也可以考虑分库啊，读写分离啊，是吧，就是导出专门是一个只读从库就可以了嘛，和主库分开部署，这样即使你现在这样做，频繁访问数据库其实影响也不大，如果还有很大影响，说明你从库的机器配置太低了。。。叭

标签: java, node.js, python, 后端
问题：请问现在那个使用那个跨前端技术会比较好。
React Native / Weex / Flutter / uni-app 如何选择好呢

回答：ReactNative 适合有一定 React 前端基础的人。不过性能瓶颈是天坑，稍微复杂一点儿的组件还是得写原生配合。胜在出生较早，生态相对完善，社区活跃。
Flutter 是用得 Dart 基本就是重学一门语言了，但性能要比 RN 强很多。出生虽比 RN 晚，但后发制人，社区也很活跃。
uni-app 严格意义上来说不是跨端 App，只是 Web 浏览器套壳。你写 Web 有啥瓶颈，它就有啥瓶颈。（别说它也封装了一些 Native API，性能还不如 RN 呢）。
Weex 还活着？

标签: react-native, weex, flutter, uni-app
问题：美团前端二面，读代码题求解
```
var a = 0;
if (true) {
    a = 1;
    function a() {}
    a = 21;
    console.log(a);
}
console.log(a);
```
求两次 console 出来的值。
答案是21 1。
本菜鸡想了很久也没想明白第二个为啥是1。。
求大佬解惑 ?

回答：21 1 的输出结果不完全正确，在不同的浏览器下结果不同
在safari下结果为21 21
在chrome下结果为21 1
同时ECMAScript规范中说函数声明可以嵌套在条件语句块内，但是其运行的结果依赖于JS解析器的实现，其结果具有不确定性,不推荐在生产环境下使用

标签: javascript, 前端, 面试
问题：JavaScript 两个对象大小比较的机制？
偶然见看到的一个关于类型转换的题目：
对象 ab 是两个字面量函数，在进行比较的时候，可以发现大小和相等的比较结果都是 `false`
```
var a = {b: 42};
var b = {b: 43};
```
```
a < b; //false
a == b; //false 
a > b; //false

a <= b; //true 
a >= b; //true 
```
在这个过程之，a 和 b 比较的时候，到底比较的是什么？为什么 <=> 三个运算符都是 false。？
为什么 `a<=b` 是 true？
`a<=b == true` 是因为 JS 计算的是 `!(a>b)` 吗？

回答：ES 规定如此。
`==`、`!=` 不说了，基础问题了；说下 `>`、`<` 这种吧，最后都会转换为数值形式做比较。
首先尝试调用对象的 `Symbol.toPrimitive` 方法得到原始值。
如果没有这个方法就再尝试调用 `valueOf`。
经过以上转换后，如果两边都是 `string`，则按 Unicode 逐位比较；否则，都强转成 `number` 比较。
`Object` 强转后就变成了 `NaN`。`NaN` 是个特殊的 `number`，它跟任意数（包括它自己）做任何比较运算，恒为 `false`。
至于 `>=`、`<=` 你说的对，确实是取反。
P.S. 完整讲述可以自己搜索一下 ECMA-262 文档中关于 Abstract Relational Comparison 的相关定义：https://tc39.es/ecma262/#sec-abstract-relational-comparison

标签: javascript, 前端
问题：关于实际代码中如何用策略模式处理频繁的 if else 的问题
直接上代码吧 都在代码里写清楚了
问题就是
如何把函数中 tabs.filter.forEach 这些几乎相同的逻辑 独立出来 之前学习了策略模式 但是想不到好的方案 请大佬指点一二
```
let tabs = [{index:0,name:'angelaBaby'},{index:1,'陈冠西'},{index:2,'李bingbing'},{index:3,'范特西'}]
let COMMAND_TYPE_LIST = {
    OTHER:'other',
    RIGHT:'right',
    SELF:'self',
}

/**
 * @desc 抽中的成员逻辑
 * @param {*} type:string  - 抽中的类型  
 * @param {*} randomIndex:number - 抽中的数组 index
 * @example 
 * 比如 type==='other' randomIndex=1 那么抽中的成员就是 0,2,3
 * 比如 type==='right' randomIndex=1 那么抽中的成员就是 2,3
 * 比如 type==='self' randomIndex=1 那么抽中的成员就是 1
 * @todo
 * 如何把 tabs.filter.forEach 这些几乎相同的逻辑 独立出来 之前学习了策略模式 但是想不到好的方案 请大佬指点一二
 */
function checkedPerson(type,randomIndex){
    if (type === COMMAND_TYPE_LIST.OTHER) {
        //抽中其他
        tabs
            .filter((item, index) => index !== randomIndex)
            .forEach((tab) => {
                //循环处理逻辑
            })
    } else if (type === COMMAND_TYPE_LIST.RIGHT) {
        //抽中右侧
        tabs
            .filter((item, index) => index > randomIndex)
            .forEach((tab) => {
                //循环处理逻辑
            })
    } else if (type === COMMAND_TYPE_LIST.SELF) {
        //抽中自身
        tabs
            .filter((item, index) => index === randomIndex)
            .forEach((tab) => {
                //循环处理逻辑
            })
    }
}
checkedPerson(COMMAND_TYPE_LIST.OTHER,2)//抽中的成员就是 0,1,3


```

回答：解耦最终操作放在外面，可用性更高点
```
function checkedPerson1(type,randomIndex) {
    const other = (randomIndex, index) => index !== randomIndex;
    const right = (randomIndex, index) => index > randomIndex;
    const self = (randomIndex, index) => index === randomIndex;
    const dict = {
        other,
        right,
        self
    }
    return tabs.filter((item, index) => dict[type](randomIndex, index));
}
checkedPerson1('other', 1).forEach(item => {
    console.log(item);
});
```

标签: vue.js, ecmascript-6, javascript, 前端
问题：webpack使用CopyWebpackPlugin插件一直报错，麻烦大佬看看？
```
const CopyWebpackPlugin = require("copy-webpack-plugin");
const path = require("path");

module.exports = {
  entry: "./src/index.js",
  output: {
    filename: "bundle.js",
    path: path.resolve(__dirname, "dist"),
  },
  module: {
    rules: [],
  },
  plugins: [
    new CopyWebpackPlugin([
      // {output}/file.txt
      { from: path.resolve(__dirname, './src/assets/'), },
    ]),
  ],
};
```
一直给我提示这个错误，我想知道到底错在哪？按照文档上配的，百度、google都没这个错误的。大致意思是缺少`{ from: path.resolve(__dirname, './src/assets/'), }`配置，我现在实在找不到错误在哪，麻烦大佬指明一下
```
Invalid options object. Copy Plugin has been initialized using an options object that does not match the API schema.
 - options[0] misses the property 'patterns'. Should be:
   [non-empty string | object { from, to?, context?, globOptions?, toType?, force?, flatten?, transform?, cacheTransform?, transformPath?, noErrorOnMissing? }, ...] (should not have fewer than 1 item)
ValidationError: Invalid options object. Copy Plugin has been initialized using an options object that does not match the API schema.
    at validate (D:\vscode-workspace\brower-manager\node_modules\copy-webpack-plugin\node_modules\schema-utils\dist\validate.js:88:11)
    at new CopyPlugin (D:\vscode-workspace\brower-manager\node_modules\copy-webpack-plugin\dist\index.js:24:30)
    at Object.<anonymous> (D:\vscode-workspace\brower-manager\webpack.common.js:14:5)
    at Module._compile (D:\vscode-workspace\brower-manager\node_modules\v8-compile-cache\v8-compile-cache.js:192:30)
    at Object.Module._extensions..js (internal/modules/cjs/loader.js:1153:10)
    at Module.load (internal/modules/cjs/loader.js:977:32)
    at Function.Module._load (internal/modules/cjs/loader.js:877:14)
    at Module.require (internal/modules/cjs/loader.js:1019:19)
    at require (D:\vscode-workspace\brower-manager\node_modules\v8-compile-cache\v8-compile-cache.js:161:20)
    at Object.<anonymous> (D:\vscode-workspace\brower-manager\webpack.dev.js:2:16)
    at Module._compile (D:\vscode-workspace\brower-manager\node_modules\v8-compile-cache\v8-compile-cache.js:192:30)
    at Object.Module._extensions..js (internal/modules/cjs/loader.js:1153:10)
    at Module.load (internal/modules/cjs/loader.js:977:32)
    at Function.Module._load (internal/modules/cjs/loader.js:877:14)
    at Module.require (internal/modules/cjs/loader.js:1019:19)
    at require (D:\vscode-workspace\brower-manager\node_modules\v8-compile-cache\v8-compile-cache.js:161:20)
npm ERR! code ELIFECYCLE
npm ERR! errno 1
npm ERR! brower-manager@1.0.0 dev: `webpack --config webpack.dev.js`
npm ERR! Exit status 1
npm ERR!
npm ERR! Failed at the brower-manager@1.0.0 dev script.
npm ERR! This is probably not a problem with npm. There is likely additional logging output above.

```

回答：仔细看错误信息，缺少了 `patterns` 字段。实际上 copy-webpack-plugin 从 6.0 以后，把配置改成了
```
new CopyWebpackPlugin(
  patterns:
    [
      { from: path.resolve(__dirname, './src/assets/'), },
    ]
  ),
)  
```

标签: webpack
问题：前端路由是全部都由后端返回，还是后端返回对应角色下的权限，然后前端通过遍历的方式来修改当前路由呢？
在看vue-element-admin中有个权限验证，现在不知道这2者有啥区别，请大佬说说有什么好的方式来处理这个动态路由
https://panjiachen.github.io/...

回答：第一种后台返回路由，第二种后台返回权限。
共同点：
- 两种方法都可以实现需求
- 前端都要维护一份路由地址与模块文件地址的映射
- 后段返回的数据一般都要再遍历做二次处理
- 有关页面内元素（按钮）的权限都要另做处理
- 技术点都会涉及路由守卫和路由鉴权

差异点：
- 默认路由列表：方法一只维护home、login等无权限需求路由，其他路由需要后续通过接口和路由api：`addRoutes`动态添加；方法二需要维护一个全量的路由列表，不需要额外添加路由，通过配置每个路由的access数组来做鉴权。
- 路由跳转：因为方法一返回的就是该用户权限下的路由，所以不需要再做权限鉴权；方法二需要。
- 路由的自定义程度：方法一可以通过修改数据库的路由数据来自定义前端的菜单结构，因此也需要做一个实现路由重组的递归函数，拓展性更好；方法二针对的是菜单结构相对稳定的项目，一般不支持结构变动。
- 返回报文：一般来说，返回报文大小 方法一比方法二要大

标签: vue.js, javascript
问题：promise then 的回调函数是在什么时候进入微任务队列的？
promise then 的回调函数是在遇到 resolve 语句的时候就被加入微任务队列，还是在遇到 then 语句的时候被加入到微任务队列？
在网上查了一些资料，发现有不同的说法：
1. 比如在《深入理解ES6》中文版 244 页里是这么说的：“调用 resolve( ) 后会触发一个异步操作， 传入 then( ) 和 catch( ) 方法的函数会被添加到任务队列中并异步执行”，所以 then 方法的回调应该是在调用 resolve 后就被加入到队列中的？
2. 对应的英文版原文是这么说的：“Calling resolve() triggers an asynchronous operation. Functions passed to then() and catch() are executed asynchronously, because these are also added to the job queue”
这个是知乎的回答：https://www.zhihu.com/question/62305365
这个是 StackOverflow 类似问题的回答：https://stackoverflow.com/questions/59846764/how-job-queue-works-with-promise ，答主在分析 event loop 的时候有这么一句：“The first then hooks up the first fulfillment handler, queuing a PromiseJobs job because the promise is already fulfilled”
1、2 都认为 then 的回调函数是在遇到 resolve 语句的时候就被加入微任务队列，3、4 都认为是在遇到 then 语句的时候被加入到微任务队列的。到底哪一种说法是正确的呢？

回答：都没问题。
关键在于，`then` 是在 `resolve` 之前被调用的，还是 `resolve` 之后呢？
`then` 在 `resolve` 之前，`then` 不会加微任务，而是缓存起来，`resolve` 看到缓存里又 `then` 的回调，于是加微任务。
`resolve` 在 `then` 之前，`resolve` 的时候还没有任何回调要执行，自然不会加微任务。`then` 的时候发现已经 fullfilled ，于是直接加微任务。
也就是说，他们都有可能加，也都有可能不加，就看调用时的 promise 的状态了。
你的引用有些只说明了其一，但是说明里条件写得还是很清楚的，比如 4 里 "because the promise is already fulfilled"

标签: eventloop, 异步编程, javascript
问题：JS怎么获取变量名称（反射）
JS中怎么在程序执行的时候，获取变量名称。
比如通过一个函数返回指定参数的参数名。
```
jsvar paramName = 'asdf' // 不管等于什么。
var paramName2 = new Object(); // 不管等于什么。

function getParamName(p) {
    // ...实现过程
    var result = ...; // 这里只是模拟
    return result;
}

getParamName(paramName);
getParamName(paramName2);

```
最终参数的返回结果应该为：
```
output  Output:
  paramName
  paramName2

```
请问，类似的需求怎么实现，有没有变通的方法？
看了一下，感觉 http://segmentfault.com/q/1010000002761696#a-1020000002765874 说的比较合理。

回答：2017.1.5 答案已重写
每一个上下文都有一个与之对应的变量对象，任何声明的变量都是这个变量对象的属性。全局上下文的变量对象是全局对象，因而在全局声明的变量就变成了全局对象的属性。相应地，函数上下文的变量对象是活化对象，在函数上下文声明的变量是活化对象的属性。然而，活化对象是引擎内部的一个对象，不可被外部访问，因而不可在 JS 中获取函数上下文变量的名称。
此外基本类型的函数实参是以传值的方式传入函数的，对象类型的实参是以引用的方式传入函数的，无论哪种方式，函数都只能得到传入的值或者引用，而无法访问调用函数的上下文的变量对象，因而不能在函数内部获得传入函数的变量的名称。
既然没有函数能够返回调用这个函数的上下文中变量的名称，JS 也没有提供能做到这件事的操作符，那么在执行上下文内部获取该执行上下文内部变量的名称也就是不可能的。
当然啦，对于函数代码的执行上下文，如果我们能获得这个函数的代码，就可以解析这段代码得到这段代码当中变量的名称。
示例代码：
```
function getParamName() {}

function fn() {
  var param1 = 0
  var a = 2

  console.log(getParamName(param1))
  console.log(getParamName(a))
}

function callWithVariableName(fn) {
  eval('(' + fn.toString().replace(/\bgetParamName\s*\(([a-zA-Z_$][\w_$]*)\)/g, function(u, v) {
    return "'" + v + "'"
  }) + '())')
}

callWithVariableName(fn)

```
控制台输出：
```
param1
a
```

标签: javascript, 反射, node.js
问题：在DolphinDB中，如何对连续的相同值分组 
比如有表如下：
```
t = table(1 1 1 1 2 2 2 2 1 1 1 1 3 3 3 as bs_flag)  
```
希望根据 bs_flag 进行分组，连续相同的 bs_flag 的行group 为一组 
 即希望分组后是  
1  
2  
1  
3  
如何写sql语句？

回答：可以用
```
select first(bs_flag)  from t group by segmentby(first, bs_flag.rowNo(), bs_flag)
```
或
```
select first(bs_flag), first(bs_flag.rowNo()) from t group by eachPre(ne, bs_flag).cumsum()
```

标签: nosql, sql, 数据库, dolphindb
问题：DolphinDB的流数据表怎么查询
我有一个流数据表，然后
```
enableTableShareAndPersistence(table, tableName, [asynWrite=true], [compress=true], [cacheSize=-1], [retentionMinutes=1440], [flushMode=0]) 
```
我把cacheSize 设为100,000.
现在假设流进来的数据已经超过了100,000行, 也就是部分数据已经存入persisenceDir。如果用select * from tableName会不会返回所有的行？
问的原因，是因为我在考虑要不要单独建一个实时数据仓，来query所有已经流进来的数据。

回答：用select * from tableName不会返回所有的行。默认情况下，流计算的表把所有数据保存在内存中。如果流数据表太大，系统可能会出现内存不足的情况。为了避免内存不足的问题，我们可以设置缓存大小的界限。如果流数据表的行数达到这个界限，前面一半的记录行会从内存中清理。  
可以用getPersistenceMeta(table)观察一下。

标签: 大数据, nosql, 数据库, sql, dolphindb
问题：在DolphinDB中怎么拼接Matrix
如果我有一个结果是一个ANY 向量,他的每一个元素是shape相同的Matrix,我怎么样可以把他合并成一个大的Matrix?  最好是纵向拼接,列数不变增加行数。

回答：横向拼接两个矩阵：  
a = 1..4$2:2
b = 1..4$2:2
a.join(b)  
纵向拼接：用 a.transpose().join(b.transpose()).transpose()  
假设ANY vector c，如下
```
c = [a, b]
def transposeJoin(a, b) {
  return a.transpose().join(b.transpose()).transpose()
}
reduce(transposeJoin, c)
```

标签: nosql, 数据库, sql, dolphindb
问题：提问关于 mysql得联合主键和复合主键的问题
如下sql：
CREATE TABLE `film_actor` (
`actor_id` smallint(5) unsigned NOT NULL,
`film_id` smallint(5) unsigned NOT NULL,
`name` varchar(50)  NOT NULL,
`last_update` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
  PRIMARY KEY (`actor_id`,`film_id`) USING BTREE,
  KEY `idx_fk_film_id` (`film_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
（一）`primary key` 是联合主键还是复合主键，我个人理解的是联合主键，毕竟`actor_id`不是自增的？但是针对`actor_id`和`film_id`设值时，只能1-1的for primary key。
（二）但是如果`actor_id`设有自增`auto_increment`，然后`primary key`是 `primary key(actor_id,film_id)`这种是联合还是复合？
（三）又或者设有自增的`actor_id`，`primary key(actor_id,name)`是复合还是联合？求大神解答一下疑惑，谢谢

回答：这个问题要是问外国开发者，TA们能一脸懵逼。
在英文语境中只有 `Composite Primary Key`（也有叫 `Compound Primary Key` 的），就是一个表中如果是多个字段组成一个主键，那么这个主键就被称为这玩意儿。
至于这玩意儿你翻译成“联合主键”、“组合主键”、“混合主键”还是“复合主键”，都特么是一回事儿。
结果到了中文编程界，不知道是哪位神仙起的头儿，非得编个“联合主键”和“复合主键”的区别出来。再加上中文编程界有一大特点，就是博客或者问答习惯性东抄西搬，结果错的知识也能流传广泛甚至成为“主流”。
搬一个“流传最广”的对于“联合主键”和“复合主键”区别的文章：
“联合主键”还是单一主键，只不过往往用在关联表里，一个关联表里会涉及到多个其他表的主键组合形成一条数据，你既可以为它们设置一个“复合主键”、也可以再新加一个自增列设为“联合主键”。
举例：
```
--- 学生表
CREATE TABLE `student` (
    `student_id` int(10) unsigned NOT NULL AUTO_INCREMENT,
    `name` varchar(20) NOT NULL,
    PRIMARY KEY (`student_id`) USING BTREE
);

--- 科目表
CREATE TABLE `subject` (
    `subject_id` int(10) unsigned NOT NULL AUTO_INCREMENT,
    `name` varchar(20) NOT NULL,
    PRIMARY KEY (`subject_id`) USING BTREE
);

--- 分数表，用所谓的“复合主键”
CREATE TABLE `score` (
    `student_id` int(10) unsigned NOT NULL,
    `subject_id` int(10) unsigned NOT NULL,
    `value` int(10) unsigned NOT NULL,
    PRIMARY KEY (`student_id`, `subject_id`) USING BTREE
);

--- 分数表，用所谓的“联合主键”
CREATE TABLE `score` (
    `id` int(10) unsigned NOT NULL AUTO_INCREMENT,
    `student_id` int(10) unsigned NOT NULL,
    `subject_id` int(10) unsigned NOT NULL,
    `value` int(10) unsigned NOT NULL,
    PRIMARY KEY (`id`) USING BTREE,
    UNIQUE INDEX `un`(`student_id`, `subject_id`) USING BTREE
);
```
写在最后：
再次重申，这种概念是中文编程界（或者说是中文编程博客界）人为造出的。
上面的例子看看就得了，根本就是胡编一个 `联合主键` 的定义出来，你会发现它其实就是一个最简单的自增主键，只不过表的逻辑上 `student_id` + `subject_id` 是唯一的，`id` 就成了所谓的二者的 `联合主键`。

标签: mysql
问题：new Promise((resolve)=>{resolve()}) 与 Promise.resolve() 等价吗
### 处理以下类型的时候这个两个方法感觉差不多
参数是一个 Promise 实例
参数是一个thenable对象
参数不是具有then方法的对象，或根本就不是对象
但我看有人说：`promise.resolve(v)`不等于`new Promise(r => r(v))`，因为如果 v 是一个 Promise 对象，前者会直接返回 v，而后者需要经过一系列的处理（主要是 PromiseResolveThenableJob）

回答：## 我自己做了一个详细的测试
先说结论：在v是一个promise实例的时候，`promise.resolve(v)`与`new Promise(r => r(v))`有明显的差异
区别表现：new Promise(r => r(v))的.then()回调会被推迟两个时序（事件循环）
原因：new Promise(r => r(v))里浏览器会创建一个 PromiseResolveThenableJob 去处理这个 Promise 实例，这是一个微任务。具体分析如下
```
    // v是一个实例化的promise，且状态为fulfilled
    let v = new Promise(resolve => {
      console.log("begin");
      resolve("then");
    });

    // 在promise里面resolve一个状态为fulfilled的promise

    // 模式一 new Promise里的resolve()
    // begin->1->2->3->then->4 可以发现then推迟了两个时序
    // 推迟原因：浏览器会创建一个 PromiseResolveThenableJob 去处理这个 Promise 实例，这是一个微任务。
    // 等到下次循环到来这个微任务会执行，也就是PromiseResolveThenableJob 执行中的时候，因为这个Promise 实例是fulfilled状态，所以又会注册一个它的.then()回调
    // 又等一次循环到这个Promise 实例它的.then()回调执行后，才会注册下面的这个.then(),于是就被推迟了两个时序
    new Promise(resolve => {
      resolve(v);
    }).then((v)=>{
        console.log(v)
    });

    //  模式二 Promise.resolve(v)直接创建
    // begin->1->then->2->3->4 可以发现then的执行时间正常了，第一个执行的微任务就是下面这个.then
    // 原因：Promise.resolve()API如果参数是promise会直接返回这个promise实例，不会做任何处理
/*     Promise.resolve(v).then((v)=>{
        console.log(v)
    }); */

    new Promise(resolve => {
      console.log(1);
      resolve();
    })
      .then(() => {
        console.log(2);
      })
      .then(() => {
        console.log(3);
      })
      .then(() => {
        console.log(4);
      });
```

标签: promise, javascript
问题：上海字节跳动一道关于http缓存的面试题
首先这个问题是http的缓存，我答了强缓存和协商缓存。
我说道协商缓存，是会请求到后端服务器，然后对比响应头last-modified或者etag，来决定时候使用缓存。
此时面试官问我，已经请求到后端了，为什么还要再去取缓存，而不是直接使用response，我就没答出来。
网上搜了很多，主要还是都是介绍2种缓存的，并没有提到这个问题，望解答！

回答：我猜你说的 response 指的是 response body？
协商缓存如果命中返回的是 304 啊……只有 header 没有 body。
虽然还是请求了一次服务器，但只需要传输 header 部分就可以了，节省了传输文件本身的开销。

标签: 前端, javascript, http, http缓存