内鬼消息:串联高频面试问题,值得一看!(下)

本文涉及的产品
.cn 域名,1个 12个月
全局流量管理 GTM,标准版 1个月
公共DNS(含HTTPDNS解析),每月1000万次HTTP解析
简介: 开宗明义,本瓜深知汝之痛点:前端面试知识点太杂,卿总为了面试而面试,忘了记,记了又忘,循环往复,为此叫苦不迭。

四、组件编译


组件的思想也是 Vue 核心,将组件编译为 vdom ,则也是一重难点!

image.png


你可以发现在 Vue 这一节有很多引用的图,其实它们有的相似,更多的是侧重点不同,建议都可按照流程图理解理解,做到融会贯通。


组件


官方示例:

// 定义一个名为 button-counter 的新组件
Vue.component('button-counter', {
  data: function () {
    return {
      count: 0
    }
  },
  template: '<button v-on:click="count++">You clicked me {{ count }} times.</button>'
})
<div id="components-demo">
  <button-counter></button-counter>
</div>
new Vue({ el: '#components-demo' })

组件还涉及:组件之间的通信、插槽、动态组件等内容。后续再表(OS: 这是自己给自己挖了个多大的坑)。


parse


编译过程首先就是对模板做解析,生成 AST,它是一种抽象语法树,是对源代码的抽象语法结构的树状表现形式。在很多编译技术中,如 babel 编译 ES6 的代码都会先生成 AST。这个过程会用到大量的正则表达式对字符串解析,源码很难读。

但是我们需要知道的是 start、end、comment、chars 四大函数。


对于普通标签的处理流程大致:

  1. 识别开始标签,生成匹配结构match。
  2. 处理attrs,将数组处理成 {name:'xxx',value:'xxx'}
  3. 生成astElement,处理for,if和once的标签。
  4. 识别结束标签,将没有闭合标签的元素一起处理。
  5. 建立父子关系,最后再对astElement做所有跟Vue 属性相关对处理。slot、component等等。


参考阅读:Vue parse之 从template到astElement 源码详解


optimize


当我们的模板 template 经过 parse 过程后,会输出生成 AST 树,那么接下来我们需要对这颗树做优化 —— optimize。


optimize 中最重要的是标记静态根(markStaticRoots )、静态节点(markStatic )。如果是静态节点则它们生成的DOM永远不需要改变,这让模板的更新更搞笑(不变的节点不用更新)。

问题:为什么子节点的元素类型是静态文本类型,就会给 optimize 过程加大成本呢? 首先来分析一下,之所以在 optimize 过程中做这个静态根节点的优化,目的是什么,成本是什么?

目的:在 patch 过程中,减少不必要的比对过程,加速更新。

成本:a. 需要维护静态模板的存储对象。b. 多层render函数调用。

推荐阅读


codegen


编译的最后一步就是把优化后的 AST 树转换成可执行的代码,即在 codegen 环节。

主要步骤(调用函数):

  1. generate
  2. genIf
  3. genFor
  4. genData & genChildren

此节考的不多,仅做了解。了解更多,得看源码。


五、常用补充


keep-alive(常考)

keepalive 是 Vue 内置的一个组件,可以使被包含的组件保留状态,或避免重新渲染 。也就是所谓的组件缓存。


v-if、v-show、v-for


三个高频子问题:


  1. v-if、v-show 区别?


答:v-if 相当于 display; v-show 相当于 visibility; 前者会控制是否创建,后者仅控制是否隐藏显示。


  1. v-if、v-for 为什么不能放一起用?


答:因为 v-for 优先级比 v-if 高,所以在每次重新渲染的时候会先遍历整个列表,再进行 if 判断是否展示,消耗性能。


  1. v-for 中能用 index 作 key 吗?


答:key 是 diff 算法中用来对比的,用 index 作为 key 并未唯一识别,当插入元素时,key 也会变化。index 作为 key,只适用于不依赖子组件状态或临时 DOM 状态 (例如:表单输入值) 的列表渲染输出(官网说明)。

本瓜这里不做细答,想了解更多请自行解决。


自定义指令


Vue 中的混入(Minxin)、自定义指令(directive)、过滤器(filter)有共通之处,在注册的时候,需要平衡局部注册和全局注册的优劣。


transition


本瓜读源码的过程中,发现源码中有较大的篇幅在描述关于transition。在官方文档中,transition 也是作为独立的重要一节来说明。进入/离开 & 列表过渡,Vue 动画&过渡,是容易忽视的点。


六、全家桶


自从用上了 Vue 全家桶,腿也不疼了,腰也不酸了。咦,一口气写五个页面,妈妈再也不用担心我的学习了。(好像有点串广告了......)


Vue-Router


官方的路由管理器

分为两种模式:hash 和 history

  • 默认 hash 模式,通过加锚点的方式
  • history 利用 history.pushState API实现

原生: HTML5引入了 history.pushState() 和 history.replaceState() 方法,它们分别可以添加和修改历史记录条目。这些方法通常与window.onpopstate 配合使用。详情链接


Vuex


官方状态管理

由以下几部分核心组成:

  1. state:数据状态;
  2. mutations:更改状态(计算状态);
  3. getters:将state中的某个状态进行过滤然后获取新的状态;
  4. actions:执行多个mutation,它可以进行异步操作(async );
  5. modules:把状态和管理规则分类来装,让目录结构更清晰;


VueCLI


官方脚手架

  • VueCLI4中很重要的是 vue.config.js 这个文件的配置。


VuePress


静态网站生成器

  • 采用 Vue + webpack,可以在 Markdown 中使用 Vue 组件,页面简洁大方,与 Vue 官网风格统一。


NuxtJS


服务端渲染


七、webpack


只要你做前端有两年经验左右,那一样就得要求自己掌握一款打包工具了。

webpack 原理


官方解释:

webpack 是一个现代 JavaScript 应用程序的静态模块打包工具。当 webpack 处理应用程序时,它会在内部构建一个 依赖图(dependency graph),此依赖图会映射项目所需的每个模块,并生成一个或多个_bundle_。


核心概念:

  1. Entry:入口,Webpack 执行构建的第一步将从 Entry 开始,可抽象成输入。
  2. Module:模块,在 Webpack 里一切皆模块,一个模块对应着一个文件。Webpack 会从配置的 Entry 开始递归找出所有依赖的模块。
  3. Chunk:代码块,一个 Chunk 由多个模块组合而成,用于代码合并与分割。
  4. Loader:模块转换器,用于把模块原内容按照需求转换成新内容。
  5. Plugin:扩展插件,在 Webpack 构建流程中的特定时机会广播出对应的事件,插件可以监听这些事件的发生,在特定时机做对应的事情。


功能:

代码转换、文件优化、代码分割、模块合并、自动刷新、代码校验、自动发布。


优化打包速度


  1. 搭载 webpack-parallel-uglify-plugin 插件,加速“压缩JS=>编译成 AST=>还原JS”的过程。
  2. 使用 HappyPack 提升 loader 解析速度。
  3. 使用 DLLPlugin 和 DLLReferencePlugin 插件,提前打包。
  4. tree-shaking 用来消除无用模块。


AMD、CMD


前端模块化有四种规范:CommonJS、AMD、CMD、ES6。

  1. AMD(异步模块定义)
  2. CMD(通用模块定义)
AMD(异步模块定义) CMD(通用模块定义)
速度快 性能较差
会浪费资源 只有真正需要才加载依赖
预先加载所有的依赖,直到使用的时候才执行 直到使用的时候才定义依赖
  1. Node.js是commonJS规范的主要实践者:module、module.exports(exports)require、global。
  2. ES6 在语言标准的层面上,实现了模块功能,主要由两个命令构成:exportimport。export命令用于规定模块的对外接口,import命令用于输入其他模块提供的功能。


问:比较 import 和 require 的区别?


import require
ES6标准中的模块化解决方案 是node中遵循CommonJS规范的模块化解决方案
不支持动态引入 支持动态引入
是关键词 不是关键词
编译时加载,必须放在模块顶部 运行时加载,理论上来说放在哪里都可以
性能较好 性能较差
实时绑定方式,即导入和导出的值都指向同一个内存地 导出时是值拷贝,就算导出的值变化了,导入的值也不会变化
会编译成require/exports来执行 -


更多:前端模块化:CommonJS,AMD,CMD,ES6


实现plugin插件(腾讯WXG考点)


  1. 创建 plugins/demo-plugin.js 文件;
  2. 传递参数 Options;
  3. 通过 Compilation 写入文件;
  4. 管理 Warnings 和 Errors


从零实现一个 Webpack Plugin


串联四:



你最喜欢什么算法?



其实本瓜想回答:我最喜欢减法!因为幸福生活需要用减法。😄

算法这一个 part 也已久远,既然逃不掉,那就正面挑战它!其实也没那么难。

一图胜万言


image.png

  • 原创脑图,转载请说明出处


串联知识点:数据结构、基础算法、排序算法、进阶算法。


串联记忆:


算法算法我不怕

数据结构打趴下

遍历排序我最溜

指针动态贪心刷


一、数据结构


队列

先入先出。


先入后出。


堆通常是一个可以被看做一棵树的数组对象。

堆总是满足下列性质:

  1. 堆中某个节点的值总是不大于或不小于其父节点的值;
  2. 堆总是一棵完全二叉树。
  • 完全二叉树:在一颗二叉树中,若除最后一层外的其余层都是满的,并且最后一层要么是满的,要么在右边缺少连续若干节点,则此二叉树为完全二叉树(Complete Binary Tree)—— wiki。

(本瓜曾被拷问过这个点,大厂就是会考《数据结构》,别逃避,出来混迟早是要还的😭)。


链表

  • 循环列表(考点)
// 循环链表
function Node(element){
    this.element = element;
    this.prev = null;
    this.next = null;
}
function display(){
    var current = this.head;
    //检查头节点当循环到头节点时退出循环
    while(!(current.next == null) && !(current.next.element=='head')){
        print(current.next.element);
        current = current.next;
    }
}
function Llist(){
    this.head = new Node('head');
    this.head.next = this.head;
    this.find = find;
    this.insert = insert;
    this.display = display;
    this.findPrevious = findPrevious;
    this.remove = remove;
}


哈希

散列函数(英语:Hash function)又称散列算法、哈希函数。以 Key:Value 的方式存储数据。

哈希表最大的特点是可以快速定位到要查找的数据,查询的时间复杂度接近O(1)。本瓜建议大家可以把这里所有的数据结构的“增删改查”操作的时间复杂度都理一下,也是会被考的。


时间复杂度、空间复杂度

简单理解:

  1. 循环的次数写成 n 的表达式,就是时间复杂度。
  2. 申请的变量数量写成 n 的表达式,就是空间复杂度。

时间复杂度更多重要一点,常见的时间复杂度:O(1)、O(n)、O(logn)、O(n2)。本瓜小TIP:面试/笔试如果不知道怎么算,就在这里面猜吧。实在不行就答:O(n) ~ O(n2) 之间,大概率不会错🐶。


树的遍历(广度、深度)

广度优先遍历(BFS):

需要用到队列(Queue)来存储节点对象,队列的特点就是先进先出。示例

深度优先遍历(DFS):

  1. 前序(根结点 -> 左子树 -> 右子树)
  2. 中序(左子树 -> 根结点 -> 右子树)
  3. 后序(左子树 -> 右子树 -> 根结点)


二、基础算法


递归思想

  • 著名的斐波那契数列,你要知道!
function result(){
    if(n==1||n==2){
        return 1
    }
    return reslt(n-2)+result(n-1)
}


  • 函数柯里化,你也要知道!

函数柯里化:是把接受多个参数的函数变换成接受一个单一参数(最初函数的第一个参数)的函数,并且返回接受余下的参数而且返回结果的新函数的技术。—— wiki

// 普通方式
    var add1 = function(a, b, c){
        return a + b + c;
    }
    // 柯里化
    var add2 = function(a) {
        return function(b) {
            return function(c) {
                return a + b + c;
            }
        }
    }
    // demo
    var foo = function(x) {
        return function(y) {
            return x + y
        }
    }
    foo(3)(4)       // 7

这样处理参数,可以直接追加,而不需要初始传参就写完全。这里只是浅谈,更多请自行探索。


二分法


要求手写二分法,实在不行,能默写也可以啊!

// 二分法:先排序,再找目标
function binary_search(arr,target) {
    let min=0
    let max=arr.length-1
    while(min<=max){
      let mid=Math.ceil((min+max)/2)
      if(arr[mid]==target){
        return mid
      }else if(arr[mid]>target){
        max=mid-1
      }else if(arr[mid]<target){
        min=mid+1
      }
    }
  return "null"
}
console.log(binary_search([1,5,7,19,88],19))//3


三、排序算法


排序是比较常用也比较重要的一块,此处并未全列出。仅强调快排冒泡,会用双循环也行啊。


快速排序

// 快排:选取基准,比基准大的放右边,比基准小的放左边,然后两边用递归
function quickSort(arr, i, j) {
  if(i < j) {
    let left = i;
    let right = j;
    let pivot = arr[left];
    while(i < j) {
      while(arr[j] >= pivot && i < j) {  // 从后往前找比基准小的数
        j--;
      }
      if(i < j) {
        arr[i++] = arr[j];
      }
      while(arr[i] <= pivot && i < j) {  // 从前往后找比基准大的数
        i++;
      }
      if(i < j) {
        arr[j--] = arr[i];
      }
    }
    arr[i] = pivot;
    quickSort(arr, left, i-1);
    quickSort(arr, i+1, right);
    return arr;
  }
}


冒泡排序

// 冒泡:双层循环
    var arr=[10,20,50,100,40,200];
    for(var i=0;i<arr.length-1;i++){
        for(var j=0;j<arr.length-1-i;j++){
        if(arr[j]>arr[j+1]){
            var temp=arr[j]
            arr[j]=arr[j+1]
            arr[j+1]=temp
        }
    }
    }
    console.log(arr)


四、进阶算法


双指针


看到“有序”和“数组”。立刻把双指针法调度进你的大脑内存。普通双指针走不通,立刻想对撞指针!


示例:合并两个有序数组(双指针解法)


示例: 输入:
nums1 = [1,2,3,0,0,0], m = 3
nums2 = [2,5,6], n = 3
输出: [1,2,2,3,5,6]
/**
 * @param {number[]} nums1
 * @param {number} m
 * @param {number[]} nums2
 * @param {number} n
 * @return {void} Do not return anything, modify nums1 in-place instead.
 */
const merge = function(nums1, m, nums2, n) {
    // 初始化两个指针的指向,初始化 nums1 尾部索引k
    let i = m - 1, j = n - 1, k = m + n - 1
    // 当两个数组都没遍历完时,指针同步移动
    while(i >= 0 && j >= 0) {
        // 取较大的值,从末尾往前填补
        if(nums1[i] >= nums2[j]) {
            nums1[k] = nums1[i] 
            i-- 
            k--
        } else {
            nums1[k] = nums2[j] 
            j-- 
            k--
        }
    }
    // nums2 留下的情况,特殊处理一下 
    while(j>=0) {
        nums1[k] = nums2[j]  
        k-- 
        j--
    }
};


高级!


动态规划


动态规划的思想就是把一个大的问题进行拆分,细分成一个个小的子问题,且能够从这些小的子问题的解当中推导出原问题的解。


同时需要满足以下两个重要性质才能进行动态规划:

  1. 最优子结构性
  2. 子问题重叠性质


动态规划实例:斐波拉契数列(上文有提到)

斐波拉契数列:采用递归,虽然代码很简洁,但是明显随着次数的增加,导致递归树增长的非常庞大,耗时较久。


用动态规划实现斐波拉契数列,代码如下

function feiBoLaQie(n) {
        //创建一个数组,用于存放斐波拉契数组的数值
        let val = [];
        //将数组初始化,将数组的每一项都置为0
        for(let i =0 ;i<=n;i++){
            val[i] = 0;
        }
        if (n==1 || n==2){
            return 1;
        } else{
            val[1] = 1;
            val[2] = 2;
            for (let j =3; j<=n;j++){
                val[j] = val[j-1] + val[j-2];
            }
        }
        return val[n-1];
    }
    console.log(feiBoLaQie(40));//102334155


通过数组 val 中保存了中间结果, 如果要计算的斐波那契数是 1 或者 2, 那么 if 语句会返回 1。 否则,数值 1 和 2 将被保存在 val 数组中 1 和 2 的位置。

循环将会从 3 到输入的参数之间进行遍历, 将数组的每个元素赋值为前两个元素之和, 循环结束, 数组的最后一个元素值即为最终计算得到的斐波那契数值, 这个数值也将作为函数的返回值。


动态规划解决速度更快。


贪心算法


贪心算法遵循一种近似解决问题的技术,期盼通过每个阶段的局部最优选择(当前最好的解),从而达到全局的最优(全局最优解)。


注:贪心得到结果是一个可以接受的解,不一定总是得到最优的解。


示例:最少硬币找零问题


是给出要找零的钱数,以及可以用硬币的额度数量,找出有多少种找零方法。
如:美国面额硬币有:1,5,10,25
我们给36美分的零钱,看能得怎样的结果?
复制代码
function MinCoinChange(coins){
    var coins = coins;
    var cache = {};
    this.makeChange = function(amount){
        var change = [], total = 0;
        for(var i = coins.length; i >= 0; i--){
            var coin = coins[i];
            while(total + coin <= amount){
                change.push(coin);
                total += coin;
            }
        }
        return change;
    }
}
var minCoinChange = new MinCoinChange([1, 5, 10, 25]);
minCoinChange.makeChange(36);
//[25, 10, 1] 即一个25美分、一个10美分、一个1美分


串联五:



web 安全你知道那些?



一图胜万言

image.png


  • 原创脑图,转载请说明出处

串联知识点:跨域、XSS(跨站脚本攻击)、CRFS(跨站请求伪造)、SQL 注入、DNS 劫持、HTTP 劫持。


串联记忆:三跨两劫持一注入


一、跨域


跨域定义

当一个请求url的协议、域名、端口三者之间任意一个与当前页面url不同即为跨域。


跨域限制 是浏览器的一种保护机制,若跨域,则:

  1. 无法读取非同源网页的 Cookie、LocalStorage 和 IndexedDB。
  2. 无法接触非同源网页的 DOM。
  3. 无法向非同源地址发送 AJAX 请求


跨域解决

跨域解决:

  1. JSONP;
  2. CORS(跨域资源分享);
// 普通跨域请求:只需服务器端设置 Access-Control-Allow-Origin。
// 带cookie跨域请求:前后端都需要进行设置。
// 如前端在 axios 中设置
axios.defaults.withCredentials = true


  1. vue项目 设置 proxy 代理;
  2. nginx 代理;
  3. 设置document.domain解决无法读取非同源网页的 Cookie问题;
  4. 跨文档通信 API:window.postMessage();


二、XSS


XSS 原理

XSS的原理是WEB应用程序混淆了用户提交的数据和JS脚本的代码边界,导致浏览器把用户的输入当成了JS代码来执行。XSS的攻击对象是浏览器一端的普通用户。


示例:

<input type="text" value="<%= getParameter("keyword") %>">
<button>搜索</button>
<div>
  您搜索的关键词是:<%= getParameter("keyword") %>
</div>


当浏览器请求

http://xxx/search?keyword="><script>alert('XSS');</script>


恶意代码,就会其执行


反射型 XSS


存储型 XSS 的攻击步骤:

  1. 攻击者将恶意代码提交到目标网站的数据库中。
  2. 用户打开目标网站时,网站服务端将恶意代码从数据库取出,拼接在 HTML 中返回给浏览器。
  3. 用户浏览器接收到响应后解析执行,混在其中的恶意代码也被执行。
  4. 恶意代码窃取用户数据并发送到攻击者的网站,或者冒充用户的行为,调用目标网站接口执行攻击者指定的操作。

这种攻击常见于带有用户保存数据的网站功能,如论坛发帖、商品评论、用户私信等。


存储型 XSS


反射型 XSS 的攻击步骤:

  1. 攻击者构造出特殊的 URL,其中包含恶意代码。
  2. 用户打开带有恶意代码的 URL 时,网站服务端将恶意代码从 URL 中取出,拼接在 HTML 中返回给浏览器。
  3. 用户浏览器接收到响应后解析执行,混在其中的恶意代码也被执行。
  4. 恶意代码窃取用户数据并发送到攻击者的网站,或者冒充用户的行为,调用目标网站接口执行攻击者指定的操作。

反射型 XSS 跟存储型 XSS 的区别是:存储型 XSS 的恶意代码存在数据库里,反射型 XSS 的恶意代码存在 URL 里。


DOM型 XSS


DOM 型 XSS 的攻击步骤:

  1. 攻击者构造出特殊的 URL,其中包含恶意代码。
  2. 用户打开带有恶意代码的 URL。
  3. 用户浏览器接收到响应后解析执行,前端 JavaScript 取出 URL 中的恶意代码并执行。
  4. 恶意代码窃取用户数据并发送到攻击者的网站,或者冒充用户的行为,调用目标网站接口执行攻击者指定的操作。

DOM 型 XSS 跟前两种 XSS 的区别:DOM 型 XSS 攻击中,取出和执行恶意代码由浏览器端完成,属于前端 JavaScript 自身的安全漏洞,而其他两种 XSS 都属于服务端的安全漏洞。


XSS 防御


  1. 输入过滤,不要相信任何客户端的输入;
  2. 对 HTML 做充分转义;
  3. 设置 HTTP-only:禁止 JavaScript 读取某些敏感 Cookie,攻击者完成 XSS 注入后也无法窃取此 Cookie。
  4. 验证码:防止脚本冒充用户提交危险操作。
  5. 谨慎使用:.innerHTML、.outerHTML、document.write();


三、CSRF


CSRF 原理


跨站请求伪造:攻击者诱导受害者进入第三方网站,在第三方网站中,向被攻击网站发送跨站请求。利用受害者在被攻击网站已经获取的注册凭证,绕过后台的用户验证,达到冒充用户对被攻击的网站执行某项操作的目的。


曾在 09 年发生了著名的“谷歌邮箱窃取”事件,利用的就是 “CSRF”。


主要流程:

  1. 受害者登录a.com,并保留了登录凭证(Cookie)。
  2. 攻击者引诱受害者访问了b.com。
  3. b.com 向 a.com 发送了一个请求:a.com/act=xx。浏览器会默认携带a.com的Cookie。
  4. a.com接收到请求后,对请求进行验证,并确认是受害者的凭证,误以为是受害者自己发送的请求。
  5. a.com以受害者的名义执行了act=xx。
  6. 攻击完成,攻击者在受害者不知情的情况下,冒充受害者,让a.com执行了自己定义的操作。

注:攻击者无法直接窃取到用户的信息(Cookie,Header,网站内容等),仅仅是冒用 Cookie 中的信息。


这告诉我们冲浪的时候不能随便点链接,是有风险哒。


CSRF 防御


防御策略:

  1. 自动防御策略:同源检测(Origin 和 Referer 验证)。
  2. 主动防御措施:Token验证 或者 双重Cookie验证 以及配合Samesite Cookie。
  3. 保证页面的幂等性,后端接口不要在GET页面中做用户操作。


四、SQL 注入


SQL 注入原理

Sql 注入攻击是通过将恶意的 Sql 查询或添加语句插入到应用的输入参数中,再在后台 Sql 服务器上解析执行进行的攻击,它目前黑客对数据库进行攻击的最常用手段之一。


SQL 注入防御


防御:用sql语句预编译和绑定变量,是防御sql注入的最佳方法。还可以通过严格检查参数的数据类型的方式来防御。


五、DNS 劫持


DNS 劫持原理


DNS劫持又称域名劫持,是指在劫持的网络范围内拦截域名解析的请求,分析请求的域名,把审查范围以外的请求放行,否则返回假的IP地址或者什么都不做使请求失去响应,其效果就是对特定的网络不能访问或访问的是假网址。其实本质就是对DNS解析服务器做手脚


DNS 劫持防御


解决办法:

DNS的劫持过程是通过攻击运营商的解析服务器来达到目的。我们可以不用运营商的DNS解析而使用自己的解析服务器或者是提前在自己的App中将解析好的域名以IP的形式发出去就可以绕过运营商DNS解析,这样一来也避免了DNS劫持的问题。


六、HTTP 劫持


HTTP 劫持原理


在运营商的路由器节点上,设置协议检测,一旦发现是HTTP请求,而且是html类型请求,则拦截进行恶意处理。 常见有两种:

  1. 类似DNS劫持返回302让用户浏览器跳转到另外的地址。(钓鱼网站就是这么干)
  2. 在服务器返回的HTML数据中插入js或dom节点(广告)。(常见)


HTTP 劫持防御


防御方法:

  1. 使用HTTPS;
  2. 使用禁止转码申明;
  3. 在开发的网页中加入代码过滤:用 js 检查所有的外链是否属于白名单;
  4. 联系运营商;

这一 part 更多的是概念上的东西,不求完全记住,但是也要大致上能说一下。咱老话说的好:知之为知之 不知为不知,面试官在非关键性的问题上是不做严格要求的,千万别满嘴跑火车或者直接说不知道,这是两个极端,都不可取。


小结


显然,前端面试“串联”问题不仅局限以上,本瓜会继续按照这个“串联联想”的思路去整理,不断去打破体系、形成体系(自己给自己挖的天坑呐)。期待在这个反复的过程中找寻真理的痕迹!


相关文章
|
存储 设计模式 自然语言处理
内鬼消息:串联高频面试问题,值得一看!(中)
开宗明义,本瓜深知汝之痛点:前端面试知识点太杂,卿总为了面试而面试,忘了记,记了又忘,循环往复,为此叫苦不迭。
|
存储 缓存 移动开发
内鬼消息:串联高频面试问题,值得一看!(上)
开宗明义,本瓜深知汝之痛点:前端面试知识点太杂,卿总为了面试而面试,忘了记,记了又忘,循环往复,为此叫苦不迭。
|
4月前
|
存储 Java
【IO面试题 四】、介绍一下Java的序列化与反序列化
Java的序列化与反序列化允许对象通过实现Serializable接口转换成字节序列并存储或传输,之后可以通过ObjectInputStream和ObjectOutputStream的方法将这些字节序列恢复成对象。
|
1月前
|
存储 缓存 算法
面试官:单核 CPU 支持 Java 多线程吗?为什么?被问懵了!
本文介绍了多线程环境下的几个关键概念,包括时间片、超线程、上下文切换及其影响因素,以及线程调度的两种方式——抢占式调度和协同式调度。文章还讨论了减少上下文切换次数以提高多线程程序效率的方法,如无锁并发编程、使用CAS算法等,并提出了合理的线程数量配置策略,以平衡CPU利用率和线程切换开销。
面试官:单核 CPU 支持 Java 多线程吗?为什么?被问懵了!
|
1月前
|
存储 算法 Java
大厂面试高频:什么是自旋锁?Java 实现自旋锁的原理?
本文详解自旋锁的概念、优缺点、使用场景及Java实现。关注【mikechen的互联网架构】,10年+BAT架构经验倾囊相授。
大厂面试高频:什么是自旋锁?Java 实现自旋锁的原理?
|
1月前
|
存储 缓存 Java
大厂面试必看!Java基本数据类型和包装类的那些坑
本文介绍了Java中的基本数据类型和包装类,包括整数类型、浮点数类型、字符类型和布尔类型。详细讲解了每种类型的特性和应用场景,并探讨了包装类的引入原因、装箱与拆箱机制以及缓存机制。最后总结了面试中常见的相关考点,帮助读者更好地理解和应对面试中的问题。
57 4
|
2月前
|
算法 Java 数据中心
探讨面试常见问题雪花算法、时钟回拨问题,java中优雅的实现方式
【10月更文挑战第2天】在大数据量系统中,分布式ID生成是一个关键问题。为了保证在分布式环境下生成的ID唯一、有序且高效,业界提出了多种解决方案,其中雪花算法(Snowflake Algorithm)是一种广泛应用的分布式ID生成算法。本文将详细介绍雪花算法的原理、实现及其处理时钟回拨问题的方法,并提供Java代码示例。
92 2
|
2月前
|
JSON 安全 前端开发
第二次面试总结 - 宏汉科技 - Java后端开发
本文是作者对宏汉科技Java后端开发岗位的第二次面试总结,面试结果不理想,主要原因是Java基础知识掌握不牢固,文章详细列出了面试中被问到的技术问题及答案,包括字符串相关函数、抽象类与接口的区别、Java创建线程池的方式、回调函数、函数式接口、反射以及Java中的集合等。
37 0
|
4月前
|
XML 存储 JSON
【IO面试题 六】、 除了Java自带的序列化之外,你还了解哪些序列化工具?
除了Java自带的序列化,常见的序列化工具还包括JSON(如jackson、gson、fastjson)、Protobuf、Thrift和Avro,各具特点,适用于不同的应用场景和性能需求。
|
4月前
|
Java
【Java基础面试三十七】、说一说Java的异常机制
这篇文章介绍了Java异常机制的三个主要方面:异常处理(使用try、catch、finally语句)、抛出异常(使用throw和throws关键字)、以及异常跟踪栈(异常传播和程序终止时的栈信息输出)。