如何在 VS Code 中调试 LeetCode 代码?

摘要: 面试刷题指南。原文:如何在 VS Code 中调试 LeetCode 代码作者:neo-csFundebug经授权转载,版权归原作者所有。近期收到不少小伙伴的求助,希望知道如何在 VS Code 中调试 LeetCode 代码。通常来说,为了调试本地代码,我们需要安装相关的语言支持插件。本文中,我们就以调试 LeetCode Java 代码为例,给大家介绍本地调试 LeetCode 代码的常用套路。想要了解如何在 VS Code 中刷题的小伙伴,可以移步:LeetCode for VS Code: 春招 Offer 收割利器。准备工作首先确保系统内安装了JDK,相关教程有很多,此处就不赘述了。之后我们需要确保在 VS Code 中安装了下列插件:1. LeetCode用来生成题目,提交答案。2. Language Support for Java(TM) by Red Hat提供智能提示等语言相关的功能。3. Debugger for Java,Java 调试器。安装完成之后,VS Code 的插件管理栏中,就可以看到这三个插件了:如果在打开 Java 文件后,VS Code 提示找不到 JDK,请检查一下相关配置是否正确。编写调试代码:我们就拿第 20 题:有效的括号作为例子。在作答过程中,可能会看到编辑器里出现一些红线。不要担心,这表明 Language Support for Java 插件正在起作用。通常这意味着你的代码存在语法错误,下面的例子展示的错误原因是用到了依赖包但没有 import 到当前文件当中。我们可以利用 Quick Fix 功能进行修复:将依赖包导入时为了确保文件能够被正确编译。LeetCode 在检查答案的时候,并不会要求文件中存在相应的 import 语句,因此存不存在 import 语句不会影响最后的检查结果。写完答案之后,我们还需要在同一个文件中,增加一个 Main 函数作为调试程序的执行入口,整个文件的代码结构如下:class Main { public static void main(String[] args) { // Create a new Solution instance Solution solution = new Solution(); // Create a test case String testCase = “()[]{}”; // Get the answer boolean answer = solution.isValid(testCase); // Print the answer System.out.println(answer); }}class Solution { … public boolean isValid(String s) { … return answer; }}此时我们会看到在 Main 函数的上方出现了两个 CodeLens 按钮:点击 Run 按钮会运行 Main 函数,我们可以在下方弹出的 Debug Console 中看到程序的输出结果(因为我们在最后一行代码用了 println 输出答案)。如果想要调试的话,可以在相应的行号位置设置好断点,点击 Debug 按钮,就可以进入调试模式查看代码运行情况了。这里有一点需要注意的是,由于 LeetCode 生成的答题模板的类名均为 Solution,因此会造成同一个目录下存在多个同名类的情况出现,可能导致代码无法正确执行,因此如果希望调试 LeetCode Java 代码,但当前目录又存在有多个 LeetCode Java 文件时,需要保证类名的唯一性,我们可以把被调试的 Solution 类改一个名字(但要记住提交时把名字改回来),或者干脆拷贝到另一个干净的目录下调试即可。以上就是如何在 VS Code 中调试 LeetCode Java 代码的步骤,对于其他语言来说,基本也是大同小异的步骤,如果你有更好的建议或者有自己喜欢的调试技巧,欢迎在评论区留言! ...

April 20, 2019 · 1 min · jiezi

《前端面试手记》之ES6重难点整理

???? 内容速览 ????let和constSet和MapGenerator和yieldPromise、async/await介绍Proxy代理器…????查看全部教程 / 阅读原文????let和constES6新增了let和const,它们声明的变量,都处于“块级作用域”。并且不存在“变量提升”,不允许重复声明。同时,const声明的变量所指向的内存地址保存的数据不得改变:对于简单类型的数据(数值、字符串、布尔值),值就保存在变量指向的那个内存地址,因此等同于常量。对于复合类型的数据(主要是对象和数组),变量指向的内存地址,保存的只是一个指向实际数据的指针,const只能保证这个指针是固定的(即总是指向另一个固定的地址),不能保证指向的数据结构不可变。如果要保证指向的数据结构也不可变,需要自行封装:/** * 冻结对象 * @param {Object} obj * @return {Object} */function constantize(obj) { if(Object.isFrozen(obj)) { return obj } Reflect.ownKeys(obj).forEach(key => { // 如果属性是对象,递归冻结 typeof obj[key] === ‘object’ && (obj[key] = constantize(obj[key])) }); return Object.freeze(obj)}/********测试代码 ***/const obj = { a: 1, b: { c: 2, d: { a: 1 } }, d: [ 1, 2 ]}const fronzenObj = constantize(obj)try { fronzenObj.d = [] fronzenObj.b.c = 3} catch(error) { console.log(error.message)}Set和Map题目:解释下Set和Map。Set元素不允许重复Map类似对象,但是它的键(key)可以是任意数据类型①Set常用方法// 实例化一个setconst set = new Set([1, 2, 3, 4]);// 遍历setfor (let item of set) { console.log(item);}// 添加元素,返回Set本身set.add(5).add(6);// Set大小console.log(set.size);// 检查元素存在console.log(set.has(0));// 删除指定元素,返回boollet success = set.delete(1);console.log(success);set.clear();其他遍历方法:由于没有键名,values()和keys()返回同样结果。for (let item of set.keys()) { console.log(item);}for (let item of set.values()) { console.log(item);}for (let item of set.entries()) { console.log(item);}②Map常用方法Map接口基本和Set一致。不同的是增加新元素的API是:set(key, value)const map = new Map();// 以任意对象为 Key 值// 这里以 Date 对象为例let key = new Date(); map.set(key, “today”);console.log(map.get(key));Generator与yieldgenerator函数是es6提供的新特性,它的最大特点是:控制函数的执行。让我们从网上最火的一个例子来看:function foo(x) { var y = 2 * (yield x + 1); var z = yield y / 3; return x + y + z;}var b = foo(5);b.next(); // { value:6, done:false }b.next(12); // { value:8, done:false }b.next(13); // { value:42, done:true }通俗的解释下为什么会有这种输出:给函数foo传入参数5,但由于它是generator,所以执行到第一个yield前就停止了。第一次调用next(),这次传入的参数会被忽略暂停。第二次调用next(12),传入的参数会被当作上一个yield表达式的返回值。因此,y = 2 * 12 = 24。执行到第二个yield,返回其后的表达式的值 24 / 3 = 8。然后函数在此处暂停。第三次调用next(13),没有yield,只剩return了,按照正常函数那样返回return的表达式的值,并且done为true。难点:在于为什么最后的value是42呢?首先,x的值是刚开始调用foo函数传入的5。而最后传入的13被当作第二个yield的返回值,所以z的值是13。对于y的值,我们在前面第三步中已经计算出来了,就是24。所以,x + y + z = 5 + 24 + 13 = 42看懂了上面的分析,再看下面这段代码就很好理解了:function foo(x) { var y = 2 * (yield x + 1); var z = yield y / 3; return x + y + z;}var a = foo(5);a.next(); // Object{value:6, done:false}a.next(); // Object{value:NaN, done:false}a.next(); // Object{value:NaN, done:true}只有第一次调用next函数的时候,输出的value是6。其他时候由于没有给next传入参数,因此yield的返回值都是undefined,进行运算后自然是NaN。Promise介绍简单归纳下 Promise:三个状态、两个过程、一个方法三个状态:pending、fulfilled、rejected两个过程(单向不可逆):pending->fulfilledpending->rejected一个方法then:Promise本质上只有一个方法,catch和all方法都是基于then方法实现的。请看下面这段代码:// 构造 Promise 时候, 内部函数立即执行new Promise((resolve, reject) => { console.log(“new Promise”); resolve(“success”);});console.log(“finifsh”);// then 中 使用了 return,那么 return 的值会被 Promise.resolve() 包装Promise.resolve(1) .then(res => { console.log(res); // => 1 return 2; // 包装成 Promise.resolve(2) }) .then(res => { console.log(res); // => 2 });async/await介绍async函数返回一个Promise对象,可以使用then方法添加回调函数。当函数执行的时候,一旦遇到await就会先返回,等到异步操作完成,再接着执行函数体内后面的语句。这也是它最受欢迎的地方:能让异步代码写起来像同步代码,并且方便控制顺序。可以利用它实现一个sleep函数阻塞进程:function sleep(millisecond) { return new Promise(resolve => { setTimeout(() => resolve, millisecond) })}/ * 以下是测试代码 /async function test() { console.log(‘start’) await sleep(1000) // 睡眠1秒 console.log(’end’)}test() // 执行测试函数虽然方便,但是它也不能取代Promise,尤其是我们可以很方便地用Promise.all()来实现并发,而async/await只能实现串行。function sleep(second) { return new Promise(resolve => { setTimeout(() => { console.log(Math.random()); resolve(); }, second); });}async function chuanXingDemo() { await sleep(1000); await sleep(1000); await sleep(1000);}async function bingXingDemo() { var tasks = []; for (let i = 0; i < 3; ++i) { tasks.push(sleep(1000)); } await Promise.all(tasks);}运行bingXingDemo(),几乎同时输出,它是并发执行;运行chuanXingDemo(),每个输出间隔1s,它是串行执行。ES6对象和ES5对象题目:es6 class 的new实例和es5的new实例有什么区别?在ES6中(和ES5相比),class的new实例有以下特点:class的构造参数必须是new来调用,不可以将其作为普通函数执行es6 的class不存在变量提升最重要的是:es6内部方法不可以枚举。es5的prototype上的方法可以枚举。为此我做了以下测试代码进行验证:console.log(ES5Class()) // es5:可以直接作为函数运行// console.log(new ES6Class()) // 会报错:不存在变量提升function ES5Class(){ console.log(“hello”)}ES5Class.prototype.func = function(){ console.log(“Hello world”) }class ES6Class{ constructor(){} func(){ console.log(“Hello world”) }}let es5 = new ES5Class()let es6 = new ES6Class()// 推荐在循环对象属性的时候,使用for…in// 在遍历数组的时候的时候,使用for…ofconsole.log(“ES5 :")for(let _ in es5){ console.log()}// es6:不可枚举console.log(“ES6 :")for(let _ in es6){ console.log()}参考/推荐:《JavaScript创建对象—从es5到es6》Proxy代理器他可以实现js中的“元编程”:在目标对象之前架设拦截,可以过滤和修改外部的访问。它支持多达13种拦截操作,例如下面代码展示的set和get方法,分别可以在设置对象属性和访问对象属性时候进行拦截。const handler = { // receiver 指向 proxy 实例 get(target, property, receiver) { console.log(GET: target is ${target}, property is ${property}) return Reflect.get(target, property, receiver) }, set(target, property, value, receiver) { console.log(SET: target is ${target}, property is ${property}) return Reflect.set(target, property, value) }}const obj = { a: 1 , b: {c: 0, d: {e: -1}}}const newObj = new Proxy(obj, handler)/ * 以下是测试代码 */newObj.a // output: GET…newObj.b.c // output: GET…newObj.a = 123 // output: SET…newObj.b.c = -1 // output: GET…运行这段代码,会发现最后一行的输出是 GET …。也就是说它触发的是get拦截器,而不是期望的set拦截器。这是因为对于对象的深层属性,需要专门对其设置Proxy。更多请见:《阮一峰ES6入门:Proxy》EsModule和CommonJS的比较目前js社区有4种模块管理规范:AMD、CMD、CommonJS和EsModule。 ES Module 是原生实现的模块化方案,与 CommonJS 有以下几个区别:CommonJS 支持动态导入,也就是 require(${path}/xx.js),后者目前不支持,但是已有提案:import(xxx)CommonJS 是同步导入,因为用于服务端,文件都在本地,同步导入即使卡住主线程影响也不大。而后者是异步导入,因为用于浏览器,需要下载文件,如果也采用同步导入会对渲染有很大影响commonJs输出的是值的浅拷贝,esModule输出值的引用ES Module 会编译成 require/exports 来执行的更多系列文章⭐在GitHub上收藏/订阅⭐《前端知识体系》JavaScript基础知识梳理(上)JavaScript基础知识梳理(下)ES6重难点整理谈谈promise/async/await的执行顺序与V8引擎的BUG前端面试中常考的源码实现Flex上手与实战……《设计模式手册》单例模式策略模式代理模式迭代器模式订阅-发布模式桥接模式备忘录模式模板模式抽象工厂模式……《Webpack4渐进式教程》webpack4 系列教程(二): 编译 ES6webpack4 系列教程(三): 多页面解决方案–提取公共代码webpack4 系列教程(四): 单页面解决方案–代码分割和懒加载webpack4 系列教程(五): 处理 CSSwebpack4 系列教程(八): JS Tree Shakingwebpack4 系列教程(十二):处理第三方 JavaScript 库webpack4 系列教程(十五):开发模式与 webpack-dev-server……⭐在GitHub上收藏/订阅⭐ ...

April 19, 2019 · 3 min · jiezi

每日一面——仿写reverse方法

引言今天小K问了我一个面试题,怎么实现一个实reverse方法,在实现的过程中我还是犯了一些错,实现完以后,对一些知识点的理解又加深了。错误的写法最开始我是这么写的var arr = [1,2,3,4,5] var reverse1 = function (arr) { let newArr = [] while (arr.length>0){ newArr.push(arr.pop()) } console.log(arr,newArr) // [],[5,4,3,2,1] arr = newArr //让arr等于反转后的新数组 console.log(arr) // [5,4,3,2,1] return arr } reverse1(arr)结果有点打脸console.log(arr) // []函数参数的传递方式上面的现象显示:我将arr当参数传入reverse1里面,我可以改变arr里面的内容但是我却无法改变arr(arr由[1,2,3,4,5]pop()五次变成[ ],但是随后的赋值操作却没有成功)为什么会出现这个问题,我们得从函数的参数传递方式说起函数的参数都是值传递怎么理解这句话,按照高程的说法,如果这里是引用传递,那么我在代码第8行已经让arr变成了[5,4,3,2,1],那么外面的arr也应该变,但是现在外面arr却是[ ],所以函数的参数的传递方法都是值传递如果你觉得这么说还是比较抽象,你可以听听鄙人陋见,我们完全可以把js里面数组名当成一个指针变量,储存的是实际的数组对象的地址。指针意味着我们通过它可以访问它指向的对象。变量意味者我可以改变这个变量。在函数参数里面当我们传入一个arr的时候,实际传递的是一个形参address1,储存arr这个对象在内存里的地址,address1=xxxx xxxxx xxxx xxx1。通过address1可以对arr进行任何操作,一旦现在我为address1赋上新的地址值,也就是address1=xxxx xxxxx xxxx xxx2。address1就和arr失去了联系。此时arr还是在xxxx xxxxx xxxx xxx1上,并且至少在当前的reverse1方法中不会再被改变了,因为没有哪个指针能指向它了。正确的写法ok说了这么多,实际想说的是,你在方法中可以通过索引,通过原生方法操作一个当参数传进来的数组,但是绝对不允许对数组名直接进行赋值那么这个题目我们就可以尝试使用原生方法来操作传进来的数组,这里提供一种思路var arr = [1,2,3,4,5]var reverse1 = function () { for(var i = 0; i < arr.length; i ++){ arr.splice(i,0,arr.pop()) } }reverse1(arr)console.log(arr) // [5,4,3,2,1]但是我们可以看到上面的方法还是不是很好,这是因为这个reverse1貌似只能对arr进行操作,耦合性太强,我们需要解耦,让reverse1对所有的数组都适用,这里适用this和原型方面的知识//一个完美的写法:Array.prototype.reverse1 = function () { for(var i = 0; i < this.length; i ++){ //this 解耦 this.splice(i,0,this.pop()) } return this }总结今天介绍了一下reverse的实现思路,this解耦,以及函数参数是值传递这么一个概念,建议在方法中通过索引,通过原生方法改变一个当参数传进来的数组,但是绝对不允许对数组名直接进行赋值希望对大家有所帮助。 ...

April 19, 2019 · 1 min · jiezi

Java面试题:稳定和不稳定排序算法之间的区别-MergeSort与QuickSort

来源 | 愿码(ChainDesk.CN)内容编辑愿码Slogan | 连接每个程序员的故事网站 | http://chaindesk.cn愿码愿景 | 打造全学科IT系统免费课程,助力小白用户、初级工程师0成本免费系统学习、低成本进阶,帮助BAT一线资深工程师成长并利用自身优势创造睡后收入。官方公众号 | 愿码 | 愿码服务号 | 区块链部落免费加入愿码全思维工程师社群 | 任一公众号回复“愿码”两个字获取入群二维码本文阅读时长:6min你是否理解QuickSort与MergeSort之间的区别?你稳定和不稳定的排序算法的含义是什么?当面试官问到以上问题应如何回答?如果排序算法保持数字/记录的相对顺序,即如果需要排序1 1 2 3,那么如果不更改前两个排序的顺序,则认为排序算法是稳定的,但如果交换它们,则尽管总体结果或排序顺序保持不变,但它将变得不稳定。当您对对象进行排序时(例如,按键对键值对进行排序),这种差异会变得更加明显。在稳定算法的情况下,保留键值对的原始顺序,如下例所示。实际上,如果您忘记提及这些概念,面试官可能会将此问题视为快速排序与合并排序的后续工作。quicksort和mergesort之间的主要区别之一是快速排序不稳定,而合并排序是一种稳定的排序算法。顺便说一句,如果您不熟悉Quicksort和Mergesort等基本排序算法,那么我建议您学习下全面的数据结构课程,如数据结构和算法:深度使用Java。它将为您提供进一步探索所需的所有基础知识。稳定与不稳定算法假设您需要按键的递增顺序对以下键值对进行排序:INPUT:(4,5)(3,2)(4,3)(5,4)(6,4)现在,有两个密钥相同的两对的可能解决方案即(4,5)和(4,3)如下所示:OUTPUT1:(3,2),(4,5),(4,3),(5,4),(6,4)OUTPUT2:(3,2 ),(4,3),(4,5),(5,4),(6,4)将产生第一个输出的排序算法称为稳定排序算法,因为保持了相等键的原始顺序,您可以看到(4,5)以排序顺序出现在(4,3)之前,这是原始顺序,即在给定的输入中,(4,5)出现在(4,3)之前。产生第二输出的算法将被称为不稳定的排序算法,因为具有相同键的对象的顺序不按排序顺序维持。你可以看到,在第二个输出中,(4,3)出现在(4,5)之前,原始输入中不是这种情况。现在,最大的问题是稳定和不稳定排序算法的一些例子是什么?那么,你可以把所有众所周知的排序算法分成稳定和不稳定的。稳定算法的一些示例是合并排序,插入排序,冒泡排序和二叉树排序。而QuickSort,堆排序和选择排序是不稳定的排序算法。如果你还记得,Collections.sort()Java Collection框架中的方法使用迭代合并排序,这是一种稳定的算法。如果输入数组被部分排序,它的比较也比NLog(N)少得多。如果您有兴趣了解有关此主题的更多信息,我建议您学习数据结构和算法,比如算法和数据结构-第1部分和第2部分。它是Java程序员算法的综合课程之一。稳定与不稳定算法示例以下图片解释了稳定和不稳定的排序是如何工作的:这就是稳定和不稳定排序算法之间的区别。请记住,如果在排序的输出中保持相等键或数字的原始顺序,则该算法称为排序算法。稳定排序算法的一些常见示例是合并排序,插入排序和冒泡排序。 你还想要知道哪些关于面试题的知识呢?请在下方留言!

April 18, 2019 · 1 min · jiezi

面试官问你数组和ArrayList怎么答?

我在想每个人在面试的时候都会被问到集合相关的问题,有好大一部分人在回答的时候并没有那么多的逻辑性,通常都是想到哪里说到哪里,这篇文章大概的捋一捋关于集合的相关问题。在每种编程语言中,都会有循环、数组、流程控制语句,数组是一种线性表数据结构,内存空间是连续的,保存的数据类型也是一致的。正是因为这两点,数组的随机访问才会非常的高效,这同时也是一把双刃剑,使得数组的其他操作效率变得很低,比如说,增加,删除,为了保持数组里面数据的连续性,就会做大量的消耗性能的数据迁移操作。针对数组这种类型,java中有容器类,比如ArrayList,ArrayList是对数组的包装,在底层就是数组实现的,因为数组在定义的时候必须是指定的长度,定义之后就无法再增加长度了,就是说不可能在原来的数组上接上一段,所以ArrayList就解决了这个问题,当超过数组容量的时候,ArrayList会进行扩容,扩容之后的容量是之前的1.5倍,然后再把之前数组中的数据复制过来。接下来,我们看下ArrayList的源码: private static final int MAX_ARRAY_SIZE = Integer.MAX_VALUE - 8;源码中数组最大的容量是Integer.MAX_VALUE -8,为什么要减去8 呢,这个是上面的定义上面的注释: /** * The maximum size of array to allocate. * Some VMs reserve some header words in an array. * Attempts to allocate larger arrays may result in * OutOfMemoryError: Requested array size exceeds VM limit */首先是一些VM在数组中保存的头信息;尝试着去分配更大的数组可能会导致OutOfMemoryError,请求的数组大小超过了VM的限制。在看这句代码的时候,脑中有没有出现两个大大的问号??首先,有没有想到为什么这个数组属性需要用 transient修饰?(想知道这个关键字是干什么的,可以看下我之前的一篇文章:面试问你java中的序列化怎么答?)transient Object[] elementData; // non-private to simplify nested class access大家可以随便的想一下,如果是面试的时候,你会怎么回答?由于 ArrayList 是基于动态数组实现的,所以并不是所有的空间都被使用。因此使用了 transient 修饰,可以防止被自动序列化。因此 ArrayList 自定义了序列化与反序列化,具体可以看 writeObject 和 readObject 两个方法。需要注意的一点是,当对象中自定义了 writeObject 和 readObject 方法时,JVM 会调用这两个自定义方法来实现序列化与反序列化。第二个问题:这个属性的类型为什么是Object而不是泛型?这里和大家说下:java中泛型运用的目的就是对象的重用,就是同一个方法,可以支持多种对象类型,Object和泛型在编写的时候其实没有太大的区别,只是JVM中没有T这个概念,T只是存在编写的时候,进入虚拟机运行的时候,虚拟机会对这种泛型标志进行擦除,也就是替换T到指定的类型,如果没有指定类型,就会用Object替换,同时Object可以new Object(),就是说可以实例化,而T则不能实例化。在反射方面来说,从运行时,返回一个T的实例时,不需要经过强制转换,然后Object则需要经过转换才能得到。当我们试图向ArrayList中添加一个元素的时候,java会自动检查,以确保集合中确实还有容量来添加新元素,如果没有,就会自动扩容,下面是核心代码,我已经在代码里加了注释,帮助大家能够更好的理解:private void ensureExplicitCapacity(int minCapacity) { modCount++; // overflow-conscious code if (minCapacity - elementData.length > 0) grow(minCapacity);}上面这段代码中有个 变量 modCount++,看到这里的时候确实有些疑惑,我找了下,这个变量是在AbstractList中定义的protected修饰的全局变量,这个变量是记录了结构性改变的次数,结构性改变就是说修改列表大小的操作。ArrayList是一个线程不安全的类,这个变量就是用来保证在多线程环境下使用迭代器的时候,同时又对集合进行了修改,同一时刻只能有一个线程修改集合,如果多于一个,就会抛出ConcurrentModficationException。private void grow(int minCapacity) { // overflow-conscious code int oldCapacity = elementData.length; //核心的扩容代码:扩容之后的容量, int newCapacity = oldCapacity + (oldCapacity >> 1); if (newCapacity - minCapacity < 0) //扩容之后的容量与本次操作需要的容量对比,取更大的 newCapacity = minCapacity; if (newCapacity - MAX_ARRAY_SIZE > 0) //在与数组的最大容量对比,如果比最大的容量大,进入下一个方法 newCapacity = hugeCapacity(minCapacity); // minCapacity is usually close to size, so this is a win: //接下来,是把原数组d 数据复制到新的数组里 elementData = Arrays.copyOf(elementData, newCapacity);} private static int hugeCapacity(int minCapacity) { if (minCapacity < 0) // overflow throw new OutOfMemoryError(); //当 Integer-8 依然无法满足需求,就会取Integer的最大值 return (minCapacity > MAX_ARRAY_SIZE) ? Integer.MAX_VALUE : MAX_ARRAY_SIZE;}接下来我们看下,向指定位置添加元素是什么样的:public void add(int index, E element) { rangeCheckForAdd(index); //扩容校验 ensureCapacityInternal(size + 1); // Increments modCount!! System.arraycopy(elementData, index, elementData, index + 1, size - index); elementData[index] = element; size++;}可以看到,当一个集合足够大的时候,add操作向数组的尾部添加元素效率还是非常高的,但是当向指定的位置添加元素的时候,也是需要大量的移动复制操作:System.arraycopy()。到这里,ArrayList最大的优势是什么呢?我们在平时的开发中涉及到容器的时候为什么会选择List家族的成员,而不直接选择数组呢?大家回想一下自己之前敲代码的经历,答案也就出来了:ArrayList封装了大部分数组的操作方法,比如插入、删除、搬移数据等等,都在集合内部帮你做好了,还有就是支持动态扩容,这点是数组不能比拟的。这里需要注意一点,当我们在开始定义集合的时候,如果知道我们需要多大的集合,就应该在一开始就指定集合的大小,因为在集合的内部来进行数据的搬移,复制也是非常耗时的。那么数组在什么时候会用到呢?1、java ArrayList无法存储基本类型,int,long,如果保存的话,就需要封装为Integer、Long,而自动的拆装箱,也有性能的消耗,所以总结下这点就是说如果要保存基本类型,同时还特别关注性能,就可以使用数组。2、如果对数据的数量大小已知,操作也非常简单,也不需要ArrayList中的大部分方法,也是可以直接使用数组的。如果对本文有任何异议,可以加我好友(有没有问题都欢迎大家加我好友),也可以在下面留言区留言,我会及时修改。希望这篇文章能帮助大家在面试路上乘风破浪。这样的分享我会一直持续,你的关注、转发和好看是对我最大的支持,感谢。关注我,我们一起成长。 ...

April 18, 2019 · 1 min · jiezi

udp协议-看这篇就够了

UDP 概述用户数据报协议 UDP 只在 IP 的数据报服务之上增加了很少一点的功能,这就是复用和分用的功能以及查错检测的功能UDP 的主要特点UDP 是无连接的,即发送数据之前不需要建立连接(发送数据结束时也没有连接可释放),减少了开销和发送数据之前的时延UDP 使用尽最大努力交付,即不保证可靠交付,主机不需要维持复杂的连接状态表UDP 是面向报文的,发送方的 UDP 对应用程序交下来的报文,在添加首部后就向下交付 IP 层。UDP 对应用层交下来的报文,既不合并,也不拆分,而是保留这些报文的边界UDP 没有拥塞控制,网络出现的拥塞不会使源主机的发送速率降低。这对某些实时应用是很重要的UDP 支持一对一、一对多、多对一和多对多的交互通信UDP 的首部开销小,只有8个字节,比 TCP 的20个字节的首部要短《PHP面试问答》 https://github.com/colinlet/P…结合实际 PHP 面试,系统的汇总面试中的各种各样的问题,尝试提供简洁准确的答案。如果你在 PHP 面试中遇到问题,欢迎提 Issues 交流。包含网络协议、数据结构与算法、PHP、Web、MySQL、Redis、Linux、安全、设计模式、架构、自我介绍、离职原因、职业规划、准备问题等部分 如果觉得不错欢迎 star 关注,正在不断持续更新中~~存在问题某些实时应用需要使用没有拥塞控制的 UDP,但很多的源主机同时都向网络发送高速率的实时视频流时,网络就有可能发生拥塞,导致大家都无法正常接收。还有一些使用 UDP 的实时应用,需要对 UDP 的不可靠传输进行适当的改进,以减少数据的丢失。应用进程可以在不影响应用的实时性的前提下,增加一些提高可靠性的措施,如采用前向纠错或重传已丢失的报文UDP 的首部格式用户数据报 UDP 有两个字段:数据字段和首部字段。首部字段很简单,只有8个字节,由四个字段组成,每个字段都是两个字节首部字段源端口 源端口号。在需要对方回信时。不需要时可用全0目的端口 目的端口号。这在终点交付报文时必须使用长度 UDP 用户数据报的长度,其最小值是8(仅有首部)检验和 检测 UDP 用户数据报在传输中是否有错。有错就丢弃端口分用当运输层从 IP 层收到 UDP 数据报时,就根据首部中的目的端口,把 UDP 数据报通过相应的端口,上交最后的终点——应用进程如果接受方 UDP 发现收到的报文中的目的端口号不正确(即不存在对应于该端口号的应用程序),就丢弃该报文,并由网际控制报文协议 ICMP 发送“端口不可达”差错报文给发送方伪首部UDP 用户数据报首部中检验和的计算方法有些特殊。在计算检验和时,要在 UDP 用户数据报之前增加 12 个字节的伪首部。所谓“伪首部”是因为这种伪首部并不是 UDP 用户数据报真正的首部。只是在计算检验和时,临时添加在 UDP 用户数据报前面,得到一个临时的 UDP 用户数据报。检验和就是按照这个临时用户数据报来计算的。伪首部既不向下传也不向上递交,而仅仅是为了计算检验和本文转载自 枫叶林博客,《用户数据报协议UDP》https://blog.maplemark.cn/201…

April 17, 2019 · 1 min · jiezi

2019春招实习前端面试经历总结

春招前端实习面试记录(2019.3 ~ 2019.5)从2019.1就开始渐渐的进行复习,2月末开始面试,到现在四月中旬基本宣告结束。在3月和4月经历了无数次失败,沮丧,意外,期待,崩溃,焦虑等一系列感觉。我尽力回忆每一场面试问的问题,希望给后辈们做个参考。碰壁的三月企家有道(一面挂)2019.2.27CSS实现三角形数组乱序for in 和 for of 区别Promise接收的函数中resolve()后的代码是否会执行?监听一段时间内用户对我方网页的操作图片懒加载,预加载第一次面试,问了很多基础,非常非常紧张,都有些结巴。面完恶补了基础,研究了很多平时常用却不未深挖的东西。字节跳动(一面)第一次在牛客网上视频面试,头条真的非常非常注重代码能力css两列布局,右列定宽,左列自适应。flex,轴cookie,session,token各种细节跨域cors预请求原型链最顶层是什么?addEventListener细节手撕代码:reduce实现mapfor in 和 for of手撕代码: call实现bind手撕代码:实现一个函数,每隔wait秒执行func,一共执行times次箭头函数this指向的问题变量提升,从EC的VO答字节跳动(二面)项目相关手撕代码:实现一个函数,该函数接收一个obj, 一个path, 一个value,实现obj[path] = value,obj类似json格式react 中的 setState为什么异步?能不能同步?什么时候异步?什么时候同步?promisereact virtual dom, diff箭头函数this变量提升上下文字节跳动(三面挂)自我介绍如何删除addEventListener绑定的事件dva解决了什么?如何解决?为什么使用?react <Form>原理redux-sagasetState异步受控组件 vs 非受控组件手撕代码:数组扁平化面完头条后恶补了很多js专题和react部分功能的实现原理,虽然三面挂了很遗憾,但是增加了信心,对自己的水平有了一个大概的估计腾讯PCG提前批(一面)自我介绍HTTP缓存HTTP vs HTTPShttp1.0/1.1/2.0SSL/TLS握手, 保密性? 完整性?证书?浏览器如何验证CA是否正确?中间人攻击?浏览器向服务器发送请求,相应数据包被拦截怎么办?http 2.0 新特性? 头部压缩详细讲讲?XSS, CSRF?区别?举个例子?event loop? 使用场景原型?new ?206 ?腾讯PCG提前批(二面挂)cookie,session,token,withcredintrals ?token为什么能抵抗csrf?闭包?使用场景?缺点?如何定位(检查)内存泄漏?GC轮询websocket感觉腾讯非常看重网络和安全,面完研究了半个月计算机网络和网络安全UBNT(一面挂)自我介绍cookie ? session ? httponly?事件委托? 阻止冒泡?css 水平垂直居中?把arguments变成数组?兼容?跨域?原型?react setState为什么异步?高阶函数? 高阶组件? 功能?区别?例子? @connect ?redux?mobx?为什么reducer是纯函数?手撕代码:快排尾递归?react 中的 叶子节点之间如何通信?vue通过没 ?字节跳动(找人内推又被捞了)自我介绍项目组件之间通信redux观察者,深问,怼了好久antd 表单组件api?底层如何实现的?有没有看过源码antd Form.create?事件模型event loop手撕代码 二叉树节点之和 leetcode原题这一面又挂在了设计模式和算法,之后又研究了几种常用设计模式,刷了一遍剑指offer和leetcode树部分题。开奖的四月58企服(一面后加了wx)从输入url到展现的全过程dnscdn浏览器内核协议布局BFCposition回流,重绘同程艺龙(offer)回流,重绘节流,防抖reactreduxMVC vs MVVMmobx电话面试,有些没听清,有些久远,记不太清了,不算很难,都是一些之前问过的东西用友(面试通过,但因实习时间问题没发offer)来来回回也就是三月那些问题,且难度低于三月金蝶(hr面,等offer)不难久其数字科技(offer)不难蘑菇街(蜜汁一面挂)项目相关数据库设计学习路程闭包,使用场景观察者模式事件循环几种继承方法如果给我一个规定期限内无法完成的任务,我怎么办对于前端学习的深度和广度有什么理解这一面都是些开放性问题,感觉答得确实也不错,面试官在结尾也说本轮面试一结束,下一轮面试hr会再联系。然后就挂了,感觉是不想招人,也可能是我太菜了吧 =.=亚信科技(史上最短)块级元素和内联元素CSS初始化配过webpack吗?react生命周期boss直聘上海投的,面试时间很短,没超过10分钟,感觉不想招人~~酷家乐(暂无消息)Promise 链式调用的时候怎么终止它?Map ?304闭包继承原型感觉挺简单的,对方是做2D/3D/ThreeJS的部门,刚面完,在等电话。总结找工作真的是实力与运气共存的。个人感觉像阿里/腾讯/头条那些业界面试难度较高的公司是8成实力,2成运气。其他一二线公司/各种独角兽是6成实力,4成运气。其他的就是5/5开了。还有2家公司之前约好了,面后会再更新的,面完就不再投了,等秋招再战了。最后我的春招基本是到此结束了,虽然有很多遗憾,有时距殿堂只有一步之遥,却未能跨过。学校的奇葩教学计划也给了我这种渣渣无数阻力,很多时候真的想放弃,但还是坚持下来了,暂时上岸,希望之后能越来越好。感谢以上公司给了我一个展现自己的机会,谢谢!祝大家都拿到满意的offer。

April 17, 2019 · 1 min · jiezi

面试题里的那些各种手写

最近准备初级前端面试,发现有很多手写实现什么的,例如什么手写实现bind,promise。手写ajax,手写一些算法。翻阅了很多书籍和博客。这里做一个总结改进,算是对我后面大概为期一个月找工作的准备。手写实现bind() Function.prototype._bind = function (context) { var self = this; var args = Array.prototype.slice.call(arguments, 1); var fBind = function () { var bindArgs = Array.prototype.slice.call(arguments); return self.apply(this instanceof fBind ? this : context, args.concat(bindArgs)); } fBind.prototype = self.prototype&&Object.create(self.prototype) return fBind; }简单的说明:这里之所以传参的时候需要两个数组,是因为考虑到用new以构造函数的形式调用硬绑定函数的情况:this这时是应该指向实例对象的。这样子就需要继承之前函数的方法, fBind.prototype = self.prototype&&Object.create(self.prototype),同时也可以用 Object.setPrototypeOf(fBind.prototype,self.prototype)。考虑到存在undefined的情况,前面加一个判断self.prototype&&…..关于apply的第一个参数,如果考虑到之前的情况,是不能传入context的,这需要做一个判断。像是下面的情况 function Foo(price){ this.price =price this.fn = ()=>{ console.log(‘hi fn’) } console.log(this.name) } Foo.prototype.sayMyName = function(){ console.log(this.name) } var Obj1 = { name:‘obj1’ } var b =Foo._bind(Obj1) b() //“obj1” var c = new b(1000)//“i am c” c.name = ‘i am c’ c.sayMyName()这里的this的指向就是c,它指向实例对象本身。后面以这道题为引线面试官可能会追问:什么是执行上下文this的判断call,bind的区别手写一个函数实现斐波那契数列首先拷一个阮神在他es6教程里的一个写法。function* fibonacci() { let [prev, curr] = [0, 1]; for (;;) { yield curr; [prev, curr] = [curr, prev + curr]; }}for (let n of fibonacci()) { if (n > 1000) break; console.log(n);}更精简的const feibo= max=>{ let [a,b,i]= [0,1,1] while(i++<=max) { [a,b] = [b,a + b ] console.log(b) } return a }相对是非常简单的,感觉也不会多问啥,就不多说了。手写一个简单的ajax let xhr = new XMLHttpRequest() xhr.open(‘get’, url, true) xhr.onreadystatechange = function(){ if(xhr.readyState === 4){ console.log(‘请求完成’) if(this.status >= 200 &&this.status<300){ conso.log(‘成功’) }else{ consol.log(‘失败’) } } } xhr.onerror = function(e) { console.log(‘连接失败’) } xhr.send()大概是这么个意思就差不多了,顺势可能会问一些状态码和状态值的问题,或者直接问到关于http上面的问题。原型继承 function inherit(supertype,subtype){ Object.setPrototypeOf(subtype.prototype,supertype.prototype) subtype.prototype.constructor = subtype } function Car(name,power){ this.name=name this.power = power } Car.prototype.showPower = function(){ console.log(this.power) } function Changan(price,name,power){ this.price = price Car.call(this,name,power) } inherit(Car,Changan) Changan.prototype.showName = function(){ console.log(this.name) } var star = new Changan(100000,‘star’,500) star.showPower()防抖与节流 function debounce(fn,duration){ var timer window.clearTimeout(timer) timer = setTimeout(()=>{ fn.call(this) },duration) } function throttle(fn,duration){ let canIRun if(!canIRun)return fn.call(this) canIRun = false setTimeout(()=>{ canIRun = true },duration) } 数组去重我一般就用这两种,大部分情况都能应付了。[…new Set(array)]//hash function unique(array) { const object = {} array.map(number=>{ object[number] = true }) return Object.keys(object).map(//…….) }//大概是这样的意思,写法根据数组的不同可能会有所改变深拷贝应该是面试里面手写xxx出现频率最高的题了,无论是笔试还是面试。 总是让你手写实现深拷贝函数。事实上,js并不能做到真正完全的标准的深拷贝所以不管你写什么样的深拷贝函数都会有不适用的地方,这取决于使用的场景和拷贝的对象,如果面试官在这上面钻研比较深的话,是很难做到完美的。既然这样就写个将就一点的深拷贝吧,面向面试的那种。function deepClone(item) { return result; }首先在类型判断上做一个选择,一般情况来说,用new创建的实例对象用typeof判断会出问题的,相比之下instanceof也不靠谱。这里面相对比较靠谱的Object.prototype.toString.call(item)。(这个其实也不兼容到全部情况和性能要求,但是面向面试代码可读性会高一点)。type = Object.prototype.toString.call(item).slice(8,this.length-1),//[object String],[object Array],[object Object]函数的拷贝,这里不能使用bind,会改变this指向或影响后续使用call调用该拷贝的函数,大部分情况是不必要的,这里就直接赋值吧。于是这里可以把基本数据类型和Function放一起。fk= [‘String’,‘Number’,‘Boolean’,‘Function’].indexOf(type)>-1dom对象的拷贝: result = item.cloneNode(true); 忽略正则Date,[object Object], [object Array]放到后面的判断 let other = { //需要递归或者其他的操作 Array() { result = [] item.forEach((child, index)=>{ hash.set(item, result); result[index] = deepClone(child,hash) }) }, Date(){ result = new Date(item) }, Object(){ result = {} Reflect.ownKeys(item).forEach(child=>{ hash.set(item, result); result[child] = deepClone(item[child],hash) }) } } othertype这样子是不是相对清晰一些了,应付一般的情况应该差不多了,但是没考虑循环引用。这里给个思路是使用ES6的WeakMap,不知道的兄弟可以看看阮神的ES6博客,为防止爆栈,我把循环引用直接扔给它,完美拷贝。 就相当于var wm = new WeakMap()var obj = { name:null }obj.name = objwm.set(obj,wm.get(obj))console.log(wm)现在就需要在开头检查一下循环引用,然后直接返回WeakMap对象键名为item参数对象的值 所以最终代码就是function deepClone(item,hash = new WeakMap()) { if (!item) return item if (hash.has(item))return hash.get(item); //检查循环引用 var result, type = Object.prototype.toString.call(item).slice(8,this.length-1), fk= [‘String’,‘Number’,‘Boolean’,‘Function’].indexOf(type)>-1 if(fk){ result = item;//直接赋值 }else if(item.nodeType && typeof item.cloneNode === “function”){ result = item.cloneNode(true); //是否是dom对象 }else{ let other = { //需要递归或者其他的操作 Array() { result = [] item.forEach((child, index)=>{ hash.set(item, result); result[index] = deepClone(child,hash) }) }, Date(){ result = new Date(item) }, Object(){ result = {} Reflect.ownKeys(item).forEach(child=>{ hash.set(item, result); result[child] = deepClone(item[child],hash) }) } } othertype } return result; }意思就大概是这个意思,当然深拷贝的方法有很多,甚至不一定用到递归。面试官总会有找茬的地方的。 我觉得我写的这个还是满足我现在找工作的级别要求的。然后是我用来测试的对象var obj1 = { name:‘obj1’, one : { a:new Date(), b:new String(‘1-2’), c:new Array([’this’,‘is’,1,{a:23}]), d: function () { if(true){ return ’d’ } }, e:new Number(15), f:new Boolean(true) }, two(x){ console.log(x+’ ‘+this.name) }, three : [ { a:’this is a’, b:document.body, c:{ a:[1,2,3,4,5,[13,[3],true],10], b:{ a:{ a:[1,2,3] }, b:2 } } }, ], four:[1,2]} obj1.name=obj1 obj1.four[3] = obj1 var copy = deepClone(obj1) console.log(copy) copy.two.call(window,‘hi’)## new 简单说下大概是这么一个过程创建一个空对象执行传入的构造函数,执行过程中对 this 操作就是对 这个空对象 进行操作。返回这个空对象模拟需要考虑的问题是一个空对象,这里面的写法obj原型链是没有上一级的,即不存在与其他任何对象之间的联系,虽然在这里面没多少区别:var obj = Object.create(null),this指向这个空对象:let rt = Constructor.apply(obj, arguments);可以访问构造函数的原型链, Object.setPrototypeOf(obj, Constructor.prototype);如果构造函数有返回值,并且是一个对象,那么实例对象拿到的就是这个对象(应该只是值,并不是引用)。所以这里要做个判断return typeof rt === ‘object’ ? rt : obj;最终的代码function _new(){ var obj = Object.create(null), Constructor = [].shift.call(arguments); Object.setPrototypeOf(obj, Constructor.prototype); let rt = Constructor.apply(obj, arguments); return rt instanceof Object ? rt : obj;}<br/> <br/>快速排序快排 :代码精简了一点function quickSort(arr){ if(arr.length<=1)return arr var index = Math.floor(arr.length/2), number = arr.splice(index,1)[0], left = [], right = []; arr.forEach(item=>{ item<=number?left.push(item):right.push(item) }) return quickSort(left).concat([number],quickSort(right)) }这期间会不断更新并修改,这里面的手写实现您如果有更好的写法或者新的思路,也希望可以说明交流。最后谢谢大佬些的观看。 ...

April 16, 2019 · 3 min · jiezi

「前端面试题系列9」浅拷贝与深拷贝的含义、区别及实现(文末有岗位内推哦~)

前言这是前端面试题系列的第 9 篇,你可能错过了前面的篇章,可以在这里找到:数组去重(10 种浓缩版)JavaScript 中的事件机制(从原生到框架)理解函数的柯里化ES6 中箭头函数的用法this 的原理以及用法伪类与伪元素的区别及实战如何实现一个圣杯布局?今日头条 面试题和思路解析面试的时候,我经常会问候选人深拷贝与浅拷贝的问题。因为它可以考察一个人的很多方面,比如基本功,逻辑能力,编码能力等等。另外在实际工作中,也常会遇到它。比如用于页面展示的数据状态,与需要传给后端的数据包中,有部分字段的值不一致的话,就需要在传参时根据接口文档覆写那几个字段的值。最常见的可能就是 status 这个参数了。界面上的展示需要 Boolean 值,而后端同学希望拿到的是 Number 值,1 或者 0。为了不影响展示效果,往往就需要深拷贝一下,再进行覆写,否则界面上就会因为某些值的变化,出现奇怪的现象。至于为什么会这样,下文会讲到。马上开始今天的主题,让我们先从赋值开始说起。赋值Javascript 的原始数据类型有这几种:Boolean、Null、Undefined、Number、String、Symbol(ES6)。它们的赋值很简单,且赋值后两个变量互不影响。let test1 = ‘chao’;let test2 = test1;// test2: chaotest1 = ‘chao_change’;// test2: chao// test1: chao_change另外的引用数据类型有:Object 和 Array。深拷贝与浅拷贝的出现,就与这两个数据类型有关。const obj = {a:1, b:2};const obj2 = obj;obj2.a = 3;console.log(obj.a); // 3依照赋值的思路,对 Object 引用类型进行拷贝,就会出问题。很多情况下,这不是我们想要的。这时,就需要用浅拷贝来实现了。浅拷贝什么是浅拷贝?可以这么理解:创建一个新的对象,把原有的对象属性值,完整地拷贝过来。其中包括了原始类型的值,还有引用类型的内存地址。让我们用 Object.assign 来改写一下上面的例子:const obj = {a:1, b:2};const obj2 = Object.assign({}, obj);obj2.a = 3;console.log(obj.a); // 1Ok,改变了 obj2 的 a 属性,但 obj 的 a 并没有发生变化,这正是我们想要的。可是,这样的拷贝还有瑕疵,再改一下例子:const arr = [{a:1,b:2}, {a:3,b:4}];const newArr = [].concat(arr);newArr.length = 1; // 为了方便区分,只保留新数组的第一个元素console.log(newArr); // [{a:1,b:2}]console.log(arr); // [{a:1,b:2},{a:3,b:4}]newArr[0].a = 123; // 修改 newArr 中第一个元素的aconsole.log(arr[0]); // {a: 123, b: 2},竟然把 arr 的第一个元素的 a 也改了oh,no!这不是我们想要的…经过一番查找,才发现:原来,对象的 Object.assign(),数组的 Array.prototype.slice() 和 Array.prototype.concat(),还有 ES6 的 扩展运算符,都有类似的问题,它们都属于 浅拷贝。这一点,在实际工作中处理数据的组装时,要格外注意。所以,我将浅拷贝这样定义:只拷贝第一层的原始类型值,和第一层的引用类型地址。深拷贝我们当然希望当拷贝多层级的对象时,也能实现互不影响的效果。所以,深拷贝的概念也就油然而生了。我将深拷贝定义为:拷贝所有的属性值,以及属性地址指向的值的内存空间。也就是说,当遇到对象时,就再新开一个对象,然后将第二层源对象的属性值,完整地拷贝到这个新开的对象中。按照浅拷贝的思路,很容易就想到了递归调用。所以,就自己封装了个深拷贝的方法:function deepClone(obj) { if(!obj && typeof obj !== ‘object’){ return; } var newObj= toString.call(obj) === ‘[object Array]’ ? [] : {}; for (var key in obj) { if (obj[key] && typeof obj[key] === ‘object’) { newObj[key] = deepClone(obj[key]); } else { newObj[key] = obj[key]; } } return newObj;}再试试看:let arr = [{a:1,b:2}, {a:3,b:4}];let newArr = deepClone(arr);newArr.length = 1; // 为了方便区分,只保留新数组的第一个元素console.log(newArr); // [{a:1, b:2}]console.log(arr); // [{a:1, b:2}, {a:3, b:4}]newArr[0].a = 123; // 修改 newArr 中第一个元素的 aconsole.log(arr[0]); // {a:1, b:2}ok,这下搞定了。不过,这个方法貌似会存在 引用丢失 的的问题。比如这样:var b = {};var a = {a1: b, a2: b};a.a1 === a.a2 // truevar c = clone(a);c.a1 === c.a2 // false如果我们的需求是,应该丢失引用,那就可以用这个方法。反之,就得想办法解决。一行代码的深拷贝当然,还有最简单粗暴的深拷贝方法,就是利用 JSON 了。像这样:let newArr2 = JSON.parse(JSON.stringify(arr));console.log(arr[0]); // {a:1, b:2}newArr2[0].a = 123;console.log(arr[0]); // {a:1, b:2}但是,JSON 内部用了递归的方式。数据一但过多,就会有递归爆栈的风险。// Maximum call stack size exceeded深拷贝的终极方案有位大佬给出了深拷贝的终极方案,利用了“栈”的思想。function cloneForce(x) { // 用来去重 const uniqueList = []; let root = {}; // 循环数组 const loopList = [ { parent: root, key: undefined, data: x, } ]; while(loopList.length) { // 深度优先 const node = loopList.pop(); const parent = node.parent; const key = node.key; const data = node.data; // 初始化赋值目标,key为undefined则拷贝到父元素,否则拷贝到子元素 let res = parent; if (typeof key !== ‘undefined’) { res = parent[key] = {}; } // 数据已经存在 let uniqueData = uniqueList.find((item) => item.source === data ); if (uniqueData) { parent[key] = uniqueData.target; // 中断本次循环 continue; } // 数据不存在 // 保存源数据,在拷贝数据中对应的引用 uniqueList.push({ source: data, target: res, }); for(let k in data) { if (data.hasOwnProperty(k)) { if (typeof data[k] === ‘object’) { // 下一次循环 loopList.push({ parent: res, key: k, data: data[k], }); } else { res[k] = data[k]; } } } } return root;}其思路是:引入一个数组 uniqueList 用来存储已经拷贝的数组,每次循环遍历时,先判断对象是否在 uniqueList 中了,如果在的话就不执行拷贝逻辑了。这个方法是在解决递归爆栈问题的基础上,加以改进解决循环引用的问题。但如果你并不想保持引用,那就改用 cloneLoop(用于解决递归爆栈)即可。有兴趣的同学,可以前往 深拷贝的终极探索(90%的人都不知道),查看更多的细节。总结所谓深拷贝与浅拷贝,指的是 Object 和 Array 这样的引用数据类型。浅拷贝,只拷贝第一层的原始类型值,和第一层的引用类型地址。深拷贝,拷贝所有的属性值,以及属性地址指向的值的内存空间。通过递归调用,或者 JSON 来做深拷贝,都会有一些问题。而 cloneForce 方法倒是目前看来最完美的解决方案了。在日常的工作中,我们要特别注意,对象的 Object.assign(),数组的 Array.prototype.slice() 和 Array.prototype.concat(),还有 ES6 的 扩展运算符,都属于浅拷贝。当需要做数据组装时,一定要用深拷贝,以免影响界面展示效果。岗位内推莉莉丝游戏招 中高级前端工程师 啦!!!你玩过《小冰冰传奇([刀塔传奇])》么?你玩过《剑与家园》么?你想和 薛兆丰老师 成为同事么?有兴趣的同学,可以 关注下面的公众 号加我微信 详聊哈~ ...

April 16, 2019 · 2 min · jiezi

【Leetcode】114. 二叉树展开为链表

题目给定一个二叉树,原地将它展开为链表。例如,给定二叉树 1 / \ 2 5 / \ \3 4 6将其展开为:1 \ 2 \ 3 \ 4 \ 5 \ 6题解这算是比较经典的一道题目了, 博主面试快手的时候原题。最开始一想,觉得递归的求解不就好了,但是递归的时候发现需要注意一个地方就是:需要先递归右子树,然后记录下右子树展开完成之后的链表头。然后再递归的求解左子树,把左子树的最后一个链到右子树的链表头。基于这个,我们用一个pre指针来记录右子树的头结点。class Solution { private TreeNode prev = null; public void flatten(TreeNode root) { if (root == null) return; flatten(root.right); flatten(root.left); root.right = prev; root.left = null; prev = root; }}递归的方式转换为迭代的方式用stack就好了,反而比较好理解。class Solution { public void flatten(TreeNode root) { if (root == null) return; Stack<TreeNode> stack = new Stack<TreeNode>(); stack.push(root); while (!stack.isEmpty()) { TreeNode current = stack.pop(); if (current.right != null) stack.push(current.right); if (current.left != null) stack.push(current.left); if (!stack.isEmpty()) current.right = stack.peek(); current.left = null; } }}有问题加手撕代码QQ群讨论:805423079 ...

April 16, 2019 · 1 min · jiezi

蚂蚁金服实习生面经总结(已拿口头offer)

我自己总结的Java学习的系统知识点以及面试问题,已经开源,目前已经 35k+ Star。会一直完善下去,欢迎建议和指导,同时也欢迎Star: https://github.com/Snailclimb…本文来自 Anonymous 的投稿,原文地址:https://zhuanlan.zhihu.com/p/… 。一面 (37 分钟左右)一面是上海的小哥打来的,3.12 号中午确认的内推,下午就打来约时间了,也是唯一一个约时间的面试官。约的晚上八点。紧张的一比,人生第一次面试就献给了阿里。幸运的是一面的小哥特温柔。好像是个海归?口语中夹杂着英文。废话不多说,上干货:面试官: 先自我介绍下吧!我: 巴拉巴拉…。关于自我介绍:从 HR 面、技术面到高管面/部门主管面,面试官一般会让你先自我介绍一下,所以好好准备自己的自我介绍真的非常重要。网上一般建议的是准备好两份自我介绍:一份对 HR 说的,主要讲能突出自己的经历,会的编程技术一语带过;另一份对技术面试官说的,主要讲自己会的技术细节,项目经验,经历那些就一语带过。面试官: 我看你简历上写你做了个秒杀系统?我们就从这个项目开始吧,先介绍下你的项目。关于项目介绍:如果有项目的话,技术面试第一步,面试官一般都是让你自己介绍一下你的项目。你可以从下面几个方向来考虑:对项目整体设计的一个感受(面试官可能会让你画系统的架构图)在这个项目中你负责了什么、做了什么、担任了什么角色从这个项目中你学会了那些东西,使用到了那些技术,学会了那些新技术的使用另外项目描述中,最好可以体现自己的综合素质,比如你是如何协调项目组成员协同开发的或者在遇到某一个棘手的问题的时候你是如何解决的又或者说你在这个项目用了什么技术实现了什么功能比如:用 redis 做缓存提高访问速度和并发量、使用消息队列削峰和降流等等。我: 我说了我是如何考虑它的需求(秒杀地址隐藏,记录订单,减库存),一开始简单的用 synchronized 锁住方法,出现了问题,后来乐观锁改进,又有瓶颈,再上缓存,出现了缓存雪崩,于是缓存预热,错开缓存失效时间。最后,发现先记录订单再减库存会减少行级锁等待时间。一面面试官很耐心地听,并给了我一些指导,问了我乐观锁是怎么实现的,我说是基于 sql 语句,在减库存操作的 where 条件里加剩余库存数>0,他说这应该不算是一种乐观锁,应该先查库存,在减库存的时候判断当前库存是否与读到的库存一样(可这样不是多一次查询操作吗?不是很理解,不过我没有反驳,只是说理解您的意思。事实证明千万别怼面试官,即使你觉得他说的不对)面试官: 我缓存雪崩什么情况下会发生?如何避免?我: 当多个商品缓存同时失效时会雪崩,导致大量查询数据库。还有就是秒杀刚开始的时候缓存里没有数据。解决方案:缓存预热,错开缓存失效时间面试官: 问我更新数据库的同时为什么不马上更新缓存,而是删除缓存?我: 因为考虑到更新数据库后更新缓存可能会因为多线程下导致写入脏数据(比如线程 A 先更新数据库成功,接下来要取更新缓存,接着线程 B 更新数据库,但 B 又更新了缓存,接着 B 的时间片用完了,线程 A 更新了缓存)逼逼了将近 30 分钟,面试官居然用周杰伦的语气对我说:我突然受宠若惊,连忙说谢谢,也正是因为第一次面试得到了面试官的肯定,才让我信心大增,二三面稳定发挥。面试官又曰: 我看你还懂数据库是吧,答:略懂略懂。。。那我问个简单的吧!我: 因为这个问题太简单了,所以我忘记它是什么了。面试官: 你还会啥数据库知识?我: 我一听,问的这么随意的吗。。。都让我选题了,我就说我了解索引,慢查询优化,巴拉巴拉面试官: 等等,你说索引是吧,那你能说下索引的存储数据结构吗?我: 我心想这简单啊,我就说 B+树,还说了为什么用 B+树面试官: 你简历上写的这个 J.U.C 包是什么啊?(他居然不知道 JUC)我: 就是 java 多线程的那个包啊。。。面试官: 那你都了解里面的哪些东西呢?我: 哈哈哈!这可是我的强项,从 ConcurrentHashMap,ConcurrentLinkedQueue 说到 CountDownLatch,CyclicBarrier,又说到线程池,分别说了底层实现和项目中的应用。面试官: 我觉得差不多了,那我再问个与技术无关的问题哈,虽然这个问题可能不应该我问,就是你是如何考虑你的项目架构的呢?我: 先用最简单的方式实现它,再去发掘系统的问题和瓶颈,于是查资料改进架构。。。面试官: 好,那我给你介绍下我这边的情况吧总结: 一面可能是简历面吧,问的比较简单,我在讲项目中说出了我做项目时的学习历程和思考,赢得了面试官的好感,感觉他应该给我的评价很好。二面 (33 分钟左右)然而开心了没一会,内推人问我面的怎么样啊?看我流程已经到大大 boss 那了。我一听二面不是主管吗???怎么直接跳了一面。于是瞬间慌了,赶紧(下床)学习准备二面。隔了一天,3.14 的早上 10:56 分,杭州的大大 boss 给我打来了电话,卧槽我当时在上毛概课,万恶的毛概课每节课都点名,我还在最后一排不敢跑出去。于是接起电话来怂怂地说不好意思我在上课,晚上可以面试吗?大大 boss 看来很忙啊,跟我说晚上没时间啊,再说吧!于是又隔了一天,3.16 中午我收到了北京的电话,当时心里小失望,我的大大 boss 呢???接起电话来,就是一番狂轰乱炸。。。第一步还是先自我介绍,这个就不多说了,提前准备好要说的重点就没问题!面试官: 我们还是从你的项目开始吧,说说你的秒杀系统。我: 一面时的套路。。。我考虑到秒杀地址在开始前不应暴露给用户。。。面试官: 等下啊,为什么要这样呢?暴露给用户会怎么样?我: 用户提前知道秒杀地址就可以写脚本来抢购了,这样不公平面试官: 那比如说啊,我现在是个黑客,我在秒杀开始时写好了脚本,运行一万个线程获取秒杀地址,这样是不是也不公平呢?我: 我考虑到了这方面,于是我自己写了个 LRU 缓存(划重点,这么多好用的缓存我为啥不用偏要自己写?就是为了让面试官上钩问我是怎么写的,这样我就可以逼逼准备好的内容了!),用这个缓存存储请求的 ip 和用户名,一个 ip 和用户名只能同时透过 3 个请求。面试官: 那我可不可以创建一个 ip 代理池和很多用户来抢购呢?假设我有很多手机号的账户。我: 这就是在为难我胖虎啊,我说这种情况跟真实用户操作太像了。。。我没法区别,不过我觉得可以通过地理位置信息或者机器学习算法来做吧。。。面试官: 好的这个问题就到这吧,你接着说我: 我把生成订单和减库存两条 sql 语句放在一个事务里,都操作成功了则认为秒杀成功。面试官: 等等,你这个订单表和商品库存表是在一个数据库的吧,那如果在不同的数据库中呢?我: 这面试官好变态啊,我只是个本科生?!?!我觉得应该要用分布式锁来实现吧。。。面试官: 有没有更轻量级的做法?我: 不知道了。后来查资料发现可以用消息队列来实现。使用消息队列主要能带来两个好处:(1) 通过异步处理提高系统性能(削峰、减少响应所需时间);(2) 降低系统耦合性。关于消息队列的更多内容可以查看这篇文章:https://snailclimb.gitee.io/j…后来发现消息队列作用好大,于是现在在学手写一个消息队列。面试官: 好的你接着说项目吧。我: 我考虑到了缓存雪崩问题,于是。。。面试官: 等等,你有没有考虑到一种情况,假如说你的缓存刚刚失效,大量流量就来查缓存,你的数据库会不会炸?我: 我不知道数据库会不会炸,反正我快炸了。当时说没考虑这么高的并发量,后来发现也是可以用消息队列来解决,对流量削峰填谷。面试官: 好项目聊(怼)完了,我们来说说别的,操作系统了解吧,你能说说 NIO 吗?我: NIO 是。。。面试官: 那你知道 NIO 的系统调用有哪些吗,具体是怎么实现的?我: 当时复习 NIO 的时候就知道是咋回事,不知道咋实现。最近在补这方面的知识,可见 NIO 还是很重要的!面试官: 说说进程切换时操作系统都会发生什么?我: 不如杀了我,我最讨厌操作系统了。简单说了下,可能不对,需要答案自行百度。面试官: 说说线程池?答: 卧槽这我熟啊,把 Java 并发编程的艺术里讲的都说出来了,说了得有十分钟,自夸一波,毕竟这本书我看了五遍????面试官: 好问问计网吧如果设计一个聊天系统,应该用 TCP 还是 UDP?为什么我: 当然是 TCP!原因如下:面试官: 好的,你有什么要问我的吗?我: 我还有下一次面试吗?面试官: 应该。应该有的,一周内吧。还告诉我居然转正前要实习三个月?wtf,一个大三满课的本科生让我如何在八月底前实习三个月?我: 面试官再见三面 (46 分钟)3.18 号,三面来了,这次又是那个大大 boss!第一步还是先自我介绍,这个就不多说了,提前准备好要说的重点就没问题!面试官: 聊聊你的项目?我: 经过二面的教训,我迅速学习了一下分布式的理论知识,并应用到了我的项目(吹牛逼)中。面试官: 看你用到了 Spring 的事务机制,你能说下 Spring 的事务传播吗?我: 完了这个问题好像没准备,虽然之前刷知乎看到过。。。我就只说出来一条,面试官说其实这个有很多机制的,比如事务嵌套,内事务回滚外事务回滚都会有不同情况,你可以回去看看。面试官: 说说你的分布式事务解决方案?我: 我叭叭的照着资料查到的解决方案说了一通,面试官怎么好像没大听懂???阿里巴巴之前开源了一个分布式 Fescar(一种易于使用,高性能,基于 Java 的开源分布式事务解决方案),后来,Ant Financial 加入 Fescar,使其成为一个更加中立和开放的分布式交易社区,Fescar 重命名为 Seata。Github 地址:https://github.com/seata/seata面试官: 好,我们聊聊其他项目,说说你这个 MapReduce 项目?MapReduce 原理了解过吗?我: 我叭叭地说了一通,面试官好像觉得这个项目太简单了。要不是没项目,我会把我的实验写上吗???面试官: 你这个手写 BP 神经网络是干了啥?我: 这是我选修机器学习课程时的一个作业,我又对它进行了扩展。面试官: 你能说说为什么调整权值时要沿着梯度下降的方向?我: 老大,你太厉害了,怎么什么都懂。我压根没准备这个项目。。。没想到会问,做过去好几个月了,加上当时一紧张就忘了,后来想起来大概是….。面试官: 好我们问问基础知识吧,说说什么叫 xisuo?我:???xisuo,您说什么,不好意思我没听清。(这面试官有点口音。。。)就是 xisuo 啊!xisuo 你不知道吗?。。。尴尬了十几秒后我终于意识到,他在说死锁!!!面试官: 假如 A 账户给 B 账户转钱,会发生 xisuo 吗?能具体说说吗?我: 当时答的不好,后来发现面试官又是想问分布式,具体答案参考这个:https://blog.csdn.net/taylorc…面试官: 为什么不考研?我: 不喜欢学术氛围,巴拉巴拉。面试官: 你有什么问题吗?我: 我还有下一面吗。。。面试官说让我等,一周内答复。等了十天,一度以为我凉了,内推人说我流程到 HR 了,让我等着吧可能 HR 太忙了,3.28 号 HR 打来了电话,当时在教室,我直接飞了出去。HR 面面试官: 你好啊,先自我介绍下吧我: 巴拉巴拉….HR 面的技术面试和技术面的还是有所区别的!面试官人特别好,一听就是很会说话的小姐姐!说我这里给你悄悄透露下,你的评级是 A 哦!接下来就是几个经典 HR 面挂人的问题,什么难给我来什么,我看别人的 HR 面怎么都是聊聊天。。。面试官: 你为什么选择支付宝呢,你怎么看待支付宝?我: 我从个人情怀,公司理念,环境氛围,市场价值,趋势导向分析了一波(说白了就是疯狂夸支付宝,不过说实话我说的那些一点都没撒谎,阿里确实做到了。比如我举了个雷军和格力打赌 5 年 2000 亿销售额,大部分企业家关注的是利益,而马云更关注的是真的为人类为世界做一些事情,利益不是第一位的。)面试官: 明白了解,那你的优点我们都很明了了,你能说说你的缺点吗?缺点肯定不能是目标岗位需要的关键能力!!!总之,记住一点,面试官问你这个问题的话,你可以说一些不影响你这个职位工作需要的一些缺点。比如你面试后端工程师,面试官问你的缺点是什么的话,你可以这样说:自己比较内向,平时不太爱与人交流,但是考虑到以后可能要和客户沟通,自己正在努力改。我: 据说这是 HR 面最难的一个问题。。。我当时翻了好几天的知乎才找到一个合适的,也符合我的答案:我有时候会表现的不太自信,比如阿里的内推二月份就开始了,其实我当时已经复习了很久了,但是老是觉得自己还不行,不敢投简历,于是又把书看了一遍才投的,当时也是舍友怂恿一波才投的,面了之后发现其实自己也没有很差。(划重点,一定要把自己的缺点圆回来)。面试官: HR 好像不太满意我的答案,继续问我还有缺点吗?我: 我说比较容易紧张吧,举了自己大一面实验室因为紧张没进去的例子,后来不断调整心态,现在已经好很多了。接下来又是个好难的问题。面试官: BAT 都给你 offer 了,你怎么选?其实我当时好想说,BT 是什么?不好意思我只知道阿里。我 : 哈哈哈哈开玩笑,就说了阿里的文化,支付宝给我们带来很多便利,想加入支付宝为人类做贡献!最后 HR 问了我实习时间,现在大几之类的问题,说肯定会给我发 offer 的,让我等着就好了,希望过两天能收到好的结果。公众号如果大家想要实时关注我更新的文章以及分享的干货的话,可以关注我的公众号。《Java面试突击》: 由本文档衍生的专为面试而生的《Java面试突击》V2.0 PDF 版本公众号后台回复 “Java面试突击” 即可免费领取!Java工程师必备学习资源: 一些Java工程师常用学习资源公众号后台回复关键字 “1” 即可免费无套路获取。 ...

April 15, 2019 · 2 min · jiezi

活动:送两本《PHP 程序员面试笔试真题库》

你好,是我琉忆。由于近期工作忙碌,没有时间去更新PHP面试专栏。现在特此搞个抽奖活动送2本书给大家。给各位面试助力,尽快找到一份满意的PHP工作。《 PHP 程序员面试笔试真题库》历时一年,由机械工业出版社出版,在 2019 年 4 月问世。本书的适用群体:刚接触 PHP,自学一段时间 PHP 后打算去找 PHP 相关的 PHP 面试工作的群体。本书主要抽取了各大互联网公司往年 PHP 的面试笔试真题,然后抽出整理成书,为你的面试笔试助力。现免费送出 2 本《 PHP 程序员面试笔试真题库 》,具体参与规则如下:1、在本文下留言即可参与,并且一个用户的多次留言只算作一次。2、活动截止日期为北京时间 2019 年 4 月 19 日 19:59:59(本周五),以评论时间为准。(2019年4月 19 日晚上 21 点开奖)3、在活动结束后随机抽取 2 名幸运网友。4、获赠名单在本帖后会公布,并 私信该网友。5、幸运网友把收件地址、姓名和手机号发送给我安排邮寄。6、全国包邮(不含新疆、西藏、港澳台)7、保留最终解释权。PS:该书已在天猫、京东、当当等电商平台销售。获取更多PHP程序员面试笔试资料可以关注:琉忆编程库

April 15, 2019 · 1 min · jiezi

【Leetcode】113. 路径总和II

题目给定一个二叉树和一个目标和,找到所有从根节点到叶子节点路径总和等于给定目标和的路径。说明: 叶子节点是指没有子节点的节点。示例:给定如下二叉树,以及目标和 sum = 22, 5 / \ 4 8 / / \ 11 13 4 / \ / \ 7 2 5 1返回:[ [5,4,11,2], [5,8,4,5]]题解这道题目是上一道的延伸,但是需要记录下路径,返回回去。这就是一个典型的backtrack的题目了。我们用迭代的方式需要记录中间的路径状态,稍显复杂,所以我们想用递归的方式来解,先探索左子树,然后探索右子树。如果都探索完之后,右满足的就加入到最终结果中。public class Solution { public List<List<Integer>> pathSum(TreeNode root, int sum) { List<List<Integer>> res = new LinkedList<>(); helper(root, sum, res, new LinkedList<>()); return res; } public void helper(TreeNode root, int sum, List<List<Integer>> res, List<Integer> current) { if (root == null) { return; } current.add(root.val); if (root.left == null && root.right == null && sum == root.val) { // leaf node. res.add(new LinkedList<>(current)); // back track. current.remove(current.size() - 1); return; } helper(root.left, sum - root.val, res, current); helper(root.right, sum - root.val, res, current); // back track. current.remove(current.size() - 1); }}热门阅读技术文章汇总【Leetcode】103. 二叉树的锯齿形层次遍历【Leetcode】102. 二叉树的层次遍历【Leetcode】101. 对称二叉树【Leetcode】100. 相同的树【Leetcode】98. 验证二叉搜索树手撕代码QQ群:805423079, 群密码:1024 ...

April 14, 2019 · 1 min · jiezi

javascript数据类型转换

javascript数据类型转换在面试中经常会碰见问类型转换的问题,例如[] == false、[] == {}返回的是真还是假等等。。。默认是false的五个值null undefined NaN 0 ‘‘记住只有这五个值是假的剩下的全部是真的。转换规则如果是一个值判断是否是真假,除了默认是false的五个值剩下的全部是true。如果是两个值比较是否相等,遵循如下规则。val1 == val2 如果两个值可能不是同一数据类型,如果是==比较的话,会默认进行数据转换。2.1 object == object,比较永远不相等。2.2 object == string 先将对象转换成字符串(调用toString方法),然后再比较。[] 转换成字符串 “”{} 转换成字符串 “[object Object]“2.3 object == boolean 先将对象转换成字符串(toString),再把字符串转换成数字(Number)、布尔值转换成数字(true 转换成 1 false 换成成 0)然后让两个数字进行比较。Number(””) 会输出 02.4 object == number 先将对象转换成字符串(toString),再把字符串转换成数字(Number),再进行比较。2.5 number == boolean 布尔值转换成数字,然后再比较。2.6 number == string 字符串转换成数字,然后再比较。2.7 string == boolean 都转换成数字,,然后再比较。2.8 null == undefined 结果是true。2.9 null 或者 undefined 比较另外的所有值,结果都是false,不相等。例如: [] == false 返回 true 2.3规则。 [] == [] 返回 false 2.1规则。 2 == true 返回 false 2.5规则。=== 三个等号还会比较数据类型。这些规则背下来,就能完美的应付这样的面试题了,加油!我的博客和GitHub地址https://github.com/lanpangzhihttp://blog.langpz.com ...

April 12, 2019 · 1 min · jiezi

如何有计划,高效率,优简历应对面试

前言前一篇文章讲述了我在三月份毫无准备就去面试的后果,一开始心态真的爆炸,但是又不服气,一想到每次回来后家人朋友问我面试结果的期待脸,越觉得必须付出的行动来证明自己了。面经传送门:一个1年工作经验的PHP程序员是如何被面试官虐的?下面是我花费两个星期做的准备,主要分三部分:有计划——计划好每天要复习的知识模块(内容较多)高效率——从整体到细节,着重把知识点“手写”到自己的电脑记录本优简历——展示你的项目亮点,难点下面的文章和网站链接我都会注明出处,如果有问题请联系我,我会及时修改或撤销有计划反思:为什么好多知识看过后不能记住,面试一紧张更加想不起?一方面自己没有深入学习,另一方面复习过程杂乱,和写代码一个道理,如果你的业务逻辑和其他乱七八糟的都放在控制器上,代码一多你要调用某个方法可能就会让你抓狂,所以一般采取controller和server分层,同理,我们最好提前做好计划,分种类,分层次来学习。复习前如果很迷茫,可以网上Google一下面试知识点,看到的无非都是操作系统,计算机网络,数据结构,mysql,设计模式,Linux,Redis,再者根据你所学语言方面的知识和简历,例如我求职PHP,简历上有写Larvel框架,那就再加PHP基础,框架Laravel,服务器nginx。第一天:操作系统如果你是科班出身,那应该对操作系统不陌生,至少在高校里面是学过这门课程的。但估计很多人像我一样,后悔没去好好学这门课,或者工作上用不上就抛弃它了(PHP更是如此),我要说的是操作系统很重要,就以我面试了几家公司来说,基本如果你答出操作系统相关知识点基本都是加分的。进程和线程的概念,以及两者的区别什么是上下文切换多进程和多线程各自的优势(可以Google一下有关php-fpm和nginx进程)进程通信方式进程基本状态抢占式调度和非抢占式调度进程线程同步,死锁理解协程附加帮助我整理这些知识的网站和博客文章:小土刀的面试刷题笔记——操作系统并发技术·笔试面试知识整理这里推荐有精力的朋友可以看下《Linux高性能服务器编程》,粗略看下8,9,10,13,14章,以及《深入理解计算机系统》的第1章和第12章,我就是结合这两本书,记住并理解简单的知识点,后续我还要花时间去深入阅读。第二天:计算机网络计算机网络可以说是大多数程序员需要学习的大课程,特别是PHPer这些web打交道的程序员。当然,计算机网络太底层的东西很枯燥很难懂,我只针对一些比较常见,比较基础的知识面来学习。计算机网络体系包括TCP/IP体系结构,五层协议体系结构TCP,UDP三次握手,四次握手(过程要熟悉,能清晰描述)TCP协议如何保证可靠传输(包括重传,流量控制,拥塞控制,定时器等知识点要了解)附加帮助我整理这些知识的网站和博客文章:小土刀的面试刷题笔记——网络TCP协议·笔试面试知识整理复习完这些基础的知识后,试着要去了解稍微底层一点的东西,因为很多面试官喜欢由浅入深地问,如果能答出一两点基本都是加分的推荐一篇很好的文章,仔细读完会有很大的收获:TCP 的那些事儿(上)第三天:数据结构很多人在大学里面都学过这门课程,也是很多程序员的启蒙课程,自然很重要,(大学唯一一次挂科就是它了。。。)从事PHP工作的朋友应该都用不上它,所以这也是一个很容易被忽视的基础知识点,自然有很多大公司面试的时候很注重这方面的基础,特别是应届毕业生。我复习这方面的知识是重新翻开我大学的课本,把其中比较重要的东西手写几遍。我不久前面试过一家要我手写链表,结果早就忘了,没写出来队列,循环队列,栈,单链表等,要理解,最好多手写几遍树和图,着重复习二叉树方面的知识,例如前中后遍历;B树,B+树,红黑树最好要了解一下排序算法,冒泡排序必须很熟悉,其他几种常见的算法:简单选择排序,直接插入排序,快速排序也要知道冒泡排序可以了解下如何优化改进,有些面试官会问,如果答不出他会提示你怎么做,考察你的思考能力关于数据结构的参考资料基本Google一下就有很多(顺便说下,21世纪程序员还是少用某度),大学的课程书是最好的,一般大学的数据结构都是面向学生的,还是很基础,很好理解的。第四天:数据库mysql说实话,我觉得绝大多数程序员都是在CURD,学那么多东西很大方面也是为了辅助最后的curd。所以数据库知识的重要性毋庸置疑,后面我打算买本《高性能MySQL》来提升自己这方面的能力。基础的增删改查操作存储引擎MyISAM和InnoDB理解索引数据结构,即B+树方面的知识 参考:InnoDB索引实现·MySQL索引背后的数据结构及算法原理·看云mysql语句优化,着重索引优化,explain查看索引情况 参考:Mysql索引优化事务四大特性隔离级别脏读,不可重复读,幻读了解共享锁,排他锁,死锁了解主从复制原理,分库分表第五天:nginx和PHP-fpmnginx和php-fpm应该算是PHP程序员接触比较多,但是可能没有去深入了解过,有些面试官喜欢结合nginx,php-fpm和操作系统进程方面的知识来考察。日常用到的nginx配置语法,常用的变量nginx常见的参数优化 参考:Nginx高并发下的优化nginx负载均衡,访问控制,防盗链,设置静态文件过期时间等php-fpm工作流程php-fpm进程管理(静态和动态)可以Google了解下php-fpm和nginx各自的进程管理模式,参考文章:Nginx内部有使用多线程吗?面试过程可能会问到网站负载低,访问慢怎么排查,参考:论坛偶尔出现负载低但是访问很慢排查思路第六天:redis工作项目不大,用到redis的地方并不多,所以一直也没去学习redis,但是从面试情况看来,被问到redis的概率还是很大的,毕竟这款基于内存的数据库功能强大,市场占用率很高。与memcached的区别5种基本的数据结构以及各自的使用场景,最好能结合自己项目来描述持久化,RDB和AOF 参考: 面试中经常被问到的 Redis 持久化与恢复如何与mysql保持数据一致 参考:Redis和mysql数据怎么保持数据一致的?第七天:Laravel由于我自己是一直用Laravel框架开发的,简历里面的项目也多处标明Larvel,所以这里就介绍Larvel相关的知识,你对哪个框架熟悉就去做相应的复习,基本Google和手册都有。Laravel生命周期什么是服务容器,服务提供器,Facade门面依赖注入中间件,异常处理,ORM等手册上的知识第八天:设计模式看一个人的代码水平,可以从是否灵活使用设计模式看出来的。写代码也是一门艺术,好的代码低耦合,易扩展,设计模式的理解和使用也决定了你的代码水平。这里推荐一本《大话设计模式》,目前我只看了一点点,对于我这样的菜鸟觉得还不错,后面需抽点时间好好看看。六大原则,着重理解,最好能清晰口述结合你的项目经验描述几个设计模式。常见的有单例,三大工厂,观察者,策略等有一次面试官问我简单工厂模式是不是符合六大原则之一开闭原则?我答不出来,随便选了个符合。。。实际简单工厂是不符合开闭原则的,这就涉及到你开闭原则的理解。第九天:PHP内核基础知识真正要读懂PHP内核的人应该很少,大多数人都没时间和精力去研究底层,毕竟PHP一直以开发效率高著称,只要底层有人写好给广大程序员用就好了。确实是这样,但是我记得有个面试过我的大佬跟我说,很多东西不要只会用,要多想想为什么可以这样用,PHP语言我觉得也是一样,虽然现在能力有限,但我可以提前把一些比较基础,容易理解的底层知识学习一下,这不也是一种自我提升?面试的时候你也可以拿出来炫一下,说错也没关系,至少你展示出你的好学,你的认知层面。PHP的生命周期和zend引擎变量存储结构写时复制和引用参考文章:深入理解PHP内核PHP内核剖析第十天:根据你的简历内容做复习面试都是拿着你的简历来问问题的,你的简历内容会直接影响面试内容的走向,所以简历写完还要做响应的复习。我的简历有涉及rabbitMQ,elasticsearch等知识,我就对其进行简单的复习。另外很重要的一点:提前想好怎么描述你做过的项目,其中遇到什么问题,怎么解决,尽量把你好的一面展示出来,这些都可以提前写下了的,好记性不如烂笔头,下面第二部分会说到。高效率复习的内容实在是太多了,单单写上一部分内容都快把我的键盘打烂,手写断了。虽然有了十全的计划,但如何高效率去记住上面那么多东西是一个大问题,看看我是怎么做的。例如第一天复习操作系统的知识,我是早上Google了一下有关操作系统的面试知识点,然后打开几个标签页,选取内容比较齐全的。所谓“好记性不如烂笔头”,没有烂笔头,只有烂键盘,所以我选择把这些知识点一一整理到自己的电脑上,自己平时有写博客,用markdown比较多(推荐Typora),所以每天就用一个md文件把知识整理下来,基本都是手打的,很少复制黏贴。这样做,一方面让自己印象深刻,另一方面锻炼自己摘取核心内容,描述能力,这也是我能写下这么多内容的原因。每天晚上计划好明天的复习内容,可以选择睡前回顾今天学习的知识,也可以在第二天早上回顾一遍最好选择一个安静的地方,能让你认真学习的环境,我由于提交了离职申请需要一个月才能走,交接项目也很快,所以每天都在公司有计划的复习(远离手机)总之,真想要离职,找一份好工作,必须对自己狠一点,找到心仪的工作再好好去放松几天。其实有不少人一直在埋怨大环境不好,确实是,但有何用,想为自己菜,自己不努力找一个借口?王小波说的好:人的一切痛苦,本质上都是对自己无能的愤怒。当然运气也很重要,保持乐观,别轻易放弃。优简历简历不用多说,就是很重要,怎么写?其实我也是参考一篇文章的:我本以为你们会写简历概括一下:基本的,用PDF格式,个人信息写完整,别写精通技能亮点,围绕 熟悉 , 有所 , 掌握 , 了解 , 有一定心得 等不要这样:熟悉PHP , 熟悉YiiLavarel框架 ( 复制粘贴 , CURD , 就是干! )熟悉Linux使用 , 可以搭建XXXX环境 ( 会敲cd , ls命令 , 会apt install nginx )熟悉git或svn版本管理的使用 ( 会git push , 会git pull )熟悉MySQL以及对数据库的优化 ( 会select update 和 delete , 会添加索引 )熟悉Redis或Memcache的使用 ( 会set key , 会get key )最好这样:PHP : 熟悉PHP语法 , 熟悉PHP面向对象 , 可以根据业务逻辑结合合适的设计模式 . 熟悉PHP SPL标准库 , 对PHP的一些高级用法有所心得体验 , 诸如pcntl多进程模块 , socket模块 . 对SWOOLE所有涉猎 , 有一些自己的积累和经验 . 对于底层 , ZendVM如何如何 。Redis : 熟悉Redis常用数据结构的使用 , 可结合业务场景选择合适的数据结构 . 熟悉Redis集群 , 对集群实现方案原理有一定掌握 , 对于市面常用的集中集群方案的优缺点比较了解 . 对于底层 , 对Redis SET等底层数据结构的实现有所掌握。一定要写项目亮点和难点大多数人都是这么写的 : 负责用户登录注册模块 , 后台管理 , 多角色权限控制 , 负责广告业务模块的管理和筛查(太普通,太泛了)亮点 . 你觉得这个项目中哪一部分值得自豪或学到新东西了 . 比如项目中用到ECDH , 使用了MySQL中间件等等。难点 . 你觉得这个项目哪一部分当时难了你几天 , 然后你通过自己努力解决了以及解决方案是什么。总结面试并不简单,不要太轻敌,做好准备,打好基础是关键。毫无准备就去投简历面试大多是浪费时间,甚至浪费进入好公司的机会。还是那句话程序员应该脚踏实地,不要眼高手低,用实际行动证明自己。我是即将入职广州一家游戏公司PHP开发岗位的程序员,2018年毕业,毕业到现在确实收获了不少,也让我看清自己,需要恶补的知识还有很多很多。欢迎大家关注我的个人博客https://zgxxx.github.io/,比较简陋,还是希望能和万千大神交流技术问题,一起成长。 ...

April 12, 2019 · 1 min · jiezi

????JavaScript设计模式实践:18份笔记、例子和源码????

背景介绍之前在阅读《JavaScript设计模式和开发实践》这本书的时候,收货颇丰,学习了设计模式在很多场景下的应用。但也是因为书上场景过多,所以当记不清某一种设计模式的时候,翻书温习复杂案例的成本是相对较高的。有时候,只需要一段经典、简洁的demo就可以迅速回顾起精髓,在快速业务开发中,这是个比较经济的做法。除此之外,当主要工作语言发生变化的时候(例如:js -> python),简洁的demo更能帮助开发者快速回忆某种设计模式的精髓和实现思路,方便开发者根据语言特性再做实现。因此,对于比较重要的18种设计模式,我都挑选了它的一种经典应用,并且尽量使用ES6的语法和编程习惯来进行实现。 前10个设计模式还提供了Python3的实现版本(后来比较忙,遂放弃)。文章地址一共记录了18个设计模式,部分文章发到了掘金,由于精力有限,后面几篇文章就直接放在了Github仓库 / 个人博客单例模式:https://godbmw.com/passages/2018-10-23-singleton-pattern/策略模式: https://godbmw.com/passages/2018-10-25-stragegy-pattern/代理模式: https://godbmw.com/passages/2018-11-01-proxy-pattern/迭代器模式: https://godbmw.com/passages/2018-11-06-iter-pattern/订阅-发布模式: https://godbmw.com/passages/2018-11-18-publish-subscribe-pattern/命令模式: https://godbmw.com/passages/2018-11-25-command-pattern/组合模式: https://godbmw.com/passages/2018-12-12-composite-pattern/享元模式:https://godbmw.com/passages/2018-12-16-flyweight-pattern/责任链模式: https://godbmw.com/passages/2019-01-07-chain-of-responsibility-pattern/装饰者模式: https://godbmw.com/passages/2019-01-12-decorator-pattern/状态模式: https://godbmw.com/passages/2019-01-16-state-pattern/适配器模式: https://godbmw.com/passages/2019-01-17-adapter-pattern/桥接模式: https://godbmw.com/passages/2019-01-19-bridge-pattern/解释器模式: https://godbmw.com/passages/2019-01-25-interpreter-pattern/备忘录模式: https://godbmw.com/passages/2019-01-26-memento-pattern/模板模式: https://godbmw.com/passages/2019-01-31-template-pattern/工厂模式: https://godbmw.com/passages/2019-03-31-factory-pattern/抽象工厂模式: https://godbmw.com/passages/2019-04-01-abstract-factory-pattern/放在最后其实整理这些的原因还有一个,就是为了准备今年春招的面试。然后过了腾讯的校招和阿里的前三面发现,竟然没有专门问到设计模式相关知识!但回首看,系统地学习、理智地使用设计模式(不是为了用而用),确实能提升代码的可读性,实现业务解耦。而在写这些文章的过程中,每种设计模式自己也是会找很多的实现(包括不限于python、java、c++)来参考,探索式学习还是蛮有趣的。尽管如此,有2篇文章的瑕疵还是很多,希望您抱着交流的心态来阅读,如有不当,欢迎指出、共同提升。

April 11, 2019 · 1 min · jiezi

一个 1年工作经验的 PHP 程序员是如何被面试官虐的?

前言首先需要说明的一点,本人只是一个毕业一年,只有一年工作经验的普通PHPer,能力有限,这篇文章只是将我这几周来的感受和体验分享出来,希望能给许多像我一样,或者互联网行业的新手带来一些收获,当然哪里说的不对或不足还是希望大神们可以加以点评补充。金三银四,每年这个时候都有很多人想借此机会跳槽或者投身某个行业,目的很明确:需求更好的环境,兑现自己的价值。特别是近十年来非常火爆的互联网行业,无论是刚毕业的,敲代码敲了几年的,还是跨行进入IT行业的,都希望自己能“拿高薪,学知识,享福利”,而如今,高校扩招,IT门槛降低也导致市场太多“低技术,高目标”的幼年程序猿(我也是其中之一),再加上今年互联网寒冬,僧多粥少,想要寻求一个面试机会都不容易,更别说拿offer了。我是不甘心浪费时间在目前这家公司,具体原因不说,所以我下定决心找一份新的工作,一方面证明自己,另一方面想让自己走出舒适圈,给自己多一点挑战。狼狈的三月一开始心高气傲的我,用半天时间把自己的简历整理了一遍,那时候我的简历应该还是不错的(后续文章会详细描述如何优化简历),第二天开始在Boss直聘和拉勾上开始投递简历,一开始投递的是广州地区PHP的1-3年开发岗位,虽然反馈比例不高,但还是很幸运收到几个面试邀请。接着我留了两天时间给自己复习,其实也就是在网上Google一下PHP面试题,打开十几个标签页各种面试题,这就是我所谓的“多刷题”操作,结果可想而知,看的东西很乱导致去面试那天基本忘的七七八八,有很多还只是记住一些答案,压根就没清楚,惨状见下面具体面试。准备不充分 – 第一轮不过第一家,广州琶洲一家环境超级好,福利也不错,主营美颜APP的公司,这也是我最感遗憾的一次面试机会。一开始有笔试,主要内容有点偏向C语言那方面,PHP的几乎没有,仅靠大学的模糊知识写了一些答案,具体的时间有点久忘记了,这里就不详细说笔试内容。主要是第一轮面试:第一个问题:Redis的5种数据类型,只答了一个String。第二个问题:你知道Redis怎么做到数据落地,同步数据库吗?连数据类型都不知道,我只能说不清楚,没用过。第三个问题:如果数据库量很大,你怎么优化?我的回答:做好索引,mysql语句优化。第四个问题:在工作中你做了哪些mysql语句优化?我的回答:我用where in(id1,id2…)来做优化。。。一年的PHP开发中其实我没怎么用过Redis,就简单的用过String(也看出我真的菜)其实where in那个是Laravel ORM预加载使用where in来解决N + 1问题(具体可以看Larave手册),where in并不比left join效率高最后聊了差不多20分钟,就让hr来问我一些问题,例如我对公司的印象等,应该就是走走流程,接着就让我回去等通知,当然也就没通知了。基础不过关 – 第二轮不过第二家,深圳大学旁边一家保险业务的公司,给了我最大的打击。没有笔试题,第一轮是一个差不多和我同龄的年轻小伙来面试,问的问题很简单,基本都是网上那些面试题,比如tcp和udp的区别,三次握手,include/require的区别等,我基本都回答出来了。接着是一个头发相对稀疏,经验应该更加老道的人来面试我:我的简历有提到一个JWT相关的知识,所以他让我描述具体的东西,和session的区别等。项目时间有点久,我也就简单的把jwt的东西跟他说,但是明显不能满足他,一直问到底层,那时候我就很慌了,更加想不起来了。PHP这些框架是怎么实现文件自动加载的?我回答魔术方法__autoload和include,对方反问了一句再具体一点,还有没有别的?我实在想不出写一个链表吧?不会。。。其实有关JWT的问题,面试官应该是没用过,服务器这边其实有一个私钥Secret字符串用来签名,从而达到前后加密解密验证的效果第二个问题应该是还有一个spl_autoload_register可以用来注册多个文件第三个链表,后悔大学没好好学数据结构,后续得补一补最后面试官直接给结果:很抱歉,你的不适合我们公司,希望你之后多去补一补基础知识,不要只停留在会用,要知道为什么可以这样用。虽然打击很大,但是也是这个面试让我认清自己,知道自己欠缺的知识面太多,基础太差了。还是很喜欢这种面试直接给面试结果的。实践不足,知识面太窄 – 直接不过第三家,面试官是大牛,之前和韩天峰大神一起做朋友网的,人很好,很亲切。没有笔试,没有自我介绍,主要问我的工作经历,印象比较深的是:有没有抓过包,用什么工具,对TCP的理解,有没有用一些语言来实现TCP的过程。。。无奈自己真的没实践过,知识面太窄了,仅仅知道一点死记硬背的知识,聊了20分钟,就走了,也没结果了。白高兴的offer – 幸运过了第四家,也是在深圳,招聘网上看到的公司不小,有100-500人,但发offer却是另外一家公司,网上查了一下貌似是子公司,有点外包性质。没有笔试,一个年轻的小伙子面试的我,主要问我一些有关Laravel的内容,一般手册上都有,我答的也不怎么完整。最后有个问题:你知道哪些设计模式,六大原则是什么?对设计模式我只停留在单例和工厂模式,六大原则也回答不完整。最后很意外让我过了,我想是公司急需人员。给的薪资也挺满意,心想面试了那么多家终于有一家肯收留我了,也答应了对方下一周就过去上班。第二天回公司第一件事就是找我们总监要辞职,要一个星期后走,结果被告知要走一个月的交接流程,其实那时候我东西基本交接的差不多了,项目也不大,但是公司给的理由是,如果你没有工作交接说明你平时没做事,工作不饱和,会影响后续招人。直接把我整懵逼了,一方面我不敢提前裸辞,很多人都是互联网寒冬,所以就等到拿了offer再提离职,这时候老东家行政放话一定要走一个月流程,新的公司又继续一个星期后到岗,怎么都没得商量。。。最后,我还是放弃那个offer,一方面想到这家公司有点外包性质,另一方面以子公司名字来发offer总让我有点担心,毕竟之前实习的时候,有一家以腾讯的名字来招聘,进去后发现是腾讯汽车代理商。。。结尾以上便是三月份我面试过比较印象深刻的公司,基本都是被虐了,当然无非都是自己没有好好准备,基础不好的问题,导致浪费了很多机会。所以在此还是要告诫大家,如果下定决心要走人,要做好十足的准备,这种准备不应该只是所谓的刷面试题,后续我会另外开一篇文章用来具体讲述我在多次面试失败后,如何花两个星期做到——”有计划, 高效率,优简历“,最后成功拿到自己心仪的offer。有计划——如果规划好每天的学习目标,主要有操作系统,计算机网络,mysql等近10个方面知识高效率——我会介绍我如何高效记住一些核心知识,尽可能覆盖面试知识点优简历——面试是第一步,简历是最关键的,我会介绍我是如何优化自己的简历三月份投的简历应该有上百家,反馈的十几家,给面试的应该就只有七八个吧,除了上面说的几家其他的都是一些外包或创业公司,甚至有人找过我要我跟他们一起创业,和一个前端实习生一起开发小程序(笑哭)。总之目前这种市场状况不好,但是还是有机会拿到面试机会的,如果你刚毕业,最好走校招,如果你和我一样,一两年工作经验,更应该好好准备,而不是埋怨环境不好,只想不做。当然,一些985,211的大神就应该找大厂或者考研,目标更远大一点,我是比较考虑现实的人,目前我的实力和教育背景想要去大厂是很困难的,所以只能在条件不至于太差的中小型公司寻求成长。程序员更应该用实际行动来证明自己,脚踏实地,不要眼高手低,这便是我的个人想法。

April 11, 2019 · 1 min · jiezi

百度社招面试题——如何用Redis实现分布式锁

关于Redis实现分布式锁的问题,网络上很多,但是很多人的讨论基本就是把原来博主的贴过来,甚至很多面试官也是一知半解经不起推敲就来面候选人,最近结合我自己的学习和资料查阅,整理一下用Redis实现分布式锁的方法,欢迎评论、交流、讨论。1.单机Redis实现分布式锁1.1获取锁获取锁的过程很简单,客户端向Redis发送命令:SET resource_name my_random_value NX PX 30000my_random_value是由客户端生成的一个随机字符串,它要保证在足够长的一段时间内在所有客户端的所有获取锁的请求中都是唯一的。NX表示只有当resource_name对应的key值不存在的时候才能SET成功。这保证了只有第一个请求的客户端才能获得锁,而其它客户端在锁被释放之前都无法获得锁。PX 30000表示这个锁有一个30秒的自动过期时间。1.2 释放锁if redis.call(“get”,KEYS[1]) == ARGV[1] then return redis.call(“del”,KEYS[1])else return 0end之前获取锁的时候生成的my_random_value 作为参数传到Lua脚本里面,作为:ARGV[1],而 resource_name作为KEYS[1]。Lua脚本可以保证操作的原子性。1.3 关于单点Redis实现分布式锁的讨论网络上有文章说用如下命令获取锁:SETNX resource_name my_random_valueEXPIRE resource_name 30由于这两个命令不是原子的。如果客户端在执行完SETNX后crash了,那么就没有机会执行EXPIRE了,导致它一直持有这个锁,其他的客户端就永远获取不到这个锁了。为什么my_random_value 要设置成随机值?保证了一个客户端释放的锁是自己持有的那个锁。如若不然,可能出现锁不安全的情况。客户端1获取锁成功。客户端1在某个操作上阻塞了很长时间。过期时间到了,锁自动释放了。客户端2获取到了对应同一个资源的锁。客户端1从阻塞中恢复过来,释放掉了客户端2持有的锁。用 SETNX获取锁网上大量文章说用如下命令获取锁:SETNX lock.foo <current Unix time + lock timeout + 1>原文在Redis对SETNX的官网说明,Redis官网文档建议用Set命令来代替,主要原因是SETNX不支持超时时间的设置。https://redis.io/commands/setnx2.Redis集群实现分布式锁上面的讨论中我们有一个非常重要的假设:Redis是单点的。如果Redis是集群模式,我们考虑如下场景:客户端1从Master获取了锁。Master宕机了,存储锁的key还没有来得及同步到Slave上。Slave升级为Master。客户端2从新的Master获取到了对应同一个资源的锁。客户端1和客户端2同时持有了同一个资源的锁,锁不再具有安全性。就此问题,Redis作者antirez写了RedLock算法来解决这种问题。2.1 RedLock获取锁获取当前时间。按顺序依次向N个Redis节点执行获取锁的操作。这个获取操作跟前面基于单Redis节点的获取锁的过程相同,包含随机字符串my_random_value,也包含过期时间(比如PX 30000,即锁的有效时间)。为了保证在某个Redis节点不可用的时候算法能够继续运行,这个获取锁的操作还有一个超时时间(time out),它要远小于锁的有效时间(几十毫秒量级)。客户端在向某个Redis节点获取锁失败以后,应该立即尝试下一个Redis节点。计算整个获取锁的过程总共消耗了多长时间,计算方法是用当前时间减去第1步记录的时间。如果客户端从大多数Redis节点(>= N/2+1)成功获取到了锁,并且获取锁总共消耗的时间没有超过锁的有效时间(lock validity time),那么这时客户端才认为最终获取锁成功;否则,认为最终获取锁失败。如果最终获取锁成功了,那么这个锁的有效时间应该重新计算,它等于最初的锁的有效时间减去第3步计算出来的获取锁消耗的时间。如果最终获取锁失败了(可能由于获取到锁的Redis节点个数少于N/2+1,或者整个获取锁的过程消耗的时间超过了锁的最初有效时间),那么客户端应该立即向所有Redis节点发起释放锁的操作(即前面介绍的单机Redis Lua脚本释放锁的方法)。2.2 RedLock释放锁客户端向所有Redis节点发起释放锁的操作,不管这些节点当时在获取锁的时候成功与否。2.3 关于RedLock的问题讨论如果有节点发生崩溃重启假设一共有5个Redis节点:A, B, C, D, E。设想发生了如下的事件序列:客户端1成功锁住了A, B, C,获取锁成功(但D和E没有锁住)。节点C崩溃重启了,但客户端1在C上加的锁没有持久化下来,丢失了。节点C重启后,客户端2锁住了C, D, E,获取锁成功。客户端1和客户端2同时获得了锁。为了应对这一问题,antirez又提出了延迟重启(delayed restarts)的概念。也就是说,一个节点崩溃后,先不立即重启它,而是等待一段时间再重启,这段时间应该大于锁的有效时间(lock validity time)。这样的话,这个节点在重启前所参与的锁都会过期,它在重启后就不会对现有的锁造成影响。如果客户端长期阻塞导致锁过期解释一下这个时序图,客户端1在获得锁之后发生了很长时间的GC pause,在此期间,它获得的锁过期了,而客户端2获得了锁。当客户端1从GC pause中恢复过来的时候,它不知道自己持有的锁已经过期了,它依然向共享资源(上图中是一个存储服务)发起了写数据请求,而这时锁实际上被客户端2持有,因此两个客户端的写请求就有可能冲突(锁的互斥作用失效了)。如何解决这个问题呢?引入了fencing token的概念:客户端1先获取到的锁,因此有一个较小的fencing token,等于33,而客户端2后获取到的锁,有一个较大的fencing token,等于34。客户端1从GC pause中恢复过来之后,依然是向存储服务发送访问请求,但是带了fencing token = 33。存储服务发现它之前已经处理过34的请求,所以会拒绝掉这次33的请求。这样就避免了冲突。但是其实这已经超出了Redis实现分布式锁的范围,单纯用Redis没有命令来实现生成Token。时钟跳跃问题假设有5个Redis节点A, B, C, D, E。客户端1从Redis节点A, B, C成功获取了锁(多数节点)。由于网络问题,与D和E通信失败。节点C上的时钟发生了向前跳跃,导致它上面维护的锁快速过期。客户端2从Redis节点C, D, E成功获取了同一个资源的锁(多数节点)。客户端1和客户端2现在都认为自己持有了锁。这个问题用Redis实现分布式锁暂时无解。而生产环境这种情况是存在的。结论Redis并不能实现严格意义上的分布式锁。但是这并不意味着上面讨论的方案一无是处。如果你的应用场景为了效率(efficiency),协调各个客户端避免做重复的工作,即使锁失效了,只是可能把某些操作多做一遍而已,不会产生其它的不良后果。但是如果你的应用场景是为了正确性(correctness),那么用Redis实现分布式锁并不合适,会存在各种各样的问题,且解决起来就很复杂,为了正确性,需要使用zab、raft共识算法,或者使用带有事务的数据库来实现严格意义上的分布式锁。参考资料Distributed locks with Redis基于Redis的分布式锁到底安全吗(上)? - 铁蕾的个人博客https://martin.kleppmann.com/…热门阅读技术文章汇总【Leetcode】101. 对称二叉树【Leetcode】100. 相同的树【Leetcode】98. 验证二叉搜索树 ...

April 10, 2019 · 1 min · jiezi

【Leetcode】125. 验证回文串

题目给定一个字符串,验证它是否是回文串,只考虑字母和数字字符,可以忽略字母的大小写。说明:本题中,我们将空字符串定义为有效的回文串。示例 1:输入: “A man, a plan, a canal: Panama"输出: true示例 2:输入: “race a car"输出: false题解这道题目就比较简单了,用两个指针一前一后,遇到不是字母的就直接忽略过就好了。可能对java的一些方法不太熟悉,注释说明一下:class Solution { public boolean isPalindrome(String s) { if (s == null) return false; if (s.length() == 0) return true; int i = 0; int j = s.length() - 1; while (i < j) { // isLetterOrDigit 判断是不是字母或者数字 while (i < j && !Character.isLetterOrDigit(s.charAt(i))) i++; while (i < j && !Character.isLetterOrDigit(s.charAt(j))) j–; // toLowerCase 都转化为小写的字母 if (Character.toLowerCase(s.charAt(i)) != Character.toLowerCase(s.charAt(j))) return false; i++; j–; } return true; }}手撕代码QQ群:805423079, 群密码:1024 ...

April 9, 2019 · 1 min · jiezi

社招面试总结——算法题篇

面试结果总结下最近的面试:头条后端:3面技术面挂蚂蚁支付宝营销-机器学习平台开发: 技术面通过,年后被通知只有P7的hc蚂蚁中台-机器学习平台开发: 技术面通过, 被蚂蚁HR挂掉(脉脉上好多人遇到这种情况,一个是今年大环境不好,另一个,面试尽量不要赶上阿里财年年底,这算是一点tips吧)快手后端: 拿到offer百度后端: 拿到offer最终拒了百度,去快手了, 一心想去阿里, 个人有点阿里情节吧,缘分差点。总结下最近的面试情况, 由于面了20多面, 就按照题型分类给大家一个总结。推荐大家每年都要抽出时间去面一下,不一定跳槽,但是需要知道自己的不足,一定要你的工龄匹配上你的能力。比如就我个人来说,通过面试我知道数据库的知识不是很懂,再加上由于所在组对数据库接触较少,这就是短板,作为一个后端工程师对数据库说不太了解是很可耻的,在选择offer的时候就可以适当有偏向性。下面分专题把最近的面试总结和大家总结一下。过分简单的就不说了,比如打印一个图形啥的, 还有的我不太记得清了,比如快手一面好像是一道动态规划的题目。当然,可能有的解决方法不是很好,大家可以在手撕代码群里讨论。最后一篇我再谈一下一些面试的技巧啥的。麻烦大家点赞转发支持下~股票买卖(头条)Leetcode 上有三题股票买卖,面试的时候只考了两题,分别是easy 和medium的难度Leetcode 121. 买卖股票的最佳时机给定一个数组,它的第 i 个元素是一支给定股票第 i 天的价格。如果你最多只允许完成一笔交易(即买入和卖出一支股票),设计一个算法来计算你所能获取的最大利润。注意你不能在买入股票前卖出股票。示例 1:输入: [7,1,5,3,6,4]输出: 5解释: 在第 2 天(股票价格 = 1)的时候买入,在第 5 天(股票价格 = 6)的时候卖出,最大利润 = 6-1 = 5 。 注意利润不能是 7-1 = 6, 因为卖出价格需要大于买入价格。示例 2:输入: [7,6,4,3,1]输出: 0解释: 在这种情况下, 没有交易完成, 所以最大利润为 0。题解纪录两个状态, 一个是最大利润, 另一个是遍历过的子序列的最小值。知道之前的最小值我们就可以算出当前天可能的最大利润是多少class Solution { public int maxProfit(int[] prices) { // 7,1,5,3,6,4 int maxProfit = 0; int minNum = Integer.MAX_VALUE; for (int i = 0; i < prices.length; i++) { if (Integer.MAX_VALUE != minNum && prices[i] - minNum > maxProfit) { maxProfit = prices[i] - minNum; } if (prices[i] < minNum) { minNum = prices[i]; } } return maxProfit; }}Leetcode 122. 买卖股票的最佳时机 II这次改成股票可以买卖多次, 但是你必须要在出售股票之前把持有的股票卖掉。示例 1:输入: [7,1,5,3,6,4]输出: 7解释: 在第 2 天(股票价格 = 1)的时候买入,在第 3 天(股票价格 = 5)的时候卖出, 这笔交易所能获得利润 = 5-1 = 4 。 随后,在第 4 天(股票价格 = 3)的时候买入,在第 5 天(股票价格 = 6)的时候卖出, 这笔交易所能获得利润 = 6-3 = 3 。示例 2:输入: [1,2,3,4,5]输出: 4解释: 在第 1 天(股票价格 = 1)的时候买入,在第 5 天 (股票价格 = 5)的时候卖出, 这笔交易所能获得利润 = 5-1 = 4 。 注意你不能在第 1 天和第 2 天接连购买股票,之后再将它们卖出。 因为这样属于同时参与了多笔交易,你必须在再次购买前出售掉之前的股票。示例 3:输入: [7,6,4,3,1]输出: 0解释: 在这种情况下, 没有交易完成, 所以最大利润为 0。题解由于可以无限次买入和卖出。我们都知道炒股想挣钱当然是低价买入高价抛出,那么这里我们只需要从第二天开始,如果当前价格比之前价格高,则把差值加入利润中,因为我们可以昨天买入,今日卖出,若明日价更高的话,还可以今日买入,明日再抛出。以此类推,遍历完整个数组后即可求得最大利润。class Solution { public int maxProfit(int[] prices) { // 7,1,5,3,6,4 int maxProfit = 0; for (int i = 0; i < prices.length; i++) { if (i != 0 && prices[i] - prices[i-1] > 0) { maxProfit += prices[i] - prices[i-1]; } } return maxProfit; }}LRU cache (头条、蚂蚁)这道题目是头条的高频题目,甚至我怀疑,头条这个面试题是题库里面的必考题。看脉脉也是好多人遇到。第一次我写的时候没写好,可能由于这个挂了。转自知乎:https://zhuanlan.zhihu.com/p/…题目运用你所掌握的数据结构,设计和实现一个 LRU (最近最少使用) 缓存机制。它应该支持以下操作: 获取数据 get 和 写入数据 put 。获取数据 get(key) - 如果密钥 (key) 存在于缓存中,则获取密钥的值(总是正数),否则返回 -1。写入数据 put(key, value) - 如果密钥不存在,则写入其数据值。当缓存容量达到上限时,它应该在写入新数据之前删除最近最少使用的数据值,从而为新的数据值留出空间。进阶:你是否可以在 O(1) 时间复杂度内完成这两种操作?LRUCache cache = new LRUCache( 2 /* 缓存容量 / );cache.put(1, 1);cache.put(2, 2);cache.get(1); // 返回 1cache.put(3, 3); // 该操作会使得密钥 2 作废cache.get(2); // 返回 -1 (未找到)cache.put(4, 4); // 该操作会使得密钥 1 作废cache.get(1); // 返回 -1 (未找到)cache.get(3); // 返回 3cache.get(4); // 返回 4题解这道题在今日头条、快手或者硅谷的公司中是比较常见的,代码要写的还蛮多的,难度也是hard级别。最重要的是LRU 这个策略怎么去实现,很容易想到用一个链表去实现最近使用的放在链表的最前面。比如get一个元素,相当于被使用过了,这个时候它需要放到最前面,再返回值,set同理。那如何把一个链表的中间元素,快速的放到链表的开头呢?很自然的我们想到了双端链表。基于 HashMap 和 双向链表实现 LRU 的整体的设计思路是,可以使用 HashMap 存储 key,这样可以做到 save 和 get key的时间都是 O(1),而 HashMap 的 Value 指向双向链表实现的 LRU 的 Node 节点,如图所示。LRU 存储是基于双向链表实现的,下面的图演示了它的原理。其中 head 代表双向链表的表头,tail 代表尾部。首先预先设置 LRU 的容量,如果存储满了,可以通过 O(1) 的时间淘汰掉双向链表的尾部,每次新增和访问数据,都可以通过 O(1)的效率把新的节点增加到对头,或者把已经存在的节点移动到队头。下面展示了,预设大小是 3 的,LRU存储的在存储和访问过程中的变化。为了简化图复杂度,图中没有展示 HashMap部分的变化,仅仅演示了上图 LRU 双向链表的变化。我们对这个LRU缓存的操作序列如下:save(“key1”, 7)save(“key2”, 0)save(“key3”, 1)save(“key4”, 2)get(“key2”)save(“key5”, 3)get(“key2”)save(“key6”, 4)相应的 LRU 双向链表部分变化如下:总结一下核心操作的步骤:save(key, value),首先在 HashMap 找到 Key 对应的节点,如果节点存在,更新节点的值,并把这个节点移动队头。如果不存在,需要构造新的节点,并且尝试把节点塞到队头,如果LRU空间不足,则通过 tail 淘汰掉队尾的节点,同时在 HashMap 中移除 Key。get(key),通过 HashMap 找到 LRU 链表节点,因为根据LRU 原理,这个节点是最新访问的,所以要把节点插入到队头,然后返回缓存的值。 private static class DLinkedNode { int key; int value; DLinkedNode pre; DLinkedNode post; } /* * 总是在头节点中插入新节点. / private void addNode(DLinkedNode node) { node.pre = head; node.post = head.post; head.post.pre = node; head.post = node; } /* * 摘除一个节点. / private void removeNode(DLinkedNode node) { DLinkedNode pre = node.pre; DLinkedNode post = node.post; pre.post = post; post.pre = pre; } /* * 摘除一个节点,并且将它移动到开头 / private void moveToHead(DLinkedNode node) { this.removeNode(node); this.addNode(node); } /* * 弹出最尾巴节点 / private DLinkedNode popTail() { DLinkedNode res = tail.pre; this.removeNode(res); return res; } private HashMap<Integer, DLinkedNode> cache = new HashMap<Integer, DLinkedNode>(); private int count; private int capacity; private DLinkedNode head, tail; public LRUCache(int capacity) { this.count = 0; this.capacity = capacity; head = new DLinkedNode(); head.pre = null; tail = new DLinkedNode(); tail.post = null; head.post = tail; tail.pre = head; } public int get(int key) { DLinkedNode node = cache.get(key); if (node == null) { return -1; // cache里面没有 } // cache 命中,挪到开头 this.moveToHead(node); return node.value; } public void put(int key, int value) { DLinkedNode node = cache.get(key); if (node == null) { DLinkedNode newNode = new DLinkedNode(); newNode.key = key; newNode.value = value; this.cache.put(key, newNode); this.addNode(newNode); ++count; if (count > capacity) { // 最后一个节点弹出 DLinkedNode tail = this.popTail(); this.cache.remove(tail.key); count–; } } else { // cache命中,更新cache. node.value = value; this.moveToHead(node); } } 二叉树层次遍历(头条)这个题目之前也讲过,Leetcode 102题。题目给定一个二叉树,返回其按层次遍历的节点值。 (即逐层地,从左到右访问所有节点)。例如:给定二叉树: [3,9,20,null,null,15,7], 3 / \ 9 20 / \ 15 7返回其层次遍历结果:[ [3], [9,20], [15,7]]题解我们数据结构的书上教的层序遍历,就是利用一个队列,不断的把左子树和右子树入队。但是这个题目还要要求按照层输出。所以关键的问题是: 如何确定是在同一层的。我们很自然的想到:如果在入队之前,把上一层所有的节点出队,那么出队的这些节点就是上一层的列表。由于队列是先进先出的数据结构,所以这个列表是从左到右的。/* * Definition for a binary tree node. * public class TreeNode { * int val; * TreeNode left; * TreeNode right; * TreeNode(int x) { val = x; } * } /class Solution { public List<List<Integer>> levelOrder(TreeNode root) { List<List<Integer>> res = new LinkedList<>(); if (root == null) { return res; } LinkedList<TreeNode> queue = new LinkedList<>(); queue.add(root); while (!queue.isEmpty()) { int size = queue.size(); List<Integer> currentRes = new LinkedList<>(); // 当前队列的大小就是上一层的节点个数, 依次出队 while (size > 0) { TreeNode current = queue.poll(); if (current == null) { continue; } currentRes.add(current.val); // 左子树和右子树入队. if (current.left != null) { queue.add(current.left); } if (current.right != null) { queue.add(current.right); } size–; } res.add(currentRes); } return res; }}这道题可不可以用非递归来解呢?递归的子问题:遍历当前节点, 对于当前层, 遍历左子树的下一层层,遍历右子树的下一层递归结束条件: 当前层,当前子树节点是null/* * Definition for a binary tree node. * public class TreeNode { * int val; * TreeNode left; * TreeNode right; * TreeNode(int x) { val = x; } * } /class Solution { public List<List<Integer>> levelOrder(TreeNode root) { List<List<Integer>> res = new LinkedList<>(); if (root == null) { return res; } levelOrderHelper(res, root, 0); return res; } /* * @param depth 二叉树的深度 */ private void levelOrderHelper(List<List<Integer>> res, TreeNode root, int depth) { if (root == null) { return; } if (res.size() <= depth) { // 当前层的第一个节点,需要new 一个list来存当前层. res.add(new LinkedList<>()); } // depth 层,把当前节点加入 res.get(depth).add(root.val); // 递归的遍历下一层. levelOrderHelper(res, root.left, depth + 1); levelOrderHelper(res, root.right, depth + 1); }}二叉树转链表(快手)这是Leetcode 104题。给定一个二叉树,原地将它展开为链表。例如,给定二叉树 1 / \ 2 5 / \ \3 4 6将其展开为:1 \ 2 \ 3 \ 4 \ 5 \ 6这道题目的关键是转换的时候递归的时候记住是先转换右子树,再转换左子树。所以需要记录一下右子树转换完之后链表的头结点在哪里。注意没有新定义一个next指针,而是直接将right 当做next指针,那么Left指针我们赋值成null就可以了。class Solution { private TreeNode prev = null; public void flatten(TreeNode root) { if (root == null) return; flatten(root.right); // 先转换右子树 flatten(root.left); root.right = prev; // 右子树指向链表的头 root.left = null; // 把左子树置空 prev = root; // 当前结点为链表头 }}用递归解法,用一个stack 记录节点,右子树先入栈,左子树后入栈。class Solution { public void flatten(TreeNode root) { if (root == null) return; Stack<TreeNode> stack = new Stack<TreeNode>(); stack.push(root); while (!stack.isEmpty()) { TreeNode current = stack.pop(); if (current.right != null) stack.push(current.right); if (current.left != null) stack.push(current.left); if (!stack.isEmpty()) current.right = stack.peek(); current.left = null; } }}二叉树寻找最近公共父节点(快手)Leetcode 236 二叉树的最近公共父亲节点题解从根节点开始遍历,如果node1和node2中的任一个和root匹配,那么root就是最低公共祖先。 如果都不匹配,则分别递归左、右子树,如果有一个 节点出现在左子树,并且另一个节点出现在右子树,则root就是最低公共祖先. 如果两个节点都出现在左子树,则说明最低公共祖先在左子树中,否则在右子树。public class Solution { public TreeNode lowestCommonAncestor(TreeNode root, TreeNode p, TreeNode q) { //发现目标节点则通过返回值标记该子树发现了某个目标结点 if(root == null || root == p || root == q) return root; //查看左子树中是否有目标结点,没有为null TreeNode left = lowestCommonAncestor(root.left, p, q); //查看右子树是否有目标节点,没有为null TreeNode right = lowestCommonAncestor(root.right, p, q); //都不为空,说明做右子树都有目标结点,则公共祖先就是本身 if(left!=null&&right!=null) return root; //如果发现了目标节点,则继续向上标记为该目标节点 return left == null ? right : left; } }数据流求中位数(蚂蚁)面了蚂蚁中台的团队,二面面试官根据汇报层级推测应该是P9级别及以上,在美国面我,面试风格偏硅谷那边。题目是hard难度的,leetcode 295题。这道题目是Leetcode的hard难度的原题。给定一个数据流,求数据流的中位数,求中位数或者topK的问题我们通常都会想用堆来解决。但是面试官又进一步加大了难度,他要求内存使用很小,没有磁盘,但是压榨空间的同时可以忍受一定时间的损耗。且面试官不仅要求说出思路,要写出完整可经过大数据检测的production code。先不考虑内存不考虑内存的方式就是Leetcode 论坛上的题解。基本思想是建立两个堆。左边是大根堆,右边是小根堆。如果是奇数的时候,大根堆的堆顶是中位数。例如:[1,2,3,4,5]大根堆建立如下: 3 / \ 1 2小根堆建立如下: 4 / 5 偶数的时候则是最大堆和最小堆顶的平均数。例如: [1, 2, 3, 4]大根堆建立如下: 2 / 1 小根堆建立如下: 3 / 4 然后再维护一个奇数偶数的状态即可求中位数。public class MedianStream { private PriorityQueue<Integer> leftHeap = new PriorityQueue<>(5, Collections.reverseOrder()); private PriorityQueue<Integer> rightHeap = new PriorityQueue<>(5); private boolean even = true; public double getMedian() { if (even) { return (leftHeap.peek() + rightHeap.peek()) / 2.0; } else { return leftHeap.peek(); } } public void addNum(int num) { if (even) { rightHeap.offer(num); int rightMin = rightHeap.poll(); leftHeap.offer(rightMin); } else { leftHeap.offer(num); int leftMax = leftHeap.poll(); rightHeap.offer(leftMax); } System.out.println(leftHeap); System.out.println(rightHeap); // 奇偶变换. even = !even; }}压榨内存但是这样做的问题就是可能内存会爆掉。如果你的流无限大,那么意味着这些数据都要存在内存中,堆必须要能够建无限大。如果内存必须很小的方式,用时间换空间。流是可以重复去读的, 用时间换空间;可以用分治的思想,先读一遍流,把流中的数据个数分桶;分桶之后遍历桶就可以得到中位数落在哪个桶里面,这样就把问题的范围缩小了。public class Median { private static int BUCKET_SIZE = 1000; private int left = 0; private int right = Integer.MAX_VALUE; // 流这里用int[] 代替 public double findMedian(int[] nums) { // 第一遍读取stream 将问题复杂度转化为内存可接受的量级. int[] bucket = new int[BUCKET_SIZE]; int step = (right - left) / BUCKET_SIZE; boolean even = true; int sumCount = 0; for (int i = 0; i < nums.length; i++) { int index = nums[i] / step; bucket[index] = bucket[index] + 1; sumCount++; even = !even; } // 如果是偶数,那么就需要计算第topK 个数 // 如果是奇数, 那么需要计算第 topK和topK+1的个数. int topK = even ? sumCount / 2 : sumCount / 2 + 1; int index = 0; int indexBucketCount = 0; for (index = 0; index < bucket.length; index++) { indexBucketCount = bucket[index]; if (indexBucketCount >= topK) { // 当前bucket 就是中位数的bucket. break; } topK -= indexBucketCount; } // 划分到这里其实转化为一个topK的问题, 再读一遍流. if (even && indexBucketCount == topK) { left = index * step; right = (index + 2) * step; return helperEven(nums, topK); // 偶数的时候, 恰好划分到在左右两个子段中. // 左右两段中 [topIndex-K + (topIndex-K + 1)] / 2. } else if (even) { left = index * step; right = (index + 1) * step; return helperEven(nums, topK); // 左边 [topIndex-K + (topIndex-K + 1)] / 2 } else { left = index * step; right = (index + 1) * step; return helperOdd(nums, topK); // 奇数, 左边topIndex-K } }}这里边界条件我们处理好之后,关键还是helperOdd 和 helperEven这两个函数怎么去求topK的问题. 我们还是转化为一个topK的问题,那么求top-K和top(K+1)的问题到这里我们是不是可以用堆来解决了呢? 答案是不能,考虑极端情况。中位数的重复次数非常多eg:[100,100,100,100,100…] (1000亿个100)你的划分恰好落到这个桶里面,内存同样会爆掉。再用时间换空间假如我们的划分bucket大小是10000,那么最大的时候区间就是20000。(对应上面的偶数且落到两个分桶的情况)那么既然划分到某一个bucket了,我们直接用数数字的方式来求topK 就可以了,用堆的方式也可以,更高效一点,其实这里问题规模已经划分到很小了,两种方法都可以。我们选用TreeMap这种数据结构计数。然后分奇数偶数去求解。 private double helperEven(int[] nums, int topK) { TreeMap<Integer, Integer> map = new TreeMap<>(); for (int i = 0; i < nums.length; i++) { if (nums[i] >= left && nums[i] <= right) { if (!map.containsKey(nums[i])) { map.put(nums[i], 1); } else { map.put(nums[i], map.get(nums[i]) + 1); } } } int count = 0; int kNum = Integer.MIN_VALUE; int kNextNum = 0; for (Map.Entry<Integer, Integer> entry : map.entrySet()) { int currentCountIndex = entry.getValue(); if (kNum != Integer.MIN_VALUE) { kNextNum = entry.getKey(); break; } if (count + currentCountIndex == topK) { kNum = entry.getKey(); } else if (count + currentCountIndex > topK) { kNum = entry.getKey(); kNextNum = entry.getKey(); break; } else { count += currentCountIndex; } } return (kNum + kNextNum) / 2.0; } private double helperOdd(int[] nums, int topK) { TreeMap<Integer, Integer> map = new TreeMap<>(); for (int i = 0; i < nums.length; i++) { if (nums[i] >= left && nums[i] <= right) { if (!map.containsKey(nums[i])) { map.put(nums[i], 1); } else { map.put(nums[i], map.get(nums[i]) + 1); } } } int count = 0; int kNum = Integer.MIN_VALUE; for (Map.Entry<Integer, Integer> entry : map.entrySet()) { int currentCountIndex = entry.getValue(); if (currentCountIndex + count >= topK) { kNum = entry.getKey(); break; } else { count += currentCountIndex; } } return kNum; }至此,我觉得算是一个比较好的解决方案,leetcode社区没有相关的解答和探索,欢迎大家交流。热门阅读技术文章汇总【Leetcode】101. 对称二叉树【Leetcode】100. 相同的树【Leetcode】98. 验证二叉搜索树 ...

April 9, 2019 · 8 min · jiezi

daily-question(前端每日一题)

在这里记录着每天自己遇到的一道印象深刻的前端问题,以及一道生活中随处可见的小问题。强迫自己形成积累的习惯,鞭挞自己不断前行,共同学习。Github 地址2019/04/08 - 2019/04/14函数的防抖与节流 ?防抖所谓防抖,就是指触发事件后在 n 秒内函数只能执行一次,如果在 n 秒内又触发了事件,则会重新计算函数执行时间。(防误触)// 延缓执行function debounce(func, wait) { var timeout; return function() { var context = this; var args = arguments; console.log(args); console.log(func); if (timeout) clearTimeout(timeout); timeout = setTimeout(function() { func.apply(context, args); }, wait); };}// 立即执行function debounce(func, wait) { var timeout; return function() { var context = this; var args = arguments; if (timeout) clearTimeout(timeout); var callNow = !timeout; timeout = setTimeout(function() { timeout = null; }, wait); if (callNow) func.apply(context, args); };}节流所谓节流,就是指连续触发事件但是在 n 秒中只执行一次函数。(限制流量)// 时间戳function throttle(func, wait) { var previous = 0; return function() { var now = Date.now(); var context = this; var args = arguments; if (now - previous > wait) { func.apply(context, args); previous = now; } };}// 定时器function throttle(func, wait) { var timeout; return function() { var context = this; var args = arguments; if (!timeout) { timeout = setTimeout(function() { timeout = null; func.apply(context, args); }, wait); } };}2019/04/01 - 2019/04/07为何 [] == ![] 结果为 true,而 {} == !{} 却为 false首先了解一下类型转化的规则:1、如果有一个操作数是布尔值,则在比较相等性之前先将其转换为数值——false 转换为 0,而 true 转换为 1;2、如果一个操作数是字符串,另一个操作数是数值,在比较相等性之前先将字符串转换为数值3、如果一个操作数是对象,另一个操作数不是,则调用对象的 valueOf()(boolean 对象方法)方法或者 toString()方法,用得到的基本类型值按照前面的规则进行比较null 和 undefined 是相等的4、要比较相等性之前,不能将 null 和 undefined 转换成其他任何值5、如果有一个操作数是 NaN,则相等操作符返回 false ,而不相等操作符返回 true。重要提示:即使两个操作数都是 NaN,相等操作符也返回 false 了;因为按照规则, NaN 不等于 NaN (NaN 不等于任何值,包括他本身)6、如果两个操作数都是对象,则比较它们是不是同一个对象,如果两个操作数都指向同一个对象,则相等操作符返回 true;否则,返回 false7、 !可将变量转换成 boolean 类型,null、undefined、NaN 以及空字符串(’’)取反都为 true,其余都为 false。现在开始分析题目[] == ![];// 先转化右边 ![],// !可将变量转换成 boolean 类型,null、undefined、NaN 以及空字符串(’’)取反都为 true,其余都为 false。// 所以 ![] => false => 0// 左边 [], 因为[].toString() 为空字符串,所以 [] => ‘’// 综上, ’’ == 0, 为 true{} == !{}// 先转化右边 !{},// !可将变量转换成 boolean 类型,null、undefined、NaN 以及空字符串(’’)取反都为 true,其余都为 false。// 所以 !{} => false => 0// 左边 ({}).toString() => “[object Object]”// 综上, “[object Object]” == 0, 为 falseRESTful 接口的优缺点什么是 restful 接口 ?REST – REpresentational State Transfer,英语的直译就是“表现层状态转移”,它包含以下三个方面:URL 设计: RESTful 的核心思想就是,客户端发出的数据操作指令都是"动词 + 宾语"的结构。比如,GET /articles 这个命令,GET 是动词,/articles 是宾语。动词通常就是五种 HTTP 方法,对应 CRUD 操作。GET:读取(Read)POST:新建(Create)PUT:更新(Update)PATCH:更新(Update),通常是部分更新DELETE:删除(Delete)状态码: 客户端的每一次请求,服务器都必须给出回应。回应包括 HTTP 状态码和数据两部分。服务器回应: API 返回的数据格式,不应该是纯文本,而应该是一个 JSON 对象,因为这样才能返回标准的结构化数据。所以,服务器回应的 HTTP 头的 Content-Type 属性要设为 application/json。优点简洁明了,一目了然;轻量,直接通过 http,不需要额外的协议,post/get/put/delete 操作缺点当一次更新的内容多的时候需要调用更多的接口。删除也是,如果我想批量删除呢?对后端开发人员要求高,业务逻辑有时难以被抽象为资源的增删改查。对前端开发人员不友好,API 粒度较粗,难以查询符合特殊要求的数据,同样的业务要比普通的 API 需要更多次 HTTP 请求。对视口 viewport 的理解 ?视口分为:layout viewport – 布局视口,visual viewport – 视觉视口,ideal viewport – 理想视口如果把移动设备上浏览器的可视区域设为 viewport 的话,某些网站就会因为 viewport 太窄而显示错乱,所以这些浏览器就决定默认情况下把 viewport 设为一个较宽的值,比如 980px,这样的话即使是那些为桌面设计的网站也能在移动浏览器上正常显示了。这个浏览器默认的 viewport 叫做 layout viewport。这个 layout viewport 的宽度可以通过 document.documentElement.clientWidth 来获取。layout viewport 的宽度是大于浏览器可视区域的宽度的,所以我们还需要一个 visual viewport 来代表浏览器可视区域的大小。visual viewport 的宽度可以通过 window.innerWidth 来获取ideal viewport 即每个设备完美适配的视口。所谓的完美适配指的是,第一不需要用户缩放和横向滚动条就能正常的查看网站的所有内容;第二是无论文字,图片等在不同的设备都能显示出差不多的效果。ideal viewport 并没有一个固定的尺寸,不同的设备拥有有不同的 ideal viewport。mata 标签与 viewport 的关系<meta name=“viewport” content=“width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=0”/>移动设备默认的是 layout viewport , 但是我们需要的是 ideal viewport, 那么通过 meta 标签的作用就是:让当前 viewport 的宽度等于设备的宽度,同时不允许用户手动缩放。meta 标签中 content 的属性和值如下:width 设置 layout viewport 的宽度,为一个正整数,或字符串"width-device"initial-scale 设置页面的初始缩放值,为一个数字,可以带小数minimum-scale 允许用户的最小缩放值,为一个数字,可以带小数maximum-scale 允许用户的最大缩放值,为一个数字,可以带小数height 设置 layout viewport 的高度,这个属性对我们并不重要,很少使用user-scalable 是否允许用户进行缩放,值为"no"或"yes", no 代表不允许,yes 代表允许移动端的像素?1、物理像素(设备像素)红蓝绿可以调配出任何颜色,通常说的手机像素就是由许多红蓝绿组成的一个小块,1 个小块表示 1 个像素。一个物理像素是显示器(手机屏幕)上最小的物理显示单元,通过控制每个像素点的颜色,使屏幕显示出不同的图像。屏幕从工厂出来那天起,它上面的物理像素点就固定不变了,单位 pt - 固定单位。比如:iPhone6、7、8 的分辨率为 1334750 像素表示,横向 750 个像素,纵向 1334 个像素2、CSS 像素CSS 和 JS 使用的抽象单位,浏览器内的一切长度都是以 CSS 像素为单位的,CSS 像素的单位是 px。一倍屏:当设备像素比为 1:1 时,使用 1(1×1)个设备像素显示 1 个 CSS 像素;二倍屏:当设备像素比为 2:1 时,使用 4(2×2)个设备像素显示 1 个 CSS 像素;三倍屏:当设备像素比为 3:1 时,使用 9(3×3)个设备像素显示 1 个 CSS 像素。3、像素密度(PPI)每英寸像素取值,也就是衡量单位物理面积内拥有像素值的情况。ppi 越高,每英寸像素点越多,图像越清晰;我们可以类比物体的密度,密度越大,单位体积的质量就越大,ppi 越高,单位面积的像素越多。ppi 在 120-160 之间的手机被归为低密度手机,160-240 被归为中密度,240-320 被归为高密度,320 以上被归为超高密度(例如:苹果的 Retina 屏幕)proto__和 prototype 的区别 ?在 JS 里,万物皆对象。方法(Function)是对象,方法的原型(Function.prototype)是对象。因此,它们都会具有对象共有的特点。即:对象具有属性__proto,可称为隐式原型,一个对象的隐式原型指向构造该对象的构造函数的原型,这也保证了实例能够访问在构造函数原型中定义的属性和方法。方法(Function)方法这个特殊的对象,除了和其他对象一样有上述 proto 属性之外,还有自己特有的属性——原型属性(prototype),这个属性是一个指针,指向一个对象,这个对象的用途就是包含所有实例共享的属性和方法(我们把这个对象叫做原型对象)。原型对象也有一个属性,叫做 constructor,这个属性包含了一个指针,指回原构造函数。双精度浮点数是如何保存的 ?在计算机中,浮点表示法,分为三大部分:第一部分用来存储符号位(sign),用来区分正负数,0 表示正数第二部分用来存储指数(exponent)第三部分用来存储小数(fraction), 多出的末尾如果是 1 需要进位;双精度浮点数一共占据 64 位:符号位(sign)占用 1 位指数位(exponent)占用 11 位小数位(fraction)占用 52 位举个例子:0.1 的二进制为0.00011001100110011001100110011001100110011001100110011001 10011…转化为 2 进制科学计数法1.1001100110011001100110011001100110011001100110011001 * (2 ^ -4);也就是说 0.1 的:符号位为:0小数位为:1001100110011001100110011001100110011001100110011001指数位为:-4指数位为负数的怎么保存?为了减少不必要的麻烦,IEEE 规定了一个偏移量,对于指数部分,每次都加这个偏移量进行保存,这样即使指数是负数,那么加上这个偏移量也变为正数啦。为了使所有的负指数加上这个偏移量都能够变为正数,IEEE 规定 1023 为双精度的偏移量。因此指数部分为 -4 + 1023 = 1019, 转化成 11 位二进制为:01111111011因此 0.1 在内存中的保存为: 0 01111111011 1001100110011001100110011001100110011001100110011001如何找出字符串中出现最多的字母 (ababccdeajxac)?最先想到的解法是用 map 纪录每个字符的次数,然后找出最多的即可:function getMaxNumberOfChar(str) { return (str + “”).split("").reduce( function(pre, cur, index, arr) { cur in pre ? pre[cur]++ : (pre[cur] = 1); pre[cur] > pre.value && ((pre.char = cur), (pre.value = pre[cur])); return pre; }, { value: 0 } );}getMaxNumberOfChar(“ababccdeajxac”); // Object {value: 4, a: 4, char: “a”, b: 2, c: 3…}此外,可以考虑用正则来辅助处理:function getMaxNumberOfChar(str) { return (str + “”) .split("") .sort() .join("") .match(/(\w)\1/g) .reduce( function(pre, cur) { return cur.length > pre.value ? { value: cur.length, char: cur[0] } : pre; }, { value: 0 } );}getMaxNumberOfChar(“ababccdeajxac”); // Object {value: 4, char: “a”}这里拓展一下 reduce 函数的用法// reduce 函数// array.reduce(function(accumulator, currentValue, currentIndex, arr), initialValue)// reducer回调函数本身接受几个参数,第一个参数是 accumulator 累加器,第二个是数组中的 item,第三个参数是该项的索引,最后一个参数是原始数组的引用。// initialValue 为reduce初始值,否则视数组第一个值为初始值,选填const array1 = [1, 2, 3, 4];// 1 + 2 + 3 + 4console.log( array1.reduce((accumulator, currentValue) => { console.log(accumulator, currentValue); return accumulator + currentValue; }));2019/03/27 - 2019/03/31请问这句语句 var args=[].slice.call(arguments,1) 是什么意思?先看原函数:function a() { var args = [].slice.call(arguments, 1); console.log(args);}a(“haha”, 1, 2, 3, 4, 5); // log出[1, 2, 3, 4, 5]a(“run”, “-g”, “-b”); // log出[’-g’, ‘-b’]首先,函数 call() 方法,第一个参数改变函数的 this 指向,后面剩余参数传入原函数 slice 中arguments 是什么?arguments 是函数中的一个类数组的参数集合对象如: {‘0’: ‘haha’, ‘1’: 1, ‘2’: 2}slice 为数组可从已有的数组中返回选定的元素。此题为从 index = 1 往后综上,这句语句的作用是——将函数中的实参值转化成数组</details>连等赋值问题var a = { n: 1 };var b = a;a.x = a = { n: 2 };// a ? b ? a.x ? 结果是什么?<details><summary>点击</summary>我们可以先尝试交换下连等赋值顺序(a = a.x = {n: 2};),可以发现输出不变,即顺序不影响结果。那么现在来解释对象连等赋值的问题:按照 es5 规范,题中连等赋值等价于a.x = (a = { n: 2 });,按优先获取左引用(lref),然后获取右引用(rref)的顺序,a.x 和 a 中的 a 都指向了{ n: 1 }。至此,至关重要或者说最迷惑的一步明确。(a = {n: 2})执行完成后,变量 a 指向{n: 2},并返回{n: 2};接着执行 a.x = { n: 2 },这里的 a 就是 b(指向{ n: 1 }),所以 b.x 就指向了{ n: 2 }。赋值时有返回该值, 如 a = 4 // return 4 , 赋值变量 let n = 5 //return undefinded````jsa = { n: 2 };b = { n: 1, x: { n: 2 } };a.x = undefinded;```如何手动实现一个 Promise ?promise 的三种状态 pending, resolve, rejectfunction MyPromise(callback) { let that = this; //定义初始状态 //Promise状态 that.status = "pending"; //value that.value = "undefined"; //reason 是一个用于描述Promise被拒绝原因的值。 that.reason = "undefined"; //用来解决异步问题的数组 that.onFullfilledArray = []; that.onRejectedArray = []; //定义resolve function resolve(value) { //当status为pending时,定义Javascript值,定义其状态为fulfilled if (that.status === "pending") { that.value = value; that.status = "resolved"; that.onFullfilledArray.forEach(func =&gt; { func(that.value); }); } } //定义reject function reject(reason) { //当status为pending时,定义reason值,定义其状态为rejected if (that.status === "pending") { that.reason = reason; that.status = "rejected"; that.onRejectedArray.forEach(func =&gt; { func(that.reason); }); } } //捕获callback是否报错 try { callback(resolve, reject); } catch (error) { reject(error); }}MyPromise.prototype.then = function(onFulfilled, onRejected) { let that = this; //需要修改下,解决异步问题,即当Promise调用resolve之后再调用then执行onFulfilled(that.value)。 //用两个数组保存下onFulfilledArray if (that.status === "pending") { that.onFullfilledArray.push(value =&gt; { onFulfilled(value); }); that.onRejectedArray.push(reason =&gt; { onRejected(reason); }); } if (that.status === "resolved") { onFulfilled(that.value); } if (that.status === "rejected") { onRejected(that.reason); }};AST(抽象语法树)?什么是 AST(抽象语法树)?It is a hierarchical program representation that presents source code structure according to the grammar of a programming language, each AST node corresponds to an item of a source code.它是一种分层程序表示,它根据编程语言的语法呈现源代码结构,每个 AST 节点对应一个源代码项。Babel,Webpack,vue-cli 和 esLint 等很多的工具和库的核心都是通过 Abstract Syntax Tree (抽象语法树)这个概念来实现对代码的检查、分析等操作的。解析(parsing),转译(transforming),生成(generation)。将源码解析成 AST 抽象语法树,再对此语法树进行相应的转译,最后生成我们所需要的代码。第三方的生成 AST 库有很多,这里推荐几个——esprima, babylon(babel 使用)其转化的内容大致是这样的:{ "type": "Program", "start": 0, "end": 16, "body": [ { "type": "FunctionDeclaration", "start": 0, "end": 16, "id": { "type": "Identifier", "start": 9, "end": 12, "name": "ast" }, "expression": false, "generator": false, "params": [], "body": { "type": "BlockStatement", "start": 14, "end": 16, "body": [] } } ], "sourceType": "module"}AST 的使用场景代码语法的检查、代码风格的检查、代码的格式化、代码的高亮、代码错误提示、代码自动补全等等.代码混淆压缩优化变更代码,改变代码结构使达到想要的结构为什么 0.1 + 0.2 !== 0.3 ?IEEE-754 精度问题所有使用 IEEE-754 数字实现的编程语言都有这个问题。0.1 和 0.2 的二进制浮点数表示并不是精确的,所以相加后不等于 0.3。这个相加的结果接近 0.30000000000000004。首先将 0.1 转化为 2 进制// 0.1 十进制 -&gt; 二进制0.1 * 2 = 0.2 取00.2 * 2 = 0.4 取00.4 * 2 = 0.8 取00.8 * 2 = 1.6 取10.6 * 2 = 1.2 取10.2 * 2 = 0.4 取00.4 * 2 = 0.8 取00.8 * 2 = 1.6 取10.6 * 2 = 1.2 取1//0.000110011(0011) 0.1二进制(0011)里面的数字表示循环你会发现 0.1 转二级制会一直无线循环下去,根本算不出一个正确的二进制数。所以我们得出 0.1 = 0.000110011(0011),那么 0.2 的演算也基本如上所示,所以得出 0.2 = 0.00110011(0011)六十四位中符号位占一位,整数位占十一位,其余五十二位都为小数位。因为 0.1 和 0.2 都是无限循环的二进制了,所以在小数位末尾处需要判断是否进位(就和十进制的四舍五入一样)那么把这两个二进制加起来会得出 0.010011….0100 , 这个值算成十进制就是 0.30000000000000004 ...

April 8, 2019 · 5 min · jiezi

《前端面试手记》之JavaScript基础知识梳理(下)

???? 内容速览 ????实现ES5继承的4种方法原型和原型链作用域和作用域链Event Loop执行上下文闭包的理解和分析????查看全部教程 / 阅读原文????ES5继承题目:ES5中常用继承方法。方法一:绑定构造函数缺点:不能继承父类原型方法/属性function Animal(){ this.species = ‘动物’}function Cat(){ // 执行父类的构造方法, 上下文为实例对象 Animal.apply(this, arguments)}/** * 测试代码 /var cat = new Cat()console.log(cat.species) // output: 动物方法二:原型链继承缺点:无法向父类构造函数中传递参数;子类原型链上定义的方法有先后顺序问题。注意:js中交换原型链,均需要修复prototype.constructor指向问题。function Animal(species){ this.species = species}Animal.prototype.func = function(){ console.log(‘Animal’)}function Cat(){}/* * func方法是无效的, 因为后面原型链被重新指向了Animal实例 /Cat.prototype.func = function() { console.log(‘Cat’)}Cat.prototype = new Animal()Cat.prototype.constructor = Cat // 修复: 将Cat.prototype.constructor重新指向本身/* * 测试代码 /var cat = new Cat()cat.func() // output: Animalconsole.log(cat.species) // undefined方法3:组合继承结合绑定构造函数和原型链继承2种方式,缺点是:调用了2次父类的构造函数。function Animal(species){ this.species = species}Animal.prototype.func = function(){ console.log(‘Animal’)}function Cat(){ Animal.apply(this, arguments)}Cat.prototype = new Animal()Cat.prototype.constructor = Cat /* * 测试代码 /var cat = new Cat(‘cat’)cat.func() // output: Animalconsole.log(cat.species) // output: cat方法4:寄生组合继承改进了组合继承的缺点,只需要调用1次父类的构造函数。它是引用类型最理想的继承范式。(引自:《JavaScript高级程序设计》)/* * 寄生组合继承的核心代码 * @param {Function} sub 子类 * @param {Function} parent 父类 /function inheritPrototype(sub, parent) { // 拿到父类的原型 var prototype = Object(parent.prototype) // 改变constructor指向 prototype.constructor = sub // 父类原型赋给子类 sub.prototype = prototype}function Animal(species){ this.species = species}Animal.prototype.func = function(){ console.log(‘Animal’)}function Cat(){ Animal.apply(this, arguments) // 只调用了1次构造函数}inheritPrototype(Cat, Animal)/* * 测试代码 */var cat = new Cat(‘cat’)cat.func() // output: Animalconsole.log(cat.species) // output: cat原型和原型链所有的引用类型(数组、对象、函数),都有一个__proto__属性,属性值是一个普通的对象所有的函数,都有一个prototype属性,属性值也是一个普通的对象所有的引用类型(数组、对象、函数),proto__属性值指向它的构造函数的prototype属性值注:ES6的箭头函数没有prototype属性,但是有__proto__属性。const obj = {};// 引用类型的 proto 属性值指向它的构造函数的 prototype 属性值console.log(obj.proto === Object.prototype); // output: true原型题目:如何JS中的原型?// 构造函数function Foo(name, age) { this.name = name}Foo.prototype.alertName = function () { alert(this.name)}// 创建示例var f = new Foo(‘zhangsan’)f.printName = function () { console.log(this.name)}// 测试f.printName()f.alertName()但是执行alertName时发生了什么?这里再记住一个重点 当试图得到一个对象的某个属性时,如果这个对象本身没有这个属性,那么会去它的__proto(即它的构造函数的prototype)中寻找,因此f.alertName就会找到Foo.prototype.alertName。原型链题目:如何JS中的原型链?以上一题为基础,如果调用f.toString()。f试图从__proto__中寻找(即Foo.prototype),还是没找到toString()方法。继续向上找,从f.proto.__proto__中寻找(即Foo.prototype.__proto__中)。因为Foo.prototype就是一个普通对象,因此Foo.prototype.proto = Object.prototype最终对应到了Object.prototype.toString这是对深度遍历的过程,寻找的依据就是一个链式结构,所以叫做“原型链”。作用域和作用域链题目:如何理解 JS 的作用域和作用域链。①作用域ES5有”全局作用域“和”函数作用域“。ES6的let和const使得JS用了”块级作用域“。为了解决ES5的全局冲突,一般都是闭包编写:(function(){ … })()。将变量封装到函数作用域。②作用域链当前作用域没有找到定义,继续向父级作用域寻找,直至全局作用域。这种层级关系,就是作用域链。Event Loop单线程题目:讲解下面代码的执行过程和结果。var a = true;setTimeout(function(){ a = false;}, 100)while(a){ console.log(‘while执行了’)}这段代码会一直执行并且输出"while…"。JS是单线程的,先跑执行栈里的同步任务,然后再跑任务队列的异步任务。执行栈和任务队列题目:说一下JS的Event Loop。简单总结如下:JS是单线程的,其上面的所有任务都是在两个地方执行:执行栈和任务队列。前者是存放同步任务;后者是异步任务有结果后,就在其中放入一个事件。当执行栈的任务都执行完了(栈空),js会读取任务队列,并将可以执行的任务从任务队列丢到执行栈中执行。这个过程是循环进行,所以称作Loop。执行上下文题目:解释下“全局执行上下文“和“函数执行上下文”。①全局执行上下文解析JS时候,创建一个 全局执行上下文 环境。把代码中即将执行的(内部函数的不算,因为你不知道函数何时执行)变量、函数声明都拿出来。未赋值的变量就是undefined。下面这段代码输出:undefined;而不是抛出Error。因为在解析JS的时候,变量a已经存入了全局执行上下文中了。console.log(a);var a = 1;②函数执行上下文和全局执行上下文差不多,但是多了this和arguments和参数。在JS中,this是关键字,它作为内置变量,其值是在执行的时候确定(不是定义的时候确定)。闭包的理解和分析题目:解释下js的闭包直接上MDN的解释:闭包是函数和声明该函数的词法环境的组合。而在JavaScript中,函数是被作为一级对象使用的,它既可以本当作值返回,还可以当作参数传递。理解了:“Js中的函数运行在它们被定义的作用域,而不是它们被执行的作用域”(摘自《JavaScript语言精粹》) 这句话即可。题目:闭包优缺点闭包封住了变量作用域,有效地防止了全局污染;但同时,它也存在内存泄漏的风险:在浏览器端可以通过强制刷新解决,对用户体验影响不大在服务端,由于node的内存限制和累积效应,可能会造成进程退出甚至服务器沓机解决方法是显式对外暴露一个接口,专门用以清理变量:function mockData() { const mem = {} return { clear: () => mem = null, // 显式暴露清理接口 get: (page) => { if(page in mem) { return mem[page] } mem[page] = Math.random() } }}更多系列教程⭐在GitHub上收藏/订阅⭐《前端知识体系》JavaScript基础知识梳理(上)JavaScript基础知识梳理(下)谈谈promise/async/await的执行顺序与V8引擎的BUG前端面试中常考的源码实现Flex上手与实战……《设计模式手册》单例模式策略模式代理模式迭代器模式订阅-发布模式桥接模式备忘录模式模板模式抽象工厂模式……《Webpack4渐进式教程》webpack4 系列教程(二): 编译 ES6webpack4 系列教程(三): 多页面解决方案–提取公共代码webpack4 系列教程(四): 单页面解决方案–代码分割和懒加载webpack4 系列教程(五): 处理 CSSwebpack4 系列教程(八): JS Tree Shakingwebpack4 系列教程(十二):处理第三方 JavaScript 库webpack4 系列教程(十五):开发模式与 webpack-dev-server……⭐在GitHub上收藏/订阅⭐ ...

April 3, 2019 · 2 min · jiezi

CSS选择器的分类

《PHP 面试问答》结合实际 PHP 面试,系统的汇总面试中的各种各样的问题,尝试提供简洁准确的答案。如果你在 PHP 面试中遇到问题,欢迎提 Issues 交流。包含网络协议、数据结构与算法、PHP、Web、MySQL、Redis、Linux、安全、设计模式、架构、自我介绍、离职原因、职业规划、准备问题等部分基本规则通过 CSS 可以向文档中的一组元素类型应用某些规则利用 CSS,可以创建易于修改和编辑的规则,且能很容易地将其应用到定义的所有文本元素规则结构每个规则都有两个基本部分:选择器和声明块;声明块由一个或多个声明组成;每个声明则是一个属性-值对每个样式表由一系列规则组成如规则左边所示,选择器定义了将影响文档中的哪些部分规则右边包含声明块,它由一个或多个声明组成。每个声明是一个 CSS 属性和该属性的值的组合元素选择器最常见的选择器往往是 HTML 元素。文档的元素就是最基本的选择器声明和关键字声明块包含一个或多个声明。声明总有如下格式:一个属性后面跟一个冒号,再后面是一个值,然后是一个分号。冒号和分号后面可以有0个或多个空格如果一个属性的值可以取多个关键字,在这种情况下,关键字通常由空格分隔。并不是所偶属性都能接受多个关键字,不过确实有许多属性是这样p {font: medium Helvetica;}选择器通配选择器CSS2引入了一种新的简单选择器,称为通配选择器(universal selector),显示为一个星号()。这个选择器可以与任何元素匹配,就像是一个通配符 {color: red;}类选择器要应用样式而不考虑具体涉及的元素,最常用的方法就是使用类选择器。在使用类选择器之前,需要修改具体的文档标记,以便选择器正常工作为了将一个类选择器的样式与元素关联,必须将 class 属性指定为一个适当的值*.warning {font-weight: bold;}p.warning {font-weight: bold;}.warning {font-weight: bold;}多类选择器.warning {font-weight: bold;}.urgent {font-style: italic;}.warning.urgent {background: silver;}ID 选择器ID 选择器前面有一个 # 号ID 选择器不引用 class 属性的值在一个 HTML 文档中,ID 选择器会使用一次,且仅一次*#first-para {font-weight: bold;}#first-para {font-weight: bold;}属性选择器在某些标记语言中,不能使用类和 ID 选择器。为了解决这个问题,CSS2引入了属性选择器(attribute selector),它可以根据元素的属性及属性值来选择元素简单属性选择如果希望选择某个属性的元素,而不讨论该属性的值是什么,可以使用一个简单属性选择器<h1 class=“hoopla”>Hello</h1><h1 class=“severe”>Serenity</h1><h1 class=“fancy”>Fooling</h1>h1[class] {color: silver;}img[alt] {border: 3px solid red;} /对所有带有 alt 属性的图像应用样式/[title] {font-weight: bold;} /包含标题(title)信息的所有元素变为粗体显示/根据具体属性值选择除了选择有某些属性的元素,还可以进一步缩小选择范围,只选择有特定属性值的元素/将指向 Web 服务器上某个特定超链接变成粗体/a[href=“https://blog.maplemark.cn”] {font-weight: bold;}<planet>Venus</planet><planet moons=“1”>Earth</planet><planet moons=“2”>Mars</planet>/将第二个元素文本变成粗体/planet[moons=“1”] { font-weight: bold;}根据部分属性值选择如果属性能接受词列表(词之间用空格分隔),可以根据其中的任意一个词进行选择img[title~=“Figure”] {border: 1px solid gray;}子串匹配属性选择器类型描述[foo^=“bar”]选择 foo 属性值以"bar"开头的所有元素[foo$=“bar”]选择 foo 属性值以"bar"结尾的所有元素[foo=“bar”]选择 foo 属性值中包含子串"bar"的所有元素特定属性选择类型<h1 lang=“en”>Hello!</h1><p lang=“en-us”>Greetings!</p><div lang=“en-au”>G’day!</div><p lang=“fr”>Bonjour!</p><h4 lang=“cy-en”>Jrooana!</h4>*[lang|=“en”] {color: white;}这种属性选择器最常见的用途是匹配语言值后代选择器通过文档树结构,可以很形象的理解什么是后代选择器(descendant selector),也称为包含选择器/上下文选择器。定义后代选择器就是来创建一些规则,它们仅在某些结构中起作用,而在另外一些结构中不起作用<div class=“row”><p>文字一</p></div><div class=“row”><div><p>文字一后代</p></div></div><div class=“row”>文字二</div><p>文字三</p>.row p{color: red;}选择子元素在某些情况下,可能并不想选择一个任意的后代元素;而是希望缩小范围,只选择另一个元素的子元素.row > p{color: red;}选择相邻兄弟元素<ol> <li>List item 1</li> <li>List item 1</li> <li>List item 1</li> <li>List item 1</li></ol><ul> <li>A list item</li> <li>Another list item</li> <li>Yet Another list item</li> <li>Lat list item</li></ul>ol + ul {font-weight: bold;} /将命中 ul/ul 必须紧跟在 ol 后面伪类选择器锚类型称为伪类链接伪类CSS2.1定义了两个只应用于超链接的伪类伪类名描述:link指示作为超链接并指向一个未访问地址的所有锚:visited指示作为已访问超链接的所有锚a {color: black;}a:link {color: bule;}a:visited {color: red;}动态伪类CSS2.1定义了3个动态伪类,它们可以根据用户行为改变文档的外观伪类名描述:focus指示当前拥有输入焦点的元素:hover指示鼠标指针停留在哪个元素上:active指示被用户输入激活的元素伪类顺序:link-visited-focus-hover-active选择第一个子元素可以使用静态伪类:first-child 来选择元素的第一个子元素p:first-child {font-weight: bold;}伪元素选择器就像伪类为锚指定幻像类一样,伪元素能够在文档中插入假象的元素,从而得到某种效果。CSS2.1中定义了4个伪元素:设置首字母样式、设置第一行样式、设置之前和之后元素的样式设置首字母样式p:first-letter {color: red;}设置第一行样式p:first-line {color: purple;}:first-letter 和 :first-line 的限制在 CSS2 中,:first-letter 和:first-line 伪元素只能应用于标记或段落之类的块级元素,而不能应用于超链接等的行内元素设置之前和之后元素的样式p:before {color: black;}p:after {color: red;}本文转载自枫叶林博客,《CSS选择器的分类》 ...

April 3, 2019 · 1 min · jiezi

47道HTTP面试题总结(长期更新)

写在前面参考答案在看云平台发布,如果大家想阅读参考答案,可直接购买。一次购买,文档所有部分(vue、http、js等)均可以观看,无时间限制,文档长期更新!有什么意见与建议欢迎您及时联系作者或留言回复!阅读文档 98道vue面试题加入前端开发交流微信群,获取更多资源:本文档基于前端所需要掌握的网络协议知识,总结了:网络基础Http协议HTTP状态码与HTTP协作的 Web 服务器HTTP首部HTTPSSPDY协议websock协议HTTP 2.0等9个关于计算机网络方面的前端面试题。必须掌握问题:输入 URL 到页面渲染的整个流程参考答案: https://www.kancloud.cn/hanxu…网络基础1、什么是TCP/IP协议族2、TCP/IP协议族按层次划分了哪几次?3、TCP/IP协议族各层的作用是什么?4、请画出并说明HTTP请求时,TCP/IP通信传输流5、网络层:IP协议6、TCP协议位于哪一层?7、什么是字节流服务?8、TCP头部有哪些重要字段?9、TCP协议的三次握手10、为什么 TCP 建立连接需要三次握手,明明两次就可以建立起连接?11、UDP 与 TCP 的区别是什么?12、什么是ARQ协议?13、什么是滑动窗口?14、拥塞怎么处理?15、什么是DNS服务?16、什么是RFC?参考答案: https://www.kancloud.cn/hanxu...Http协议1、HTTP请求报文由哪几部分组成?2、HTTP协议的状态3、HTTP方法有哪些?4、GET和POST方法有什么区别的实操回答。5、什么是TCP的持久化连接?6、HTTP是如何利用Cookie进行状态管理的?7、在ajax请求中如何使用Cookie?8、在ajax请求中使用Cookie时,如何解决跨越问题?9、 HTTP报文有哪几种,HTTP报文包含哪写部分?10、常见的状态码有哪些?参考答案: https://www.kancloud.cn/hanxu…与HTTP协作的 Web 服务器1、代理2、网关3、隧道参考答案: https://www.kancloud.cn/hanxu...HTTP首部1、什么是HTTP首部字段2、HTTP首部字段有哪几种类型?3、通用首部字段有哪些?4、请求首部字段有哪些?5、响应首部字段有哪些?6、实体首部字段有哪些?7、非HTTP/1.1 首部字段8、为Cookie服务的首部字段有哪些?9、其他首部字段参考答案: https://www.kancloud.cn/hanxu...HTTPS1、HTTP的缺点有哪些?2、什么是HTTPS3、HTTPS是如何进行加密的?4、HTTPS的通信步骤是什么?5、相比HTTP,HTTPS有哪些缺点?参考答案: https://www.kancloud.cn/hanxu…基于HTTP的功能追加协议1、什么是SPDY协议2、什么是webSocket协议?3、webSocket有什么特点?4、HTTP/2.0 协议的新特性参考答案: https://www.kancloud.cn/hanxu…

April 2, 2019 · 1 min · jiezi

一篇带你读懂TCP之“滑动窗口”协议

前言你现在的努力,是为了以后有更多的选择。在上一篇文章通过“表白”方式,让我们快速了解网络七层协议 了解了网络七层协议。接下来我们要把重心放在网络传输的可靠性上面。一起来看TCP协议,它是如何解决网络传输不可靠的问题。这其中有个很关键的部分,就是我们的滑动窗口协议。从工程学角度上,我们来看一看滑动窗口协议,它到底解决了一个怎样的问题?滑动窗口协议:TCP协议的使用维持发送方/接收方缓冲区缓冲区是 用来解决网络之间数据不可靠的问题,例如丢包,重复包,出错,乱序在TCP协议中,发送方和接受方通过各自维护自己的缓冲区。通过商定包的重传机制等一系列操作,来解决不可靠的问题。问题一:如何保证次序?提出问题:在我们滑动窗口协议之前,我们如何来保证发送方与接收方之间,每个包都能被收到。并且是按次序的呢?发送方发送一个包1,这时候接收方确认包1。发送包2,确认包2。就这样一直下去,知道把数据完全发送完毕,这样就结束了。那么就解决了丢包,出错,乱序等一些情况!同时也存在一些问题。问题:吞吐量非常的低。我们发完包1,一定要等确认包1.我们才能发送第二个包。问题二:如何提高吞吐量?提出问题:那么我们就不能先连发几个包等他一起确认吗?这样的话,我们的速度会不会更快,吞吐量更高些呢?如图,这个就是我们把两个包一起发送,然后一起确认。可以看出我们改进的方案比之前的好很多,所花的时间只是一个来回的时间。接下来,我们还有一个问题:改善了吞吐量的问题问题三:如何实现最优解?问题:我们每次需要发多少个包过去呢?发送多少包是最优解呢?我们能不能把第一个和第二个包发过去后,收到第一个确认包就把第三个包发过去呢?而不是去等到第二个包的确认包才去发第三个包。这样就很自然的产生了我们"滑动窗口"的实现。在图中,我们可看出灰色1号2号3号包已经发送完毕,并且已经收到Ack。这些包就已经是过去式。4、5、6、7号包是黄色的,表示已经发送了。但是并没有收到对方的Ack,所以也不知道接收方有没有收到。8、9、10号包是绿色的。是我们还没有发送的。这些绿色也就是我们接下来马上要发送的包。 可以看出我们的窗口正好是11格。后面的11-16还没有被读进内存。要等4号-10号包有接下来的动作后,我们的包才会继续往下发送。正常情况可以看到4号包对方已经被接收到,所以被涂成了灰色。“窗口”就往右移一格,这里只要保证“窗口”是7格的。 我们就把11号包读进了我们的缓存。进入了“待发送”的状态。8、9号包已经变成了黄色,表示已经发送出去了。接下来的操作就是一样的了,确认包后,窗口往后移继续将未发送的包读进缓存,把“待发送“状态的包变为”已发送“。丢包情况有可能我们包发过去,对方的Ack丢了。也有可能我们的包并没有发送过去。从发送方角度看就是我们没有收到Ack。发生的情况:一直在等Ack。如果一直等不到的话,我们也会把读进缓存的待发送的包也一起发过去。但是,这个时候我们的窗口已经发满了。所以并不能把12号包读进来,而是始终在等待5号包的Ack。如果我们这个Ack始终不来怎么办呢?超时重发这时候我们有个解决方法:超时重传这里有一点要说明:这个Ack是要按顺序的。必须要等到5的Ack收到,才会把6-11的Ack发送过去。这样就保证了滑动窗口的一个顺序。这时候可以看出5号包已经接受到Ack,后面的6、7、8号包也已经发送过去已Ack。窗口便继续向后移动。文末从我们为了增加网络的吞吐量,想讲数据包一起发送过去,这时候便产生了“滑动窗口”这种协议。有了“滑动窗口”这个概念,我们又解决了其中出现的一些问题。例如丢包,我们又通过重发的机制去解决了。以上来自ccmouse老师教学视频,作为学习记录整理。如果文章对你有用的话,欢迎关注公众号:Coder编程获取最新原创技术文章和相关免费学习资料,随时随地学习技术知识!

March 30, 2019 · 1 min · jiezi

面试分享:最全Spring事务面试考点整理

Spring和事务的关系关系型数据库、某些消息队列等产品或中间件称为事务性资源,因为它们本身支持事务,也能够处理事务。 Spring很显然不是事务性资源,但是它可以管理事务性资源,所以Spring和事务之间是管理关系。 就像Jack Ma虽然不会写代码,但是他却管理者一大批会写代码的码农。Spring事务三要素数据源:表示具体的事务性资源,是事务的真正处理者,如MySQL等。事务管理器:像一个大管家,从整体上管理事务的处理过程,如打开、提交、回滚等。事务应用和属性配置:像一个标识符,表明哪些方法要参与事务,如何参与事务,以及一些相关属性如隔离级别、超时时间等。Spring事务的注解配置把一个DataSource(如DruidDataSource)作为一个@Bean注册到Spring容器中,配置好事务性资源。把一个@EnableTransactionManagement注解放到一个@Configuration类上,配置好事务管理器,并启用事务管理。把一个@Transactional注解放到类上或方法上,可以设置注解的属性,表明该方法按配置好的属性参与到事务中。事务注解的本质@Transactional这个注解仅仅是一些(和事务相关的)元数据,在运行时被事务基础设施读取消费,并使用这些元数据来配置bean的事务行为。大致来说具有两方面功能,一是表明该方法要参与事务,二是配置相关属性来定制事务的参与方式和运行行为。Spring声明式事务实现原理声明式事务成为可能,主要得益于Spring AOP。使用一个事务拦截器,在方法调用的前后/周围进行事务性增强(advice),来驱动事务完成。如何回滚一个事务就是在一个事务上下文中当前正在执行的代码里抛出一个异常,事务基础设施代码会捕获任何未处理的异常,并且做出决定是否标记这个事务为回滚。默认回滚规则默认只把runtime, unchecked exceptions标记为回滚,即RuntimeException及其子类,Error默认也导致回滚。Checked exceptions默认不导致回滚。这些规则和EJB是一样的。如何配置回滚异常使用@Transactional注解的rollbackFor/rollbackForClassName属性,可以精确配置导致回滚的异常类型,包括checked exceptions。noRollbackFor/noRollbackForClassName属性,可以配置不导致回滚的异常类型,当遇到这样的未处理异常时,照样提交相关事务。事务注解在类/方法上@Transactional注解既可以标注在类上,也可以标注在方法上。当在类上时,默认应用到类里的所有方法。如果此时方法上也标注了,则方法上的优先级高。事务注解在类上的继承性@Transactional注解的作用可以传播到子类,即如果父类标了子类就不用标了。但倒过来就不行了。子类标了,并不会传到父类,所以父类方法不会有事务。父类方法需要在子类中重新声明而参与到子类上的注解,这样才会有事务。事务注解在接口/类上@Transactional注解可以用在接口上,也可以在类上。在接口上时,必须使用基于接口的代理才行,即JDK动态代理。事实是Java的注解不能从接口继承,如果你使用基于类的代理,即CGLIB,或基于织入方面,即AspectJ,事务设置不会被代理和织入基础设施认出来,目标对象不会被包装到一个事务代理中。Spring团队建议注解标注在类上而非接口上。只在public方法上生效?当采用代理来实现事务时,(注意是代理),@Transactional注解只能应用在public方法上。当标记在protected、private、package-visible方法上时,不会产生错误,但也不会表现出为它指定的事务配置。可以认为它作为一个普通的方法参与到一个public方法的事务中。如果想在非public方法上生效,考虑使用AspectJ(织入方式)。目标类里的自我调用没有事务?在代理模式中(这是默认的),只有从外部的方法调用进入通过代理会被拦截,这意味着自我调用(实际就是,目标对象中的一个方法调用目标对象的另一个方法)在运行时不会导致一个实际的事务,即使被调用的方法标有注解。如果你希望自我调用也使用事务来包装,考虑使用AspectJ的方式。在这种情况下,首先是没有代理。相反,目标类被织入(即它的字节码被修改)来把@Transactional加入到运行时行为,在任何种类的方法上都可以。事务与线程和JavaEE事务上下文一样,Spring事务和一个线程的执行相关联,底层是一个ThreadLocal<Map<Object, Object>>,就是每个线程一个map,key是DataSource,value是Connection。逻辑事务与物理事务事务性资源实际打开的事务就是物理事务,如数据库的Connection打开的事务。Spring会为每个@Transactional方法创建一个事务范围,可以理解为是逻辑事务。在逻辑事务中,大范围的事务称为外围事务,小范围的事务称为内部事务,外围事务可以包含内部事务,但在逻辑上是互相独立的。每一个这样的逻辑事务范围,都能够单独地决定rollback-only状态。那么如何处理逻辑事务和物理事务之间的关联关系呢,这就是传播特性解决的问题。事务的传播特性REQUIRED,SUPPORTS,MANDATORY,REQUIRES_NEW,NOT_SUPPORTED,NEVER,NESTEDREQUIRED强制要求要有一个物理事务。如果没有已经存在的事务,就专门打开一个事务用于当前范围。或者参与到一个已存在的更大范围的外围事务中。在相同的线程中,这是一种很好的默认方式安排。(例如,一个service外观/门面代理到若干个仓储方法,所有底层资源必须参与到service级别的事务里)在标准的REQUIRED行为情况下,所有这样的逻辑事务范围映射到同一个物理事务。因此,在内部事务范围设置了rollback-only标记,确实会影响外围事务进行实际提交的机会。注:默认,一个参与到外围事务的事务,会使用外围事务的特性,安静地忽略掉自己的隔离级别,超时值,只读标识等设置。当然可以在事务管理器上设置validateExistingTransactions标识为true,这样当你自己的事务和参与到的外围事务设置不一样时会被拒绝。REQUIRES_NEW与REQUIRED相比,总是使用一个独立的物理事务用于每一个受影响的逻辑事务范围,从来不参与到一个已存在的外围事务范围。这样安排的话,底层的事务资源是不同的,因此,可以独立地提交或回滚。外围事务不会被内部事务的回滚状态影响。这样一个独立的内部事务可以声明自己的隔离级别,超时时间和只读设置,并不继承外围事务的特性。NESTED使用同一个物理事务,带有多个保存点,可以回滚到这些保存点,可以认为是部分回滚,这样一个内部事务范围触发了一个回滚,外围事务能够继续这个物理事务,尽管有一些操作已经被回滚。典型地,它对应于JDBC的保存点,所以只对JDBC事务资源起作用。SUPPORTS支持当前事务。如果当前有事务,就参与进来,如果没有,就以非事务的方式运行。这样的一个逻辑事务范围,它背后可能没有实际的物理事务,此时的事务也成为空事务。NOT_SUPPORTED不支持当前事务。总是以非事务方式运行。当前的事务会被挂起,并在适合的时候恢复。MANDATORY支持当前事务。如果当前没有事务存在,就抛出异常。NEVER不支持当前事务。如果当前有事务存在,就抛出异常。事务的隔离级别DEFAULT,READ_UNCOMMITTED,READ_COMMITTED,REPEATABLE_READ,SERIALIZABLE脏读一个事务修改了一行数据但没有提交,第二个事务可以读取到这行被修改的数据,如果第一个事务回滚,第二个事务获取到的数据将是无效的。不可重复读一个事务读取了一行数据,第二个事务修改了这行数据,第一个事务重新读取这行数据,将获得到不同的值。幻读一个事务按照一个where条件读取所有符合的数据行,第二个事务插入了一行数据且恰好也满足这个where条件,第一个事务再以这个where条件重新读取,将会获取额外多出来的这一行。帮助记忆:写读是脏读,读写读是不可重复读,where insert where是幻读。DEFAULT使用底层数据存储的默认隔离级别。MySQL的默认隔离级别是REPEATABLE-READ。READ_UNCOMMITTED读未提交。脏读、不可重复读、幻读都会发生。READ_COMMITTED读已提交。脏读不会发生,不可重复读、幻读都会发生。REPEATABLE_READ可重复读。脏读、不可重复读都不会发生,幻读会发生。SERIALIZABLE可串行化。脏读、不可重复读、幻读都不会发生。spring事务考点我就总结在这里了,如果有遗漏或者改进还请各位大佬留言指点同时spring事务这个知识点也为大家总结我的部分学习笔记和与之相匹配的架构进阶视频资料:spring事务部分笔记资料获取方式:请加JAVA架构技术交流群:171662117注:加群要求1、具有1-5工作经验的,面对目前流行的技术不知从何下手,需要突破技术瓶颈的可以加。2、在公司待久了,过得很安逸,但跳槽时面试碰壁。需要在短时间内进修、跳槽拿高薪的可以加。3、如果没有工作经验,但基础非常扎实,对java工作机制,常用设计思想,常用java开发框架掌握熟练的,可以加。4、觉得自己很牛B,一般需求都能搞定。但是所学的知识点没有系统化,很难在技术领域继续突破的可以加。5.阿里Java高级大牛直播讲解知识点,分享知识,多年工作经验的梳理和总结,带着大家全面、科学地建立自己的技术体系和技术认知!

March 29, 2019 · 1 min · jiezi

前端面试题 -- webpack

前言上一篇 前端面试题-小程序随着前端的不断发展,现代前端开发的复杂度和规模越来越庞大。工程化的思想催生了很多流行框架的进程,作为现在最流行的前端构建工具–webpack,很多面试、工作场景中都会出现了它的身影。所以对于现在的前端来说,了解并能够使用webpack,无论对个人技能或者职场求职来说,都是一种有力的提升感兴趣的小伙伴可以点击 这里,查看完整版前端面试题如果文章中有出现纰漏、错误之处,还请看到的小伙伴留言指正,先行谢过以下 ↓1. 对webpack的了解官方文档本质上,webpack 是一个现代 JavaScript 应用程序的静态模块打包器(module bundler),将项目当作一个整体,通过一个给定的的主文件,webpack将从这个文件开始找到你的项目的所有依赖文件,使用loaders处理它们,最后打包成一个或多个浏览器可识别的js文件核心概念:入口(entry)入口起点(entry point)指示 webpack 应该使用哪个模块,来作为构建其内部依赖图的开始可以通过在 webpack 配置中配置 entry 属性,来指定一个入口起点(或多个入口起点)module.exports = { entry: ‘./path/to/my/entry/file.js’};输出(output)output 属性告诉 webpack 在哪里输出它所创建的 bundles ,以及如何命名这些文件,默认值为 ./distloaderloader 让 webpack 能够去处理那些非 JavaScript 文件(webpack 自身只理解 JavaScript)插件(plugins)loader 被用于转换某些类型的模块,而插件则可以用于执行范围更广的任务。插件的范围包括,从打包优化和压缩,一直到重新定义环境中的变量模式通过选择 development 或 production 之中的一个,来设置 mode 参数,你可以启用相应模式下的 webpack 内置的优化module.exports = { mode: ‘production’};2. webpack,里面的webpack.config.js怎么配置let webpack = require(‘webpack’);module.exports = { entry:’./entry.js’, //入口文件 output:{ //node.js中__dirname变量获取当前模块文件所在目录的完整绝对路径 path:__dirname, //输出位置 filename:‘build.js’ //输入文件 }, module:{ // 关于模块的加载相关,我们就定义在module.loaders中 // 这里通过正则表达式去匹配不同后缀的文件名,然后给它们定义不同的加载器。 // 比如说给less文件定义串联的三个加载器(!用来定义级联关系): rules:[ { test:/.css$/, //支持正则 loader:‘style-loader!css-loader’ } ] }, //配置服务 devServer:{ hot:true, //启用热模块替换 inline:true //此模式支持热模块替换:热模块替换的好处是只替换更新的部分,而不是页面重载. }, //其他解决方案配置 resolve:{ extensions:[’’,’.js’,’.json’,’.css’,’.scss’] }, //插件 plugins:[ new webpack.BannerPlugin(‘This file is create by baibai’) ]}3. webpack本地开发怎么解决跨域的下载 webpack-dev-server 插件配置 webpack.config.js 文件// webpack.config.jsvar WebpackDevServer = require(“webpack-dev-server”);module.exports = { … devServer: { … port: ‘8088’, //设置端口号 // 代理设置 proxy: { ‘/api’: { target: ‘http://localhost:80/index.php’, // 目标代理 pathRewrite: {’^/api’ : ‘’}, // 重写路径 secure: false, // 是否接受运行在 HTTPS 上 } } }}4. 如何配置多入口文件配置多个入口文件entry: { home: resolve(__dirname, “src/home/index.js”), about: resolve(__dirname, “src/about/index.js”)}5. webpack与grunt、gulp的不同三者都是前端构建工具grunt 和 gulp 是基于任务和流的。找到一个(或一类)文件,对其做一系列链式操作,更新流上的数据, 整条链式操作构成了一个任务,多个任务就构成了整个web的构建流程webpack 是基于入口的。webpack 会自动地递归解析入口所需要加载的所有资源文件,然后用不同的 Loader 来处理不同的文件,用 Plugin 来扩展 webpack 功能webpack 与前者最大的不同就是支持代码分割,模块化(AMD,CommonJ,ES2015),全局分析为什么选择webpack6. 有哪些常见的Loader?他们是解决什么问题的css-loader:加载 CSS,支持模块化、压缩、文件导入等特性style-loader:把 CSS 代码注入到 JavaScript 中,通过 DOM 操作去加载 CSSslint-loader:通过 SLint 检查 JavaScript 代码babel-loader:把 ES6 转换成 ES5file-loader:把文件输出到一个文件夹中,在代码中通过相对 URL 去引用输出的文件url-loader:和 file-loader 类似,但是能在文件很小的情况下以 base64 的方式把文件内容注入到代码中去7. 有哪些常见的Plugin?他们是解决什么问题的define-plugin:定义环境变量commons-chunk-plugin:提取公共代码8. Loader和Plugin的不同loader 加载器Webpack 将一切文件视为模块,但是 webpack 原生是只能解析 js 文件. Loader 的作用是让 webpack 拥有了加载和解析非 JavaScript 文件的能力在 module.rules 中配置,也就是说他作为模块的解析规则而存在,类型为数组Plugin 插件扩展 webpack 的功能,让 webpack 具有更多的灵活性在 plugins 中单独配置。类型为数组,每一项是一个 plugin 的实例,参数都通过构造函数传入9. webpack的构建流程是什么初始化参数:从配置文件和 Shell 语句中读取与合并参数,得出最终的参数开始编译:用上一步得到的参数初始化 Compiler 对象,加载所有配置的插件,执行对象的 run 方法开始执行编译确定入口:根据配置中的 entry 找出所有的入口文件编译模块:从入口文件出发,调用所有配置的 Loader 对模块进行翻译,再找出该模块依赖的模块,再递归本步骤直到所有入口依赖的文件都经过了本步骤的处理完成模块编译:在经过第4步使用 Loader 翻译完所有模块后,得到了每个模块被翻译后的最终内容以及它们之间的依赖关系输出资源:根据入口和模块之间的依赖关系,组装成一个个包含多个模块的 Chunk,再把每个 Chunk 转换成一个单独的文件加入到输出列表,这步是可以修改输出内容的最后机会输出完成:在确定好输出内容后,根据配置确定输出的路径和文件名,把文件内容写入到文件系统在以上过程中,Webpack 会在特定的时间点广播出特定的事件,插件在监听到感兴趣的事件后会执行特定的逻辑,并且插件可以调用 Webpack 提供的 API 改变 Webpack 的运行结果10. 是否写过Loader和Plugin?描述一下编写loader或plugin的思路编写 Loader 时要遵循单一原则,每个 Loader 只做一种"转义"工作。 每个 Loader 的拿到的是源文件内容(source),可以通过返回值的方式将处理后的内容输出,也可以调用 this.callback() 方法,将内容返回给 webpack 。 还可以通过 this.async() 生成一个 callback 函数,再用这个 callback` 将处理后的内容输出出去相对于 Loader 而言,Plugin 的编写就灵活了许多。 webpack 在运行的生命周期中会广播出许多事件,Plugin 可以监听这些事件,在合适的时机通过 Webpack 提供的 API 改变输出结果11. webpack的热更新是如何做到的?说明其原理具体可以参考 这里12. 如何利用webpack来优化前端性能压缩代码。删除多余的代码、注释、简化代码的写法等等方式利用 CDN 加速。在构建过程中,将引用的静态资源路径修改为 CDN 上对应的路径删除死代码 Tree Shaking)。将代码中永远不会走到的片段删除掉优化图片,对于小图可以使用 base64 的方式写入文件中按照路由拆分代码,实现按需加载,提取公共代码给打包出来的文件名添加哈希,实现浏览器缓存文件13. 如何提高webpack的构建速度参考 这里14. 怎么配置单页应用?怎么配置多页应用单页应用可以理解为 webpack 的标准模式,直接在 entry 中指定单页应用的入口即可多页应用的话,可以使用 webpack 的 AutoWebPlugin 来完成简单自动化的构建,但是前提是项目的目录结构必须遵守他预设的规范15. 什么是bundle,什么是chunk,什么是modulebundle 是由 webpack 打包出来的文件,chunk 是指 webpack 在进行模块的依赖分析的时候,代码分割出来的代码块。module是开发中的单个模块后记前端发展日新月异,只有保持不断学习的姿态,才能走的更远希望这些面试题可以帮助大家温故知新、查缺补漏,不断充实自己的专业技能,走的更远最后推荐一波 GitHub前端面试题完整版,欢迎小伙伴们 star 关注 不定期更新,期待同行以上 ...

March 29, 2019 · 2 min · jiezi

前端面试题 -- 小程序

前言首先说一些为什么总结小程序相关的面试题吧我们可以随便打开一个招聘网站,在那里你会发现市场对于小程序的需求还是蛮高的,有些公司可能就只需要写小程序的前端人员虽然小程序的开发很大一部分都是很简单的,但是有些常用的东西还是有必要了解一下的。故此,有了这样一篇小程序面试题的总结。当然感兴趣或者有需要的小伙伴也可以 点击这里,查看完整版前端面试题如果文章中有出现纰漏、错误之处,还请看到的小伙伴留言指正,先行谢过以下 ↓1. 简单描述下微信小程序的相关文件类型微信小程序项目结构主要有四个文件类型WXML(WeiXin Markup Language)是框架设计的一套标签语言,结合基础组件、事件系统,可以构建出页面的结构。内部主要是微信自己定义的一套组件WXSS (WeiXin Style Sheets)是一套样式语言,用于描述 WXML 的组件样式js 逻辑处理,网络请求json 小程序设置,如页面注册,页面标题及tabBar主要文件app.json 必须要有这个文件,如果没有这个文件,项目无法运行,因为微信框架把这个作为配置文件入口,整个小程序的全局配置。包括页面注册,网络设置,以及小程序的 window 背景色,配置导航条样式,配置默认标题app.js 必须要有这个文件,没有也是会报错!但是这个文件创建一下就行 什么都不需要写以后我们可以在这个文件中监听并处理小程序的生命周期函数、声明全局变量app.wxss 可选2. 简述微信小程序原理微信小程序采用 JavaScript、WXML、WXSS 三种技术进行开发,本质就是一个单页面应用,所有的页面渲染和事件处理,都在一个页面内进行,但又可以通过微信客户端调用原生的各种接口微信的架构,是数据驱动的架构模式,它的 UI 和数据是分离的,所有的页面更新,都需要通过对数据的更改来实现小程序分为两个部分 webview 和 appService 。其中 webview 主要用来展现 UI ,appService 有来处理业务逻辑、数据及接口调用。它们在两个进程中运行,通过系统层 JSBridge 实现通信,实现 UI 的渲染、事件的处理3. 小程序的双向绑定和vue哪里不一样小程序直接 this.data 的属性是不可以同步到视图的,必须调用:this.setData({ // 这里设置})4. 小程序的wxss和css有哪些不一样的地方WXSS 和 CSS 类似,不过在 CSS 的基础上做了一些补充和修改尺寸单位 rpxrpx 是响应式像素,可以根据屏幕宽度进行自适应。规定屏幕宽为 750rpx。如在 iPhone6 上,屏幕宽度为 375px,共有 750 个物理像素,则 750rpx = 375px = 750 物理像素使用 @import 标识符来导入外联样式。@import 后跟需要导入的外联样式表的相对路径,用;表示语句结束/** index.wxss **/@import ‘./base.wxss’;.container{ color: red;}5. 小程序页面间有哪些传递数据的方法使用全局变量实现数据传递在 app.js 文件中定义全局变量 globalData, 将需要存储的信息存放在里面// app.jsApp({ // 全局变量 globalData: { userInfo: null }})使用的时候,直接使用 getApp() 拿到存储的信息使用 wx.navigateTo 与 wx.redirectTo 的时候,可以将部分数据放在 url 里面,并在新页面 onLoad 的时候初始化//pageA.js// Navigatewx.navigateTo({ url: ‘../pageD/pageD?name=raymond&gender=male’,})// Redirectwx.redirectTo({ url: ‘../pageD/pageD?name=raymond&gender=male’,})// pageB.js…Page({ onLoad: function(option){ console.log(option.name + ‘is’ + option.gender) this.setData({ option: option }) }})需要注意的问题:wx.navigateTo 和 wx.redirectTo 不允许跳转到 tab 所包含的页面onLoad 只执行一次使用本地缓存 Storage 相关6. 小程序的生命周期函数onLoad 页面加载时触发。一个页面只会调用一次,可以在 onLoad 的参数中获取打开当前页面路径中的参数onShow() 页面显示/切入前台时触发onReady() 页面初次渲染完成时触发。一个页面只会调用一次,代表页面已经准备妥当,可以和视图层进行交互onHide() 页面隐藏/切入后台时触发。 如 navigateTo 或底部 tab 切换到其他页面,小程序切入后台等onUnload() 页面卸载时触发。如 redirectTo 或 navigateBack 到其他页面时详见 生命周期回调函数7. 怎么封装微信小程序的数据请求参考 这里8. 哪些方法可以用来提高微信小程序的应用速度1、提高页面加载速度2、用户行为预测3、减少默认 data 的大小4、组件化方案9. 微信小程序的优劣势优势即用即走,不用安装,省流量,省安装时间,不占用桌面依托微信流量,天生推广传播优势开发成本比 App 低缺点用户留存,即用即走是优势,也存在一些问题入口相对传统 App 要深很多限制较多,页面大小不能超过1M。不能打开超过5个层级的页面10. 怎么解决小程序的异步请求问题小程序支持 ES6 语法在返回成功的回调里面处理逻辑Promise 异步async/await11. 小程序关联微信公众号如何确定用户的唯一性如果开发者拥有多个移动应用、网站应用、和公众帐号(包括小程序),可通过 unionid 来区分用户的唯一性,因为只要是同一个微信开放平台帐号下的移动应用、网站应用和公众帐号(包括小程序),用户的 unionid 是唯一的。换句话说,同一用户,对同一个微信开放平台下的不同应用,unionid 是相同的12. 如何实现下拉刷新首先在全局 config 中的 window 配置 enablePullDownRefresh在 Page 中定义 onPullDownRefresh 钩子函数,到达下拉刷新条件后,该钩子函数执行,发起请求方法请求返回后,调用 wx.stopPullDownRefresh 停止下拉刷新参考 这里13. bindtap和catchtap的区别是什么相同点:首先他们都是作为点击事件函数,就是点击时触发。在这个作用上他们是一样的,可以不做区分不同点:他们的不同点主要是bindtap是不会阻止冒泡事件的,catchtap是阻值冒泡的14. 简述下 wx.navigateTo(), wx.redirectTo(), wx.switchTab(), wx.navigateBack(), wx.reLaunch()的区别</h5>wx.navigateTo():保留当前页面,跳转到应用内的某个页面。但是不能跳到 tabbar 页面wx.redirectTo():关闭当前页面,跳转到应用内的某个页面。但是不允许跳转到 tabbar 页面wx.switchTab():跳转到 abBar 页面,并关闭其他所有非 tabBar 页面wx.navigateBack()关闭当前页面,返回上一页面或多级页面。可通过 getCurrentPages() 获取当前的页面栈,决定需要返回几层wx.reLaunch():关闭所有页面,打开到应用内的某个页面后记很多东西真正做一遍,收获一定是最大的,那些途中踩过的坑、迸发的灵性火花都是我们成长路上宝贵的东西后面还是会不定期更新一些面试题或者有意思的东西,分享给有需要的小伙伴 最后,推荐一波 GitHub完整版面试题 欢迎小伙伴们 star 关注以上 ...

March 28, 2019 · 1 min · jiezi

JS中的逻辑运算符&&、||,位运算符|,&

1、JS中的||符号:运算方法: 只要“||”前面为false,不管“||”后面是true还是false,都返回“||”后面的值。 只要“||”前面为true,不管“||”后面是true还是false,都返回“||”前面的值。总结:真前假后2、JS中的&&符号:运算方法: 只要“&&”前面是false,无论“&&”后面是true还是false,结果都将返“&&”前面的值; 只要“&&”前面是true,无论“&&”后面是true还是false,结果都将返“&&”后面的值;总结:假前真后弄懂了以上说的还应该知道: js的6个蛋蛋:在js逻辑运算中,0、”“、null、false、undefined、NaN都会判为false,其他都为true。举个栗子:3、位运算符:|运算方法: 两个位只要有一个为1,那么结果都为1。否则就为0继续举栗子|运算符还能进行取整运算4、位运算符:&运算方法: 两个数值的个位分别相与,同时为1才得1,只要一个为0就为0。还是举栗子:

March 27, 2019 · 1 min · jiezi

代码面试需要知道的8种数据结构(附面试题及答案链接)

译者按: 搞定面试,不要急着刷题,先弄懂什么是数据结构!原文:The top data structures you should know for your next coding interview译者:Fundebug为了保证可读性,本文采用意译而非直译。另外,本文版权归原作者所有,翻译仅用于学习。1976 年,一个瑞士计算机科学家写一本书《Algorithms + Data Structures = Programs》。即:算法 + 数据结构 = 程序。40 多年过去了,这个等式依然成立。很多代码面试题都要求候选者深入理解数据结构,不管你来自大学计算机专业还是编程培训机构,也不管你有多少年编程经验。有时面试题会直接提到数据结构,比如“给我实现一个二叉树”,然而有时则不那么明显,比如“统计一下每个作者写的书的数量”。什么是数据结构?数据结构是计算机存储、组织数据的方式。对于特定的数据结构(比如数组),有些操作效率很高(读某个数组元素),有些操作的效率很低(删除某个数组元素)。程序员的目标是为当前的问题选择最优的数据结构。为什么我们需要数据结构?数据是程序的核心要素,因此数据结构的价值不言而喻。无论你在写什么程序,你都需要与数据打交道,比如员工工资、股票价格、杂货清单或者电话本。在不同场景下,数据需要以特定的方式存储,我们有不同的数据结构可以满足我们的需求。8 种常用数据结构数组栈队列链表图树前缀树哈希表1. 数组数组(Array)大概是最简单,也是最常用的数据结构了。其他数据结构,比如栈和队列都是由数组衍生出来的。下图展示了 1 个数组,它有 4 个元素:每一个数组元素的位置由数字编号,称为下标或者索引(index)。大多数编程语言的数组第一个元素的下标是 0。根据维度区分,有 2 种不同的数组:一维数组(如上图所示)多维数组(数组的元素为数组)数组的基本操作Insert - 在某个索引处插入元素Get - 读取某个索引处的元素Delete - 删除某个索引处的元素Size - 获取数组的长度常见数组代码面试题查找数组中第二小的元素查找第一个没有重复的数组元素合并 2 个排序好的数组重新排列数组中的正数和负数2. 栈撤回,即 Ctrl+Z,是我们最常见的操作之一,大多数应用都会支持这个功能。你知道它是怎么实现的吗?答案是这样的:把之前的应用状态(限制个数)保存到内存中,最近的状态放到第一个。这时,我们需要栈(stack)来实现这个功能。栈中的元素采用 LIFO (Last In First Out),即后进先出。下图的栈有 3 个元素,3 在最上面,因此它会被第一个移除:栈的基本操作Push — 在栈的最上方插入元素Pop — 返回栈最上方的元素,并将其删除isEmpty — 查询栈是否为空Top — 返回栈最上方的元素,并不删除常见的栈代码面试题使用栈计算后缀表达式使用栈为栈中的元素排序检查字符串中的括号是否匹配正确3. 队列队列(Queue)与栈类似,都是采用线性结构存储数据。它们的区别在于,栈采用 LIFO 方式,而队列采用先进先出,即FIFO(First in First Out)。下图展示了一个队列,1 是最上面的元素,它会被第一个移除:队列的基本操作Enqueue — 在队列末尾插入元素Dequeue — 将队列第一个元素删除isEmpty — 查询队列是否为空Top — 返回队列的第一个元素常见的队列代码面试题使用队列实现栈倒转队列的前 K 个元素使用队列将 1 到 n 转换为二进制4. 链表链表(Linked List)也是线性结构,它与数组看起来非常像,但是它们的内存分配方式、内部结构和插入删除操作方式都不一样。链表是一系列节点组成的链,每一个节点保存了数据以及指向下一个节点的指针。链表头指针指向第一个节点,如果链表为空,则头指针为空或者为 null。链表可以用来实现文件系统、哈希表和邻接表。下图展示了一个链表,它有 3 个节点:链表分为 2 种:单向链表双向链表链表的基本操作InsertAtEnd — 在链表结尾插入元素InsertAtHead — 在链表开头插入元素Delete — 删除链表的指定元素DeleteAtHead — 删除链表第一个元素Search — 在链表中查询指定元素isEmpty — 查询链表是否为空常见的队列代码面试题倒转 1 个链表检查链表中是否存在循环返回链表倒数第 N 个元素移除链表中的重复元素5. 图图(graph)由多个节点(vertex)构成,节点之间阔以互相连接组成一个网络。(x, y)表示一条边(edge),它表示节点 x 与 y 相连。边可能会有权值(weight/cost)。图分为两种:无向图有向图在编程语言中,图有可能有以下两种形式表示:邻接矩阵(Adjacency Matrix)邻接表(Adjacency List)遍历图有两周算法广度优先搜索(Breadth First Search)深度优先搜索(Depth First Search)常见的图代码面试题实现广度优先搜索实现深度优先搜索检查图是否为树统计图中边的个数使用 Dijkstra 算法查找两个节点之间的最短距离6. 树树(Tree)是一个分层的数据结构,由节点和连接节点的边组成。树是一种特殊的图,它与图最大的区别是没有循环。树被广泛应用在人工智能和一些复杂算法中,用来提供高效的存储结构。下图是一个简单的树以及与树相关的术语:树有很多分类:N 叉树(N-ary Tree)平衡树(Balanced Tree)二叉树(Binary Tree)二叉查找树(Binary Search Tree)平衡二叉树(AVL Tree)红黑树(Red Black Tree)2-3 树(2–3 Tree)其中,二叉树和二叉查找树是最常用的树。常见的树代码面试题计算树的高度查找二叉平衡树中第 K 大的元素查找树中与根节点距离为 k 的节点查找二叉树中某个节点所有祖先节点7. 前缀树前缀树(Prefix Trees 或者 Trie)与树类似,用于处理字符串相关的问题时非常高效。它可以实现快速检索,常用于字典中的单词查询,搜索引擎的自动补全甚至 IP 路由。下图展示了“top”, “thus”和“their”三个单词在前缀树中如何存储的:单词是按照字母从上往下存储,“p”, “s”和“r”节点分别表示“top”, “thus”和“their”的单词结尾。常见的树代码面试题统计前缀树表示的单词个数使用前缀树为字符串数组排序8. 哈希表哈希(Hash)将某个对象变换为唯一标识符,该标识符通常用一个短的随机字母和数字组成的字符串来代表。哈希可以用来实现各种数据结构,其中最常用的就是哈希表(hash table)。哈希表通常由数组实现。哈希表的性能取决于 3 个指标:哈希函数哈希表的大小哈希冲突处理方式下图展示了有数组实现的哈希表,数组的下标即为哈希值,由哈希函数计算,作为哈希表的键(key),而数组中保存的数据即为值(value):常见的哈希表代码面试题查找数组中对称的组合确认某个数组的元素是否为另一个数组元素的子集确认给定的数组是否互斥参考Fundebug 博客 - Node.js 面试题之 2017Fundebug 博客 - 快速掌握 JavaScript 面试基础知识(一)Fundebug 博客 - 快速掌握 JavaScript 面试基础知识(二)Fundebug 博客 - 快速掌握 JavaScript 面试基础知识(三)GeeksforGeeks关于FundebugFundebug专注于JavaScript、微信小程序、微信小游戏、支付宝小程序、React Native、Node.js和Java线上应用实时BUG监控。 自从2016年双十一正式上线,Fundebug累计处理了10亿+错误事件,付费客户有Google、360、金山软件、百姓网等众多品牌企业。欢迎大家免费试用!版权声明转载时请注明作者Fundebug以及本文地址:https://blog.fundebug.com/2018/08/27/code-interview-data-structure/ ...

March 27, 2019 · 1 min · jiezi

前端面试题 -- 综合(一)

前言这篇文章总结一些前端面试过程当中经常遇到的 HTTP、浏览器、SEO 等方面的问题,如果有需要了解其他面试问题的小伙伴, 请点击 这里,查看 HTML+CSS+JavaScript 等方面的问题。总结问题,分享给有需要的人(未完待续)如果文章中有出现纰漏、错误之处,还请看到的小伙伴留言指正,先行谢过以下 ↓HTTP相关1. HTTP有什么特点简单快速:客户向服务器请求服务时,只需传送请求方法和路径灵活:HTTP允许传输任意类型的数据对象。正在传输的类型由 Content-Type 加以标记无连接:无连接的含义是限制每次连接只处理一个请求。服务器处理完客户的请求,并收到客户的应答后,即断开连接 (深入-持久连接、管线化)无状态:HTTP协议是无状态协议( Cookie 的出现)2. http和https协议有什么区别http: 是互联网上应用最为广泛的一种网络协议,是一个客户端和服务器端请求和应答的标准(TCP),用于从WWW服务器传输超文本到本地浏览器的传输协议,它可以使浏览器更加高效,使网络传输减少https: 是以安全为目标的HTTP通道,简单讲是 HTTP 的安全版,即 HTTP 下加入 SSL 层,HTTPS 的安全基础是 SSL ,因此加密的详细内容就需要 SSLhttp 是超文本传输协议,信息是明文传输,https 则是具有安全性的 ssl 加密传输协议http 和 https 使用的是完全不同的连接方式,用的端口也不一样,前者是 80 ,后者是 443http 的连接很简单,是无状态的;HTTPS 协议是由 SSL+HTTP 协议构建的可进行加密传输、身份认证的网络协议,比 http 协议安全参考 http与https的区别3. http状态码有那些?分别代表是什么意思常用 http 状态码:200 OK 服务器成功处理了请求301/302 Moved Permanently(重定向)请求的URL已移走404 Not Found (页面丢失)未找到资源403 服务器拒绝请求408 (请求超时) 服务器等候请求时发生超时501 Internal Server Error 服务器遇到一个错误,使其无法对请求提供服务502 (错误网关) 服务器作为网关或代理,从上游服务器收到无效响应504 (网关超时) 服务器作为网关或代理,但是没有及时从上游服务器收到请求更多 参考 这里4. 什么是HTTP持久化和管线化出现背景: HTTP 最初的版本中,每进行一次 HTTP 通信,就要断开一次 TCP 连接(无连接)为解决上述问题,HTTP/1.1 增加了持久连接(HTTP Persistent Connections )的方法,其特点是,只要一方未明确提出断开连接,则另一方保持 TCP 连接状态管线化是指将多个 HTTP 请求整批发送,在发送过程中不用等待对方响应管线化是在持久连接的基础上实现的,管线化的实现,能够同时并行发送多个请求,而不需要一个接一个的等待响应5. Http报文HTTP 报文是面向文本的,报文中的每一个字段都是一些 ASCII 码串,各个字段的长度是不确定的。HTTP 有两类报文:请求报文和响应报文HTTP的这两种报文都由三部分组成:开始行、首部行、实体主体参考 这里6. 从输入URL到页面加载全过程参考 这里7. 为什么利用多个域名来存储网站资源会更有效CDN 缓存更方便突破浏览器并发限制节约 cookie 带宽节约主域名的连接数,优化页面响应速度防止不必要的安全问题浏览器相关1. 浏览器是由什么组成的从原理构成上分为七个模块,分别是 User Interface(用户界面)、 Browser engine(浏览器引擎) 、 Rendering engine(渲染引擎) 、 Networking(网络) 、 JavaScript Interpreter(js解释器) 、 UI Backend(UI后端) 、Date Persistence(数据持久化存储)其中,最重要的是渲染引擎(内核)和 JavaScript 解释器(JavaScript引擎)浏览器内核主要负责 HTML 、CSS 的解析,页面布局、渲染与复合层合成; JavaScript 引擎负责 JavaScript 代码的解释与执行2. 浏览器缓存机制浏览器的缓存机制也就是我们说的 HTTP 缓存机制,其机制是根据 HTTP 报文的缓存标识进行的参考 这里3. 浏览器渲染机制参考 这里4. 几个很实用的BOM属性对象方法location 对象:主要存储 url 相关信息history 对象:浏览历史信息相关history.go() // 前进或后退指定的页面数 history.go(num);history.back() // 后退一页history.forward() // 前进一页navigator 对象:浏览器信息相关navigator.userAgent //返回用户代理头的字符串表示(就是包括浏览器版本信息等的字符串)navigator.cookieEnabled // 返回浏览器是否支持(启用)cookie其他1. 谈谈你对SEO的理解SEO:搜索引擎优化,其目的是为了使网站能够更好的被搜索引擎抓取,提高在搜索引擎内的自然排名,从而带来更多的免费流量,获取收益SEO主要有两种方法,站内优化和站外优化前端SEO优化2. 前端怎么控制管理路由路由就是浏览器地址栏中的 url 与所见网页的对应关系前端路由的实现方式:基于 hash(ocation.hash+hashchange事件)展示层面也就是切换 # 后面的内容,呈现给用户不同的页面。现在越来越多的单页面应用,基本都是基于 hash 实现特性:url 中 hash 值的变化并不会重新加载页面hash 值的改变,都会在浏览器的访问历史中增加一个记录,也就是能通过浏览器的回退、前进按钮控制 hash 的切换我们可以通过 hashchange 事件,监听到 hash 值的变化,从而响应不同路径的逻辑处理基于 istory 新 API( history.pushState()+popState 事件)window.history.pushState(null, null, “http://www.google.com”);这两个 API 的相同之处是都会操作浏览器的历史记录,而不会引起页面的刷新。不同之处在于,pushState 会增加一条新的历史记录,而 replaceState 则会替换当前的历史记录详见History API -MDN3. 防抖和节流的区别防抖:任务频繁触发的情况下,只有任务触发的间隔超过指定间隔的时候,任务才会执行节流:指定时间间隔内只会执行一次任务推荐 这里4. 页面重构怎么操作页面重构就是根据原有页面内容和结构的基础上,通过 div+css 写出符合 web 标准的页面结构。具体实现要达到以下三点:功能不全页面的重构:页面功能符合用户体验、用户交互结构完整,可通过标准验证,代码重构:代码质量、SEO 优化、页面性能、更好的语义化、浏览器兼容、CSS 优化充分考虑到页面在站点中的“作用和重要性”,并对其进行有针对性的优化后记这篇文章收集的问题虽然不多,但是每一部分单独拎出来都可以写一篇深入理解,所以还是很需要耐心才可以看完的这段时间收集了很多前端面试的相关问题,发现有些东西还是很需要深入理解的,有时候仅仅停留在 ‘会用’ 的阶段还是远远不够的。所以,后面有时间也会聊一聊自己尝试的东西,发现的问题,有兴趣一起探索的小伙伴可以关注哦期待同行 GitHub完整版面试题以上 ...

March 26, 2019 · 1 min · jiezi

聊一聊Iterable与Iterator的那些事!

前言欢迎关注公众号:Coder编程获取最新原创技术文章和相关免费学习资料,随时随地学习技术知识!在上一篇文章通过面试题,让我们来了解Collection,我们会发现Collection接口之上还有一个接口Iterable,Iterable接口里面又有Iterator接口,那他们到底有什么区别呢?我们接下来就来了解下Iterable与Iterator相关内容,也就是本章的主要内容了,说不定在我们面试过程中,也会遇到一些问题呢。那我们就开始吧涉及面试题:1.说一说Iterable?2.Iterable结构?3.说一说Iterator?4.Iterator结构?5.forEachRemaining()与forEach()方法的区别?6.Iterator如何使用?7.Iterable与Iterator之间的区别与联系?上面的面试题可以看出,其实都是一回事,只是换了一种提问方式,只要我们能掌握核心要点,随便面试官怎么提问,我们都能轻松应对!来源百度百科:Iterable: 百度的时候,我却只看到了百度翻译:可迭代的; 可重复的; 迭代的; 因此我们可以知道,实现了这个接口的集合对象支持迭代,是可迭代的。Iterator: Iterator我们一般叫迭代器,它就是提供迭代机制的对象,具体如何迭代,都是Iterator接口规范的。通过UML图,我们也可以看出Iterable接口是Java集合框架的顶级接口,实现此接口使集合对象可以通过迭代器遍历自身元素。同时在Java设计模式中也有一种模式——迭代器模式.(在后续的文章我们会介绍相关设计模式,敬请关注)可以看出Iterator是迭代器模式最好的一个应用例子!1.说一说Iterable?由源码图可以看出,Iterable有三个方法,分别是Iterator<T> iterator();default void forEach(Consumer<? super T> action){}; JDK 1.8后新增default Spliterator<T> spliterator(){}; JDK 1.8后新增接下来我们简单介绍下这里面的方法。1.1 Iterable接口中的iterator() 方法Iterator<T> iterator();该接口主要是返回T类型的元素上的一个迭代器。下面再详细介绍Iterator。1.2 Iterable接口中的forEach() 方法default void forEach(Consumer<? super T> action) { // 验证action是否为null,如果action为null,则抛出NullPointerException Objects.requireNonNull(action); for (T t : this) { action.accept(t); } }该方法是循环输出,对内部元素进行遍历,并对元素进行指定的操作。例如:List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5, 6, 7, 8, 9); numbers.forEach(integer -> System.out.println(integer));对源码注释翻译:对这个Iterable的每一个元素执行给定的动作指导所有元素都被处理或者动作抛出一个异常为止。除非被实现类指定,动作将以迭代的顺序执行(如果一个迭代的顺序被指定。)被动作抛出的异常将被传递给调用者1.3 Iterable接口中的spliterator() 方法default Spliterator<T> spliterator() { return Spliterators.spliteratorUnknownSize(iterator(), 0);}该方法提供了一个可以并行遍历元素的迭代器,以适应现在cpu多核时代并行遍历的需求。简单说:分割,增加并行处理能力对源码注释翻译:创建一个被这个Iterable接口描述的元素上Spliterator。默认实现从iterable的Iterator中创建一个早期绑定的spliterator。这个spliterator继承iterable的iterator的fail-fast性质。 默认实现应该总是被重写。被默认实现返回的spliterator拥有不好分解能力,是不依据指定大小定制的,而且不报告任何spliterator的性质。实现类差不多总是能提供更好的实现。2.说一说Iterator?2.1 Iterator是什么?Iterator被称之为顺序遍历迭代器,jdk中默认对集合框架中数据结构做了实现。Iterator在实际应用中有一个比较好的点就是,可以一边遍历一遍删除元素。后面我会通过ArrayList中的Iterator()方法进行说明。2.2 Iterator结构?由源码图Iterator接口中定义了四个方法,分别是boolean hasNext():如果被迭代遍历的集合还没有被遍历完,返回TrueObject next():返回集合里面的下一个元素remove():删除集合里面上一次next()方法返回的元素void forEachRemaining(Consumer action):JDK 1.8后新增默认方法 使用Lambda表达式来遍历集合元素2.3 Iterator接口中的forEachRemaining() 方法在JDK1.8之后Iterator中增加的一个默认方法//使用方法List<String> arr=new ArrayList<>();arr.add(“hello”);arr.add((“world”));arr.iterator().forEachRemaining(str-> System.out.println(str));2.3.1 forEachRemaining()与forEach()方法之间的区别?forEachRemaining()源码:default void forEachRemaining(Consumer<? super E> action) { Objects.requireNonNull(action); while (hasNext()) action.accept(next());}forEach()源码:default void forEach(Consumer<? super T> action) { Objects.requireNonNull(action); for (T t : this) { action.accept(t); }}通过源码,我们可以看出他们之间的区别与联系。相同点:都可以遍历集合都是接口的默认方法都是1.8版本引入的区别:forEachRemaining()方法内部是通过使用迭代器Iterator的所有元素,forEach()方法内部使用的是增强for循环。forEach()方法可以多次调用,forEachRemaining()方法第二次调用不会做任何操作,因为不会有下一个元素。3.Iterator如何使用?简单举个小栗子List list = new ArrayList();list.add(“公众号”);list.add(“Coder编程”);for (Iterator iter = list.iterator(); iter.hasNext();) {String str = (String)iter.next();System.out.println(str);}/迭代器用于while循环Iterator iter = list.iterator();while(iter.hasNext()){String str = (String) iter.next();System.out.println(str);}/推荐阅读带你了解Collection相关知识!一篇让你理解进程与线程的区别与联系!文末本章节主要介绍了Iterable与Iterator之间的区别与联系,以及其他方面的小知识点,也是面试过程中会出现的内容点。欢迎关注公众号:Coder编程获取最新原创技术文章和相关免费学习资料,随时随地学习技术知识!Github个人主页目录Gitee个人主页目录欢迎大家关注并Star~ ...

March 25, 2019 · 1 min · jiezi

vue-router实现原理

近期面试,遇到关于vue-router实现原理的问题,在查阅了相关资料后,根据自己理解,来记录下。我们知道vue-router是vue的核心插件,而当前vue项目一般都是单页面应用,也就是说vue-router是应用在单页面应用中的。那么,什么是单页面应用呢?在单页面应用出现之前,多页面应用又是什么样子呢?单页面应用与多页面应用单页面即 第一次进入页面的时候会请求一个html文件,刷新清除一下。切换到其他组件,此时路径也相应变化,但是并没有新的html文件请求,页面内容也变化了。原理是:JS会感知到url的变化,通过这一点,可以用js动态的将当前页面的内容清除掉,然后将下一个页面的内容挂载到当前页面上,这个时候的路由不是后端来做了,而是前端来做,判断页面到底是显示哪个组件,清除不需要的,显示需要的组件。这种过程就是单页应用,每次跳转的时候不需要再请求html文件了。多页面即 每一次页面跳转的时候,后台服务器都会给返回一个新的html文档,这种类型的网站也就是多页网站,也叫做多页应用。原理是:传统的页面应用,是用一些超链接来实现页面切换和跳转的其实刚才单页面应用跳转原理即 vue-router实现原理vue-router实现原理原理核心就是 更新视图但不重新请求页面。vue-router实现单页面路由跳转,提供了三种方式:hash模式、history模式、abstract模式,根据mode参数来决定采用哪一种方式。路由模式vue-router 提供了三种运行模式:● hash: 使用 URL hash 值来作路由。默认模式。● history: 依赖 HTML5 History API 和服务器配置。查看 HTML5 History 模式。● abstract: 支持所有 JavaScript 运行环境,如 Node.js 服务器端Hash模式hash即浏览器url中#后面的内容,包含#。hash是URL中的锚点,代表的是网页中的一个位置,单单改变#后的部分,浏览器只会加载相应位置的内容,不会重新加载页面。也就是说即#是用来指导浏览器动作的,对服务器端完全无用,HTTP请求中,不包含#。每一次改变#后的部分,都会在浏览器的访问历史中增加一个记录,使用”后退”按钮,就可以回到上一个位置。所以说Hash模式通过锚点值的改变,根据不同的值,渲染指定DOM位置的不同数据。History模式HTML5 History API提供了一种功能,能让开发人员在不刷新整个页面的情况下修改站点的URL,就是利用 history.pushState API 来完成 URL 跳转而无须重新加载页面;由于hash模式会在url中自带#,如果不想要很丑的 hash,我们可以用路由的 history 模式,只需要在配置路由规则时,加入"mode: ‘history’",这种模式充分利用 history.pushState API 来完成 URL 跳转而无须重新加载页面。有时,history模式下也会出问题:eg:hash模式下:xxx.com/#/id=5 请求地址为 xxx.com,没有问题。history模式下:xxx.com/id=5 请求地址为 xxx.com/id=5,如果后端没有对应的路由处理,就会返回404错误;为了应对这种情况,需要后台配置支持:在服务端增加一个覆盖所有情况的候选资源:如果 URL 匹配不到任何静态资源,则应该返回同一个 index.html 页面,这个页面就是你 app 依赖的页面。abstract模式abstract模式是使用一个不依赖于浏览器的浏览历史虚拟管理后端。根据平台差异可以看出,在 Weex 环境中只支持使用 abstract 模式。 不过,vue-router 自身会对环境做校验,如果发现没有浏览器的 API,vue-router 会自动强制进入 abstract 模式,所以 在使用 vue-router 时只要不写 mode 配置即可,默认会在浏览器环境中使用 hash 模式,在移动端原生环境中使用 abstract 模式。 (当然,你也可以明确指定在所有情况下都使用 abstract 模式)。具体更加详细的文章,请参考:Vue番外篇 – vue-router浅析原理vue-router的原理 ...

March 20, 2019 · 1 min · jiezi

前端面试题 -- JavaScript(二)

前言上一篇 前端面试题-JavaScript(一), 感兴趣的小伙伴也可以移步这里查看 完整版JavaScript面试题,面试题会不定期更新加进去一些个人工作中遇到的或者认为比较重要的东西,后面会涉及到前端的各个方面,感兴趣的小伙伴可以关注哦!如果文章中有出现纰漏、错误之处,还请看到的小伙伴留言指正,先行谢过以下 ↓1. 同步和异步的区别,怎么异步加载 JavaScript同步模式同步模式,又称阻塞模式。javascript 在默认情况下是会阻塞加载的。当前面的 javascript 请求没有处理和执行完时,会阻止浏览器的后续处理异步模式异步加载又叫非阻塞,浏览器在下载执行 js 同时,还会继续进行后续页面的处理异步加载 JavaScript动态添加 script 标签deferasyncdefer属性和async都是属于 script 标签上面的属性,两者都能实现 JavaScript 的异步加载。不同之处在于:async 在异步加载完成的时候就马上开始执行了,defer 会等到 html 加载完毕之后再执行2. 跨域问题的产生,怎么解决它由于浏览器的 同源策略,在出现 域名、端口、协议有一种不一致时,就会出现跨域,属于浏览器的一种安全限制。解决跨域问题有很多种方式,常用的就是以下几种:jsonp 跨域:动态创建script,再请求一个带参网址实现跨域通信.缺点就是只能实现 get 一种请求document.domain + iframe跨域:两个页面都通过js强制设置document.domain为基础主域,就实现了同域.但是仅限主域相同,子域不同的跨域应用场景跨域资源共享(CORS):只服务端设置Access-Control-Allow-Origin即可,前端无须设置,若要带cookie请求:前后端都需要设置nginx反向代理接口跨域:同源策略是浏览器的安全策略,不是HTTP协议的一部分。服务器端调用HTTP接口只是使用HTTP协议,不会执行JS脚本,不需要同源策略,也就不存在跨越问题WebSocket协议跨域3. 对 this 的理解在 JavaScript 中,研究 this 一般都是 this 的指向问题,核心就是 this 永远指向最终调用它的那个对象,除非改变 this 指向或者箭头函数那种特殊情况function test() { console.log(this);}test() // windowvar obj = { foo: function () { console.log(this.bar) }, bar: 1};var foo = obj.foo;var bar = 2;obj.foo() // 1foo() // 2// 函数调用的环境不同,所得到的结果也是不一样的4. apply()、call()和 bind() 是做什么的,它们有什么区别相同点:三者都可以改变 this 的指向不同点:apply 方法传入两个参数:一个是作为函数上下文的对象,另外一个是作为函数参数所组成的数组var obj = { name : ‘sss’}function func(firstName, lastName){ console.log(firstName + ’ ’ + this.name + ’ ’ + lastName);}func.apply(obj, [‘A’, ‘B’]); // A sss Bcall 方法第一个参数也是作为函数上下文的对象,但是后面传入的是一个参数列表,而不是单个数组var obj = { name: ‘sss’}function func(firstName, lastName) { console.log(firstName + ’ ’ + this.name + ’ ’ + lastName);}func.call(obj, ‘C’, ‘D’); // C sss Dbind 接受的参数有两部分,第一个参数是是作为函数上下文的对象,第二部分参数是个列表,可以接受多个参数var obj = { name: ‘sss’}function func() { console.log(this.name);}var func1 = func.bind(null, ‘xixi’);func1();apply、call 方法都会使函数立即执行,因此它们也可以用来调用函数bind 方法不会立即执行,而是返回一个改变了上下文 this 后的函数。而原函数 func 中的 this 并没有被改变,依旧指向全局对象 windowbind 在传递参数的时候会将自己带过去的参数排在原函数参数之前function func(a, b, c) { console.log(a, b, c);}var func1 = func.bind(this, ‘xixi’);func1(1,2) // xixi 1 25. 什么是内存泄漏,哪些操作会造成内存泄漏内存泄漏:是指一块被分配的内存既不能使用,又不能回收,直到浏览器进程结束可能造成内存泄漏的操作:意外的全局变量闭包循环引用被遗忘的定时器或者回调函数你可能还需要知道 垃圾回收机制 此外,高程上面对垃圾回收机制的介绍也很全面,有兴趣的小伙伴可以看看6. 什么是事件代理,它的原理是什么事件代理:通俗来说就是将元素的事件委托给它的父级或者更外级元素处理原理:利用事件冒泡机制实现的优点:只需要将同类元素的事件委托给父级或者更外级的元素,不需要给所有元素都绑定事件,减少内存空间占用,提升性能; 动态新增的元素无需重新绑定事件7. 对AMD和CMD的理解,它们有什么区别AMD和CMD都是为了解决浏览器端模块化问题而产生的,AMD规范对应的库函数有 Require.js,CMD规范是在国内发展起来的,对应的库函数有Sea.jsAMD和CMD最大的区别是对依赖模块的执行时机处理不同1、AMD推崇依赖前置,在定义模块的时候就要声明其依赖的模块 2、CMD推崇就近依赖,只有在用到某个模块的时候再去require参考:AMD-中文版 CMD-规范8. 对ES6的了解ECMAScript 6.0 是 JavaScript 语言的下一代标准新增的特性:声明变量的方式 let const变量解构赋值字符串新增方法 includes() startsWith() endsWith() 等数组新增方法 Array.from() Array.of() entries() keys() values() 等对象简洁写法以及新增方法 Object.is() Object.assign() entries() keys() values()等箭头函数、rest 参数、函数参数默认值等新的数据结构: Set 和 MapProxyPromise对象async函数 await命令Class类Module 体系 模块的加载和输出方式了解更多,参考 ES6入门-阮一峰9. 箭头函数有什么特点ES6 允许使用“箭头”(=>)定义函数var f = v => v;// 等同于var f = function (v) { return v;}注意点:函数体内的 this 对象,就是定义时所在的对象,而不是使用时所在的对象不可以当作构造函数,也就是说,不可以使用 new 命令,否则会抛出一个错误不可以使用 arguments 对象,该对象在函数体内不存在。如果要用,可以用 rest 参数代替10. Promise 对象的了解Promise 是异步编程的一种解决方案,比传统的解决方案——回调函数和事件——更合理和更强大.所谓Promise,简单说就是一个容器,里面保存着某个未来才会结束的事件(通常是一个异步操作)的结果 –ES6入门-阮一峰Promise 对象代表一个异步操作,有三种状态:pending(进行中)、fulfilled(已成功)和 rejected(已失败)。只有异步操作的结果,可以决定当前是哪一种状态,任何其他操作都无法改变这个状态特点:对象的状态不受外界影响一旦状态改变,就不会再变,任何时候都可以得到这个结果Promise 新建后就会立即执行const promise = new Promise(function(resolve, reject) { // … some code if (/* 异步操作成功 */){ resolve(value); } else { reject(error); }})Promise实例生成以后,可以用then方法分别指定resolved状态和rejected状态的回调函数promise.then(function(value) { // success}, function(error) { // failure})then 方法返回的是一个新的Promise实例Promise.prototype.catch 用于指定发生错误时的回调函数,具有“冒泡”性质,会一直向后传递,直到被捕获为止。也就是说,错误总是会被下一个catch语句捕获getJSON(’/post/1.json’).then(function(post) { return getJSON(post.commentURL);}).then(function(comments) { // some code}).catch(function(error) { // 处理前面三个Promise产生的错误});catch 方法返回的还是一个 Promise 对象,因此后面还可以接着调用 then 方法出去上述方法,Promise还有其他用法,小伙伴们可以在这里查看大佬写的文章 ES6入门-阮一峰11. async 函数以及 awit 命令async 函数是什么?一句话,它就是 Generator 函数的语法糖了解Generator函数的小伙伴,这里 传送门async 特点:async 函数返回一个 Promise 对象,可以使用 then 方法添加回调函数。当函数执行的时候,一旦遇到 await 就会先返回,等到异步操作完成,再接着执行函数体内后面的语句async 函数内部 return 语句返回的值,会成为 then 方法回调函数的参数async 函数返回的 Promise 对象,必须等到内部所有 await 命令后面的 Promise 对象执行完,才会发生状态改变,除非遇到 return 语句或者抛出错误async 函数内部抛出错误,会导致返回的 Promise 对象变为 reject 状态。抛出的错误对象会被 catch 方法回调函数接收到function timeout(ms) { return new Promise((resolve) => { setTimeout(resolve, ms); });}async function asyncPrint(value, ms) { await timeout(ms); console.log(value);}asyncPrint(‘hello world’, 50);await 命令: await 命令后面是一个 Promise 对象,返回该对象的结果。如果不是 Promise 对象,就直接返回对应的值async function f() { // 等同于 // return 123; return await 123;}f().then(v => console.log(v))// 123await 命令后面是一个thenable对象(即定义then方法的对象),那么await会将其等同于 Promise 对象.也就是说就算一个对象不是Promise对象,但是只要它有then这个方法, await 也会将它等同于Promise对象使用注意点:await 命令后面的 Promise 对象,运行结果可能是 rejected,所以最好把 await 命令放在 try…catch 代码块中多个 await 命令后面的异步操作,如果不存在继发关系,最好让它们同时触发await 命令只能用在 async 函数之中,如果用在普通函数,就会报错了解更多,请点击 这里12. export 与 export default有什么区别export 与 export default 均可用于导出常量、函数、文件、模块等在一个文件或模块中,export、import 可以有多个,export default 仅有一个通过 export 方式导出,在导入时要加 { },export default 则不需要使用 export default命令,为模块指定默认输出,这样就不需要知道所要加载模块的变量名; export 加载的时候需要知道加载模块的变量名export default 命令的本质是将后面的值,赋给 default 变量,所以可以直接将一个值写在 export default 之后13. 前端性能优化参见 雅虎14条前端性能优化14. 对JS引擎执行机制的理解首选明确两点:JavaScript 是单线程语言JavaScript 的 Event Loop 是 JS 的执行机制, 也就是事件循环console.log(1) setTimeout(function(){ console.log(2)},0)console.log(3)// 1 3 2JavaScript 将任务分为同步任务和异步任务,执行机制就是先执行同步任务,将同步任务加入到主线程,遇到异步任务就先加入到 event table ,当所有的同步任务执行完毕,如果有可执行的异步任务,再将其加入到主线程中执行视频详解,移步 这里setTimeout(function(){console.log(1);},0);new Promise(function(resolve){ console.log(2); for(var i = 0; i < 10000; i++){ i == 99 && resolve(); } }).then(function(){ console.log(3) }); console.log(4); // 2 4 3 1在异步任务中,定时器也属于特殊的存在。有人将其称之为 宏任务、微任务,定时器就属于宏任务的范畴。参考 JS引擎的执行机制后记总结的过程,自己确实也获益颇多,感谢前行的小伙伴。预祝大家都能找到自己满意的工作以上 ...

March 19, 2019 · 3 min · jiezi

浏览器缓存解析

浏览器缓存浏览器缓存分为几个阶段:浏览器缓存阶段一.强制缓存阶段1.cache-control: 决定了浏览器端和服务器端缓存的策略,可以出现在响应头response header中,或者 请求头 request header中max-age:指定缓存的最大有效时间,eg:cache-control:max-age=315360000,注意与expires做区分(与cache-control平级),max-age优先级高于 expires,这个属性时HTTP1.1中新增的属性s-maxage:指定public的缓存,缓存设备有很多,不仅仅浏览器是缓存设备,在整个网络中,可能会存在代理服务器,CDN属于public缓存设备,因为可以多用户访问并读取信息;什么是private缓存呢,指的是只是你个人访问的设备,浏览器就属于private缓存设备,eg:s-maxage=31536000;他的优先级高于max-age,只能设定public的缓存设备privatepublicno-cache:错误理解:不使用缓存;no-cache指的是不管本地是否设置了max-age(即忽略本地浏览器端的缓存策略),都要向服务器端发送请求,由服务器端来判断缓存情况no-store:完全不使用任何的缓存策略,不管是服务器端还是浏览器端的2.expires:Thu, 14 Mar 2019 17:29:17 GMT,这个属性时HTTP1.0中配置,缓存过期时间,用来指定资源到期时间,是服务器端具体的时间点。告诉浏览器在过期时间前浏览器可以直接从浏览器缓存取数据,而无需再次请求。二.协商缓存阶段基于客户端和服务器端的协商缓存机制1.last-modifiedlast-modified - response headerif-modified-since - request header需要与cache-control共同使用,如果配置了max-age 且没有过期,就不会使用last-modified;过期了之后,才会使用last-modified。last-modified 缺点:服务器端不能精确获取 文件变更时间时文件修改时间改了,文件内容没有变以秒为单位,如果是ms内修改了文件,就体现不出来2.ETag文档内容的hash值ETag —- response headerif-None-Match —-request headeretag优先级高于last-modified状态码解析200(from cache): 浏览器端缓存,cache-control:max-age=315360000 或者expires起作用304: 服务器端缓存,last-modified 或者 etag 起作用200:浏览器端没有缓存,或者服务器端缓存失效,或者用户点击了ctrl+F5 浏览器直接从服务器端下载最新的数据注意:Chrome浏览器,手动点击刷新按钮都会 在请求头中,添加 chche-control:max-age=0,这样就肯定不会使用浏览器端的缓存!更加详细,请参考:缓存详解

March 19, 2019 · 1 min · jiezi

大搜车面试总结

面试总结工作久了有时候也需要出去看看。前几天大搜车的hr约了面试,就记录一下关于这次面试的具体内容。一、笔试先填写了个人信息,第一步就是笔试。哪种盒模型包含paddingheader中哪种标记不影响文件的缓存(age, etag, last-modify,cache-control)设计一个tree组件,使用react、或者vue。可无限嵌套,最大程度的去适应各种需求。给出大致思路,参数、回调函数、事件。使一个div宽度100px;高100px;居中至少三种方式(不考虑浏览器兼容)说出你在过去项目中最满意的一部分。存在字符串a、b。找出a是否包含在b中,如果包含多个返回第一个位置,没有包含则返回-1.如:a=‘123’ b=‘4512356123’ 则返回2。不能使用indexOf、substr等。设计一个plus函数、实现plus(1)(2)()=3、plus(1)(2)(3)()=6。题目大致是这样的。细节部分可能有偏差,但是大体的意思是这样的。针对笔试部分后两个函数我写了挺久、只完成了字符串部分,后面手写确实有点难(可能是我太菜了吧)以下是我回去后给出的答案哪种盒模型包含padding。主要分为两种盒模型。标准盒模型、和IE盒模型。就如图上表示的,IE模型中width包含了content+padding+border。标准模型中width只包含content。开发者可以显示的用box-sizing去设置想要的盒模型。2.header中哪种标记不影响文件的缓存(age, etag, last-modify,cache-control)。age:请求头中的age代表的意思是当代理服务器用自己缓存的实体去响应请求时,用该头部表明该实体从产生到现在经过多长时间了。etag:web服务器响应请求时,告诉浏览器当前资源在服务器的唯一标识(生成规则由服务器觉得)。Apache中,ETag的值,默认是对文件的索引节(INode),大小(Size)和最后修改时间(MTime)进行Hash后得到的。Last-Modified:标示这个响应资源的最后修改时间。web服务器在响应请求时,告诉浏览器资源的最后修改时间。Cache-Control与Expires的作用一致,都是指明当前资源的有效期,控制浏览器是否直接从浏览器缓存取数据还是重新发请求到服务器取数据。只不过Cache-Control的选择更多,设置更细致,如果同时设置的话,其优先级高于Expires。3.设计一个tree组件,使用react、或者vue。可无限嵌套,最大程度的去适应各种需求。给出大致思路,参数、回调函数、事件。这部分我是根据自己开发的经验去写的,纯粹是个人经验。我使用Vue采用Vue提供的slot去做嵌套,这样提高了组件适应的不同需求的能力。采用emit去触发事件,在tree组件注册相应的事件来达到交互的目的。回答的不够好,仅提供参考4.使一个宽度100px高100px的div横向居中至少三种方式(不考虑浏览器兼容).box{ position:absolute;left: 50%;trasition: translate(-50%, 0) };.box{ position:absolute;left: 50%;margin-left: -50px; }.box{ position: absolute;margin: 0 auto; }5.说出你在过去项目中最满意的一部分。是做了一个vue-barrel插件,是UI布局更加美观。详情可以查看我的一篇文章木桶插件。6.存在字符串a、b。找出a是否包含在b中,如果包含多个返回第一个位置,没有包含则返回-1.如:a=‘123’ b=‘4512356123’ 则返回2。不能使用indexOf、substr等。这是我自己的代码,可能是面试比较紧张吧,一时没想起来最优的方案,这个复杂度较高,暴力查找.function findStr(a, b){ let tmpA = a.split(’’); let tmpB = b.split(’’); let lastIndex = -1; let lengthA = tmpA.length; let lengthB = tmpB.length; for( let i = 0; i< lengthB; i++ ){ let tag = true; for( let j =0; j<lengthA; j++ ){ if( tmpB[i+j] !== tmpA[j] ){ tag = false; break; } } if( tag ){ lastIndex = lastIndex > -1 ? lastIndex : i; break; } } return lastIndex;}7.设计一个plus函数、实现plus(1)(2)()=3、plus(1)(2)(3)()=6。这部分我当时没有答出来,当时只是模糊的记得柯里化函数,没能手写出来。function plus() { if( arguments.length === 0 ){ let sum = 0; plus.list.forEach(( item )=>{ sum+=item; }) return sum; } else{ plus.list = plus.list.concat(Array.prototype.slice.call(arguments, 0)); return plus; }}二面详情我会再整理 ...

March 18, 2019 · 1 min · jiezi

PHP面试常考之数据结构——链表的概念

你好,是我琉忆,PHP程序员面试笔试系列图书的作者。本周(2019.3.18至3.22)的一三五更新的文章如下:周一:PHP面试常考之数据结构——链表的概念周三:PHP面试常考之数据结构——栈和队列周五:PHP面试常考之数据结构——自己整理了一篇“PHP如何实现链表?”的文章,关注公众号:“琉忆编程库”,回复:“链表”,我发给你。一、链表链表(Linked list)是一种常见的基础数据结构,是一种线性表,但是并不会按线性的顺序存储数据,而是在每一个节点里存到下一个节点的指针(Pointer)。使用链表结构可以克服数组链表需要预先知道数据大小的缺点,链表结构可以充分利用计算机内存空间,实现灵活的内存动态管理。但是链表失去了数组随机读取的优点,同时链表由于增加了结点的指针域,空间开销比较大。链表有很三种不同的类型:单向链表,双向链表以及循环链表。二、单向链表单向链表包含两个域,一个信息域和一个指针域。这个链接指向列表中的下一个节点,而最后一个节点则指向一个空值。如图:三、双向链表每个节点有两个连接:一个指向前一个节点,(当此“连接”为第一个“连接”时,指向空值或者空列表);而另一个指向下一个节点,(当此“连接”为最后一个“连接”时,指向空值或者空列表)如图:四、循环链表在一个循环链表中,首节点和末节点被连接在一起。这种方式在单向和双向链表中皆可实现。要转换一个循环链表,你开始于任意一个节点然后沿着列表的任一方向直到返回开始的节点。再来看另一种方法,循环链表可以被视为“无头无尾”。这种列表很利于节约数据存储缓存,假定你在一个列表中有一个对象并且希望所有其他对象迭代在一个非特殊的排列下。指向整个列表的指针可以被称作访问指针。自己编写的《PHP程序员面试笔试宝典》和《PHP程序员面试笔试真题解析》书籍,已在各大电商平台销售,两本可以帮助你更快更好的拿到offer的书。更多PHP相关的面试知识、考题可以关注公众号获取:琉忆编程库对本文有什么问题或建议都可以进行留言,我将不断完善追求极致,感谢你们的支持。

March 18, 2019 · 1 min · jiezi

前端面试题 -- JavaScript (一)

前言前两天总结了一下HTML+CSS方面的面试题 (传送门),今天翻看了一些 JavaScript 面试中常见的几个问题(只是一部分,会持续更新),分享给有需要的小伙伴如果文章中有出现纰漏、错误之处,还请看到的小伙伴留言指正,先行谢过以下 ↓1. JavaScript 有哪些数据类型6种原始数据类型:Boolean: 布尔表示一个逻辑实体,可以有两个值:true 和 falseNumber: 用于表示数字类型String: 用于表示文本数据Null: Null 类型只有一个值: null,特指对象的值未设置Undefined: 一个没有被赋值的变量会有个默认值 undefinedSymbol: 符号(Symbols)是ECMAScript第6版新定义的。符号类型是唯一的并且是不可修改的引用类型:Object详见 JavaScript的数据类型-MDN2. 怎么判断不同的JS数据类型typeof操作符:返回一个字符串,表示未经计算的操作数的类型typeof 操作符对于简单数据类型,返回其本身的数据类型,函数对象返回 function ,其他对象均返回 Objectnull 返回 Objectinstanceof: 用来判断A 是否是 B的实例,表达式为 A instanceof B,返回一个Boolean类型的值instanceof 检测的是原型,只能用来判断两个对象是否属于实例关系, 而不能判断一个对象实例具体属于哪种类型let a = [];a instanceof Array // truea instanceof Object // true变量a 的 proto 直接指向Array.prototype,间接指向 Object.prototype,所以按照 instanceof 的判断规则,a 就是Object的实例.针对数组的这个问题,ES5 提供了 Array.isArray() 方法 。该方法用以确认某个对象本身是否为 Array 类型constructor: 当一个函数被定义时,JS引擎会为其添加prototype原型,然后再在 prototype上添加一个 constructor 属性,并让其指向该函数的引用null和undefined是无效的对象,因此是不会有constructor存在的,这两种类型的数据需要通过其他方式来判断函数的constructor是不稳定的,这个主要体现在自定义对象上,当开发者重写prototype后,原有的constructor引用会丢失,constructor会默认为 Objectfunction F() {};var f = new F;f.constructor == F // trueF.prototype = {a: 1}var f = new Ff.constructor == F // false 在构造函数 F.prototype 没有被重写之前,构造函数 F 就是新创建的对象 f 的数据类型。当 F.prototype 被重写之后,原有的 constructor 引用丢失, 默认为 Object因此,为了规范开发,在重写对象原型时一般都需要重新给 constructor 赋值,以保证对象实例的类型不被篡改toString: Object 的原型方法,调用该方法,默认返回当前对象的 [[Class]] 。这是一个内部属性,其格式为 [object Xxx] ,其中 Xxx 就是对象的类型Object.prototype.toString.call(’’) ; // [object String]Object.prototype.toString.call(11) ; // [object Number]Object.prototype.toString.call(true) ; // [object Boolean]Object.prototype.toString.call(Symbol()); //[object Symbol]Object.prototype.toString.call(undefined) ; // [object Undefined]Object.prototype.toString.call(null) ; // [object Null]Object.prototype.toString.call(new Function()) ; // [object Function]Object.prototype.toString.call([]) ; // [object Array]3. undefined 和 null 有什么区别null表示"没有对象",即该处不应该有值典型用法:作为函数的参数,表示该函数的参数不是对象作为对象原型链的终点undefined表示"缺少值",就是此处应该有一个值,但是还没有定义典型用法:变量被声明了,但没有赋值时,就等于undefined调用函数时,应该提供的参数没有提供,该参数等于undefined对象没有赋值的属性,该属性的值为undefined函数没有返回值时,默认返回undefined详见: undefined和null的区别-阮一峰4. 数组对象有哪些常用方法修改器方法:pop(): 删除数组的最后一个元素,并返回这个元素push():在数组的末尾增加一个或多个元素,并返回数组的新长度reverse(): 颠倒数组中元素的排列顺序shift(): 删除数组的第一个元素,并返回这个元素unshift(): 在数组的开头增加一个或多个元素,并返回数组的新长度sort(): 对数组元素进行排序,并返回当前数组splice(): 在任意的位置给数组添加或删除任意个元素访问方法:concat(): 返回一个由当前数组和其它若干个数组或者若干个非数组值组合而成的新数组join(): 连接所有数组元素组成一个字符串slice(): 抽取当前数组中的一段元素组合成一个新数组indeOf(): 返回数组中第一个与指定值相等的元素的索引,如果找不到这样的元素,则返回 -1lastIndexOf(): 返回数组中最后一个(从右边数第一个)与指定值相等的元素的索引,如果找不到这样的元素,则返回 -1迭代方法:forEach(): 为数组中的每个元素执行一次回调函数,最终返回 undefinedevery(): 如果数组中的每个元素都满足测试函数,则返回 true,否则返回 falsesome(): 如果数组中至少有一个元素满足测试函数,则返回 true,否则返回 falsefilter(): 将所有在过滤函数中返回 true 的数组元素放进一个新数组中并返回map(): 返回一个由回调函数的返回值组成的新数组更多方法请参考 MDN 传送门5. Js 有哪几种创建对象的方式对象字面量var obj = {}Object 构造函数var obj = new Object()工厂模式function Person(name, age) { var o = new Object() o.name = name; o.age = age; o.say = function() { console.log(name) } return o}缺点: 每次通过Person创建对象的时候,所有的say方法都是一样的,但是却存储了多次,浪费资源构造函数模式function Person(name, age) { this.name = name this.age = age this.say = function() { console.log(name) }}var person = new Person(‘hello’, 18)构造函数模式隐试的在最后返回return this 所以在缺少new的情况下,会将属性和方法添加给全局对象,浏览器端就会添加给window对象,可以根据return this 的特性调用call或者apply指定this原型模式function Person() {}Person.prototype.name = ‘hanmeimei’;Person.prototype.say = function() { alert(this.name);}Person.prototype.friends = [’lilei’];var person = new Person();实现了方法与属性的共享,可以动态添加对象的属性和方法。但是没有办法创建实例自己的属性和方法,也没有办法传递参数构造函数和原型组合function Person(name, age) { this.name = name this.age = age}Person.prototype.say = function() { console.log(this.name)}var person = new Person(‘hello’)还有好几种模式,感兴趣的小伙伴可以参考 红宝书,你们肯定知道的了!6. 怎么实现对对象的拷贝(浅拷贝与深拷贝)浅拷贝拷贝原对象引用可以使用Array.prototype.slice()也可以完成对一个数组或者对象的浅拷贝Object.assign()方法深拷贝最常用的方式就是 JSON.parse(JSON.stringify(目标对象),缺点就是只能拷贝符合JSON数据标准类型的对象7. 什么是闭包,为什么要用它简单来说,闭包就是能够读取其他函数内部变量的函数function Person() { var name = ‘hello’ function say () { console.log(name) } return say()}Person() // hello由于 JavaScript 特殊的作用域,函数外部无法直接读取内部的变量,内部可以直接读取外部的变量,从而就产生了闭包的概念用途:最大用处有两个,一个是前面提到的可以读取函数内部的变量,另一个就是让这些变量的值始终保持在内存中注意点:由于闭包会使得函数中的变量都被保存在内存中,内存消耗很大,所以不能滥用闭包,否则会造成网页的性能问题,在IE中可能导致内存泄露8. 介绍一下 JavaScript 原型,原型链,它们有何特点首先明确一点,JavaScript是基于原型的每个构造函数(constructor)都有一个原型对象(prototype),原型对象都包含一个指向构造函数的指针,而实例(instance)都包含一个指向原型对象的内部指针.图解:每一个构造函数都拥有一个prototype属性,这个属性指向一个对象,也就是原型对象原型对象默认拥有一个constructor属性,指向指向它的那个构造函数每个对象都拥有一个隐藏的属性[[prototype]],指向它的原型对象那么什么是原型链:JavaScript中所有的对象都是由它的原型对象继承而来。而原型对象自身也是一个对象,它也有自己的原型对象,这样层层上溯,就形成了一个类似链表的结构,这就是原型链所有原型链的终点都是Object函数的prototype属性。Objec.prototype指向的原型对象同样拥有原型,不过它的原型是null,而null则没有原型9. JavaScript 如何实现继承原型链继承function Animal() {}Animal.prototype.name = ‘cat’Animal.prototype.age = 1Animal.prototype.say = function() {console.log(‘hello’)}var cat = new Animal()cat.name // catcat.age // 1cat.say() // hello最简单的继承实现方式,但是也有其缺点来自原型对象的所有属性被所有实例共享创建子类实例时,无法向父类构造函数传参要想为子类新增属性和方法,必须要在new语句之后执行,不能放到构造器中构造继承function Animal() { this.species = “动物”}function Cat(name, age) { Animal.call(this) this.name = name this.age = age}var cat = new Cat(‘豆豆’, 2)cat.name // 豆豆cat.age // 2cat.species // 动物使用call或apply方法,将父对象的构造函数绑定在子对象上.组合继承function Animal() { this.species = “动物”}function Cat(name){ Animal.call(this) this.name = name}Cat.prototype = new Animal() // 重写原型Cat.prototype.constructor = Cat如果没有Cat.prototype = new Animal()这一行,Cat.prototype.constructor是指向Cat的;加了这一行以后,Cat.prototype.constructor指向Animal.这显然会导致继承链的紊乱(cat1明明是用构造函数Cat生成的),因此我们必须手动纠正,将Cat.prototype对象的constructor值改为Catextends 继承ES6新增继承方式,Class 可以通过extends关键字实现继承class Animal { }class Cat extends Animal { constructor() { super(); }}使用 extends 实现继承,必须添加 super 关键字定义子类的 constructor,这里的super() 就相当于 Animal.prototype.constructor.call(this)当然,还有很多种实现继承的方式,这里就不多说了。然后,再推荐一波 红宝书10. new 操作符具体干了什么创建一个空对象,并且 this 变量引用该对象,同时还继承了该函数的原型属性和方法被加入到 this 引用的对象中新创建的对象由 this 所引用,并且最后隐式的返回 this后记这里现在只是JavaScript 面试题中的一部分,后面是持续更新, 有需要的小伙伴可以关注哦好了,周末愉快 [啊!结束了……] ...

March 17, 2019 · 2 min · jiezi

不按套路求职,到底有多惨?

身为一个程序员,找工作的时候第一个想到的问题是什么?“简历该怎么写”?还是“面试有哪些细节”?很少有人注意一件事:该找一份什么样的工作?好比老王的儿子小王,人特别聪明,仅仅用了9年,就大学毕业,该成家了。自己成天上网看各种攻略,什么“和女神约会的10大技巧”、“第一次和女孩吃饭该怎么聊天”,托人介绍,天天相亲,唯独没想过:“我喜欢什么样的女孩”。有人说,这还用想,找工作嘛,得有发展,其他的好说。这话说得,好比找对象,人家问你什么标准,女孩找程序员的标准倒是挺明确,无非有车有房、工资上交、父母祥和。但是程序员找女孩就“费劲”了,人“好”就行。问题是,人哪儿“好”?很多人都说要找份“好工作”,也一样,怎么个“好”法,说不清楚,况且每个人情况还不一样。同样一份工作,月薪25k,还给股票期权,但是要经常加班,对于人家来讲这就是机会。但你要是说这段时间,打算多花点精力,和爱人一起,响应一下国家的二胎政策,这活儿对你来说,就完全没有吸引力了。那么,怎么做呢?找工作的时候要不违背下边的四条原则,就不会出大问题。很多人好不容易找份工作,干上三五个月就换一家,打过交道的HR都凑齐12星座了,钱是损失了不少,关键是耽误工夫,别人都积累到一定程度,朝着18k、20k努力去了,自己还在8到10k之间游荡。哪四条原则呢?符合价值观、感兴趣、结合自身情况和匹配职业目标。1.价值观这是最重要的事情,没有之一。听到这个词,你可能会觉得我要跟你谈理想、聊人生,但是工作时间长了,你会发现这个词,比真金白银都实在。我有同学,毕业的时候签了一家公司,月薪12k,要知道那时候普通水平的程序员薪资可是8k左右,每个月都能比同龄人多拿四千块钱!后来毕业吃散伙饭,聊到他马上要去的这家公司,据说是做古董收藏的,这样的公司也招前端?我问他:你是不是觉得收藏古董特有意义?他白了我一眼:我觉得每个月多拿点钱是真的!意义这东西,无所谓。一个月给我30k,让我去电梯里负责开门都行。转眼几年过去了,我看到他在技术博客里写:整个公司就他一个人懂前端,敲出来的东西都是给上了岁数的老年人看的,用不上什么新技术,一天天过得倒是不忙,确切地说是一种“等死”的状态。没过多久,还真把他的公司等死了,重新找工作,技术上完全没有任何优势!我问过他:没想着有空的时候钻研下新技术?他摆了摆手:活干着没劲,谁有那个心情? 所谓的价值观,就是你觉得什么东西对你来说很重要?你觉得做点什么才会有价值?本来生性自由的一个人,喜欢挑战,一毕业进了一家大公司,流程都定好了,只让他负责执行,上个厕所还得先翻翻《员工手册》,做个什么项目也不需要你自由发挥,更别说重构代码了,连给官网换张图片都得打报告!这样的活,能坚持多久?刚开始,你对自己说咬咬牙,就过去了,时间一长呢?这是价值观不符合的问题,只要还在这个公司待着,再过10年也这样,没听说哪个公司因为一个程序员把企业文化改了。2.感兴趣对于程序员来说,兴趣不仅仅是最好的老师,也是唯一的爱好、丰富的精神食粮,有的大神在电脑面前一坐就是十几个钟头,一看外边天黑了,自己也犯迷糊:我女朋友是谁来着?很多人觉得一谈兴趣,就是在灌鸡汤,但是看到技术大牛上百万的年薪,自己又羡慕。说起来这件事吧,还振振有词:你看人家大牛,本身就有天赋吧,还那么努力,往电脑面前一坐就是十几个钟头,咱是比不了啊!大神花的功夫,那是表象,根本原因还是兴趣。当你职位一时半会升不上去,每个月到手的工资也没有太多变动的时候,你在心里默念:我得努力,我得奋斗,我要为我的女神买床垫……这没什么用,就算年轻,鸡血那股劲,两天就过去了!人是感情动物,倒不如从兴趣的角度:这东西挺有意思,我看看能不能把他们家网站扒下来试试(知道你肯定也做过,但是劝你别学坏。)有的程序员上相亲网站,都不忘给人家找Bug,可以说是敬业,他们的内心戏是:唉,搞点小破坏还是挺有意思的哈!3.结合自身情况咱们先说说这“自身情况”主要指什么:你偏向于用什么技术栈?想从事哪个细分行业?这都属于“自身情况”,有人说,都是搞前端的,选技术栈我能理解,细分行业重要吗?虽然都是前端,但你在直播类平台敲代码,就和新闻类平台做项目不一样,就像老师也有很多种,教小学音乐和大学物理根本不是一回事。那怎么“结合自身实际情况”呢?从“技术栈”和“细分行业”来讲。技术栈的选择,初期需要考虑目前的积累、未来的前景,比如Angular 就业机会少入门难,如果你是个小白,完全可以从“0基础福星Vue”开始。但是如果你已经用了Angular好几年了,学它的人少,人才难求,对于你反而是一个机会和优势。这个时候你再从头学Vue,之前的经历形不成你的优势。从长远来看,并非框架决定我们选什么工作,而是工作内容决定了我们要选什么框架!当你能够从本质掌握一个框架,有足够深刻理解的时候,所有框架在你眼中,没多大差别,自然而然地,你就能根据业务需求,用最正确的框架。再说细分行业,假如你一直开发的是新闻类APP,下一份工作去今日头条或者网易新闻是没问题的,可你贸然进入直播类平台,没有任何积累,自然也没什么优势可言。4.职业目标很多人追求事业上的成功,它更像是打麻将而不是赛跑,赛跑你想赢,必须第一个冲到终点,但是麻将要赢,能通过很多方式“和牌”。如果问到目标,很多人的求职目标看起来都很简单:“多赚钱”!但问题是:赚钱的路有很多,对你来说是不是都一样?一份工作年薪50W,这个数字在IT界也不算低了,但如果它需要你996,甚至昼夜连轴转,对于一个需要照顾孩子的宝妈,就不是个好选择。一个人的职业目标可以有很多:在一个细分领域当专家,在一个大型企业当高管,或者在一个初创公司闯出一片天,如果一个人看重自己在职场中的地位,那么进入一家传统企业,对他来说就是灾难:你领导的领导都升不上去,什么时候才轮的上你?其实这四个方面,之所以按照“价值观、兴趣、自身情况、目标”这个顺序来排,除了重要程度之外,“容不容易变”也是个重要考虑因素。一个人的价值观是很难改变,就像你十年前讨厌一个人,到现在也好不到哪儿去。相对来说,职业目标就容易一些:一年前还想着在大厂做高管,现如今有了孩子,找工作的时候先看离家近的。总 结:拿到一份offer,先看它符不符合我们的价值观,工作内容感不感兴趣,再结合自身实际情况,来看它能否帮我们达成自己的职业目标。把握住这些原则,我们就能在一份合适的工作中,完成职业初期的积累;而不是深陷“跳槽漩涡”,在兜兜转转中空耗时间。3月20号我们邀请了,前端职业规划师Maxwell,在线live为大家讲解跳槽、面试相关的实战攻略。

March 17, 2019 · 1 min · jiezi

前端面试题--HTML+CSS

前言貌似又到了一年一度跑路跳槽的时刻,由于个人的一些原因最近也参加了很多面试,发现有很多基础性的东西掌握程度还是不够,故此想总结一下最近面试遇到的问题以及个人认为比较重要的东西,留给自己消化,也分享给有需要的小伙伴如果文章中有出现纰漏、错误之处,还请看到的小伙伴多多指教,先行谢过好了,废话不多说,以下 ↓HTML1. Doctype作用,HTML5 为什么只需要写 <!DOCTYPE HTML>doctype是一种标准通用标记语言的文档类型声明,目的是告诉标准通用标记语言解析器要使用什么样的文档类型定义(DTD)来解析文档.<!DOCTYPE>声明必须是HTML文档的第一行,位于html标签之前HTML5不基于SGML,所以不需要引用DTD。在HTML5中<!DOCTYPE>只有一种SGML: 标准通用标记语言,是现时常用的超文本格式的最高层次标准2. 行内元素有哪些,块级元素有哪些,空(void)元素有那些行内元素:a span i img input select b 等块级元素:div ul ol li h1~h6 p table 等空元素:br hr link 等3. 简述一下你对HTML语义化的理解简单来说,就是合适的标签做合适的事情,这样具有以下好处:有助于构架良好的HTML结构,有利于搜索引擎的建立索引、抓取,利于SEO有利于不同设备的解析有利于构建清晰的机构,有利于团队的开发、维护4. 常见的浏览器内核有哪些,介绍一下你对浏览器内核的理解Trident内核:IEGecko内核:NETSCAPE6及以上版本,火狐Presto内核:Opera7及以上。[Opera内核原为:Presto,现为:Blink;]Webkit内核:Safari,Chrome等。[Chrome的:Blink(WebKit的分支)]浏览器内核又可以分成两部分:渲染引擎和JS引擎。 渲染引擎主要负责取得网页的内容、整理讯息、计算网页的显示方式等,JS引擎则是解析Javascript语言,执行javascript语言来实现网页的动态效果。5. html5有哪些新特性语义化标签: header footer nav section article aside 等增强型表单:date(从一个日期选择器选择一个日期) email(包含 e-mail 地址的输入域) number(数值的输入域) range(一定范围内数字值的输入域) search(用于搜索域) tel(定义输入电话号码字段) 等视频和音频:audio videoCanvas绘图 SVG绘图地理定位:Geolocation拖放API:dragweb worker:是运行在后台的 JavaScript,独立于其他脚本,不会影响页面的性能web storage: localStorage sessionStorageWebSocket: HTML5开始提供的一种在单个 TCP 连接上进行全双工通讯的协议6. 描述一下 cookie,sessionStorage 和 localStorage 的区别特性 Cookie localStorage sessionStorage 生命周期 可设置失效时间,没有设置的话,默认是关闭浏览器后失效 除非被手动清除,否则将会永久保存 仅在当前网页会话下有效,关闭页面或浏览器后就会被清除 存放数据大小 4KB左右 可以保存5MB的信息 http请求 每次都会携带在HTTP头中,如果使用cookie保存过多数据会带来性能问题 仅在客户端(即浏览器)中保存,不参与和服务器的通信 7. 如何实现浏览器内多个标签页之间的通信使用localStorage: localStorage.setItem(key,value)、localStorage.getItem(key)websocket协议webworker多个标签页之间的通信8. HTML5的离线存储怎么使用,解释一下工作原理HTML5的离线存储9. src与href的区别区别:src用于替代这个元素,而href用于建立这个标签与外部资源之间的关系<link href=“style.css” rel=“stylesheet” />浏览器加载到这里的时候,html的渲染和解析不会暂停,css文件的加载是同时进行的<script src=“script.js”></script>当浏览器解析到这句代码时,页面的加载和解析都会暂停直到浏览器拿到并执行完这个js文件10. 表单提交中Get和Post方式的区别Get一般用于从服务器上获取数据,Post向服务器传送数据Get传输的数据是拼接在Url之后的,对用户是可见的;Post的传输数据对用户是不可见的Get传送的数据量较小,不能大于2KB。Post传送的数据量较大,一般被默认为不受限制Get安全性非常低,Post安全性较高在FORM提交的时候,如果不指定Method,则默认为Get请求CSS1. css盒子模型,box-sizing属性的理解css的盒模型由content(内容)、padding(内边距)、border(边框)、margin(外边距)组成。但盒子的大小由content+padding+border这几部分决定box-sizing是一个CSS3属性,与盒子模型有着密切联系。即决定元素的宽高如何计算,box-sizing有三个属性:box-sizing: content-box|border-box|inherit:content-box 使得元素的宽高即为内容区的宽高(默认模式)border-box: 计算方式content + padding + border = 本身元素大小,即缩小了content大小inherit 指定box-sizing属性的值,应该从父元素继承2. 清除浮动,什么时候需要清除浮动,清除浮动都有哪些方法浮动的元素是脱离文档标准流的,如果我们不清楚浮动,那么就会造成父元素高度塌陷,影响页面布局。清除浮动的方式:为父元素设置高度为父元素添加overflow:hidden伪元素.fix::after { content:""; display:block; clear:both;}使用伪元素的好处:不增加冗余的DOM节点,符合语义化overflow:hidden可以触发BFC机制。BFC:块级格式化上下文,创建了 BFC的元素就是一个独立的盒子,它规定了内部如何布局,并且与这个独立盒子里的布局不受外部影响,当然它也不会影响到外面的元素,计算BFC的高度时,浮动元素也参与计算3. 如何让一个不定宽高的盒子水平垂直居中定位的方式.father { position: relative;}.son { position: absolute; top: 0; right: 0; bottom: 0; left: 0; margin: auto;}css3属性.father { position: relative;}.son { position: absolute; top: 50%; left: 50%; transform: translate(-50%, -50%);}flex布局.father { display: flex; justify-content: center; align-items: center;}4. px和em和rem的区别px: 像素,相对长度单位。像素px是相对于显示器屏幕分辨率而言的em的值并不是固定的,会继承父级元素的字体大小,代表倍数rem的值并不是固定的,始终是基于根元素 <html> 的,也代表倍数5. position的值有哪些static: 默认值。没有定位,元素出现在正常的流中relative(相对定位):生成相对定位的元素,相对于其正常(原先本身)位置进行定位absolute(绝对定位):生成绝对定位的元素,相对于static定位以外的第一个父元素进行定位fixed(固定定位):生成绝对定位的元素,相对于浏览器窗口进行定位6. display:none与visibility:hidden的区别区别display:nonevisibility:hidden的是否占据空间不占据任何空间,在文档渲染时,该元素如同不存在(但依然存在文档对象模型树中)该元素空间依旧存在是否渲染会触发reflow(回流),进行渲染只会触发repaint(重绘),因为没有发现位置变化,不进行渲染是否是继承属性不是继承属性,元素及其子元素都会消失是继承属性,若子元素使用了visibility:visible,则不继承,这个子孙元素又会显现出7. CSS中link 和@import的区别link属于XHTML标签,@import完全是CSS提供的一种方式,只能加载CSS加载顺序的差别,当一个页面被加载的时候,link引用的CSS会同时被加载,而@import引用的CSS 会等到页面全部被下载完再被加载兼容性的差别。由于@import是CSS2.1提出的所以老的浏览器不支持,而link标签无此问题当使用javascript控制dom去改变样式的时候,只能使用link标签,因为@import不是dom可以控制的8. 什么是响应式设计,响应式设计的基本原理是什么响应式网站设计是一个网站能够兼容多个终端,而不是为每一个终端做一个特定的版本。基本原理是通过媒体查询检测不同的设备屏幕尺寸做处理9. 为什么要初始化CSS样式因为浏览器的兼容问题,不同浏览器对有些标签的默认值是不同的,如果没对 CSS 初始化往往会出现浏览器之间的页面显示差异初始化样式会对 SEO 有一定的影响10. CSS3有哪些新特性实现圆角border-radius,阴影box-shadow,边框图片border-image对文字加特效text-shadow,强制文本换行word-wrap,线性渐变linear-gradient实现旋转transform:rotate(90deg),缩放scale(0.85,0.90),translate(0px,-30px)定位,倾斜skew(-9deg,0deg);增加了更多的CSS选择器、多背景、rgba()唯一引入的伪元素是::selection;实现媒体查询@media,多栏布局flex过渡transition 动画animation11. ::before 和 :after中双冒号和单冒号有什么区别?解释一下这2个伪元素的作用单冒号(:)用于CSS3伪类,双冒号(::)用于CSS3伪元素。(伪元素由双冒号和伪元素名称组成),双冒号是在当前规范中引入的,用于区分伪类和伪元素12. CSS优化、提高性能的方法有哪些移除空的css规则(Remove empty rules)正确使用display的属性不滥用浮动、web字体不声明过多的font-size不在选择符中使用ID标识符遵守盒模型规则尽量减少页面重排、重绘抽象提取公共样式,减少代码量13. 重绘和回流重绘和回流14. flex布局可以参考一下阮大大的文章 flex布局教程–阮一峰15. css预处理器提供了一种css的书写方式,常见的就是 SAAS文档 和 LESS文档后记面试问题会不定时更新,后面也会抽时间总结一些javaScript、框架以及小程序方面的面试题,有需要的小伙伴可以点击关注我哦,最后祝大家都能找到一份满意的工作。 ...

March 16, 2019 · 1 min · jiezi

阿里面试题BIO和NIO数量问题附答案和代码

一、问题BIO 和 NIO 作为 Server 端,当建立了 10 个连接时,分别产生多少个线程?答案: 因为传统的 IO 也就是 BIO 是同步线程堵塞的,所以每个连接都要分配一个专用线程来处理请求,这样 10 个连接就会创建 10 个线程去处理。而 NIO 是一种同步非阻塞的 I/O 模型,它的核心技术是多路复用,可以使用一个链接上的不同通道来处理不同的请求,所以即使有 10 个连接,对于 NIO 来说,开启 1 个线程就够了。二、BIO 代码实现public class DemoServer extends Thread { private ServerSocket serverSocket; public int getPort() { return serverSocket.getLocalPort(); } public void run() { try { serverSocket = new ServerSocket(0); while (true) { Socket socket = serverSocket.accept(); RequestHandler requestHandler = new RequestHandler(socket); requestHandler.start(); } } catch (IOException e) { e.printStackTrace(); } finally { if (serverSocket != null) { try { serverSocket.close(); } catch (IOException e) { e.printStackTrace(); } } } } public static void main(String[] args) throws IOException { DemoServer server = new DemoServer(); server.start(); try (Socket client = new Socket(InetAddress.getLocalHost(), server.getPort())) { BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(client.getInputStream())); bufferedReader.lines().forEach(s -> System.out.println(s)); } } }// 简化实现,不做读取,直接发送字符串class RequestHandler extends Thread { private Socket socket; RequestHandler(Socket socket) { this.socket = socket; } @Override public void run() { try (PrintWriter out = new PrintWriter(socket.getOutputStream());) { out.println(“Hello world!”); out.flush(); } catch (Exception e) { e.printStackTrace(); } } }服务器端启动 ServerSocket,端口 0 表示自动绑定一个空闲端口。调用 accept 方法,阻塞等待客户端连接。利用 Socket 模拟了一个简单的客户端,只进行连接、读取、打印。当连接建立后,启动一个单独线程负责回复客户端请求。这样,一个简单的 Socket 服务器就被实现出来了。(图片来源于杨晓峰)三、NIO 代码实现public class NIOServer extends Thread { public void run() { try (Selector selector = Selector.open(); ServerSocketChannel serverSocket = ServerSocketChannel.open();) {// 创建 Selector 和 Channel serverSocket.bind(new InetSocketAddress(InetAddress.getLocalHost(), 8888)); serverSocket.configureBlocking(false); // 注册到 Selector,并说明关注点 serverSocket.register(selector, SelectionKey.OP_ACCEPT); while (true) { selector.select();// 阻塞等待就绪的 Channel,这是关键点之一 Set<SelectionKey> selectedKeys = selector.selectedKeys(); Iterator<SelectionKey> iter = selectedKeys.iterator(); while (iter.hasNext()) { SelectionKey key = iter.next(); // 生产系统中一般会额外进行就绪状态检查 sayHelloWorld((ServerSocketChannel) key.channel()); iter.remove(); } } } catch (IOException e) { e.printStackTrace(); } } private void sayHelloWorld(ServerSocketChannel server) throws IOException { try (SocketChannel client = server.accept();) { client.write(Charset.defaultCharset().encode(“Hello world!”)); } } // 省略了与前面类似的 main}首先,通过 Selector.open() 创建一个 Selector,作为类似调度员的角色。然后,创建一个 ServerSocketChannel,并且向 Selector 注册,通过指定 SelectionKey.OP_ACCEPT,告诉调度员,它关注的是新的连接请求。注意:为什么我们要明确配置非阻塞模式呢?这是因为阻塞模式下,注册操作是不允许的,会抛出 IllegalBlockingModeException 异常。Selector 阻塞在 select 操作,当有 Channel 发生接入请求,就会被唤醒。在 sayHelloWorld 方法中,通过 SocketChannel 和 Buffer 进行数据操作,在本例中是发送了一段字符串。可以看到,在前面两个样例中,IO 都是同步阻塞模式,所以需要多线程以实现多任务处理。而 NIO 则是利用了单线程轮询事件的机制,通过高效地定位就绪的 Channel,来决定做什么,仅仅 select 阶段是阻塞的,可以有效避免大量客户端连接时,频繁线程切换带来的问题,应用的扩展能力有了非常大的提高。下面这张图对这种实现思路进行了形象地说明。(图片来源于杨晓峰)四、参考资料Java核心36讲近期热门文章Java 最常见的 200+ 面试题如果你喜欢本文,扫描二维码关注微信公众号「王磊的博客」 ...

March 16, 2019 · 2 min · jiezi

PHP面试常考之设计模式——策略模式

你好,是我琉忆,PHP程序员面试笔试系列图书的作者。本周(2019.3.11至3.15)的一三五更新的文章如下:周一:PHP面试常考之设计模式——工厂模式周三:PHP面试常考之设计模式——建造者模式周五:PHP面试常考之设计模式——策略模式自己上传了一本电子书“5种原则和23种设计模式”到百度云,关注公众号:“琉忆编程库”,回复:“23”,我发给你。以下内容如需转载,请注明作者和出处。策略模式介绍策略模式定义了一系列的算法,并将每一个算法封装起来,而且使它们还可以相互替换。策略模式让算法独立于使用它的客户而独立变化。UML图说明抽象策略角色: 策略类,通常由一个接口或者抽象类实现。具体策略角色:包装了相关的算法和行为。环境角色:持有一个策略类的引用,最终给客户端调用。应用场景1、 多个类只区别在表现行为不同,可以使用Strategy模式,在运行时动态选择具体要执行的行为。2、 需要在不同情况下使用不同的策略(算法),或者策略还可能在未来用其它方式来实现。3、 对客户隐藏具体策略(算法)的实现细节,彼此完全独立。使用策略模式的好处1、 策略模式提供了管理相关的算法族的办法。策略类的等级结构定义了一个算法或行为族。恰当使用继承可以把公共的代码转移到父类里面,从而避免重复的代码。2、 策略模式提供了可以替换继承关系的办法。继承可以处理多种算法或行为。如果不是用策略模式,那么使用算法或行为的环境类就可能会有一些子类,每一个子类提供一个不同的算法或行为。但是,这样一来算法或行为的使用者就和算法或行为本身混在一起。决定使用哪一种算法或采取哪一种行为的逻辑就和算法或行为的逻辑混合在一起,从而不可能再独立演化。继承使得动态改变算法或行为变得不可能。3、 使用策略模式可以避免使用多重条件转移语句。多重转移语句不易维护,它把采取哪一种算法或采取哪一种行为的逻辑与算法或行为的逻辑混合在一起,统统列在一个多重转移语句里面,比使用继承的办法还要原始和落后。实现代码<?phpheader(“Content-type:text/html;Charset=utf-8”);//抽象策略接口abstract class Strategy{ abstract function wayToSchool();}//具体策略角色class BikeStrategy extends Strategy{ function wayToSchool(){ echo “骑自行车去上学”; }}class BusStrategy extends Strategy{ function wayToSchool(){ echo “乘公共汽车去上学”; }}class TaxiStrategy extends Strategy{ function wayToSchool(){ echo “骑出租车去上学”; }}//环境角色class Context{ private $strategy; //获取具体策略 function getStrategy($strategyName){ try{ $strategyReflection = new ReflectionClass($strategyName); $this->strategy = $strategyReflection->newInstance(); }catch(ReflectionException $e){ $this->strategy = “”; } } function goToSchool(){ $this->strategy->wayToSchool(); // var_dump($this->strategy); }}//测试$context = new Context();$context->getStrategy(“BusStrategy”);$context->goToSchool(); ?>自己上传了一本电子书“5种原则和23种设计模式”到百度云,关注公众号:“琉忆编程库”,回复:“23”,我发给你。自己编写的《PHP程序员面试笔试宝典》和《PHP程序员面试笔试真题解析》书籍,已在各大电商平台销售。书籍在手,Offer我有。更多PHP相关的面试知识、考题可以关注公众号获取:琉忆编程库对本文有什么问题或建议都可以进行留言,我将不断完善追求极致,感谢你们的支持。

March 15, 2019 · 1 min · jiezi

前端笔试中两道与节点有关的算法题

1.分别用广度优先遍历和深度优先遍历展开下面节点示例var tree = { name: ‘root’, children: [{ name: ‘child1’, children: [{ name: ‘child1_1’, children: [] }, { name: ‘child1_2’, children: [] }] }, { name: ‘child2’, children: [{ name: ‘child2_1’, children: [] }] }, { name: ‘child3’, children: [{ name: ‘child2_1’, children: [] }] }]};广度优先遍历:function wideTraversal(node) { var nodes = []; if (node != null) { var queue = []; queue.unshift(node); while (queue.length != 0) { var item = queue.shift(); nodes.push(item.name); var children = item.children; for (var i = 0; i < children.length; i++) { queue.push(children[i]); } } } return nodes;}console.log(wideTraversal(tree))输出结果:[ ‘root’,‘child1’,‘child2’,‘child3’,‘child1_1’,‘child1_2’,‘child2_1’,‘child2_1’ ]深度优先遍历:function traverseTree(node) { var child = node.children, arr = []; arr.push(node.name); if (child) { child.forEach(function(node) { arr = arr.concat(traverseTree(node)); }); } return arr;}console.log(traverseTree(tree))输出结果:[ ‘root’,‘child1’,‘child1_1’,‘child1_2’,‘child2’,‘child2_1’,‘child3’,‘child2_1’ ]2.关系型数组转换成树形结构对象类似:var data = [ { parentId: 0, id: 1, value: ‘1’ }, { parentId: 3, id: 2, value: ‘2’ }, { parentId: 0, id: 3, value: ‘3’ }, { parentId: 1, id: 4, value: ‘4’ }, { parentId: 1, id: 5, value: ‘5’ }]期望输出:[{id:1,value:‘1’,children:[{id:4,value:‘4’,children:[]},{id:5,value:‘5’,children:[]}]},{id:3,value:‘3’,children:[id:2,value:‘2’,children:[]]}]代码:var getJsonTree = function(data, parentId) { var itemArr = []; for (var i = 0; i < data.length; i++) { var node = data[i]; //data.splice(i, 1) if (node.parentId == parentId) { var newNode = { id: node.id, value: node.value, children: getJsonTree(data, node.id) }; itemArr.push(newNode); } } return itemArr;}console.log(getJsonTree(data, 0)) ...

March 13, 2019 · 1 min · jiezi

前端常见面试题总结

个人总结的一些常见面试题,欢迎各位star,issues,同时非常欢迎贡献问题!一起来完善.不定期更新常见前端面试题总结

March 13, 2019 · 1 min · jiezi

PHP面试常考之设计模式——建造者模式

你好,是我琉忆,PHP程序员面试笔试系列图书的作者。本周(2019.3.11至3.15)的一三五更新的文章如下:周一:PHP面试常考之设计模式——工厂模式周三:PHP面试常考之设计模式——建造者模式周五:PHP面试常考之设计模式——策略模式自己整理了一篇“什么是观察者模式?”的文章,关注公众号:“琉忆编程库”,回复:“观察”,我发给你。以下内容如需转载,请注明作者和出处。建造者模式介绍建造者模式又名生成器模式,是一种对象构建模式。它可以将复杂对象的建造过程抽象出来(抽象类别),使这个抽象过程的不同实现方法可以构造出不同表现(属性)的对象。建造者模式是一步一步创建一个复杂的对象,它允许用户只通过指定复杂对象的类型和内容就可以构建它们,用户不需要知道内部的具体构建细节。例如,一辆汽车由轮子,发动机以及其他零件组成,对于普通人而言,我们使用的只是一辆完整的车,这时,我们需要加入一个构造者,让他帮我们把这些组件按序组装成为一辆完整的车。UML图UML图说明Builder:抽象构造者类,为创建一个Product对象的各个部件指定抽象接口。ConcreteBuilder:具体构造者类,实现Builder的接口以构造和装配该产品的各个部件。定义并明确它所创建的表示。提供一个检索产品的接口Director:指挥者,构造一个使用Builder接口的对象。Product:表示被构造的复杂对象。ConcreateBuilder创建该产品的内部表示并定义它的装配过程。包含定义组成部件的类,包括将这些部件装配成最终产品的接口。实现示例<?php /*** chouxiang builer*/abstract class Builder{ protected $car; abstract public function buildPartA(); abstract public function buildPartB(); abstract public function buildPartC(); abstract public function getResult();}class CarBuilder extends Builder{ function __construct() { $this->car = new Car(); } public function buildPartA(){ $this->car->setPartA(‘发动机’); } public function buildPartB(){ $this->car->setPartB(‘轮子’); } public function buildPartC(){ $this->car->setPartC(‘其他零件’); } public function getResult(){ return $this->car; }}class Car{ protected $partA; protected $partB; protected $partC; public function setPartA($str){ $this->partA = $str; } public function setPartB($str){ $this->partB = $str; } public function setPartC($str){ $this->partC = $str; } public function show() { echo “这辆车由:”.$this->partA.’,’.$this->partB.’,和’.$this->partC.‘组成’; }}class Director{ public $myBuilder; public function startBuild() { $this->myBuilder->buildPartA(); $this->myBuilder->buildPartB(); $this->myBuilder->buildPartC(); return $this->myBuilder->getResult(); } public function setBuilder(Builder $builder) { $this->myBuilder = $builder; }}$carBuilder = new CarBuilder();$director = new Director();$director->setBuilder($carBuilder);$newCar = $director->startBuild();$newCar->show();?>自己整理了一篇“什么是观察者模式?”的文章,关注公众号:“琉忆编程库”,回复:“观察”,我发给你。自己编写的《PHP程序员面试笔试宝典》和《PHP程序员面试笔试真题解析》书籍,已在各大电商平台销售。书籍在手,offer我有。更多PHP相关的面试知识、考题可以关注公众号获取:琉忆编程库对本文有什么问题或建议都可以进行留言,我将不断完善追求极致,感谢你们的支持。 ...

March 13, 2019 · 1 min · jiezi

Java 200+ 面试题补充③ Dubbo 模块

昨天在我的 Java 面试粉丝群里,有一个只有一年开发经验的小伙伴只用了三天时间,就找到了一个年薪 20 万的工作,真是替他感到开心。他的经历告诉我们:除了加强自我实战经验之外,还要努力积累自己的理论知识。人生没有白走的路,也没有白吃的苦。你学的某一种知识,在将来某一天一定会给你惊喜!高兴之余,让我们来看,今天的内容。本文是 Java 最常见的 200+ 面试题 的第三个补充模块。第一个补充模块:面试题补充① ThreadLocal 模块第二个补充模块:面试题补充② Netty 模块1.Dubbo 是什么?Dubbo 是一款高性能、轻量级的开源 RPC 框架,提供服务自动注册、自动发现等高效服务治理方案, 可以和 Spring 框架无缝集成。2.Dubbo 的使用场景有哪些?透明化的远程方法调用:就像调用本地方法一样调用远程方法,只需简单配置,没有任何API侵入。软负载均衡及容错机制:可在内网替代 F5 等硬件负载均衡器,降低成本,减少单点。服务自动注册与发现:不再需要写死服务提供方地址,注册中心基于接口名查询服务提供者的IP地址,并且能够平滑添加或删除服务提供者。3.Dubbo 核心功能有哪些?Remoting:网络通信框架,提供对多种NIO框架抽象封装,包括“同步转异步”和“请求-响应”模式的信息交换方式。Cluster:服务框架,提供基于接口方法的透明远程过程调用,包括多协议支持,以及软负载均衡,失败容错,地址路由,动态配置等集群支持。Registry:服务注册,基于注册中心目录服务,使服务消费方能动态的查找服务提供方,使地址透明,使服务提供方可以平滑增加或减少机器。4.Dubbo 核心组件有哪些?Provider:暴露服务的服务提供方Consumer:调用远程服务消费方Registry:服务注册与发现注册中心Monitor:监控中心和访问调用统计Container:服务运行容器5.Dubbo 服务器注册与发现的流程?Provider(提供者)绑定指定端口并启动服务。提供者连接注册中心,并发本机 IP、端口、应用信息和提供服务信息发送至注册中心存储。Consumer(消费者),连接注册中心 ,并发送应用信息、所求服务信息至注册中心。注册中心根据消费者所求服务信息匹配对应的提供者列表发送至 Consumer 应用缓存。Consumer 在发起远程调用时基于缓存的消费者列表择其一发起调用。Provider 状态变更会实时通知注册中心、在由注册中心实时推送至 Consumer。6.Dubbo 支持哪些协议,它们的优缺点有哪些?Dubbo: 单一长连接和 NIO 异步通讯,适合大并发小数据量的服务调用,以及消费者远大于提供者。传输协议 TCP,异步 Hessian 序列化。RMI: 采用 JDK 标准的 RMI 协议实现,传输参数和返回参数对象需要实现 Serializable 接口,使用 Java 标准序列化机制,使用阻塞式短连接,传输数据包大小混合,消费者和提供者个数差不多,可传文件,传输协议 TCP。多个短连接 TCP 协议传输,同步传输,适用常规的远程服务调用和 RMI 互操作。在依赖低版本的 Common-Collections 包,Java 序列化存在安全漏洞。WebService:基于 WebService 的远程调用协议,集成 CXF 实现,提供和原生 WebService 的互操作。多个短连接,基于 HTTP 传输,同步传输,适用系统集成和跨语言调用。HTTP: 基于 Http 表单提交的远程调用协议,使用 Spring 的 HttpInvoke 实现。多个短连接,传输协议 HTTP,传入参数大小混合,提供者个数多于消费者,需要给应用程序和浏览器 JS 调用。Hessian:集成 Hessian 服务,基于 HTTP 通讯,采用 Servlet 暴露服务,Dubbo 内嵌 Jetty 作为服务器时默认实现,提供与 Hession 服务互操作。多个短连接,同步 HTTP 传输,Hessian 序列化,传入参数较大,提供者大于消费者,提供者压力较大,可传文件。Memcache:基于 Memcache实现的 RPC 协议。Redis:基于 Redis 实现的RPC协议。7.Dubbo 推荐什么协议?推荐使用 Dubbo 协议。8.Dubbo 有哪些注册中心?Multicast 注册中心:Multicast 注册中心不需要任何中心节点,只要广播地址,就能进行服务注册和发现,基于网络中组播传输实现。Zookeeper 注册中心:基于分布式协调系统 Zookeeper 实现,采用 Zookeeper 的 watch 机制实现数据变更。Redis 注册中心:基于 Redis 实现,采用 key/map 存储,住 key 存储服务名和类型,map 中 key 存储服务 url,value 服务过期时间。基于 Redis 的发布/订阅模式通知数据变更。Simple 注册中心。9.Dubbo 的注册中心集群挂掉,发布者和订阅者之间还能通信么?可以通讯。启动 Dubbo 时,消费者会从 Zookeeper 拉取注册的生产者的地址接口等数据,缓存在本地。每次调用时,按照本地存储的地址进行调用。10.Dubbo 使用的是什么通信框架?默认使用 Netty 作为通讯框架。11.Dubbo集群提供了哪些负载均衡策略?Random LoadBalance: 随机选取提供者策略,有利于动态调整提供者权重。截面碰撞率高,调用次数越多,分布越均匀。RoundRobin LoadBalance: 轮循选取提供者策略,平均分布,但是存在请求累积的问题。LeastActive LoadBalance: 最少活跃调用策略,解决慢提供者接收更少的请求。ConstantHash LoadBalance: 一致性 Hash 策略,使相同参数请求总是发到同一提供者,一台机器宕机,可以基于虚拟节点,分摊至其他提供者,避免引起提供者的剧烈变动。默认为 Random 随机调用。12.Dubbo的集群容错方案有哪些?Failover Cluster:失败自动切换,当出现失败,重试其它服务器。通常用于读操作,但重试会带来更长延迟。Failfast Cluster:快速失败,只发起一次调用,失败立即报错。通常用于非幂等性的写操作,比如新增记录。Failsafe Cluster:失败安全,出现异常时,直接忽略。通常用于写入审计日志等操作。Failback Cluster:失败自动恢复,后台记录失败请求,定时重发。通常用于消息通知操作。Forking Cluster:并行调用多个服务器,只要一个成功即返回。通常用于实时性要求较高的读操作,但需要浪费更多服务资源。可通过 forks=”2″ 来设置最大并行数。Broadcast Cluster:广播调用所有提供者,逐个调用,任意一台报错则报错 。通常用于通知所有提供者更新缓存或日志等本地资源信息。默认的容错方案是 Failover Cluster。13.Dubbo 支持哪些序列化方式?默认使用 Hessian 序列化,还有 Duddo、FastJson、Java 自带序列化。14.Dubbo 超时设置有哪些方式?Dubbo 超时设置有两种方式:服务提供者端设置超时时间,在Dubbo的用户文档中,推荐如果能在服务端多配置就尽量多配置,因为服务提供者比消费者更清楚自己提供的服务特性。服务消费者端设置超时时间,如果在消费者端设置了超时时间,以消费者端为主,即优先级更高。因为服务调用方设置超时时间控制性更灵活。如果消费方超时,服务端线程不会定制,会产生警告。15.服务调用超时会怎么样?dubbo 在调用服务不成功时,默认是会重试两次。16.Dubbo 在安全方面有哪些措施?Dubbo 通过 Token 令牌防止用户绕过注册中心直连,然后在注册中心上管理授权。Dubbo 还提供服务黑白名单,来控制服务所允许的调用方。17.Dubbo 类似的分布式框架还有哪些?比较著名的就是 Spring Cloud。18.Dubbo 和 Spring Cloud 有什么关系?Dubbo 是 SOA 时代的产物,它的关注点主要在于服务的调用,流量分发、流量监控和熔断。而 Spring Cloud诞生于微服务架构时代,考虑的是微服务治理的方方面面,另外由于依托了 Spirng、Spirng Boot的优势之上,两个框架在开始目标就不一致,Dubbo 定位服务治理、Spirng Cloud 是打造一个生态。19.Dubbo 和 Spring Cloud 有什么哪些区别?Dubbo 底层是使用 Netty 这样的 NIO 框架,是基于 TCP 协议传输的,配合以 Hession 序列化完成 RPC 通信。Spring Cloud 是基于 Http 协议 Rest 接口调用远程过程的通信,相对来说 Http 请求会有更大的报文,占的带宽也会更多。但是 REST 相比 RPC 更为灵活,服务提供方和调用方的依赖只依靠一纸契约,不存在代码级别的强依赖,这在强调快速演化的微服务环境下,显得更为合适,至于注重通信速度还是方便灵活性,具体情况具体考虑。最后关于更多 Dubbo 的信息,访问官网:http://dubbo.incubator.apache… 查看所有面试题:Java 最常见的 200+ 面试题参考文章http://youzhixueyuan.com/dubb…近期热文推荐Java 最常见的 200+ 面试题你真的懂 == 和 equals 的区别吗?程序员精美简历Top榜—面试必备程序员专属精美简历合集—第二弹 ...

March 12, 2019 · 2 min · jiezi

PHP面试常考之设计模式——工厂模式

你好,是我琉忆,PHP程序员面试笔试系列图书的作者。本周(2019.3.11至3.15)的一三五更新的文章如下:周一:PHP面试常考之设计模式——工厂模式周三:PHP面试常考之设计模式——建造者模式周五:PHP面试常考之设计模式——策略模式今天这篇文章主要讲解的是PHP面试常考的设计模式之工厂模式。工厂模式其实可以划分为:简单工厂模式、工厂方法模式、抽象工厂模式等。具体它们有什么区别,用途有哪些呢?以下我将进行讲解。自己整理了一篇“设计模式需要遵守的5大原则”的文章,关注公众号:“琉忆编程库”,回复:“原则”,我发给你。以下内容如需转载,请注明作者和出处。1、简单工厂模式介绍:简单工厂模式又称为静态工厂方法(Static Factory Method)模式,它属于类创建型模式。在简单工厂模式中,可以根据参数的不同返回不同类的实例。简单工厂模式专门定义一个类来负责创建其他类的实例,被创建的实例通常都具有共同的父类。UML图:说明:CashFactory类:负责创建具体产品的实例CashSuper类:抽象产品类,定义产品子类的公共接口CreateCashAccept 类:具体产品类,实现Product父类的接口功能,也可添加自定义的功能简单工厂模式最大的优点在于实现对象的创建和对象的使用分离,将对象的创建交给专门的工厂类负责,但是其最大的缺点在于工厂类不够灵活,增加新的具体产品需要修改工厂类的判断逻辑代码,而且产品较多时,工厂方法代码将会非常复杂。实现示例:<?php //简单工厂模式class Cat{ function __construct() { echo “I am Cat class <br>”; }}class Dog{ function __construct() { echo “I am Dog class <br>”; }}class Factory{ public static function CreateAnimal($name){ if ($name == ‘cat’) { return new Cat(); } elseif ($name == ‘dog’) { return new Dog(); } }}$cat = Factory::CreateAnimal(‘cat’);$dog = Factory::CreateAnimal(‘dog’);2、工厂方法模式介绍:工厂方法模式通过定义一个抽象的核心工厂类,并定义创建产品对象的接口,创建具体产品实例的工作延迟到其工厂子类去完成。这样做的好处是核心类只关注工厂类的接口定义,而具体的产品实例交给具体的工厂子类去创建。当系统需要新增一个产品是,无需修改现有系统代码,只需要添加一个具体产品类和其对应的工厂子类,是系统的扩展性变得很好,符合面向对象编程的开闭原则;UML图:说明:Product:抽象产品类ConcreteProduct:具体产品类Factory:抽象工厂类ConcreteFactory:具体工厂类工厂方法模式是简单工厂模式的进一步抽象和推广。由于使用了面向对象的多态性,工厂方法模式保持了简单工厂模式的优点,而且克服了它的缺点。在工厂方法模式中,核心的工厂类不再负责所有产品的创建,而是将具体创建工作交给子类去做。这个核心类仅仅负责给出具体工厂必须实现的接口,而不负责产品类被实例化这种细节,这使得工厂方法模式可以允许系统在不修改工厂角色的情况下引进新产品。实现示例:<?php interface Animal{ public function run(); public function say();}class Cat implements Animal{ public function run(){ echo “I ran slowly <br>”; } public function say(){ echo “I am Cat class <br>”; }}class Dog implements Animal{ public function run(){ echo “I’m running fast <br>”; } public function say(){ echo “I am Dog class <br>”; }}abstract class Factory{ abstract static function createAnimal();}class CatFactory extends Factory{ public static function createAnimal() { return new Cat(); }}class DogFactory extends Factory{ public static function createAnimal() { return new Dog(); }}$cat = CatFactory::createAnimal();$cat->say();$cat->run();$dog = DogFactory::createAnimal();$dog->say();$dog->run();自己整理了一篇“设计模式需要遵守的5大原则”的文章,关注公众号:“琉忆编程库”,回复:“原则”,我发给你。3、抽象工厂模式介绍:抽象工厂模式提供一个创建一系列相关或相互依赖对象的接口,而无须指定它们具体的类。抽象工厂模式又称为Kit模式,属于对象创建型模式。此模式是对工厂方法模式的进一步扩展。在工厂方法模式中,一个具体的工厂负责生产一类具体的产品,即一对一的关系,但是,如果需要一个具体的工厂生产多种产品对象,那么就需要用到抽象工厂模式了。为了便于理解此模式,这里介绍两个概念:产品等级结构:产品等级结构即产品的继承结构,如一个抽象类是电视机,其子类有海尔电视机、海信电视机、TCL电视机,则抽象电视机与具体品牌的电视机之间构成了一个产品等级结构,抽象电视机是父类,而具体品牌的电视机是其子类。产品族 :在抽象工厂模式中,产品族是指由同一个工厂生产的,位于不同产品等级结构中的一组产品,如海尔电器工厂生产的海尔电视机、海尔电冰箱,海尔电视机位于电视机产品等级结构中,海尔电冰箱位于电冰箱产品等级结构中。UML类图:说明:具体类图的功能可以看UML图的说明实现示例:<?php interface TV{ public function open(); public function use();}class HaierTv implements TV{ public function open() { echo “Open Haier TV <br>”; } public function use() { echo “I’m watching TV <br>”; }}interface PC{ public function work(); public function play();}class LenovoPc implements PC{ public function work() { echo “I’m working on a Lenovo computer <br>”; } public function play() { echo “Lenovo computers can be used to play games <br>”; }}abstract class Factory{ abstract public static function createPc(); abstract public static function createTv();}class ProductFactory extends Factory{ public static function createTV() { return new HaierTv(); } public static function createPc() { return new LenovoPc(); }}$newTv = ProductFactory::createTV();$newTv->open();$newTv->use();$newPc = ProductFactory::createPc();$newPc->work();$newPc->play();自己编写的《PHP程序员面试笔试宝典》和《PHP程序员面试笔试真题解析》书籍,已在各大电商平台销售,两本可以帮助你更快更好的拿到offer的书。更多PHP相关的面试知识、考题可以关注公众号获取:琉忆编程库对本文有什么问题或建议都可以进行留言,我将不断完善追求极致,感谢你们的支持。 ...

March 11, 2019 · 2 min · jiezi

Leetcode讲解视频(持续更新中...)

【Leetcode】146.LRU缓存机制【Leetcode】108.将有序数组转换为二叉搜索树【Leetcode】107.二叉树的层次遍历【Leetcode】106. 从中序与后序遍历序列构造二叉树【Leetcode】105. 从前序与中序遍历序列构造二叉树【Leetcode】101.镜像二叉树【Leetcode】100.相同的树

March 10, 2019 · 1 min · jiezi

5面阿里,终获offer(Java后端)

作者:ppxyn。本文来自读者投稿。该文已加入笔主的开源项目——JavaGuide(一份涵盖大部分Java程序员所需要掌握的核心知识的文档类项目),地址:https://github.com/Snailclimb… 。觉得不错的话,记得点个Star。在接触 Java 之前我接触的比较多的是硬件方面,用的比较多的语言就是C和C++。到了大三我才正式选择 Java 方向,到目前为止使用Java到现在大概有一年多的时间,所以Java算不上很好。刚开始投递的时候,实习刚辞职,也没准备笔试面试,很多东西都忘记了。所以,刚开始我并没有直接就投递阿里,毕竟心里还是有一点点小害怕的。于是,我就先投递了几个不算大的公司来练手,就是想着刷刷经验而已或者说是练练手(ps:还是挺对不起那些公司的)。面了一个月其他公司后,我找了我实验室的学长内推我,后面就有了这5次面试。下面简单的说一下我的这5次面试:4次技术面+1次HR面,希望我的经历能对你有所帮助。一面(技术面)自我介绍(主要讲自己会的技术细节,项目经验,经历那些就一语带过,后面面试官会问你的)。聊聊项目(就是一个很普通的分布式商城,自己做了一些改进),让我画了整个项目的架构图,然后针对项目抛了一系列的提高性能的问题,还问了我做项目的过程中遇到了那些问题,如何解决的,差不读就这些吧。可能是我前面说了我会数据库优化,然后面试官就开始问索引、事务隔离级别、悲观锁和乐观锁、索引、ACID、MVVC这些问题。浏览器输入URL发生了什么? TCP和UDP区别? TCP如何保证传输可靠性?讲下跳表怎么实现的?哈夫曼编码是怎么回事?非递归且不用额外空间(不用栈),如何遍历二叉树后面又问了很多JVM方面的问题,比如Java内存模型、常见的垃圾回收器、双亲委派模型这些你有什么问题要问吗?二面(技术面)自我介绍(主要讲自己会的技术细节,项目经验,经历那些就一语带过,后面面试官会问你的)。操作系统的内存管理机制进程和线程的区别说下你对线程安全的理解volatile 有什么作用 ,sychronized和lock有什么区别ReentrantLock实现原理用过CountDownLatch么?什么场景下用的?AQS底层原理。造成死锁的原因有哪些,如何预防?加锁会带来哪些性能问题。如何解决?HashMap、ConcurrentHashMap源码。HashMap是线程安全的吗?Hashtable呢?ConcurrentHashMap有了解吗?是否可以实习?你有什么问题要问吗?三面(技术面)有没有参加过 ACM 或者他竞赛,有没有拿过什么奖?( 我说我没参加过ACM,本科参加过数学建模竞赛,名次并不好,没拿过什么奖。面试官好像有点失望,然后我又赶紧补充说我和老师一起做过一个项目,目前已经投入使用。面试官还比较感兴趣,后面又和他聊了一下这个项目。)研究生期间,做过什么项目,发过论文吗?有什么成果吗?你觉得你有什么优点和缺点?你觉得你相比于那些比你更优秀的人欠缺什么?有读过什么源码吗?(我说我读过 Java 集合框架和 Netty 的,面试官说 Java 集合前几面一定问的差不多,就不问了,然后就问我 Netty的,我当时很慌啊!)介绍一下自己对 Netty 的认识,为什么要用。说说业务中,Netty 的使用场景。什么是TCP 粘包/拆包,解决办法。Netty线程模型。Dubbo 在使用 Netty 作为网络通讯时候是如何避免粘包与半包问题?讲讲Netty的零拷贝?巴拉巴拉问了好多,我记得有好几个我都没回答上来,心里想着凉凉了啊。用到了那些开源技术、在开源领域做过贡献吗?常见的排序算法及其复杂度,现场写了快排。红黑树,B树的一些问题。讲讲算法及数据结构在实习项目中的用处。自己的未来规划(就简单描述了一下自己未来的设想啊,说的还挺诚恳,面试官好像还挺满意的)你有什么问题要问吗?四面(半个技术面)三面面完当天,晚上9点接到面试电话,感觉像是部门或者项目主管。 这个和之前的面试不大相同,感觉面试官主要考察的是你解决问题的能力、学习能力和团队协作能力。让我讲一个自己觉得最不错的项目。然后就巴拉巴拉的聊,我记得主要是问了项目是如何进行协作的、遇到问题是如何解决的、与他人发生冲突是如何解决的这些。感觉聊了挺久。出现 OOM 后你会怎么排查问题?自己平时是如何学习新技术的?除了 Java 还回去了解其他技术吗?上一段实习经历的收获。NginX如何做负载均衡、常见的负载均衡算法有哪些、一致性哈希的一致性是什么意思、一致性哈希是如何做哈希的你有什么问题问我吗?还有一些其他的,想不起来了,感觉这一面不是偏向技术来问。五面(HR面)自我介绍(主要讲能突出自己的经历,会的编程技术一语带过)。你觉得你有什么优点和缺点?如何克服这些缺点?说一件大学里你自己比较有成就感的一件事情,为此付出了那些努力。你前面跟其他面试官讲过一些你做的项目吧?可以给我讲讲吗?你要考虑到我不是一个做技术的人,怎么让我也听得懂。项目中有什么问题,你怎么解决的?你最大的收获是什么?你目前有面试过其他公司吗?如果让你选,这些公司和阿里,你选哪个?(送分题,回答不好可能送命)你期望的工作地点是哪里?你有什么问题吗?总结可以看出面试官问我的很多问题都是比较常见的问题,所以记得一定要提前准备,还要深入准备,不要回答的太皮毛。很多时候一个问题可能会牵扯出很多问题,遇到不会的问题不要慌,冷静分析,如果你真的回答不上来,也不要担心自己是不是就要挂了,很可能这个问题本身就比较难。表达能力和沟通能力太重要了,一定要提前练一下,我自身就是一个不太会说话的人,所以,面试前我对于自我介绍、项目介绍和一些常见问题都在脑子里练了好久,确保面试的时候能够很清晰和简洁的说出来。等待面试的过程和面试的过程真的好熬人,那段时间我压力也比较大,好在我私下找到学长聊了很多,心情也好了很多。面试之后及时总结,面的好的话,不要得意,尽快准备下一场面试吧!我觉得我还算是比较幸运的,最后也祝大家都能获得心仪的Offer。专注Java知识和面试技能分享!我已经整理好了一份Java 学习必备的书籍+视频+文档汇总,内容比较多,你可以在公众号后台回复关键“1”,我会免费无套路把这些都给你。

March 10, 2019 · 1 min · jiezi

7 个Web开发工程师面试题和回答策略

翻译:疯狂的技术宅原文:https://www.indeed.com/hire/i…本文首发微信公众号:jingchengyideng欢迎关注,每天都给你推送新鲜的前端技术文章不管你是面试官还是求职者,里面的思路都能让你获益匪浅。Web开发人员的职责是什么?解析:Web工程师应该充分了解自己的角色,以及怎样为Web设计和开发做出贡献。这个问题能帮助面试官了解求职者打算怎样配合团队,以及他们是否知道自己要做些什么。面试官想知道什么:清楚地了解Web开发过程他们强调那些职责他们打算怎样发挥自己的能力参考答案:Web工程师负责设计、开发、增强、测试和部署Web应用,其最终目标是创建引人入胜而且用户友好的站点布局和功能。开发人员负责收集并定义需求、维护网站、排除故障并修复错误,遵循最佳开发实践并与其他团队协作。是什么引发了你对Web开发的兴趣?解析:求职者是否很早就成为了Web开发人员?他们是自学成才吗?了解候求职者选择这个方向的原因有助于确定他们对该职业的热情和信心。面试官想知道什么:热情动机经验参考答案:我以前的工作是营销协调员,我经常使用在线工具来制作电子邮件广告。后来我找到了拖放功能的bug,并发现如果我直接编辑HTML,可以很灵活的实现我想要的外观。这时我开始着迷于Web开发人员具有想象力、艺术性和技术性的能力,同时想开发出一种对他人有帮助的产品。什么是W3C,它为什么很重要?解析:Web开发人员应对其生成的内容负责,要确保所有用户都可以访问,并遵循W3C标准。这个问题将告诉你求职者是否具 W3C 的知识并在工作中遵循它们。面试官想知道什么:基本知识关心标准关于他们如何遵守标准的细节参考答案:W3C 的意思是 World Wide Consortium,它是一个专注于开发和标准化 Web 的国际社区。作为Web开发人员,强制执行这些标准可确保所有浏览器都能访问Web内容,并优化用户体验。例如:使用符合 W3C 标准的 CSS 和 XML 可以使每个网站的功能相似,也可以改善搜索引擎优化。解释如何优化和减少Web应用的加载时间。解析:几乎一半的用户希望在两秒钟内加载网页。提出此问题以了解求职者是否了解页面加载时间对用户体验的影响,以及Web开发人员应如何优化加载时间。面试官想知道什么:他们对优化Web应用的重视程度了解可用于分析网站速度的工具他们具有优化能力的证据参考答案:尽管有很多技术手段能减少加载时间,但我始终依赖优化图像、启用浏览器缓存和最小化HTTP 请求。我评估网站速度的首选工具是 Google PageSpeed Insights。我曾经通过 soley 启用浏览器缓存,成功地将页面加载时间从 2.1 秒减少到 0.7 秒。HTTP/2 和 HTTP 1.1 有什么区别?解析:是否了解并使用过 HTTP/2 ,能够体现求职者的知识水平,让你了解他们是否能够跟上技术的发展。面试官想知道什么:整体 HTTP 知识能够回忆起具体细节洞察他们的技术水平参考答案:HTTP/2 旨在提高 Web 应用的性能,可以减少加载时间并改善浏览器与服务器之间的通信,并且能够使应用更简单、快速。我非常支持使用 HTTP/2,因为我曾经分析过相关数据,并知道了 HTTP/2 是如何将页面加载时间减少20%的。你熟悉哪种编程语言?解析:HTML,CSS,SQL,PHP,Ruby,Python 和 JavaScript 是 Web 工程师应该能够轻松驾驭的常见编程语言。这个问题能帮助面试官评估求职者的经验、偏好、优势和劣势。面试官想知道什么:熟悉的编码语言对特定语言的偏好程度是否适合岗位和公司参考答案:我精通 HTML、CSS 和 PHP,并且 SQL 和 JavaScript的水平也不错。我打算学习 Python,最近正在研究适合自己的Python课程,以便在空闲时间学习。你怎样平衡客户的苛刻要求?解析:Web 开发工程师可能面临意想不到的挑战,包括缩短开发周期和面对过于雄心勃勃的客户。清楚地了解求职者如何处理这种情况,将有助于面试官了解他们做事的优先级和思考过程。面试官想知道什么:在压力下的表现沟通技巧致力于发布高品质的产品参考答案:在我面对苛刻的需求时,会想办法充分了解对方的真正要求,优先处理自己的任务并与客户保持开放的沟通渠道。我很难容忍Bug的产生,也不赞成用一些取巧的手段破坏最终的产品形态,不过让客户满意是一个高度优先的选项。我会随时向客户通报我的进展并高效地完成任务。欢迎继续阅读本专栏其它高赞文章:12个令人惊叹的CSS实验项目世界顶级公司的前端面试都问些什么CSS Flexbox 可视化手册过节很无聊?还是用 JavaScript 写一个脑力小游戏吧!从设计者的角度看 ReactCSS粘性定位是怎样工作的一步步教你用HTML5 SVG实现动画效果程序员30岁前月薪达不到30K,该何去何从7个开放式的前端面试题React 教程:快速上手指南本文首发微信公众号:jingchengyideng欢迎扫描二维码关注公众号,每天都给你推送新鲜的前端技术文章

March 9, 2019 · 1 min · jiezi

Java 200+ 面试题补充② Netty 模块

让我们每天都能看到自己的进步。老王带你打造最全的 Java 面试清单,认真把一件事做到最好。本文是前文《Java 最常见的 200+ 面试题》的第二个补充模块,第一模块为:《Java 200+ 面试题补充 ThreadLocal 模块》。1.Netty 是什么?Netty 是一款基于 NIO(Nonblocking I/O,非阻塞IO)开发的网络通信框架,对比于 BIO(Blocking I/O,阻塞IO),他的并发性能得到了很大提高。难能可贵的是,在保证快速和易用性的同时,并没有丧失可维护性和性能等优势。2.Netty 的特点是什么?高并发:Netty 是一款基于 NIO(Nonblocking IO,非阻塞IO)开发的网络通信框架,对比于 BIO(Blocking I/O,阻塞IO),他的并发性能得到了很大提高。传输快:Netty 的传输依赖于零拷贝特性,尽量减少不必要的内存拷贝,实现了更高效率的传输。封装好:Netty 封装了 NIO 操作的很多细节,提供了易于使用调用接口。3.什么是 Netty 的零拷贝?Netty 的零拷贝主要包含三个方面:Netty 的接收和发送 ByteBuffer 采用 DIRECT BUFFERS,使用堆外直接内存进行 Socket 读写,不需要进行字节缓冲区的二次拷贝。如果使用传统的堆内存(HEAP BUFFERS)进行 Socket 读写,JVM 会将堆内存 Buffer 拷贝一份到直接内存中,然后才写入 Socket 中。相比于堆外直接内存,消息在发送过程中多了一次缓冲区的内存拷贝。Netty 提供了组合 Buffer 对象,可以聚合多个 ByteBuffer 对象,用户可以像操作一个 Buffer 那样方便的对组合 Buffer 进行操作,避免了传统通过内存拷贝的方式将几个小 Buffer 合并成一个大的 Buffer。Netty 的文件传输采用了 transferTo 方法,它可以直接将文件缓冲区的数据发送到目标 Channel,避免了传统通过循环 write 方式导致的内存拷贝问题。4.Netty 的优势有哪些?使用简单:封装了 NIO 的很多细节,使用更简单。功能强大:预置了多种编解码功能,支持多种主流协议。定制能力强:可以通过 ChannelHandler 对通信框架进行灵活地扩展。性能高:通过与其他业界主流的 NIO 框架对比,Netty 的综合性能最优。稳定:Netty 修复了已经发现的所有 NIO 的 bug,让开发人员可以专注于业务本身。社区活跃:Netty 是活跃的开源项目,版本迭代周期短,bug 修复速度快。5.Netty 的应用场景有哪些?典型的应用有:阿里分布式服务框架 Dubbo,默认使用 Netty 作为基础通信组件,还有 RocketMQ 也是使用 Netty 作为通讯的基础。6.Netty 高性能表现在哪些方面?IO 线程模型:同步非阻塞,用最少的资源做更多的事。内存零拷贝:尽量减少不必要的内存拷贝,实现了更高效率的传输。内存池设计:申请的内存可以重用,主要指直接内存。内部实现是用一颗二叉查找树管理内存分配情况。串形化处理读写:避免使用锁带来的性能开销。高性能序列化协议:支持 protobuf 等高性能序列化协议。7.Netty 和 Tomcat 的区别?Netty 和 Tomcat 最大的区别就在于通信协议,Tomcat 是基于 http 协议的,他的实质是一个基于 http 协议的web容器,但是 Netty 不一样,他能通过编程自定义各种协议,因为 Netty 能够自己编码/解码字节流,完成类似Redis 访问的功能,这就是 Netty 和 Tomcat 最大的区别。8.Netty 中有那种重要组件?Channel:Netty 网络操作抽象类,它除了包括基本的 I/O 操作,如 bind、connect、read、write 等。EventLoop:主要是配合 Channel 处理 I/O 操作,用来处理连接的生命周期中所发生的事情。ChannelFuture:Netty 框架中所有的 I/O 操作都为异步的,因此我们需要 ChannelFuture 的 addListener()注册一个 ChannelFutureListener 监听事件,当操作执行成功或者失败时,监听就会自动触发返回结果。ChannelHandler:充当了所有处理入站和出站数据的逻辑容器。ChannelHandler 主要用来处理各种事件,这里的事件很广泛,比如可以是连接、数据接收、异常、数据转换等。ChannelPipeline:为 ChannelHandler 链提供了容器,当 channel 创建时,就会被自动分配到它专属的 ChannelPipeline,这个关联是永久性的。9.Netty 发送消息有几种方式?Netty 有两种发送消息的方式:直接写入 Channel 中,消息从 ChannelPipeline 当中尾部开始移动;写入和 ChannelHandler 绑定的 ChannelHandlerContext 中,消息从 ChannelPipeline 中的下一个 ChannelHandler 中移动。10.默认情况 Netty 起多少线程?何时启动?Netty 默认是 CPU 处理器数的两倍,bind 完之后启动。11.Netty 支持哪些心跳类型设置?readerIdleTime:为读超时时间(即测试端一定时间内未接受到被测试端消息)。writerIdleTime:为写超时时间(即测试端一定时间内向被测试端发送消息)。allIdleTime:所有类型的超时时间。最后如果大家想更深入的了解 Netty,推荐一本很不错的掘金小册给大家(扫描二维码八折优惠)。查看全部面试题目:《Java 最常见的 200+ 面试题》参考文档https://blog.csdn.net/chenssy…https://blog.csdn.net/summerZ...https://blog.csdn.net/thinkin...https://www.jianshu.com/p/a19...https://blog.csdn.net/linuu/a…往期文章推荐Java 最常见的 200+ 面试题Java 200+ 面试题补充 ThreadLocal 模块你真的懂 == 和 equals 的区别吗?程序员精美简历Top榜—面试必备程序员专属精美简历合集——第二弹 ...

March 8, 2019 · 1 min · jiezi

程序员写简历时必须注意的技术词汇拼写(持续更新...)

概 述每年到了这个时候又快到了找工作的旺季,也就是俗称的「金三银四」。对于程序员来说,这个时候也是最好找工作和跳槽的时机。很多程序员花了太多的时间在 自己的技术水平提升 和 笔试、面试题准备之上,却忽略了找工作第一步所需要的一个严谨且靠谱的简历,导致最终结果是 其实技术没问题,但投出去的简历惨不忍睹,甚至连面试的机会都没有。而我觉得程序员写简历,第一步就是需要注意严谨而规范地使用各种技术词汇,但是我发现不论个人博客或者是个人简历很多人可能都是想当然的凭自己的印象来拼写技术词汇, 因此本文就几个大的方面来总结一下程序员写简历时必须注意的词汇拼写注意事项,以供必要时查阅!当然这里列出的词汇可能有限,大家可以集思广益,一起提交和补充!后端开发正确拼写错误拼写举例RESTfulRestful、RestFulWebweb、WEBJavaJAVA、javaLinuxLINUX、linuxRuntimeruntimeMySQLmysqlSQLitesqliteSpringspringSpring BootSpringboot、SpringBootSpring CloudSpringcloud、SpringCloudSpring MVCSpringMVC、SpringmvcNoSQLnosql、NOSQLMyBatismybatis、MybatisMavenmaven、MAVENRedisredisElasticsearchelasticsearch、ElasticSearch持续更新中…持续更新中…前端开发正确拼写错误拼写举例HTMLHtml、htmlHTTPHttp、httpCSSCss、cssJavaScriptjavascript、Javascript、js、JSNode.jsnode、Node、NodeJS、nodejsVue.jsvue、VUE 、vue.js持续更新中…持续更新中…客户端开发正确拼写错误拼写举例AppAPPObjective-COC、oc、object-cXcodexcode、XCODE、XCodeAndroidandroid、安卓iOSios、IOSiPhoneiphone、IPHONEApp StoreAppStoreAndroid Studioandroid studio持续更新中…持续更新中…其 他正确拼写错误拼写举例GitGIT、gitEclipseeclipse持续更新中…持续更新中…后 记当然这里列出的词汇可能有限,大家可以集思广益,一起提交和补充,然后将其纳入进来!由于能力有限,若有错误或者不当之处,还请大家批评指正,一起学习交流!My Personal Blog:CodeSheep 程序羊

March 8, 2019 · 1 min · jiezi

PHP面试MySQL数据库的面试题

你好,是我琉忆,PHP程序员面试笔试系列图书的作者。本周(2019.3.4至3.8)的一三五更新的文章如下:周一:PHP面试MySQL数据库的基础知识周三:PHP面试MySQL数据库的索引周五:PHP面试MySQL数据库的面试真题今天周五,提前祝各位周末愉快。自己整理了一篇“什么是数据库三级封锁协议?”的文章,关注公众号:“琉忆编程库”,回复:“锁”,我发给你。以下内容部分来自《PHP程序员面试笔试宝典》和《PHP程序员面试笔试真题解析》如需转载请注明出处。【真题1】执行以下 SQL 语句后将发生( )。BEGIN TRANSACTIONDELETE FROM MYTABLE WHERE ID=1DELETE FROM OTHERTABLEROLLBACK TRANSACTIONA.OTHERTABLE 中的内容将被删除B.OTHERTABLE 和 MYTABLE 中的内容都会被删除C.OTHERTABLE 中的内容将被删除,MYTABLE 中 ID 是 1 的内容将被删除D.数据库没有变化参考答案:D。分析:这个查询是一个事务,并且这个事务的最后有回滚,数据库不会有变化。【真题2】如何进行数据库优化?数据库优化的过程可以使用以下的方法进行:1)选取最适用的字段属性,尽可能减少定义字段长度,尽量把字段设置NOT NULL,例如’省份、性别’,最好设置为ENUM。2)使用连接(JOIN)来代替子查询。① 删除没有任何订单客户:DELETE FROM customerinfo WHERE customerid NOT in(SELECT customerid FROM orderinfo)。② 提取所有没有订单客户: SELECT FROM customerinfo WHERE customerid NOT in(SELECT customerid FROM orderinfo)。③ 提高b的速度优化:SELECT FROM customerinfo LEFT JOIN orderid customerinfo. customerid=orderinfo.customerid WHERE orderinfo.customerid IS NULL。3)使用联合(UNION)来代替手动创建的临时表。创建临时表:SELECT name FROM ’nametest’ UNION SELECT username FROM ’nametest2’。4)事务处理。保证数据完整性,例如添加和修改。同时,如果两者成立,则都执行,一者失败都失败:mysql_query(“BEGIN”);mysql_query(“INSERT INTO customerinfo (name) VALUES (’$name1’)";mysql_query(“SELECT * FROM ‘orderinfo’ where customerid=”.$id”);mysql_query(“COMMIT”);5)锁定表,优化事务处理。用一个SELECT语句取出初始数据,通过一些计算,用UPDATE语句将新值更新到表中。包含有WRITE关键字的LOCK TABLE语句可以保证在UNLOCK TABLES命令被执行之前,不会有其他的访问来对customerinfo表进行插入、更新或者删除的操作。mysql_query(“LOCK TABLE customerinfo READ, orderinfo WRITE”);mysql_query(“SELECT customerid FROM ‘customerinfo’ where id=”.$id);mysql_query(“UPDATE ‘orderinfo’ SET ordertitle=’$title’ where customerid=”.$id);mysql_query(“UNLOCK TABLES”);6)使用外键,优化锁定表。把customerinfo里的customerid映射到orderinfo里的customerid,任何一条没有合法的customerid的记录不会写到orderinfo里。 CREATE TABLE customerinfo ( customerid INT NOT NULL, PRIMARY KEY(customerid) )TYPE = INNODB; CREATE TABLE orderinfo ( orderid INT NOT NULL, customerid INT NOT NULL, PRIMARY KEY(customerid,orderid), FOREIGN KEY (customerid) REFERENCES customerinfo (customerid) ON DELETE CASCADE )TYPE = INNODB;注意:‘ON DELETE CASCADE’,该参数保证当customerinfo表中的一条记录删除的话同时也会删除order。表中的该用户的所有记录,注意使用外键时要定义数据库引擎为INNODB。【真题3】如何选择正确的存储引擎?在MySQL中有两个存储引擎:MyISAM和InnoDB,每个引擎都有利有弊。MyISAM适合于一些需要大量查询的应用,但其对于有大量写操作的支持并不是很好。甚至只是需要update一个字段,整个表都会被锁起来,而其他进程,就算是读进程都无法操作直到读操作完成。另外,MyISAM 对于 SELECT COUNT(*) 这类的计算是超快无比的。InnoDB 的趋势会是一个非常复杂的存储引擎,对于一些小的应用,它会比 MyISAM 还慢。但是它支持“行锁”,于是在写操作比较多的时候,会更优秀。并且,它还支持更多的高级应用,例如事务。【真题4】用什么方法检查PHP脚本的执行效率(通常是脚本执行时间)和数据库SQL的效率(通常是数据库query时间),并定位和分析脚本执行和数据库查询的瓶颈所在?参考答案:检查PHP脚本的执行效率的方法如下:可以在检查的代码开头记录一个时间,然后在代码的结尾也记录一个时间,结尾时间减去开头时间取这个时间的差值,从而检查PHP的脚本执行效率,记录时间可以使用microtime()函数。检查数据库SQL的效率的方法如下:可以通过explain显示MySQL如何使用索引来处理select语句及连接表,帮助选择更好的索引和写出更优化的查询语句。然后启用slow query log记录慢查询,通过查看SQL的执行时间和效率来定位分析脚本执行的问题和瓶颈所在。自己整理了一篇“什么是数据库三级封锁协议?”的文章,关注公众号:“琉忆编程库”,回复:“锁”,我发给你。【真题5】 以下说法正确的是( )。A.使用索引能加快插入数据的速度B.良好的索引策略有助于防止跨站攻击C.应当根据数据库的实际应用合理设计索引D.删除一条记录将导致整个表的索引被破坏参考答案:C。分析:索引的作用主要是帮助数据库快速查找到对应的数据,并不能加快插入数据的速度,所以,选项A错误。索引不能够帮助防止跨站攻击,所以,选项B错误。创建合理的索引需要分析数据库的实际用途并找出它的弱点。优化脚本中的冗余查询同样也能提高数据库效率。索引是占用物理空间的,所以在实际的应用中是要合理设计使用索引的。所以,选项C正确。索引是一种表结构,删除一条数据也不会影响到整个表的索引,并且索引不一定是数字,也可以是字符串。所以,选项D错误。【真题6】下列关于全文检索技术的说法中,不正确的是( )。A.Sphinx是一个基于SQL的全文检索引擎,可以结合MySQL做全文搜索,它可以提供比数据库本身更专业的搜索功能B.Solr是新一代的全文检索组件,它比Lucene的搜索效率高很多,还能支持HTTP的访问方式,PHP调用Solr也很方便C.MySQL中把一个字段建立FULLTEXT索引,就可以实现全文检索,目前MyISAM和InnoDB的table都支持FULLTEXT索引D.Lucene附带的二元分词分析器CJKAnalyzer切词速度很快,能满足一般的全文检索需要参考答案:B。分析:Sphinx是一个基于SQL的全文检索引擎,可以结合MySQL、PostgreSQL做全文搜索,它可以提供比数据库本身更专业的搜索功能,使得应用程序更容易实现专业化的全文检索。Solr是一个独立的企业级搜索应用服务器,用户可以通过HTTP请求访问,它是采用JAVA5开发,基于Lucene的全文搜索服务器,同时对其进行了扩展,提供了比Lucene更为丰富的查询语言,同时实现了可配置、可扩展并对查询性能进行了优化,并且提供了一个完善的功能管理界面,是一款非常优秀的全文搜索引擎。并且Solr比Lucene的搜索效率高很多,但是PHP调用Solr并不方便,选项B的说法错误。MySQL中的MyISAM和InnoDB都是支持FULLTEXT全文索引的。全文搜索引擎可以在不使用模板匹配操作的情况下查找单词或短语。【真题7】考虑如下SQL语句,哪个选项能对返回记录的条数进行限制?( )(双选)SELECT * FROM MY_TABLEA.如果可能,那么把查询转换成存储例程B.如果程序允许,那么给查询指定返回记录的范围C.如果可能,那么添加 where 条件D.如果DBMS允许,那么把查询转换成视图参考答案:B、C。分析:有两个方法能限制返回记录的条数——使用 where 条件或limit关键字指定查询返回的记录的范围。通常情况下,如果没有特殊需要,那么尽量不要用 select *,这会浪费大量的数据缓存。以上内容摘自《PHP程序员面试笔试宝典》和《PHP程序员面试笔试真题解析》书籍,目前本书没有电子版,可到各大电商平台购买纸质版。更多PHP相关的面试知识、考题可以关注公众号获取:琉忆编程库对本文有什么问题或建议都可以进行留言,我将不断完善追求极致,感谢你们的支持。 ...

March 8, 2019 · 1 min · jiezi

7个 HTML 面试题及回答策略

翻译:疯狂的技术宅原文:https://www.indeed.com/hire/i…本文首发微信公众号:jingchengyideng欢迎关注,每天都给你推送新鲜的前端技术文章不管你是面试官还是求职者,里面的思路都能让你获益匪浅。你能不能用纯 HTML 写出页面?解析:有许多无需深入掌握 HTML 就能做出页面的工具。这个问题询问求职者,当他们可以借助一些工具生成页面时,对 HTML 还有多大的兴趣。面试官想知道什么:求职者对HTML的兴趣求职者从头开始构建网站的能力对网页构建工具的看法参考答案:网站构建工具非常适合外行和专业开发人员,但我认为了解底层技术非常重要,因此我可以更好地控制网站的外观和行为。例如,当我使用 WordPress 构建网站时,发现通过插入自己的 HTML 而不是依赖提供的工具,可以得到更好的结果。你还知道哪些其他语言?解析:今天的网站通常依赖于HTML,层叠样式表(CSS)和JavaScript。一个优秀的网站开发人员应该能够熟练使用这些语言。面试官想知道什么:面试者的知识面面试者是否可以用多种语言来构建网站面试者是否需要接受额外的培训参考答案:在我开发更大的网站时,习了CSS,因此我不必手动更新每个页面的外观。我也有一些 JavaScript 经验。我只知道基本功能,但我可以使用它们来使网站更有用和互动。我一直在观看教程,以使我的 JavaScript 功能更强大。你将如何帮助我们的内容团队获得更好的搜索引擎结果?解析:理想情况下,你需要一位了解搜索引擎友好重要性的 HTML 专家。这个问题能够解释求职者是否理解良好的 HTML 对搜索引擎的重要性,以及在搜索引擎结果页面(SERP)上获得更好的排名方面可以发挥的作用。面试官想知道什么:了解搜索引擎排名的重要性批判性思维技巧能够与他人合作参考答案:我想做几件事来确保你的网站获得最高排名。我首先要创建一个帮助搜索引擎蜘蛛浏览网站的站点地图。接下来,我会检查每个页面以确保它使用有用的标签,包括标题标签。最后,我会使用链接检查器找到网站上失效的链接。对你来说,创建一个新网站最困难的部分是什么?解析:了解申请人的优缺点非常重要。这个问题让面试官有机会了解潜在员工对工作难以胜任的部分。面试官可能需要考虑这种弱点对团队的影响。面试官想知道什么:找出求职者的优点和缺点确定求职者的正确职位团队能不能接受求职者参考答案:我对规划阶段感到非常沮丧。准确了解网站的外观会告诉我如何构建网站。我对这部分有点迷恋,因为我希望网站看起来尽可能完美。你最喜欢HTML5的哪些功能?解析:HTML5 是 HTML 的最新版本。通过提出这个问题,面试官可以发现求职者是否了解构建网站时使用的最新元素和属性。此问题还可以用来确定求职者对技术的热情。面试官想知道什么:求职者对 HTML5 的熟悉程度求职者是否跟上技术的发展求职者是否适合当前项目参考答案:今天的互联网用户希望找到包含视频、音乐和动画的多媒体内容。 HTML5 为最新的多媒体文件提供了更稳定的环境。我最喜欢的是可以向网站添加多媒体内容,而不必担心网站会崩溃或拒绝播放内容。你如何改进我们公司当前的网站?解析:这是一个重要的问题,测试求职者的技术知识和批判性思维技能。它还能揭示求职者在面试前是否花时间研究你的业务。寻找解决用户体验、布局和其他功能的深思熟虑的答案。你不必同意求职者的意见,但你应该找到提议背后的逻辑。面试官想知道什么:批判性思维技巧求职者愿意质疑了解求职者的审美观点参考答案:我认为该网站的布局很好,使访问者可以轻松找到他们需要的内容和产品。另一方面,我不喜欢当访问主页时视频自动开始播放。我会禁用自动播放功能,让访问者点击播放按钮。“你想在未来几年内开发哪些类型的项目?解析:这个问题的答案让你对求职者的职业规划有所了解。它还展示了人们感兴趣的挑战类型。面试官可能会发现自己公司的计划与未来员工的职业目标是否保持一致。面试官想知道什么:求职者的职业规划这个人能呆多久求职者需要什么类型的培训参考答案:我对开发交互式网站和应用感兴趣,因此我一直在学习更多有关 JavaScript、Ruby on Rails、Swift、React 和变得越来越重要的知识。理想情况下,我想开发一个将 VR 整合到网站或应用中的项目。欢迎继续阅读本专栏其它高赞文章:12个令人惊叹的CSS实验项目世界顶级公司的前端面试都问些什么CSS Flexbox 可视化手册过节很无聊?还是用 JavaScript 写一个脑力小游戏吧!从设计者的角度看 ReactCSS粘性定位是怎样工作的一步步教你用HTML5 SVG实现动画效果程序员30岁前月薪达不到30K,该何去何从7个开放式的前端面试题React 教程:快速上手指南本文首发微信公众号:jingchengyideng欢迎扫描二维码关注公众号,每天都给你推送新鲜的前端技术文章

March 8, 2019 · 1 min · jiezi

假如我是面试官,我会这样虐你

又是金三银四的时候,我希望这份面试题能够祝你一臂之力!自我和项目相关1、自我介绍2、你觉得自己的优点是?你觉得自己有啥缺点?3、你有哪些 offer?4、你为什么要离开上家公司?你上家公司在xxx,我们公司在xxx,离这么远为什么要选择我们这里?5、上家公司的同事和领导是怎么评价你的?6、介绍下你的上家公司是做哪块的吧7、在上家公司你做了哪些项目?8、你在项目中是什么角色?9、这个项目的数据量和并发量多大?10、这个项目用的什么技术?11、项目过程中有遇到什么很印象深刻的问题吗?12、是怎么解决的这些问题?13、项目有没有还可以继续优化的地方?14、该怎么优化?有什么思路没?15、叫你设计一个并发xxx,数据存储量xxx 量级的系统,你该如何设计?从你知道的点尽可能的多说出些?Java 基础1、Object 对象的方法有哪些?分别有什么作用?该什么场景用?2、Integer 的常量缓存池3、Java 特性?什么是多态?举个例子4、重载重写的区别?5、画下 HashMap 的结构图?HashMap 、 HashTable 和 ConcurrentHashMap 的区别?使用场景分别是?6、HashMap 中怎么解决冲突的?7、ConcurrentHashMap 和 HashTable 中线程安全的区别?为啥建议用 ConcurrentHashMap ?能把 ConcurrentHashMap 里面的实现详细的讲下吗?8、保证线程安全的还有其他的方式吗?9、讲下 Synchronized?10、讲下 RecentLock 可重入锁? 什么是可重入锁?为什么要设计可重入锁?11、Synchronized 和 RecentLock 有什么区别?这两个有没有深入了解源码?12、讲下 Volatile 吧?他是怎样做到同步的?13、Volatile 为什么不支持原子性?举个例子14、Atomic 怎么设计的?有用过里面的类吗?15、线程安全类和线程不安全的类,项目使用的时候你会怎么选择?怎么判断项目代码哪里会有线程不安全问题?16、Map、List、Set 分别说下你了解到它们有的线程安全类和线程不安全的类?17、TreeSet 清楚吗?能详细说下吗?18、ThreadLocal 了解吗?项目有用过吗?可以说说19、JUC 里面你还知道什么其他的类吗?比如 CountDownLatch、Condition20、从源码详细说下 Java 里面的线程池吧,使用线程池有什么要注意的地方?你们公司有规范吗?JVM1、JAVA 类加载器2、Java 内存结构(注:不是 Java 内存模型,别搞混)3、怎么判断对象是否可 GC?Java 对象有哪些引用类型?有什么区别?4、OOM 出现的有哪些场景?为什么会发生?5、Minor GC 和 Full GC 有什么区别?分析过 GC 日志吗?6、说下你知道的垃圾回收算法7、说下你知道的垃圾收集器8、CMS 和 G1 的区别知道吗?使用场景分别是?你项目中用的是哪个?9、你还知道哪些 JVM 调优参数?10、假如线上服务发生 OOM,有哪些措施可以找到问题?11、假如线上服务 CPU 很高该怎么做?有哪些措施可以找到问题?12、假如线上应用频繁发生 Full GC,有哪些措施可以找到问题?13、一般线上环境遇到 JVM 问题,你会使用哪些工具来分析?找到问题后又该如何去解决呢?Spring1、说下你对 Spring 生态的了解?2、说下你对 Spring AOP 和 IOC 的理解?看过实现原理吗?3、说下 Bean 在 Spring 中的生命周期?4、讲下你知道的 Spring 注解有哪些?该什么场景使用?5、Spring 事务知道吗?有了解过吗?6、说下你刚才说的 SpringBoot 吧,你觉得 SpringBoot 有什么优点?7、SpringBoot 自动化配置是怎么做的?有看过实现源码吗?8、Spring Boot 中最核心的注解 SpringBootApplication 有看过源码分析过吗?9、你的项目中 SpringBoot 用到了哪些和其他技术栈整合的?10、使用 Spring 或者 SpringBoot 有遇到过什么印象深刻的问题吗?当时是怎么解决的?数据库1、你的项目使用的是什么数据库?2、你对数据库了解多少?说下数据库的索引实现和非主键的二级索引3、说下 MySQL 的索引原理4、讲下 InnoDB 和 MyISAM 的区别?使用场景是?5、有和 ElasticSearch 的索引原理对比过吗?6、如何判断一个查询 sql 语句是否使用了索引?7、数据库事务特性和隔离级别8、项目数据库表是你设计的吗?一般要注意什么?如何考虑扩展性?9、项目 MySQL 的数据量和并发量有多大?量大后的影响有哪些,有考虑吗?SQL 调优有哪些技巧?10、说下你项目里面关于数据库印象最深的一个问题?当时是怎么解决的其他1、描述下网页一个 Http 请求到 http://www.54tianzhisheng.cn/ 的整个请求过程2、有比较过 Http 和 RPC 吗?如果叫你设计一个高性能的 Http 或者 RPC,你会从哪些方面考虑?3、项目中我看使用了 xxx (ElasticSearch、Hbase、Redis、Flink 等),有深入了解它们的原理和懂点调优技巧吗?4、项目中我看使用了 xxx (ElasticSearch、Hbase、Redis、Mysql 等),有深入了解它们数据同步是怎么做吗?5、项目中我看使用了 xxx (ElasticSearch、Hbase、Redis、Mysql 等),有深入了解它们常见的监控指标吗?6、如果叫你设计一个秒杀系统,你会从哪些方面考虑?7、如果叫你设计一个电商系统,你会从哪些方面考虑?8、如果叫你设计一个监控告警系统,你会从哪些方面考虑?总结本文的面试题以 HR & 技术官角度常问的面试题,技术方面从 Java 基础、JVM、Spring、数据库、拓展题等方面考察你,当然面试官可能还会问些其他的技术点,我一篇文章也难以概全。总的来说,还是得多准备充分,面试时灵活答辩,相信你最后能拿到满意的 offer!加油,骚年!关注我扫描上面二维码,公众号里回复 面经 可以查看更多面经和面试题! ...

March 8, 2019 · 1 min · jiezi

腾讯大厦与我有个约定(面试精华帖)

在一个雨蒙蒙的清晨,百般无聊的闲逛中,突然回忆起了我这一生中的第一次面试经历。虽然结果是以失败而告终,但此时此刻的回忆,带给我的并不是失败而是成长。作为一名实习生,我深刻的感受到在校园与社会夹缝中生活的不易。也十分的幸运,我还有足够的青春与活力驱动着我去学习,让我的未来不再遥不可及;让我能在社会的这份土壤里扎根成长。这一次的面试地点是成都腾讯大厦,对于我这类常在农村走,从未上过街的学生党来讲,我真的被眼前的惊呆了(没见过世面)。门口的迎宾,高大帅气;前台的女士,仪态端庄;一旁的植物,生机勃勃;楼道的电梯,金碧辉煌;我站的地板,闪闪发光。我内心中油然而生了一份自豪感,如果以后我能在这儿上班。那简直是哇!塞!一瞬间我就到达了指定楼层,这办公环境真的是哇塞!哇塞!超级nice!我艹,这前台小姐姐这么漂亮,我艹,这HR小姐姐好靓啊!比学校的妹子还乖!(我翻不到什么词汇来形容了)。很快我拿到了一份笔试题;1.请描述下状态码304?(5分)表示浏览器端有缓存,并且服务端未更新,不用向服务器端请求资源。2.写出5种css隐藏元素的办法(10分)1.opacity: 0;2.visibility: hidden;3.display: none;4.position: absolute;top: -9999px;left: -9999px;5.clip-path: polygon(0px 0px,0px 0px,0px 0px,0px 0px);我只写出来了4种。第5种确实没遇到过,也没有想到过。3.cookies与session有什么区别?(5分)由于http请求是无状态的,需要cookie来做身份验证1.cookies由服务端创建,发送给浏览器端,当用户发出请求后,带上cookie发送给服务端做验证。2.来回传递过程中,占用带宽,消耗网络资源,并且容易被中间人获取或浏览器端用户篡改十分不安全3.cookies大小只有4k1.session主要存在于服务端,不会被发送到浏览器所以很安全2.如果没有设置过期时间,在会话结束后session会自动消失3.session主要用于服务端保存请求信息的机制答得不够全面,所以请小伙伴们自行查阅资料4.实现一个方法,找出一个数组中重复的元素(10分)举例arr = [1,2,3,4,1,1,2,4,4]输出 [1,2,4]Array.prototype.repeNum = function(){ let new_arr = this.sort(); //先排序 let res = [] ; for( let i = 0 ; i < new_arr.length ; i++){ if(new_arr[i] == new_arr[i+1] && //判断是否重复,是否已经放入容器 new_arr[i] !=new_arr[i-1]){ res.push(new_arr[i]); } } return res}因为题目上说的要实现一个方法,所以我考虑到是给array原型加一个方法5.将这段英文this is a pen首字母大写(10分)法一:function bigLetter(str){let newArr = str.split(" “).map((v,i)=>{ return v.slice(0,1).toUpperCase() + v.slice(1)})return newArr.join(” “)}法二:function bigLetter(str){bigStr = str.toLowerCase().replace(/\b\w+\b/g, function(word){ return word.substring(0,1).toUpperCase()+word.substring(1);});return bigStr; }由于这道面试题,曾经遇见过,所以给出了两种方法。能秀的时候绝对不马虎6.请写出你常用的10个linux命令并说明作用(20分)7.请写出你常用的5个git命令并说明作用(15分)最后两题因为时间比较久远了,有点忘却了。但记得考察的是什么8.关于Promise的题(10分)我印象中有,然后输出什么Promise.then()Promise.resolve(1) Promise.catch()9.react向子组件传状态(15分)constructor(){this.state = { name:‘xxx’}}<Person data={this.state.name} />下面是Person组件<div data={this.props.data} />还有一个空我记不得了,反正一空5分然后交给了HR,过了一会儿HR:今天先回去,后面会在3天内告诉你笔试结果。我:多少分才算过呢?HR:60分我一丝不舍的被送到了门口,唉,好想在这儿多待一会儿,哪怕是让我在这儿干站到我都愿意阿!唉!回去了临近5:30时分,我接到了电话,说我笔试通过了,请问明天下午2点有没时间来面试?对于我们这类无业青年来说,除了时间,我一无所有。第二天相同的时间相同的地方,我却走错了地方,原来成都腾讯大厦有AB两栋。马叔叔,你真有钱又是那高大帅气的迎宾;仪态端庄的前台;生机勃勃的植物;金碧辉煌的电梯;闪闪发光的地板,这个地板简直比我脸还干净。又是那好看到爆的前台小姐姐,又是那漂亮到爆的hr。在如此炎热的8月,我一口就喝下了前台小姐姐送来的水,但是我的内心还是十分的紧张,丝毫没有缓解的迹象。我的腿情不自禁地抖起来,我的手不停使唤地去阻止腿的抖动,然后他们一起抖了起来,随后,HR与面试官一同走了进来,我内心暗想这是什么情况?难道HR面和技术面同时进行?HR拨通了一个电话,对方应该是另一个部门的负责人,然后HR说明到有两个部门都需要前端实习生,所以面试同时进行。我暗自高兴那我的胜算更大啦!我的紧张情绪终于得到了一丝缓解。面试正式开始(由于题目的答案,并不固定所以我就不作答了,而且我个人认为当时也回答的相当不好,所以被pass掉了)面试官:1.先做个自我介绍吧!2.看你项目你用过react,那你来说说react与vuejs的区别3.你能说说react的虚拟DOM吗?4.你这个项目中你负责开发了哪些模块?能说说你主要的开发流程吗?5.项目中有没有用过Eslint6.来讲讲http?7.http请求头有哪些属性8.说说GET与POST的区别9.知道浏览器缓存吗?10.图片懒加载能手写一下吗?11.函数节流与防抖12.讲讲怎么用Promise13.linux如何修改文件权限14.有了解过webpack吗?能说说吗?15.loader与plugin的区别?最后一个问题,你有什么想问我的吗?我在网上看到加速乐是成都团队开发的,加速乐主要有什么用?用了什么技术栈面试官:这个产品我可能讲的话,会讲很久。所以你还有没有什么其他想问的?我:请问面试官你贵姓?(虽然带了工牌,我还是没能看清楚)面试官:XX面试官:你稍等一下一会儿HR告诉我,你先回去,后面我们会给你答复。快到门口的时候,我回头问,是不是凉凉了?HR:技术部门还在商量,暂时还不清楚。我又依依不舍的,哪怕是让我多站会儿我也愿意啊。在回家的路上,我没有骑摩拜单车,而是选择一个人在偌大的城市街头游走。我不断地思索刚刚面试过程重中有哪些回答不好的地方。我一直在等待那个5点多会打过来的电话,结果6点了,我的手机安静的像个睡着的婴儿。我还怀着一丝侥幸猜想,可能还在加班哦。直到夜晚的降临,我也的懂得了这份弦外知音。这次的失利,虽然对我的打击很大但是再大也浇不灭我执着追梦热爱前端的热情,也阻挡不了我更加努力的步伐。收拾好我失落的情绪,到楼下点了份最喜欢吃的鱼香肉丝炒饭,为这次面试旅程画上一个句号。几个月过去了现在的我,回想当时,我不禁的发笑。人总是在不断成长,就算跌倒了,拍拍身上的尘土,还是会向前奔跑。感谢记忆,我将永远记住你带给我的一切,你留在我❤间无论是忧伤还是快乐,现在或将来对我来说已是甜蜜,为此我要永远怀念你,感谢你❤ ...

March 7, 2019 · 1 min · jiezi

给定线段line1(x1,y1)(x2,y2)和线段line2(x3,y3)(x4,y4),求两线段交点。

我的思路两线段交点就是两条线段相等的时候,也就是这个点既满足line1又满足line2。所以我们要先求出这两条线段所在直线的方程式。因为给了两个点,所以我们想到用斜截式(y=kx+b)来求出直线的方程式。然后求出交点。再判断这个点是否在两条线段上(即判断求出的x坐标是否在x1和x2,x3和x4之间)。因为斜截式不满足k=0(即垂直于x轴时)。所以要在k=0时加一个判断。大致流程设交点坐标为(a,b)求出两线段所在直线的方程式(需求k1、b1、k2、b2) –> 将(a,b)代入方程利用与k1、b1、k2、b2的关系求出(a,b) –> 判断特殊情况k=0 –> 判断(a,b)是否在两条线段上 –> 返回结果let line1 = [{x1:0,y1:1},{x2:0,y2:-1}];let line2 = [{x3:0,y3:-1},{x4:2,y4:1}];function point(line1,line2){ // 解构赋值取得 x1,y1,x2,y2,x3,y3,x4,y4 let [{x1,y1},{x2,y2}] = line1; let [{x3,y3},{x4,y4}] = line2; // 利用公式推导出 k、b和x、y的关系。 let k1 = (y1-y2)/(x1-x2); let b1 = y1 - (k1x1); let k2 = (y3-y4)/(x3-x4); let b2 = y3 - (k2x3); // a、b 为交点坐标 let a; let b; // 判断 k=0 时 if(x1==x2){ k1,b1 = 0; a = x1; b = k2a + b2; } if(x3==x4){ k2,b2 = 0; a = x3; b = k1a + b1; } // 判断 交点 是否在两条线段上 if(((a>x1&&a<x2)||(a<x1&&a>x2)||a==x1||a==x2)&&((a>x3&&a<x4)||(a<x3&&a>x4)||a==x3||a==x4)&&x1!=x2&&x3!=x4){ a = (b2-b1)/(k1-k2); b = k1*a + b1; } // 返回结果 let str; if(a!==undefined&&b!==undefined){ str = 交点为(${a},${b}) }else if((k1==k2)&&(b1==b2)&&(((x1==x3)&&(x2==x4))||((x1==x4)&&(x2==x3)))){ str = “两线段重合” }else if(k1==k2){ str = “两线段平行” }else{ str = “两线段不相交” } return str;}console.log(point(line1,line2))可能有些繁琐,希望有简单方法的可爱可以提出来。 ...

March 7, 2019 · 1 min · jiezi

Java 200+ 面试题补充 ThreadLocal 模块

让我们每天都有进步,老王带你打造最全的 Java 面试清单,认真把一件事做到极致。本文是前文《Java 最常见的 200+ 面试题》的第一个补充模块。1.ThreadLocal 是什么?ThreadLocal 是一个本地线程副本变量工具类。主要用于将私有线程和该线程存放的副本对象做一个映射,各个线程之间的变量互不干扰,在高并发场景下,可以实现无状态的调用,适用于各个线程不共享变量值的操作。2.ThreadLocal 工作原理是什么?ThreadLocal 原理:每个线程的内部都维护了一个 ThreadLocalMap,它是一个 Map(key,value)数据格式,key 是一个弱引用,也就是 ThreadLocal 本身,而 value 存的是线程变量的值。也就是说 ThreadLocal 本身并不存储线程的变量值,它只是一个工具,用来维护线程内部的 Map,帮助存和取变量。数据结构,如下图所示:(图片来源于网络)3.ThreadLocal 如何解决 Hash 冲突?与 HashMap 不同,ThreadLocalMap 结构非常简单,没有 next 引用,也就是说 ThreadLocalMap 中解决 Hash 冲突的方式并非链表的方式,而是采用线性探测的方式。所谓线性探测,就是根据初始 key 的 hashcode 值确定元素在 table 数组中的位置,如果发现这个位置上已经被其他的 key 值占用,则利用固定的算法寻找一定步长的下个位置,依次判断,直至找到能够存放的位置。源代码实现如下:/ * Increment i modulo len. */private static int nextIndex(int i, int len) { return ((i + 1 < len) ? i + 1 : 0);}/ * Decrement i modulo len. */private static int prevIndex(int i, int len) { return ((i - 1 >= 0) ? i - 1 : len - 1);}4.ThreadLocal 的内存泄露是怎么回事?ThreadLocal 在 ThreadLocalMap 中是以一个弱引用身份被 Entry 中的 Key 引用的,因此如果 ThreadLocal 没有外部强引用来引用它,那么 ThreadLocal 会在下次 JVM 垃圾收集时被回收。这个时候 Entry 中的 key 已经被回收,但是 value 又是一强引用不会被垃圾收集器回收,这样 ThreadLocal 的线程如果一直持续运行,value 就一直得不到回收,这样就会发生内存泄露。5.为什么 ThreadLocalMap 的 key 是弱引用?我们知道 ThreadLocalMap 中的 key 是弱引用,而 value 是强引用才会导致内存泄露的问题,至于为什么要这样设计,这样分为两种情况来讨论:key 使用强引用:这样会导致一个问题,引用的 ThreadLocal 的对象被回收了,但是 ThreadLocalMap 还持有 ThreadLocal 的强引用,如果没有手动删除,ThreadLocal 不会被回收,则会导致内存泄漏。key 使用弱引用:这样的话,引用的 ThreadLocal 的对象被回收了,由于 ThreadLocalMap 持有 ThreadLocal 的弱引用,即使没有手动删除,ThreadLocal 也会被回收。value 在下一次 ThreadLocalMap 调用 set、get、remove 的时候会被清除。比较以上两种情况,我们可以发现:由于 ThreadLocalMap 的生命周期跟 Thread 一样长,如果都没有手动删除对应 key,都会导致内存泄漏,但是使用弱引用可以多一层保障,弱引用 ThreadLocal 不会内存泄漏,对应的 value 在下一次 ThreadLocalMap 调用 set、get、remove 的时候被清除,算是最优的解决方案。6.ThreadLocal 的应用场景有哪些?ThreadLocal 适用于独立变量副本的情况,比如 Hibernate 的 session 获取场景。示例代码:private static final ThreadLocal<Session> threadLocal = new ThreadLocal<Session>();public static Session getCurrentSession(){ Session session = threadLocal.get(); try { if(session ==null&&!session.isOpen()){ //… } threadLocal.set(session); } catch (Exception e) { // TODO: handle exception } return session;}查看所有面试题:《Java 最常见 200+ 面试题》参考资料https://www.jianshu.com/p/a1c…https://www.jianshu.com/p/98b…扫描下方二维码,关注更多动态:往期文章推荐:《Java 最常见的 200+ 面试题》《你真的懂 == 和 equals 的区别吗?》《程序员精美简历Top榜—面试必备》《程序员专属精美简历合集——第二弹》 ...

March 7, 2019 · 1 min · jiezi

你真的知道 == 和 equals 的区别吗?

让我们每天都有进步,老王带你打造最全的 Java 面试清单,认真把一件事做到极致。在 Java 中 == 和 equals 的区别,感觉只有很少的人能才完全说正确。常见的错误回答就是:== 基础类型对比的是值是否相同,引用类型对比的是引用是否相同;而 equals 则是比较的值是否相同。至于为什么说它是错的,看完本文对 == 和 equals 的解读,你就知道了。1、== 解读对于基本类型和引用类型 == 的作用效果是不同的,如下所示:基本类型:比较的是值是否相同;引用类型:比较的是引用是否相同;代码示例:String x = “string”;String y = “string”;String z = new String(“string”);System.out.println(x==y); // trueSystem.out.println(x==z); // falseSystem.out.println(x.equals(y)); // trueSystem.out.println(x.equals(z)); // true代码解读:因为 x 和 y 指向的是同一个引用,所以 == 也是 true,而 new String()方法则重写开辟了内存空间,所以 == 结果为 false,而 equals 比较的一直是值,所以结果都为 true。2、equals 解读equals 本质上就是 ==,只不过 String 和 Integer 等重写了 equals 方法,把它变成了值比较。看下面的代码就明白了。首先来看默认情况下 equals 比较一个有相同值的对象,代码如下:class Cat { public Cat(String name) { this.name = name; } private String name; public String getName() { return name; } public void setName(String name) { this.name = name; }}Cat c1 = new Cat(“王磊”);Cat c2 = new Cat(“王磊”);System.out.println(c1.equals(c2)); // false输出结果出乎我们的意料,竟然是 false?这是怎么回事,看了 equals 源码就知道了,源码如下:public boolean equals(Object obj) { return (this == obj);}原来 equals 本质上就是 ==。那问题来了,两个相同值的 String 对象,为什么返回的是 true?代码如下:String s1 = new String(“老王”);String s2 = new String(“老王”);System.out.println(s1.equals(s2)); // true同样的,当我们进入 String 的 equals 方法,找到了答案,代码如下:public boolean equals(Object anObject) { if (this == anObject) { return true; } if (anObject instanceof String) { String anotherString = (String)anObject; int n = value.length; if (n == anotherString.value.length) { char v1[] = value; char v2[] = anotherString.value; int i = 0; while (n– != 0) { if (v1[i] != v2[i]) return false; i++; } return true; } } return false;}原来是 String 重写了 Object 的 equals 方法,把引用比较改成了值比较。3、总结总体来说,== 对于基本类型来说是值比较,对于引用类型来说是比较的是引用;而 equals 默认情况下是引用比较,只是很多类重写了 equals 方法,比如 String、Integer 等把它变成了值比较,所以一般情况下 equals 比较的是值是否相等。扫描下方二维码,关注更多动态:系列文章推荐:Java 最常见的 200+ 面试题程序员精美简历Top榜—面试必备你真的懂 == 和 equals 的区别吗? ...

March 7, 2019 · 2 min · jiezi

遍历数组排序,负数在左,正数在右

原文:https://tryenough.com/1999/问题描述:有一个整形数组,包含正数和负数,然后要求把数组内的所有负数移至正数的左边,且保证相对位置不变,要求时间复杂度为O(n), 空间复杂度为O(1)。例如,{10, -2, 5, 8, -4, 2, -3, 7, 12, -88, -23, 35}变化后是{-2, -4,-3, -88, -23,5, 8 ,10, 2, 7, 12, 35}。解决方法原文:https://tryenough.com/1999/实现原理是:两个变量记录左右节点,两边分别开始遍历。左边的节点遇到负值继续前进,遇到正值停止。右边的节点正好相反。然后将左右节点的只进行交换,然后再开始遍历直至左右节点相遇。这种方式的时间复杂度是O(n).空间复杂度为O(1)以下为java的实现:原文:https://tryenough.com/1999/public void setParted1(int[] a, int left, int right) { if (left >= right || left == a.length || right == 0) { for (int i = 0; i < a.length; i++) { System.out.println(a[i]); } return; } while (a[left] < 0) { left++; } while (a[right] >= 0) { right–; } if (left >= right || left == a.length || right == 0) { for (int i = 0; i < a.length; i++) { System.out.println(a[i]); } return; } swap(a, left, right); left++; right–; setParted1(a, left, right); } private void swap(int a[], int left, int right) { int temp = 0; temp = a[left]; a[left] = a[right]; a[right] = temp; }原文:https://tryenough.com/1999/ ...

March 6, 2019 · 1 min · jiezi

CVTE2019春招前端二面凉经

前言:3月5日,从中山去往广州,一大早7点多就做好准备了,在高铁站了30分钟,转广州地铁又站了90分钟,去到地铁口,就有一辆cvte的大巴车过来接送,我选择的面试时间是11:00-12:00,但前面的人还没面试完而且12:00的时候又去吃了饭,所以面试的开始时间是下午1点,直到下午3点才面完。我面试的岗位的前端开发,一面问的挺基础的,那就过了,二面感觉大多数是业务的,由于我后台学的是php,面试官喜欢考node的知识,估计这也是我凉的最大原因吧。作为一名普通二本非科班的我,能够闯进二面觉得是非常幸运的了,继续加油!线上笔试:线上笔试我是2月21日做的,其实做完之后自我感觉很一般,没想到能够进入面试的。题型分为选择题和两道编程题,其实那时我应该利用python后台截屏的,这样就能够把所有的题目截下来。选择题涉及的知识面涉及的挺广的,让我回想一下,有:①、EventLoop机制及微任务②、阻止相同事件的其他侦听器被调用(stopImmediatePropagation)③、css中margin的%是父元素的宽度作为基准(这个真不知道呀)大概记得这么多。。。编程题可以参考我这篇文章:https://segmentfault.com/a/11…一面:面试官人比较随和,所以我不怎么紧张,一面问的是基础,大部分我觉得都ok,面试是一对一的,首先自我介绍,我就说我是非科班的,前端的知识都是自学的,然后就说了各种各样的自学方法。接下来看看问的都是什么知识①、css盒子模型:有两种, IE 怪异盒子模型(border-box) 和 W3C标准盒子模型(content-box)怪异:width = content + border + padding标准:width = content可以通过css的box-sizing属性来切换这两种盒子box-sizing: border-box 怪异盒子模型box-sizing: content-box 标准盒子模型②、http状态码:1开头:(被接受,需要继续处理。)100:客户端继续请求 、101:客户端切换协议2开头:(请求成功)200:请求成功202:服务器已接受请求,但尚未处理204:服务器成功处理了请求,但未返回内容3开头:(请求被重定向) 301:(永久重定向)、 302: (临时重定向) 、303:http1.1协议,禁止被缓存 304:(协商缓存成功(资源未修改)的返回值)4开头:(客户端请求错误)400:客户端请求的语法错误,服务器无法理解403:服务器理解请求客户端的请求,但是拒绝执行此请求404:服务器无法根据客户端的请求找到资源(网页)5开头:(服务器错误)③、强缓存和协商缓存:当说到304状态码的时候,面试问我控制协商缓存的字段有哪些:控制协商缓存的字段分别有:Last-Modified / If-Modified-Since 和 Etag / If-None-Match其中Etag / If-None-Match的优先级比Last-Modified / If-Modified-Since高然后又问了我知道Etag是通过什么生成的,这个我还真没了解,只是知道一个标识符而已,面试官就说了是通过时间值生成的接着又问了强缓存的状态码我说强缓存成功的状态是200,在读取缓存缓存的时候,分为两种情况,在chrome浏览器的Network下的Size可以看到两种字段from memory cache 和 from disk cache④、闭包的概念以及内存泄漏:1、概念:有权访问另一个函数作用域和变量的函数,创建闭包最简单的方式就是在一个函数内部创建另一个函数。2、好处:由于可以读取函数内部的变量,如果希望一个变量常驻于内存中又可全局访问,同时又想避免全局变量的污染,此时使用闭包就是一种恰当的方式3、缺点:但正是因为函数内部变量被外部所引用,不会被垃圾回收,就会造成常驻内存,使用过多容易造成内存泄漏有些时候真是给自己挖坑,哈哈哈,我说闭包使用过多会造成内存泄漏,紧接着他就问我怎么查看内存泄漏,我说chrome浏览器有个面板是专门用来查看内存泄漏的,但是平时不常用,就没怎么留意,接下来他就问我常见的内存泄漏方式1.意外的全局变量a、在一个函数你忘记用变量声明符(var或let)来声明的变量,一个意外的全局变量就被创建了。b、在函数中通过this赋予变量,在函数中,this指向window2.定时器setTimeout setInterval以及回调函数当不需要setInterval或者setTimeout时,定时器没有被clear,定时器的回调函数以及内部依赖的变量都不能被回收,造成内存泄漏。比如:vue使用了定时器,需要在beforeDestroy 中做对应销毁处理。js也是一样的。3.闭包(在全局作用域上保留着闭包局部变量的引用)4.循环引用的变量或者对象⑤、防抖(debounce):手撕代码面试官把他的电脑转向我,我看到lodash,之前我只是知道这个玩意可以用来克服JSON深拷贝的缺陷,他叫我实现一个debounce的加强版(随时点击次数增加,延迟也增加)。一开始,我没有好的思路,他就叫我先实现一个普通的debounce,代码大致如下:function debounce(fn, wait=1000) { let timeout = 0; return function(…args) { if(timeout){ clearTimeout(timeout); } timeout = setTimeout(() => { fn.apply(this, args) }, wait); }}写出来后,要求写个加强版的,可能我想太多了吧。。。当时没写出来,其实只要加一条语句即可// 上面代码省略 timeout = setTimeout(() => { wait = wait1.5; // 主要增加这条语句 fn.apply(this, args)}, wait);⑥、css三角形:手撕代码一开始我以为是三角箭头,挺兴奋的,觉得很简单,就说了使用两边的border然后在rotate即可,后来才发现是三角形,一时想不出来,他问我之前有没有实现过,我说没有,他就说如果之前没有实现的话,一时半会也是想不出来的这里我百度的答案:div{ width:0; height:0; border-right:40px solid transparent; border-left: 40px solid transparent; border-bottom:40px solid red;}对于css方面,代码的实现并不重要,面试官更注重思路当他和我说了思路后,又叫我实现一个等边三角形….这个我就说了等边三角形每个角是60度,哈哈,具体不知道怎么实现⑦、原生js读取cookie一般读写cookie的时候我都是用 js-cookie 这个库的,所以对于原生忘得七七八八了因为原生js获取cookie只能通过document.cookie然后获得的是所有cookie集合在一起的字符串,需要使用正则什么的对此解析二面:从一面完到二面起码等了半个小时以上吧,面试我的又是另外一个面试官,这次面试的内容大多涉及到业务层次的,一上来就是问你使用过哪些库和框架,最后还是败在了二面①、实现一个斐波那契数列 手撕代码斐波那契数列就是1 1 2 3 5 8 13 21 34 55…这里我采用的递归的思路,因为我是非科班的,数据结构和算法没怎么学,厉害点的同学这道题就会用动态规划的方案function recurFib(num){ if(num < 3){ return 1; }else{ return recurFib(num-1) + recurFib(num-2) }}②、vue和react的差异React 和 Vue 有许多相似之处,它们都有:使用 Virtual DOM提供了响应式 (Reactive) 和组件化 (Composable) 的视图组件。将注意力集中保持在核心库,而将其他功能如路由和全局状态管理交给相关的库。然后又涉及到虚拟dom:Vitual DOM是一种虚拟dom技术,本质上是基于javascript实现的,相对于dom对象,javascript对象更简单,处理速度更快,dom树的结构,属性信息都可以很容易的用javascript对象来表示原生JS或JQ操作DOM时,浏览器会从构建DOM树开始从头到尾执行一遍流程。操作DOM的代价仍旧是昂贵的,频繁操作还是会出现页面卡顿,影响用户体验。创建虚拟DOM并将其映射成真实DOM,这样所有的更新都可以先反应到虚拟DOM上,需要用到Diff算法。③、上下固定,中间滚动布局这种布局一看就是移动端的,主要之前没有去了解移动端的布局,可能说的太不好,自己回来用代码实现了一下:功能:头部和底部自适应高度;中间占满剩余部分,超出自动滚动思路:让容器占满整个页面的高度,整体采用flex布局,中间滚动部分用 overflow: auto<div class=“cotainer”> <div class=“header”> header<br/><br/><br/><br/>header </div> <div class=“middle”> middle<br/><br/><br/><br/><br/><br/><br/><br/> middle<br/><br/><br/><br/><br/><br/><br/><br/> middle<br/><br/><br/><br/><br/><br/><br/><br/> middle<br/><br/><br/><br/><br/><br/><br/><br/> </div> <div class=“footer”> footer<br/><br/><br/><br/>footer </div></div>html, body{ margin: 0; padding: 0; width: 100%; height: 100%;}.cotainer{ display: flex; flex-direction: column; text-align: center; height: 100%;}.middle{ background-color: aquamarine; flex-grow: 1; overflow: auto;}.header, .footer{ background-color: chartreuse;}/* 隐藏PC浏览器的滚动条,移动端无需考虑 */.middle::-webkit-scrollbar { display: none; }④、事件执行机制javascript是一门单线程语言JS 在执行的过程中会产生执行环境,这些执行环境会被按照顺序的加入到执行栈中。同步和异步任务分别进入不同的执行"场所",同步的进入主线程,异步的代码,会被挂起并加入到 Task(有多种 task) 队列中除了广义的同步任务和异步任务,还包括有更加精确的微任务和宏任务微任务包括 process.nextTick ,promise ,Object.observe ,MutationObserver宏任务包括 script , setTimeout ,setInterval ,setImmediate ,I/O ,UI rendering所以正确的一次 Event loop 顺序是这样的1.执行同步代码,这属于宏任务2.执行栈为空,查询是否有微任务需要执行3.执行所有微任务4.必要的话渲染 UI5.然后开始下一轮 Event loop,执行宏任务中的异步代码⑤、node.js的知识对node不太了解…看来要好好加油了总结:面试整体难度适中,其实对于这次面试我自己本身就是抱着一种尝试的心态,在面试的时候,我们应该要以一种学习者的心态,不会就去问面试官和面试官讨论,不断强化自己的实力,路还漫长,今天也要加油鸭! ...

March 6, 2019 · 1 min · jiezi

【Leetcode】106. 从中序与后序遍历序列构造二叉树2

题目根据一棵树的中序遍历与后序遍历构造二叉树。注意:你可以假设树中没有重复的元素。例如,给出中序遍历 inorder = [9,3,15,20,7]后序遍历 postorder = [9,15,7,20,3]返回如下的二叉树: 3 / \ 9 20 / \ 15 7题解根据前序和中序可以构造一颗二叉树,根据中序和后续也可以构建一颗二叉树。反正必须要有中序才能构建,因为没有中序,你没办法确定树的形状。比如先序和后序是不能构建唯一的一颗二叉树的。例如:先序为:[1, 2]后序为:[2, 1]可以构建如下 1 / 2 1 \ 2 这个面试官也会问的。回到这个题目。那回到这个题目, 其实思路比较好想到,就是如何划分子问题,然后递归的构建左子树和右子树。inorder = [9,3,15,20,7]postorder = [9,15,7,20,3]因为后序后遍历根节点,后续最后一个节点为整棵树的根节点,可以确定根节点为3;再根据中序得到:leftInOrder = [9]RightInOrder = [15, 20 ,7]又由于中序和先序的数组大小应该相同的,所以,LeftPostOrder = [9]RightPostOrder = [15, 7, 20]至此,划分为子问题:leftInOrder = [9]LeftPostOrder = [9]构建左子树。RightPreOrder = [20, 15, 7]RightPostOrder = [15, 7, 20]构建右子树。class Solution { public TreeNode buildTree(int[] inorder, int[] postorder) { return helper(inorder, postorder, postorder.length - 1, 0, inorder.length - 1); } public TreeNode helper(int[] inorder, int[] postorder, int postEnd, int inStart, int inEnd) { if (inStart > inEnd) { return null; } int currentVal = postorder[postEnd]; TreeNode current = new TreeNode(currentVal); int inIndex = 0; for (int i = inStart; i <= inEnd; i++) { if (inorder[i] == currentVal) { inIndex = i; } } TreeNode left = helper(inorder, postorder, postEnd - (inEnd- inIndex) - 1, inStart, inIndex - 1); TreeNode right = helper(inorder, postorder, postEnd - 1, inIndex + 1, inEnd); current.left = left; current.right = right; return current; }}热门阅读技术文章汇总【Leetcode】103. 二叉树的锯齿形层次遍历【Leetcode】102. 二叉树的层次遍历【Leetcode】101. 对称二叉树【Leetcode】100. 相同的树【Leetcode】98. 验证二叉搜索树手撕代码QQ群:805423079, 群密码:1024 ...

March 6, 2019 · 1 min · jiezi

一文读懂HTTP/2 及 HTTP/3特性

前言HTTP/2 相比于 HTTP/1,可以说是大幅度提高了网页的性能,只需要升级到该协议就可以减少很多之前需要做的性能优化工作,当然兼容问题以及如何优雅降级应该是国内还不普遍使用的原因之一。虽然 HTTP/2 提高了网页的性能,但是并不代表它已经是完美的了,HTTP/3 就是为了解决 HTTP/2 所存在的一些问题而被推出来的。一、HTTP协议HTTP协议是HyperText Transfer Protocol(超文本传输协议)的缩写,它是互联网上应用最为广泛的一种网络协议。所有的WWW文件都必须遵守这个标准。伴随着计算机网络和浏览器的诞生,HTTP1.0也随之而来,处于计算机网络中的应用层,HTTP是建立在TCP协议之上,所以HTTP协议的瓶颈及其优化技巧都是基于TCP协议本身的特性,例如tcp建立连接的3次握手和断开连接的4次挥手以及每次建立连接带来的RTT延迟时间。二、HTTP/1.x的缺陷连接无法复用:连接无法复用会导致每次请求都经历三次握手和慢启动。三次握手在高延迟的场景下影响较明显,慢启动则对大量小文件请求影响较大(没有达到最大窗口请求就被终止)。HTTP/1.0传输数据时,每次都需要重新建立连接,增加延迟。HTTP/1.1虽然加入keep-alive可以复用一部分连接,但域名分片等情况下仍然需要建立多个connection,耗费资源,给服务器带来性能压力。Head-Of-Line Blocking(HOLB):导致带宽无法被充分利用,以及后续健康请求被阻塞。HOLB是指一系列包(package)因为第一个包被阻塞;当页面中需要请求很多资源的时候,HOLB(队头阻塞)会导致在达到最大请求数量时,剩余的资源需要等待其他资源请求完成后才能发起请求。HTTP 1.0:下个请求必须在前一个请求返回后才能发出,request-response对按序发生。显然,如果某个请求长时间没有返回,那么接下来的请求就全部阻塞了。HTTP 1.1:尝试使用 pipeling 来解决,即浏览器可以一次性发出多个请求(同个域名,同一条 TCP 链接)。但 pipeling 要求返回是按序的,那么前一个请求如果很耗时(比如处理大图片),那么后面的请求即使服务器已经处理完,仍会等待前面的请求处理完才开始按序返回。所以,pipeling 只部分解决了 HOLB。如上图所示,红色圈出来的请求就因域名链接数已超过限制,而被挂起等待了一段时间。协议开销大: HTTP1.x在使用时,header里携带的内容过大,在一定程度上增加了传输的成本,并且每次请求header基本不怎么变化,尤其在移动端增加用户流量。安全因素:HTTP1.x在传输数据时,所有传输的内容都是明文,客户端和服务器端都无法验证对方的身份,这在一定程度上无法保证数据的安全性三、SPDY 协议因为HTTP/1.x的问题,我们会引入雪碧图、将小图内联、使用多个域名等等的方式来提高性能。不过这些优化都绕开了协议,直到2009年,谷歌公开了自行研发的 SPDY 协议,主要解决HTTP/1.1效率不高的问题。谷歌推出SPDY,才算是正式改造HTTP协议本身。降低延迟,压缩header等等,SPDY的实践证明了这些优化的效果,也最终带来HTTP/2的诞生。SPDY 协议在Chrome浏览器上证明可行以后,就被当作 HTTP/2 的基础,主要特性都在 HTTP/2 之中得到继承。四、HTTP/2 简介2015年,HTTP/2 发布。HTTP/2是现行HTTP协议(HTTP/1.x)的替代,但它不是重写,HTTP方法/状态码/语义都与HTTP/1.x一样。HTTP/2基于SPDY3,专注于性能,最大的一个目标是在用户和网站间只用一个连接(connection)。HTTP/2由两个规范(Specification)组成:Hypertext Transfer Protocol version 2 - RFC7540HPACK - Header Compression for HTTP/2 - RFC7541五、HTTP/2 新特性1.二进制传输HTTP/2 采用二进制格式传输数据,而非 HTTP 1.x 的文本格式,二进制协议解析起来更高效。 HTTP / 1 的请求和响应报文,都是由起始行,首部和实体正文(可选)组成,各部分之间以文本换行符分隔。HTTP/2 将请求和响应数据分割为更小的帧,并且它们采用二进制编码。接下来我们介绍几个重要的概念:流:流是连接中的一个虚拟信道,可以承载双向的消息;每个流都有一个唯一的整数标识符(1、2…N);消息:是指逻辑上的 HTTP 消息,比如请求、响应等,由一或多个帧组成。帧:HTTP 2.0 通信的最小单位,每个帧包含帧首部,至少也会标识出当前帧所属的流,承载着特定类型的数据,如 HTTP 首部、负荷,等等HTTP/2 中,同域名下所有通信都在单个连接上完成,该连接可以承载任意数量的双向数据流。每个数据流都以消息的形式发送,而消息又由一个或多个帧组成。多个帧之间可以乱序发送,根据帧首部的流标识可以重新组装。2.多路复用在 HTTP/2 中引入了多路复用的技术。多路复用很好的解决了浏览器限制同一个域名下的请求数量的问题,同时也接更容易实现全速传输,毕竟新开一个 TCP 连接都需要慢慢提升传输速度。大家可以通过 该链接 直观感受下 HTTP/2 比 HTTP/1 到底快了多少。在 HTTP/2 中,有了二进制分帧之后,HTTP /2 不再依赖 TCP 链接去实现多流并行了,在 HTTP/2中:同域名下所有通信都在单个连接上完成。单个连接可以承载任意数量的双向数据流。数据流以消息的形式发送,而消息又由一个或多个帧组成,多个帧之间可以乱序发送,因为根据帧首部的流标识可以重新组装。这一特性,使性能有了极大提升:同个域名只需要占用一个 TCP 连接,使用一个连接并行发送多个请求和响应,消除了因多个 TCP 连接而带来的延时和内存消耗。并行交错地发送多个请求,请求之间互不影响。并行交错地发送多个响应,响应之间互不干扰。在HTTP/2中,每个请求都可以带一个31bit的优先值,0表示最高优先级, 数值越大优先级越低。有了这个优先值,客户端和服务器就可以在处理不同的流时采取不同的策略,以最优的方式发送流、消息和帧。如上图所示,多路复用的技术可以只通过一个 TCP 连接就可以传输所有的请求数据。3.Header 压缩在 HTTP/1 中,我们使用文本的形式传输 header,在 header 携带 cookie 的情况下,可能每次都需要重复传输几百到几千的字节。为了减少这块的资源消耗并提升性能, HTTP/2对这些首部采取了压缩策略:HTTP/2在客户端和服务器端使用“首部表”来跟踪和存储之前发送的键-值对,对于相同的数据,不再通过每次请求和响应发送;首部表在HTTP/2的连接存续期内始终存在,由客户端和服务器共同渐进地更新;每个新的首部键-值对要么被追加到当前表的末尾,要么替换表中之前的值例如下图中的两个请求, 请求一发送了所有的头部字段,第二个请求则只需要发送差异数据,这样可以减少冗余数据,降低开销4.Server PushServer Push即服务端能通过push的方式将客户端需要的内容预先推送过去,也叫“cache push”。可以想象以下情况,某些资源客户端是一定会请求的,这时就可以采取服务端 push 的技术,提前给客户端推送必要的资源,这样就可以相对减少一点延迟时间。当然在浏览器兼容的情况下你也可以使用 prefetch。例如服务端可以主动把JS和CSS文件推送给客户端,而不需要客户端解析HTML时再发送这些请求。服务端可以主动推送,客户端也有权利选择是否接收。如果服务端推送的资源已经被浏览器缓存过,浏览器可以通过发送RST_STREAM帧来拒收。主动推送也遵守同源策略,换句话说,服务器不能随便将第三方资源推送给客户端,而必须是经过双方确认才行。六、HTTP/3 新特性1.HTTP/3简介虽然 HTTP/2 解决了很多之前旧版本的问题,但是它还是存在一个巨大的问题,主要是底层支撑的 TCP 协议造成的。上文提到 HTTP/2 使用了多路复用,一般来说同一域名下只需要使用一个 TCP 连接。但当这个连接中出现了丢包的情况,那就会导致 HTTP/2 的表现情况反倒不如 HTTP/1 了。因为在出现丢包的情况下,整个 TCP 都要开始等待重传,也就导致了后面的所有数据都被阻塞了。但是对于 HTTP/1.1 来说,可以开启多个 TCP 连接,出现这种情况反到只会影响其中一个连接,剩余的 TCP 连接还可以正常传输数据。那么可能就会有人考虑到去修改 TCP 协议,其实这已经是一件不可能完成的任务了。因为 TCP 存在的时间实在太长,已经充斥在各种设备中,并且这个协议是由操作系统实现的,更新起来不大现实。基于这个原因,Google 就更起炉灶搞了一个基于 UDP 协议的 QUIC 协议,并且使用在了 HTTP/3 上,HTTP/3 之前名为 HTTP-over-QUIC,从这个名字中我们也可以发现,HTTP/3 最大的改造就是使用了 QUIC。QUIC 虽然基于 UDP,但是在原本的基础上新增了很多功能,接下来我们重点介绍几个QUIC新功能。2.QUIC新功能0-RTT通过使用类似 TCP 快速打开的技术,缓存当前会话的上下文,在下次恢复会话的时候,只需要将之前的缓存传递给服务端验证通过就可以进行传输了。0RTT 建连可以说是 QUIC 相比 HTTP2 最大的性能优势。那什么是 0RTT 建连呢?这里面有两层含义:1.传输层 0RTT 就能建立连接。2.加密层 0RTT 就能建立加密连接。上图左边是 HTTPS 的一次完全握手的建连过程,需要 3 个 RTT。就算是会话复用也需要至少 2 个 RTT。而 QUIC 呢?由于建立在 UDP 的基础上,同时又实现了 0RTT 的安全握手,所以在大部分情况下,只需要 0 个 RTT 就能实现数据发送,在实现前向加密的基础上,并且 0RTT 的成功率相比 TLS 的会话记录单要高很多。多路复用虽然 HTTP/2 支持了多路复用,但是 TCP 协议终究是没有这个功能的。QUIC 原生就实现了这个功能,并且传输的单个数据流可以保证有序交付且不会影响其他的数据流,这样的技术就解决了之前 TCP 存在的问题。同HTTP2.0一样,同一条 QUIC连接上可以创建多个stream,来发送多个HTTP请求,但是,QUIC是基于UDP的,一个连接上的多个stream之间没有依赖。比如下图中stream2丢了一个UDP包,不会影响后面跟着 Stream3 和 Stream4,不存在 TCP 队头阻塞。虽然stream2的那个包需要重新传,但是stream3、stream4的包无需等待,就可以发给用户。另外QUIC 在移动端的表现也会比 TCP 好。因为 TCP 是基于 IP 和端口去识别连接的,这种方式在多变的移动端网络环境下是很脆弱的。但是 QUIC 是通过 ID 的方式去识别一个连接,不管你网络环境如何变化,只要 ID 不变,就能迅速重连上。加密认证的报文TCP 协议头部没有经过任何加密和认证,所以在传输过程中很容易被中间网络设备篡改,注入和窃听。比如修改序列号、滑动窗口。这些行为有可能是出于性能优化,也有可能是主动攻击。但是 QUIC 的 packet 可以说是武装到了牙齿。除了个别报文比如 PUBLIC_RESET 和 CHLO,所有报文头部都是经过认证的,报文 Body 都是经过加密的。这样只要对 QUIC 报文任何修改,接收端都能够及时发现,有效地降低了安全风险。如上图所示,红色部分是 Stream Frame 的报文头部,有认证。绿色部分是报文内容,全部经过加密。向前纠错机制QUIC协议有一个非常独特的特性,称为向前纠错 (Forward Error Correction,FEC),每个数据包除了它本身的内容之外,还包括了部分其他数据包的数据,因此少量的丢包可以通过其他包的冗余数据直接组装而无需重传。向前纠错牺牲了每个数据包可以发送数据的上限,但是减少了因为丢包导致的数据重传,因为数据重传将会消耗更多的时间(包括确认数据包丢失、请求重传、等待新数据包等步骤的时间消耗)假如说这次我要发送三个包,那么协议会算出这三个包的异或值并单独发出一个校验包,也就是总共发出了四个包。当出现其中的非校验包丢包的情况时,可以通过另外三个包计算出丢失的数据包的内容。当然这种技术只能使用在丢失一个包的情况下,如果出现丢失多个包就不能使用纠错机制了,只能使用重传的方式了。七、总结HTTP/1.x 有连接无法复用、队头阻塞、协议开销大和安全因素等多个缺陷HTTP/2 通过多路复用、二进制流、Header 压缩等等技术,极大地提高了性能,但是还是存在着问题的QUIC 基于 UDP 实现,是 HTTP/3 中的底层支撑协议,该协议基于 UDP,又取了 TCP 中的精华,实现了即快又可靠的协议给大家推荐一个好用的BUG监控工具Fundebug,欢迎免费试用!欢迎关注公众号:前端工匠,你的成长我们一起见证!如果你感觉有收获,欢迎给我打赏,以激励我更多输出优质开源内容参考文章HTTP2讲解HTTP 2.0 协议详解前端面试之道一文读懂 HTTP/2 特性科普:QUIC协议原理分析HTTP2简介和基于HTTP2的Web优化HTTPHTTP2.0,SPDYHTTPS你应该知道的一些事 ...

March 6, 2019 · 2 min · jiezi

7个 Javascript 面试题及回答策略

翻译:疯狂的技术宅原文:https://www.indeed.com/hire/i…本文首发微信公众号:jingchengyideng欢迎关注,每天都给你推送新鲜的前端技术文章不管你是面试官还是求职者,里面的思路都能让你获益匪浅。单体应用和微服务架构之间有哪些主要区别,从灵活性和可扩展性的角度来看,哪个是更好选择?解析:这个问题用来评估基础编码知识。答案可以有多种,所以应该注意倾听具体的问题,并尽量全面回答,来展示自己对单体应用与微服务架构的理解。面试官想知道些什么:求职者对编码的熟悉程度求职者对不同构建方法利弊的理解是否具有从单体应用服务过渡到微服务的能力参考答案:设计单体架构在短期内成本较低,但代码在内存是单一的内聚单元。微服务架构提供了灵活性和可扩展性,因为每个组件都是一个独立的程序。同步和异步编程有什么区别,异步编程与你的 JavaScript 工作有什么关系?解析:异步编程允许多个并发操作,并且它非常适合 JavaScript 程序。这个问题评用来估求职者的编码知识,以及对 JavaScript 的具体理解。面试官想知道些什么:能够清晰地表达同步和异步编程之间的差异异步编程对用户界面的影响了解阻塞以及对性能的影响参考答案:简单来说,同步编程意味着代码以线性方式执行。这些程序通常不能同时处理多个操作。使用异步编程,有一个事件循环。一个程序线程可以处理多个操作。由于 JavaScript 通常用于用户界面,因此同时运行多个操作的能力可以带来更好的用户体验。你是否在 JavaScript 中使用过类继承和原型继承?为什么?解析:这个问题有助于了解求职者是否具有 JavaScript 经验和专业知识。原型继承比类继承更容易、更灵活,这是判断一个有经验的 JavaScript 程序员的首选方法。面试官想知道些什么:是否偏爱原型继承和组合具有阐明这种偏好的原因的能力函数继承,原型链继承,原型委托和对象组合参考答案:当使用类继承时,实例继承自类,这会产生分层。而通过原型继承,实例继承自其他对象,并且可以从多个对象创建。我更喜欢原型继承,因为它更简单、更灵活。在你看来,类继承是否是正确的选择?如果是的话,是在什么情况下?如果不是,为什么?解析:求职者的答案能让面试官深入了解他们使用 JavaScript 的经历。类继承有意义的情况很少,求职者可能会说根本没有。 否定的答案优于对适当情况冗长的描述,因为这些通常是基于错误信息的。面试官想知道些什么:类继承永远或几乎从来都不是正确的选择求职者能否阐明其回复的原因求职者有能力用逻辑来捍卫他们的结论参考答案:类继承几乎从来都不是正确的解决方案。只有在极少数情况下,它可能被用到。你如何定义函数式编程?函数式编程在 JavaScript 中的作用是什么?解析:函数式编程是目前 JavaScript 使用的基本原则。求职者是否能够完整的回答这个问题,可以证明他们的基础知识是否扎实。面试官想知道些什么:提及纯函数和避免副作用能够提供函数式编程语言的示例能够认同 JavaScript 的函数式编程,并具有使用它们的能力参考答案:函数式编程允许通过使用纯函数来设计软件。此方法避免了很多编程问题,如共享状态,副作用和可变数据。它作为面向对象编程的替代方案,其中应用状态通常与对象中的方法搭配并共享。使用函数式编程方法而不是面向对象编程有什么好处?你能提供一个例子吗?解析:任何具有 JavaScript 经验的求职者都会使用其中一种风格,因此应该能够对每种风格的优缺点提出一些看法。这个问题可以帮助面试官衡量求职者使用 JavaScript 的深度。面试官想知道些什么:能够阐明任何一种方法的优缺点提及共享资源导致的问题了解函数式编程如何简化应用程序参考答案:虽然我主要使用面向对象编程方式,但是我更希望用函数式编程开发。在用面向对象编程时遇到不同的组件竞争相同的资源的时候,更是如此。你如何定义单向数据流和双向数据绑定?两者有什么区别?解析:这个问题能够说明求职者对 JavaScript 重要基本原则的深刻理解。不熟悉这些条款的求职者可能没有该职位所需的经验。面试官想知道些什么:能够清晰、简洁的定义这两个概念能够阐明两个概念之间的差异提及相关的框架参考答案:当模型是单一事实来源时,会发生单向数据流,React 就是一个例子。双向数据绑定是用户界面与模型数据动态链接的状态。如果用户界面中的字段发生了变化,模型也会发生变化,比如 Angular。欢迎继续阅读本专栏其它高赞文章:12个令人惊叹的CSS实验项目世界顶级公司的前端面试都问些什么CSS Flexbox 可视化手册过节很无聊?还是用 JavaScript 写一个脑力小游戏吧!从设计者的角度看 ReactCSS粘性定位是怎样工作的一步步教你用HTML5 SVG实现动画效果程序员30岁前月薪达不到30K,该何去何从第三方CSS安全吗?谈谈super(props) 的重要性本文首发微信公众号:jingchengyideng欢迎扫描二维码关注公众号,每天都给你推送新鲜的前端技术文章

March 6, 2019 · 1 min · jiezi

PHP面试MySQL数据库的索引

你好,是我琉忆,PHP程序员面试笔试系列图书的作者。本周(2019.3.4至3.8)的一三五更新的文章如下:周一:PHP面试MySQL数据库的基础知识周三:PHP面试MySQL数据库的索引周五:PHP面试MySQL数据库的面试真题自己整理了一篇“索引有哪些优缺点和使用原则?”的文章,关注公众号:“琉忆编程库”,回复:“索引”,我发给你。以下内容部分来自《PHP程序员面试笔试宝典》如需转载请注明出处。一、什么是索引?索引是一种单独的、物理的对数据库表中一列或多列的值进行排序的存储结构,它是某个表中一列或若干列值的集合和相应的指向表中物理标识这些值的数据页的逻辑指针清单。索引的作用相当于图书的目录,可以根据目录中的页码快速找到所需的内容。它主要提供指向存储在表的指定列中的数据值的指针,然后根据指定的排序顺序对这些指针排序。数据库使用索引以找到特定值,然后顺指针找到包含该值的行。这样可以使对应于表的SQL语句执行得更快,可快速访问数据库表中的特定信息。索引的特点如下:①可以提高数据库的检索速度;②降低了数据库插入、修改、删除等维护任务的速度;③可以直接或间接创建;④只能创建在表上,不能创建在视图上;⑤使用查询处理器执行SQL语句时,一个表上,一次只能使用一个索引;⑥可以在优化隐藏中使用索引。索引的分类和使用如下:1.直接创建索引和间接创建索引直接创建索引:CREATE INDEX mycolumn_index ON mytable (myclumn)。间接创建索引:定义主键约束或者唯一性键约束,可以间接创建索引。2.普通索引和唯一性索引普通索引:CREATE INDEX mycolumn_index ON mytable (myclumn)。唯一性索引:保证在索引列中的全部数据是唯一的,对聚簇索引和非聚簇索引都可以使用。CREATE UNIQUE COUSTERED INDEX myclumn_cindex ON mytable(mycolumn)3.单个索引和复合索引单个索引:即非复合索引。复合索引:又称为组合索引,在索引建立语句中同时包含多个字段名,最多16个字段。CREATE INDEX name_index ON username(firstname,lastname)4.聚簇索引和非聚簇索引(聚集索引,群集索引)聚簇索引:物理索引,与基表的物理顺序相同,数据值的顺序总是按照顺序排列。CREATE CLUSTERED INDEX mycolumn_cindex ON mytable(mycolumn) WITHALLOW_DUP_ROW(允许有重复记录的聚簇索引)非聚簇索引:CREATE UNCLUSTERED INDEX mycolumn_cindex ON mytable(mycolumn)。二、索引的原理索引的目的在于提高查询效率,与我们查阅图书所用的目录是一个道理:先定位到章,然后定位到该章下的一个小节,然后找到页数。相似的例子还有:查字典,查火车车次,飞机航班等本质都是:通过不断地缩小想要获取数据的范围来筛选出最终想要的结果,同时把随机的事件变成顺序的事件,也就是说,有了这种索引机制,我们可以总是用同一种查找方式来锁定数据。数据库也是一样,但显然要复杂的多,因为不仅面临着等值查询,还有范围查询(>、<、between、in)、模糊查询(like)、并集查询(or)等等。数据库应该选择怎么样的方式来应对所有的问题呢?我们回想字典的例子,能不能把数据分成段,然后分段查询呢?最简单的如果1000条数据,1到100分成第一段,101到200分成第二段,201到300分成第三段……这样查第250条数据,只要找第三段就可以了,一下子去除了90%的无效数据。但如果是1千万的记录呢,分成几段比较好?稍有算法基础的同学会想到搜索树,其平均复杂度是lgN,具有不错的查询性能。但这里我们忽略了一个关键的问题,复杂度模型是基于每次相同的操作成本来考虑的。而数据库实现比较复杂,一方面数据是保存在磁盘上的,另外一方面为了提高性能,每次又可以把部分数据读入内存来计算,因为我们知道访问磁盘的成本大概是访问内存的十万倍左右,所以简单的搜索树难以满足复杂的应用场景。自己整理了一篇“索引有哪些优缺点和使用原则?”的文章,关注公众号:“琉忆编程库”,回复:“索引”,我发给你。三、索引的数据结构任何一种数据结构都不是凭空产生的,一定会有它的背景和使用场景,我们现在总结一下,我们需要这种数据结构能够做些什么,其实很简单,那就是:每次查找数据时把磁盘IO次数控制在一个很小的数量级,最好是常数数量级。那么我们就想到如果一个高度可控的多路搜索树是否能满足需求呢?就这样,b+树应运而生。如上图,是一颗b+树,关于b+树的定义可以参见B+树,这里只说一些重点,浅蓝色的块我们称之为一个磁盘块,可以看到每个磁盘块包含几个数据项(深蓝色所示)和指针(黄色所示),如磁盘块1包含数据项17和35,包含指针P1、P2、P3,P1表示小于17的磁盘块,P2表示在17和35之间的磁盘块,P3表示大于35的磁盘块。真实的数据存在于叶子节点即3、5、9、10、13、15、28、29、36、60、75、79、90、99。非叶子节点只不存储真实的数据,只存储指引搜索方向的数据项,如17、35并不真实存在于数据表中。b+树的查找过程如图所示,如果要查找数据项29,那么首先会把磁盘块1由磁盘加载到内存,此时发生一次IO,在内存中用二分查找确定29在17和35之间,锁定磁盘块1的P2指针,内存时间因为非常短(相比磁盘的IO)可以忽略不计,通过磁盘块1的P2指针的磁盘地址把磁盘块3由磁盘加载到内存,发生第二次IO,29在26和30之间,锁定磁盘块3的P2指针,通过指针加载磁盘块8到内存,发生第三次IO,同时内存中做二分查找找到29,结束查询,总计三次IO。真实的情况是,3层的b+树可以表示上百万的数据,如果上百万的数据查找只需要三次IO,性能提高将是巨大的,如果没有索引,每个数据项都要发生一次IO,那么总共需要百万次的IO,显然成本非常非常高。b+树性质1.索引字段要尽量的小:通过上面的分析,我们知道IO次数取决于b+数的高度h,假设当前数据表的数据为N,每个磁盘块的数据项的数量是m,则有h=㏒(m+1)N,当数据量N一定的情况下,m越大,h越小;而m = 磁盘块的大小 / 数据项的大小,磁盘块的大小也就是一个数据页的大小,是固定的,如果数据项占的空间越小,数据项的数量越多,树的高度越低。这就是为什么每个数据项,即索引字段要尽量的小,比如int占4字节,要比bigint8字节少一半。这也是为什么b+树要求把真实的数据放到叶子节点而不是内层节点,一旦放到内层节点,磁盘块的数据项会大幅度下降,导致树增高。当数据项等于1时将会退化成线性表。2.索引的最左匹配特性(即从左往右匹配):当b+树的数据项是复合的数据结构,比如(name,age,sex)的时候,b+数是按照从左到右的顺序来建立搜索树的,比如当(张三,20,F)这样的数据来检索的时候,b+树会优先比较name来确定下一步的所搜方向,如果name相同再依次比较age和sex,最后得到检索的数据;但当(20,F)这样的没有name的数据来的时候,b+树就不知道下一步该查哪个节点,因为建立搜索树的时候name就是第一个比较因子,必须要先根据name来搜索才能知道下一步去哪里查询。比如当(张三,F)这样的数据来检索时,b+树可以用name来指定搜索方向,但下一个字段age的缺失,所以只能把名字等于张三的数据都找到,然后再匹配性别是F的数据了, 这个是非常重要的性质,即索引的最左匹配特性。预告:本周五(3.8)将更新PHP面试MySQL数据库的面试题,敬请期待。以上内容摘自《PHP程序员面试笔试宝典》书籍,目前本书没有电子版,可到各大电商平台购买纸质版。更多PHP相关的面试知识、考题可以关注公众号获取:琉忆编程库对本文有什么问题或建议都可以进行留言,我将不断完善追求极致,感谢你们的支持。

March 6, 2019 · 1 min · jiezi

Java 208 道面试题:Java 基础模块答案

目前市面上的面试题存在两大问题:第一,题目太旧好久没有更新了,还都停留在 2010 年之前的状态;第二,近几年 JDK 更新和发布都很快,Java 的用法也变了不少,加上 Java 技术栈也加入了很多新的框架,比如 Spring Boot、Spring Cloud 等,但类似的面试题却极少。相比与这些问题,我的这 208 道面试题,包含了以下 4 个特点:披沙拣金提炼出每个 Java 模块中最经典的面试题;答案准确,每个题目都是我仔细校对过的;接近最真实的企业面试,题目实用有效果;难懂的题目,我加入了代码解析和原理分析。综合以上特点,可见对于《Java 最常见的 200+ 面试题》这篇文章,我花了很大的功夫,目的只有一个提供一份目前市面上最好、最全的 Java 面试题集合。本篇是这 208 道题中,第一部分“Java 基础”模块的题和答案。Java 基础1.JDK 和 JRE 有什么区别?JDK:Java Development Kit 的简称,java 开发工具包,提供了 java 的开发环境和运行环境。JRE:Java Runtime Environment 的简称,java 运行环境,为 java 的运行提供了所需环境。具体来说 JDK 其实包含了 JRE,同时还包含了编译 java 源码的编译器 javac,还包含了很多 java 程序调试和分析的工具。简单来说:如果你需要运行 java 程序,只需安装 JRE 就可以了,如果你需要编写 java 程序,需要安装 JDK。2.== 和 equals 的区别是什么?== 解读对于基本类型和引用类型 == 的作用效果是不同的,如下所示:基本类型:比较的是值是否相同;引用类型:比较的是引用是否相同;代码示例:String x = “string”;String y = “string”;String z = new String(“string”);System.out.println(x==y); // trueSystem.out.println(x==z); // falseSystem.out.println(x.equals(y)); // trueSystem.out.println(x.equals(z)); // true代码解读:因为 x 和 y 指向的是同一个引用,所以 == 也是 true,而 new String()方法则重写开辟了内存空间,所以 == 结果为 false,而 equals 比较的一直是值,所以结果都为 true。equals 解读equals 本质上就是 ==,只不过 String 和 Integer 等重写了 equals 方法,把它变成了值比较。看下面的代码就明白了。首先来看默认情况下 equals 比较一个有相同值的对象,代码如下:class Cat { public Cat(String name) { this.name = name; } private String name; public String getName() { return name; } public void setName(String name) { this.name = name; }}Cat c1 = new Cat(“王磊”);Cat c2 = new Cat(“王磊”);System.out.println(c1.equals(c2)); // false输出结果出乎我们的意料,竟然是 false?这是怎么回事,看了 equals 源码就知道了,源码如下:public boolean equals(Object obj) { return (this == obj);}原来 equals 本质上就是 ==。那问题来了,两个相同值的 String 对象,为什么返回的是 true?代码如下:String s1 = new String(“老王”);String s2 = new String(“老王”);System.out.println(s1.equals(s2)); // true同样的,当我们进入 String 的 equals 方法,找到了答案,代码如下:public boolean equals(Object anObject) { if (this == anObject) { return true; } if (anObject instanceof String) { String anotherString = (String)anObject; int n = value.length; if (n == anotherString.value.length) { char v1[] = value; char v2[] = anotherString.value; int i = 0; while (n– != 0) { if (v1[i] != v2[i]) return false; i++; } return true; } } return false;}原来是 String 重写了 Object 的 equals 方法,把引用比较改成了值比较。总结 :== 对于基本类型来说是值比较,对于引用类型来说是比较的是引用;而 equals 默认情况下是引用比较,只是很多类重写了 equals 方法,比如 String、Integer 等把它变成了值比较,所以一般情况下 equals 比较的是值是否相等。3.两个对象的 hashCode()相同,则 equals()也一定为 true,对吗?不对,两个对象的 hashCode()相同,equals()不一定 true。代码示例:String str1 = “通话”;String str2 = “重地”;System.out.println(String.format(“str1:%d | str2:%d”, str1.hashCode(),str2.hashCode()));System.out.println(str1.equals(str2));执行的结果:str1:1179395 | str2:1179395false代码解读:很显然“通话”和“重地”的 hashCode() 相同,然而 equals() 则为 false,因为在散列表中,hashCode()相等即两个键值对的哈希值相等,然而哈希值相等,并不一定能得出键值对相等。4.final 在 java 中有什么作用?final 修饰的类叫最终类,该类不能被继承。final 修饰的方法不能被重写。final 修饰的变量叫常量,常量必须初始化,初始化之后值就不能被修改。5.java 中的 Math.round(-1.5) 等于多少?等于 -1,Math.round 四舍五入大于 0.5 向上取整的。6.String 属于基础的数据类型吗?String 不属于基础类型,基础类型有 8 种:byte、boolean、char、short、int、float、long、double,而 String 属于对象。7.java 中操作字符串都有哪些类?它们之间有什么区别?操作字符串的类有:String、StringBuffer、StringBuilder。String 和 StringBuffer、StringBuilder 的区别在于 String 声明的是不可变的对象,每次操作都会生成新的 String 对象,然后将指针指向新的 String 对象,而 StringBuffer、StringBuilder 可以在原有对象的基础上进行操作,所以在经常改变字符串内容的情况下最好不要使用 String。StringBuffer 和 StringBuilder 最大的区别在于,StringBuffer 是线程安全的,而 StringBuilder 是非线程安全的,但 StringBuilder 的性能却高于 StringBuffer,所以在单线程环境下推荐使用 StringBuilder,多线程环境下推荐使用 StringBuffer。8.String str=“i"与 String str=new String(“i”)一样吗?不一样,因为内存的分配方式不一样。String str=“i"的方式,java 虚拟机会将其分配到常量池中;而 String str=new String(“i”) 则会被分到堆内存中。9.如何将字符串反转?使用 StringBuilder 或者 stringBuffer 的 reverse() 方法。示例代码:// StringBuffer reverseStringBuffer stringBuffer = new StringBuffer();stringBuffer.append(“abcdefg”);System.out.println(stringBuffer.reverse()); // gfedcba// StringBuilder reverseStringBuilder stringBuilder = new StringBuilder();stringBuilder.append(“abcdefg”);System.out.println(stringBuilder.reverse()); // gfedcba10.String 类的常用方法都有那些?indexOf():返回指定字符的索引。charAt():返回指定索引处的字符。replace():字符串替换。trim():去除字符串两端空白。split():分割字符串,返回一个分割后的字符串数组。getBytes():返回字符串的 byte 类型数组。length():返回字符串长度。toLowerCase():将字符串转成小写字母。toUpperCase():将字符串转成大写字符。substring():截取字符串。equals():字符串比较。11.抽象类必须要有抽象方法吗?不需要,抽象类不一定非要有抽象方法。示例代码:abstract class Cat { public static void sayHi() { System.out.println(“hi~”); }}上面代码,抽象类并没有抽象方法但完全可以正常运行。12.普通类和抽象类有哪些区别?普通类不能包含抽象方法,抽象类可以包含抽象方法。抽象类不能直接实例化,普通类可以直接实例化。13.抽象类能使用 final 修饰吗?不能,定义抽象类就是让其他类继承的,如果定义为 final 该类就不能被继承,这样彼此就会产生矛盾,所以 final 不能修饰抽象类,如下图所示,编辑器也会提示错误信息:14.接口和抽象类有什么区别?默认方法实现:抽象类可以有默认的方法实现;接口不能有默认的方法实现。实现:抽象类的子类使用 extends 来继承;接口必须使用 implements 来实现接口。构造函数:抽象类可以有构造函数;接口不能有。main 方法:抽象类可以有 main 方法,并且我们能运行它;接口不能有 main 方法。实现数量:类可以实现很多个接口;但是只能继承一个抽象类。访问修饰符:接口中的方法默认使用 public 修饰;抽象类中的方法可以是任意访问修饰符。15.java 中 IO 流分为几种?按功能来分:输入流(input)、输出流(output)。按类型来分:字节流和字符流。字节流和字符流的区别是:字节流按 8 位传输以字节为单位输入输出数据,字符流按 16 位传输以字符为单位输入输出数据。16.BIO、NIO、AIO 有什么区别?BIO:Block IO 同步阻塞式 IO,就是我们平常使用的传统 IO,它的特点是模式简单使用方便,并发处理能力低。NIO:New IO 同步非阻塞 IO,是传统 IO 的升级,客户端和服务器端通过 Channel(通道)通讯,实现了多路复用。AIO:Asynchronous IO 是 NIO 的升级,也叫 NIO2,实现了异步非堵塞 IO ,异步 IO 的操作基于事件和回调机制。17.Files的常用方法都有哪些?Files.exists():检测文件路径是否存在。Files.createFile():创建文件。Files.createDirectory():创建文件夹。Files.delete():删除一个文件或目录。Files.copy():复制文件。Files.move():移动文件。Files.size():查看文件个数。Files.read():读取文件。Files.write():写入文件。扫描下方二维码,关注更多动态:相关文章推荐:《Java 最常见的 200+ 面试题》《程序员精美简历Top榜—面试必备》 ...

March 5, 2019 · 2 min · jiezi

【Leetcode】106. 从中序与后序遍历序列构造二叉树

题目根据一棵树的中序遍历与后序遍历构造二叉树。注意:你可以假设树中没有重复的元素。例如,给出中序遍历 inorder = [9,3,15,20,7]后序遍历 postorder = [9,15,7,20,3]返回如下的二叉树: 3 / \ 9 20 / \ 15 7题解根据前序和中序可以构造一颗二叉树,根据中序和后续也可以构建一颗二叉树。反正必须要有中序才能构建,因为没有中序,你没办法确定树的形状。比如先序和后序是不能构建唯一的一颗二叉树的。例如:先序为:[1, 2]后序为:[2, 1]可以构建如下 1 / 2 1 \ 2 这个面试官也会问的。回到这个题目。那回到这个题目, 其实思路比较好想到,就是如何划分子问题,然后递归的构建左子树和右子树。inorder = [9,3,15,20,7]postorder = [9,15,7,20,3]因为后序后遍历根节点,后续最后一个节点为整棵树的根节点,可以确定根节点为3;再根据中序得到:leftInOrder = [9]RightInOrder = [15, 20 ,7]又由于中序和先序的数组大小应该相同的,所以,LeftPostOrder = [9]RightPostOrder = [15, 7, 20]至此,划分为子问题:leftInOrder = [9]LeftPostOrder = [9]构建左子树。RightPreOrder = [20, 15, 7]RightPostOrder = [15, 7, 20]构建右子树。class Solution { public TreeNode buildTree(int[] inorder, int[] postorder) { return helper(inorder, postorder, postorder.length - 1, 0, inorder.length - 1); } public TreeNode helper(int[] inorder, int[] postorder, int postEnd, int inStart, int inEnd) { if (inStart > inEnd) { return null; } int currentVal = postorder[postEnd]; TreeNode current = new TreeNode(currentVal); int inIndex = 0; for (int i = inStart; i <= inEnd; i++) { if (inorder[i] == currentVal) { inIndex = i; } } TreeNode left = helper(inorder, postorder, postEnd - (inEnd- inIndex) - 1, inStart, inIndex - 1); TreeNode right = helper(inorder, postorder, postEnd - 1, inIndex + 1, inEnd); current.left = left; current.right = right; return current; }}热门阅读技术文章汇总【Leetcode】103. 二叉树的锯齿形层次遍历【Leetcode】102. 二叉树的层次遍历【Leetcode】101. 对称二叉树【Leetcode】100. 相同的树【Leetcode】98. 验证二叉搜索树 ...

March 5, 2019 · 1 min · jiezi

【Leetcode】105. 从前序与中序遍历序列构造二叉树

题目根据一棵树的前序遍历与中序遍历构造二叉树。注意:你可以假设树中没有重复的元素。例如,给出前序遍历 preorder = [3,9,20,15,7]中序遍历 inorder = [9,3,15,20,7]返回如下的二叉树: 3 / \ 9 20 / \ 15 7题解根据前序和中序可以构造一颗二叉树,根据中序和后续也可以构建一颗二叉树。反正必须要有中序才能构建,因为没有中序,你没办法确定树的形状。比如先序和后序是不能构建唯一的一颗二叉树的。例如:先序为:[1, 2]后序为:[2, 1]可以构建如下 1 / 2 1 \ 2 这个面试官也会问的。那回到这个题目, 其实思路比较好想到,就是如何划分子问题,然后递归的构建左子树和右子树。preorder = [3,9,20,15,7]inorder = [9,3,15,20,7]因为先序先遍历根节点,可以确定根节点为3;再根据中序得到:leftInOrder = [9]RightInOrder = [15, 20 ,7]又由于中序和先序的数组大小应该相同的,所以,LeftPreOrder = [9]RightPreOrder = [20, 15, 7]至此,划分为子问题:leftInOrder = [9]LeftPreOrder = [9]构建左子树。RightPreOrder = [20, 15, 7]RightInOrder = [15, 20 ,7]构建右子树。/** * Definition for a binary tree node. * public class TreeNode { * int val; * TreeNode left; * TreeNode right; * TreeNode(int x) { val = x; } * } */class Solution { public TreeNode buildTree(int[] preorder, int[] inorder) { return helper(preorder, inorder, 0, 0, inorder.length - 1); } public TreeNode helper(int[] preorder, int[] inorder, int preStart, int inStart, int inEnd) { if (inStart > inEnd) { return null; } int currentVal = preorder[preStart]; TreeNode current = new TreeNode(currentVal); int inIndex = 0; for (int i = inStart; i <= inEnd; i++) { if (inorder[i] == currentVal) { inIndex = i; } } TreeNode left = helper(preorder, inorder, preStart + 1, inStart, inIndex - 1); TreeNode right = helper(preorder, inorder, preStart + inIndex - inStart + 1, inIndex + 1, inEnd); current.left = left; current.right = right; return current; }}热门阅读技术文章汇总【Leetcode】103. 二叉树的锯齿形层次遍历【Leetcode】102. 二叉树的层次遍历【Leetcode】101. 对称二叉树【Leetcode】100. 相同的树【Leetcode】98. 验证二叉搜索树手撕代码QQ群:805423079, 群密码:1024 ...

March 4, 2019 · 1 min · jiezi

前端进击的巨人(八):浅谈函数防抖与节流

本篇课题,或许早已是烂大街的解读文章。不过春招系列面试下来,不少伙伴们还是似懂非懂地栽倒在(~面试官~)深意的笑容之下,权当温故知新。JavaScript的执行过程,是基于栈来进行的。复杂的程序代码被封装到函数中,程序执行时,函数不断被推入执行栈中。所以 “执行栈” 也称 “函数执行栈”。函数中封装的代码块,一般都有相对复杂的逻辑处理(计算/判断),例如函数中可能会涉及到 DOM 的渲染更新,复杂的计算与验证, Ajax 数据请求等等。前端页面的操作权,大部分都是属于浏览断的客户爸爸们(单身三十年的手速,惹不起惹不起!!!)。如果函数被频繁调用,造成的性能开销绝对不只一点点。前: DOM 频繁重绘的卡顿让客户爸爸们想把你揪出来一顿大招。。。后: 后端同学正在提刀赶来的路上:“为什么我的接口被你玩挂了”。。。既要提升用户体验,又要减少后端服务开销,可见我们大前端的使命不只一页PPT。说好前因,接着就是后果了。既然有优化的需求,必然就要有相应的解决方案。隆重请出主角: “防抖” 与 “节流”。防抖(debounce)在事件被触发 n 秒后再执行回调函数,如果在这 n 秒内又被触发,则重新计时延迟时间。生活化理解:英雄的技能条,技能条读完才能使用技能(R大招60s)防抖的实现方式分两种 “立即执行” 和 “非立即执行”,区别在于第一次触发时,是否立即执行回调函数。非立即执行”非立即执行防抖“ 指事件触发后,回调函数不会立即执行,会在延迟时间 n 秒后执行,如果 n 秒内被调用多次,则重新计时延迟时间// e.g. 防抖 - 非立即执行function debounce(func, delay) { var timeout; return function() { var context = this; var args = arguments; // && 短路运算 == if(timeout) else {…} timeout && clearTimeout(timeout); timeout = setTimeout(function(){ func.apply(context, args); }, delay); }}// 调用var printUserName = debounce(function(){ console.log(this.value);}, 800);document.getElementById(‘username’) .addEventListener(‘keyup’, printUserName);立即执行“立即执行防抖” 指事件触发后,回调函数会立即执行,之后要想触发执行回调函数,需等待 n 秒延迟// e.g. 防抖 - 立即执行function debounce(func, delay) { var timeout; return function() { var context = this; var args = arguments; callNow = !timeout; timeout = setTimeout(function() { timeout = null; }, delay); callNow && func.apply(context, args); }}函数防抖原理:通过维护一个定时器,其延迟计时以最后一次触发为计时起点,到达延迟时间后才会触发函数执行。节流(throttle)规定在一个单位时间内,只能触发一次函数。如果这个单位时间内触发多次函数,只有一次生效(间隔执行)生活化理解:FPS射击游戏子弹射速(即使按住鼠标左键,射出子弹的速度也是限定的)水龙头的滴水(水滴攒到一定重量才会下落)函数节流实现的方式有 “时间戳” 和 “定时器” 两种。时间戳// e.g. 节流 - 时间戳function throttle(func, delay) { var lastTime = 0; return function() { var context = this; var args = arguments; var nowTime = +new Date(); if (nowTime > lastTime + delay) { func.apply(context, args) lastTime = nowTime; } }}“时间戳” 的方式,函数在时间段开始时执行。缺点:假定函数间隔1s执行,如果最后一次停止触发,卡在4.2s,则不会再执行。定时器// e.g. 节流 - 定时器function throttle(func, delay) { var timeout; return function() { var context = this; var args = arguments; if (!timeout) { setTimeout(function(){ func.apply(context, args); timeout = null; }, delay) } }}“定时器” 的方式,函数在时间段结束时执行。可理解为函数并不会立即执行,而是等待延迟计时完成才执行。(由于定时器延时,最后一次触发后,可能会再执行一次回调函数)时间戳 + 定时器(互补优化)// e.g. 节流 - 时间戳 + 定时器function throttle(func, delay) { let lastTime, timeout; return function() { let context = this; let args = arguments; let nowTime = +new Date(); if (lastTime && nowTime < lastTime + delay) { timeout && clearTimeout(timeout); timeout = setTimeout(function(){ lastTime = nowTime; func.apply(context, args); }, delay); } else { lastTime = nowTime; func.apply(context, args); } }}合并优化的原理:“时间戳”方式让函数在时间段开始时执行(第一次触发立即执行),“定时器”方式让函数在最后一次事件触发后(如4.2s)也能触发。函数节流原理:一定时间内只触发一次,间隔执行。通过判断是否到达指定触发时间,间隔时间固定。“防抖” 与 “节流” 的异同相同:都是防止某一时间段内,函数被频繁调用执行,通过时间频率控制,减少回调函数执行次数,来实现相关性能优化。区别:“防抖”是某一时间内只执行一次,最后一次触发后过段时间执行,而“节流”则是间隔时间执行,间隔时间固定。“防抖” 与 “节流” 的应用场景防抖文本输入搜索联想文本输入验证(包括 Ajax 后端验证)节流鼠标点击监听滚动 scroll窗口 resizemousemove 拖拽应用场景还有很多,具体场景需具体分析。只要涉及高频的函数调用,都可参考函数防抖节流的优化方案。鼓起勇气写在结尾:以上代码都不是 “完美” 的 “防抖 / 节流” 实现代码!!!仅就实现方式和基本原理,浅谈分解一二。实际代码开发中,一般会引入lodash 相对 “靠谱” 的第三方库,帮我们去实现防抖节流的工具函数。有兴趣的伙伴们可阅读 lodash 相关源码,加深印象理解可再读以下参考文章。参考文章7分钟理解JS的节流、防抖及使用场景函数防抖和节流 ...

March 4, 2019 · 2 min · jiezi

我本以为你们会写简历

然而并不是裁员的裁员 , 没裁员的正在准备裁员的路上 . 再加上一些人年终奖也已经骗到手了 , 依据优良传统 , 年后正是很多人辞职奔向更好的骗工资岗位的高峰期 . 所以 , 如何编简历 ( 注意是编 , 不是写 , 我认为编这个字十分有内涵 ) ?其实编简历并不是一件很难的事情 , 这件事情的本质就是 : 你在向一个同行陌生人介绍你在本行的道行 . 如果你的体系已经将对方笼罩住了 , 你暂时可以控场一波儿( 不代表一定会被录用 ) ; 如果你被对方笼罩住了 , 你就是受虐那个 ( 不代表你不会被录用 ) . 总之就是一句话 :唬住面试官就要50K , 唬不住面试官就要5K没想到 , 时至今日 , 我竟然需要写这么一篇题目类似于<老李手把手教你编简历>或<跟着永强学编简历>又或者<于巨柱细说编简历>的教程 .可见这一届PHPer是多么的差劲 .先从一个靠谱简历的外观说起 :首先 , 请使用PDF格式 , 不要用doc , docx , doc* 等拙劣的文档格式 . 原因我还是要解释一下的 , 因为我也当过面试官 , 我见到过各种神奇的doc文档打开后错位 , 乱行 , 甚至乱码 , 极其影响视觉感官 . 大家都挺忙的 , 打开这么一坨乱糟糟的玩意心情就很差 , 直接筛掉 . 我知道怎么说都会有杠精的 : " word怎么可能会乱呢 ? 我在我这里打开就不乱 . " 我不歧视杠精 , 也一视同仁地无私奉献一条友情提示 :其次 , 请文件名请专业一些 , 请采用 " 姓名 - 职业 - 工作年限.pdf “这种格式来命名你的简历 , 切忌 “简历.pdf” , 有些不讲究的人真是文件名连简历两个字都不是 , 直接就是随机的一坨字符 , 诸如 yhc.docx . 还是那句话 , 大家都挺忙的 , 主要是请尽量多提供信息给好看的HR小姐姐 , 让她们减少一些无畏的工作负担 , 次要是因为你的简历名字一眼让人获取到了很多信息 , 第一时间被捡出来 .然后 , 更重要的地方来了 , 里面写啥 .个人认为一个简历四大组成部分 , 根据重要程度依次为 : 一是个人信息部分 , 二是技能点说明部分 , 三是项目经历部分 , 四是公司经历 .下面按照顺序依次说下我觉得需要注意的地方 , 最后收尾我会提一下这个部分的可修饰程度 . 什么叫可修饰程度 ? ? ?读书人的事儿 , 能叫偷么 ? — 孔乙己先说第一部分 , 个人信息部分 . 你出去大保健被警察叔叔逮进局子后让你交代的啥 , 这里就写啥 . 可修饰程度比较低 , 拥有一丝丝可修饰价值 .再说第二部分 , 个人技能点分配说明 . 这项十分重要 . 面试官会非常注重这一项 , HR一般看不懂 . 很多人这里写的很差 , 这句话的意思就是 : 内涵不错的人这里写的很差 , 没有内涵的人这里写的也很差 , 以致于"内涵不错的人"从简历上看似乎和"没内涵的人"都差不多 . 这个地方最好不要写” 精通 “这两个字 . 虽然HR有可能真的喜欢简历上有” 精通 “字样的人 , 但是你也要知道你这两个字也会给你的面试官带来极大的反感或嘲讽欲 , 总之 , 你结合你自己情况自己看着办 , 万一你真的精通了呢 ? 然后我说下我见过大多数人这个地方都是怎么编的 , 一般都是 :熟悉PHP , 熟悉YiiLavarel框架 熟悉Linux使用 , 可以搭建XXXX环境熟悉git或svn版本管理的使用熟悉MySQL以及对数据库的优化熟悉Redis或Memcache的使用我敢说大多数人都是这么写的 , 下面我站在面试官的立场来用行内白话来解释一下当我看到这样的简历后大脑里怎么想的 .熟悉PHP , 熟悉YiiLavarel框架 ( 复制粘贴 , CURD , 就是干! )熟悉Linux使用 , 可以搭建XXXX环境 ( 会敲cd , ls命令 , 会apt install nginx )熟悉git或svn版本管理的使用 ( 会git push , 会git pull )熟悉MySQL以及对数据库的优化 ( 会select update 和 delete , 会添加索引 )熟悉Redis或Memcache的使用 ( 会set key , 会get key )问题是什么 ? 其实问题不在于这些行内白话没有提现你的水准 . 这个问题的关键是 : 大家都这么写 , 凭啥把你的简历挑出来 .所以这个地方吧 , 可以尝试用下面来表述 , 注意要结合你自己掌握程度 :PHP : 熟悉PHP语法 , 熟悉PHP面向对象 , 可以根据业务逻辑结合合适的设计模式 . 熟悉PHP SPL标准库 , 对PHP的一些高级用法有所心得体验 , 诸如pcntl多进程模块 , socket模块 . 对SWOOLE所有涉猎 , 有一些自己的积累和经验 . 对于底层 , ZendVM如何如何 .Redis : 熟悉Redis常用数据结构的使用 , 可结合业务场景选择合适的数据结构 . 熟悉Redis集群 , 对集群实现方案原理有一定掌握 , 对于市面常用的集中集群方案的优缺点比较了解 . 对于底层 , 对Redis SET等底层数据结构的实现有所掌握 .行了 , 我就举两个例子吧 , 技能点的说明最好用类似上面的说明 , 还是那句话 : 最好不要出现精通 . 那该用什么形容词呢 ? 我替你总结一下常用的几个词语 : 熟悉 , 有所 , 掌握 , 了解 , 有一定 , 心得 等 . 第二部分 : 可修饰程度略高 , 拥有可修饰价值 . 主要是你要能够应对面试官对修饰部分的问题 .继续说第三部分 , 项目经历部分 . 这一部分实际上是对第二部分技能点分配说明的实战演练说明 , 你要提现出你在这个项目中的两点 :亮点 . 你觉得这个项目中哪一部分值得自豪或学到新东西了 . 比如项目中用到ECDH , 使用了MySQL中间件等等 .难点 . 你觉得这个项目哪一部分当时难了你几天 , 然后你通过自己努力解决了以及解决方案是什么 .然而大多数人都是这么写的 :负责用户登录注册模块 , 后台管理 , 多角色权限控制 , 负责广告业务模块的管理和筛查 .你这么写的 , 别人也是这么写的 . 第三部分 : 可修饰程度比较高 , 拥有较高修饰价值 . 主要是你要能够应对面试官对修饰部分的问题 .最后一点是公司经历了 , 这个也没啥好说的 . 如果可以 , 我建议你合并一些小公司经历直接合并为一家 . 对于一些少数倒霉的同学 , 比如在不到10个人公司干了三个月就辞职的这种 , 我建议修饰成” 去朋友公司帮他临时组件了一个小团队 “.然而 , 到了最后 , 我还是要告诉你这个世界多么残酷 , 即便你的简历真的比较优秀 , 用词恰当 , 然而如果你面试遇上了傻逼一样的面试官 , 都白搭 . 这里的傻逼理解为两类 :装逼优越diss你类型的 . 这种的 , 可能依然会录用你 .看你不顺眼 , 上来说话就带刺类型的 . 根据你的心理承受能力 , 请你自己做出相应动作 .不得不承认 , 只要看双方对了眼 , 聊的投机了 , 简历什么的是可以抛到一边儿的 .找到一个合适的工作是你和这家公司的事儿 : 一个愿打 , 一个愿挨 .能应聘到这个合适的工作是你和面试官的事儿 : wangba看绿豆 – 对上眼了 .最近开了一个微信公众号(手贱弄成服务号了) ...

March 4, 2019 · 3 min · jiezi

如果想进入一家大公司面试,你会怎么做?

简评:有个人为了获得 Reddit 的工作机会,先写了一篇自荐的博客,然后通过社会工程学的方法给 Reddit CEO 投了一则 Facebook 的广告,并且成功获得了面试机会。而这一切只花了 10.62 美元,比请人做一份简历还便宜两年前,Chris Seline 从一家初创公司辞职,准备寻找新的工作机会。Chris 不想走传统的求职道路,他打算另辟蹊径,于是想出了一个计划 —— 写一篇自荐博客,使之引起公司里某个特定的人的注意,然后让他邀请自己前去工作。Chris 想去的公司是 Reddit。计划开始了。首先 Chris 知道 Reddit 的 CEO 是一个技术型创始人,所以 Chris 投入了大量在这篇自荐博客上,以此来打动 CEO。现在的问题是,如何让 Reddit CEO 看到我的博文?Chris 的第一个想法是 Email,但是直接发邮件也未免太无聊了吧。不如想办法让这篇博文出现在 Hacker News 首页,虽然 Chris 确信 Reddit CEO 仍然经常浏览 Hacker News,但是这篇自荐博文可不一定能引起足够多的人感兴趣,从而能够进入首页。后来他想起来一个初创公司常用的方法 —— 通过 Facebook 广告让潜在客户看到公司的产品。所以 Chris 决定将这篇自荐博文当成自己的产品,通过 Facebook 广告定向投放到 Reddit CEO 的视野。兵马未动,粮草先行,预算很重要!Chris 根据 Reddit CEO 的 Facebook 个人资料,通过一点社会工程学知识和细致观察,发掘他去过的每一个地方、感兴趣的每一件东西,并依据这些信息缩小投放范围。毕竟触达人群越多,广告费越贵……Chris 并不需要每一个人都点击它,只需要一个人即可。于是他根据这些信息,定制了精细广告,以便直接命中 Reddit CEO。最后广告投放给了 197 个人,4 个人点击,其中一个就是 Reddit CEO。一共花了 10.62 美元,策略成功!这个求职很巧妙了!当然打铁还需自身硬,感兴趣的话可以看看他的自荐博文是怎么写的:Hey Reddit, let’s make some recommendations!Reference:How I targeted the Reddit CEO with Facebook ads to get an interview at Reddit ...

March 4, 2019 · 1 min · jiezi

Java 最常见的 200+ 面试题:面试必备

这份面试清单是我从 2015 年做 TeamLeader 之后开始收集的,一方面是给公司招聘用,另一方面是想用它来挖掘我在 Java 技术栈中的技术盲点,然后修复和完善它,以此来提高自己的技术水平。虽然我从 2009 年就开始参加编程工作了,但依旧觉得还有很多东西要学,当然学习的过程也给我带来了很多成就感,这些成就感也推动我学习更多的技术知识。聊回面试题这件事,这份面试清单原本是我们公司内部使用的,可到后来有很多朋友在微信上联系到我,让我帮他们找一些面试方面的资料,而且这些关系也不太好拒绝,一呢,是因为这些找我,要面试题的人,不是我的好朋友的弟弟妹妹,就是我的弟弟妹妹们;二呢,我也不能马马虎虎的对付,受人之事忠人之命,我也不能辜负这份信任。慢慢的我产生了一个想法,要不要把我整理的这 200 多道面试题分享出来,来帮助更多需要的人。说实话刚开始的时候还是比较犹豫的,首先我会觉得这么做会不会有点帮人“作弊”的嫌疑,最后我想通了,这是一件值得去做的事儿。第一:让更多的人因此而学到了更多的知识,这是一件大好事。第二:这只是经验的高度提炼,让那些原本就掌握了技术却不知道怎么表达的人,学会如何在面试中展示自己。第三:如果只是死记硬背这些面试题,只要面试官再深入问纠一下,也可对这个人有一个准确的认识,之前说的“帮人作弊”的事就存在了。第四:学习有很多种方式,但只有好学者才会临池学书。如果是不想学的人,提供再多再好的资料放在他们的面前,他们也会视而不见。就像之前听过的一个故事,为什么在美国有些企业只要看你是哈佛的学历就直接录取?并不是哈佛有多么厉害,当然教学质量也是其中原因之一,但更多的是在美国上大学还是挺贵的,首先你能上的起哈佛,说明你的家庭条还不错,从小应该就有很多参加更好教育的机会;第二,你能进入哈佛,也说明你脑子不笨,能考的上哈佛;最后才是哈佛确实能给你提供一个,相对不错的教育环境。综合以上特质,所以这些企业才敢直接聘请那些有哈佛学历的人。对应到我们这份面试题其实也一样,首先你如果能记住其中大部分的答案说明你,第一,你很聪明并且记性还很好;第二,说明你有上进心,也愿意学习;第三,有了这份面试题做理论支撑之后,即使你的实践经验没有那么多,但懂得原理的你,做出来的程序也一定不会太差。所以如果您是面试官,恰好又看到这里,如果条件允许的话,请多给这样愿意学又很聪明的年轻人多一些机会。面试题模块介绍说了这么多,下面进入我们本文的主题,我们这份面试题,包含的内容了十九了模块:Java 基础、容器、多线程、反射、对象拷贝、Java Web 模块、异常、网络、设计模式、Spring/Spring MVC、Spring Boot/Spring Cloud、Hibernate、Mybatis、RabbitMQ、Kafka、Zookeeper、MySql、Redis、JVM 。如下图所示:可能对于初学者不需要看后面的框架和 JVM 模块的知识,读者朋友们可根据自己的情况,选择对应的模块进行阅读。适宜阅读人群需要面试的初/中/高级 java 程序员想要查漏补缺的人想要不断完善和扩充自己 java 技术栈的人java 面试官具体面试题下面一起来看 208 道面试题,具体的内容。一、Java 基础1.JDK 和 JRE 有什么区别?2.== 和 equals 的区别是什么?3.两个对象的 hashCode()相同,则 equals()也一定为 true,对吗?4.final 在 java 中有什么作用?5.java 中的 Math.round(-1.5) 等于多少?6.String 属于基础的数据类型吗?7.java 中操作字符串都有哪些类?它们之间有什么区别?8.String str=“i"与 String str=new String(“i”)一样吗?9.如何将字符串反转?10.String 类的常用方法都有那些?11.抽象类必须要有抽象方法吗?12.普通类和抽象类有哪些区别?13.抽象类能使用 final 修饰吗?14.接口和抽象类有什么区别?15.java 中 IO 流分为几种?16.BIO、NIO、AIO 有什么区别?17.Files的常用方法都有哪些?二、容器18.java 容器都有哪些?19.Collection 和 Collections 有什么区别?20.List、Set、Map 之间的区别是什么?21.HashMap 和 Hashtable 有什么区别?22.如何决定使用 HashMap 还是 TreeMap?23.说一下 HashMap 的实现原理?24.说一下 HashSet 的实现原理?25.ArrayList 和 LinkedList 的区别是什么?26.如何实现数组和 List 之间的转换?27.ArrayList 和 Vector 的区别是什么?28.Array 和 ArrayList 有何区别?29.在 Queue 中 poll()和 remove()有什么区别?30.哪些集合类是线程安全的?31.迭代器 Iterator 是什么?32.Iterator 怎么使用?有什么特点?33.Iterator 和 ListIterator 有什么区别?34.怎么确保一个集合不能被修改?三、多线程35.并行和并发有什么区别?36.线程和进程的区别?37.守护线程是什么?38.创建线程有哪几种方式?39.说一下 runnable 和 callable 有什么区别?40.线程有哪些状态?41.sleep() 和 wait() 有什么区别?42.notify()和 notifyAll()有什么区别?43.线程的 run()和 start()有什么区别?44.创建线程池有哪几种方式?45.线程池都有哪些状态?46.线程池中 submit()和 execute()方法有什么区别?47.在 java 程序中怎么保证多线程的运行安全?48.多线程锁的升级原理是什么?49.什么是死锁?50.怎么防止死锁?51.ThreadLocal 是什么?有哪些使用场景?52.说一下 synchronized 底层实现原理?53.synchronized 和 volatile 的区别是什么?54.synchronized 和 Lock 有什么区别?55.synchronized 和 ReentrantLock 区别是什么?56.说一下 atomic 的原理?四、反射57.什么是反射?58.什么是 java 序列化?什么情况下需要序列化?59.动态代理是什么?有哪些应用?60.怎么实现动态代理?五、对象拷贝61.为什么要使用克隆?62.如何实现对象克隆?63.深拷贝和浅拷贝区别是什么?六、Java Web64.jsp 和 servlet 有什么区别?65.jsp 有哪些内置对象?作用分别是什么?66.说一下 jsp 的 4 种作用域?67.session 和 cookie 有什么区别?68.说一下 session 的工作原理?69.如果客户端禁止 cookie 能实现 session 还能用吗?70.spring mvc 和 struts 的区别是什么?71.如何避免 sql 注入?72.什么是 XSS 攻击,如何避免?73.什么是 CSRF 攻击,如何避免?七、异常74.throw 和 throws 的区别?75.final、finally、finalize 有什么区别?76.try-catch-finally 中哪个部分可以省略?77.try-catch-finally 中,如果 catch 中 return 了,finally 还会执行吗?78.常见的异常类有哪些?八、网络79.http 响应码 301 和 302 代表的是什么?有什么区别?80.forward 和 redirect 的区别?81.简述 tcp 和 udp的区别?82.tcp 为什么要三次握手,两次不行吗?为什么?83.说一下 tcp 粘包是怎么产生的?84.OSI 的七层模型都有哪些?85.get 和 post 请求有哪些区别?86.如何实现跨域?87.说一下 JSONP 实现原理?九、设计模式88.说一下你熟悉的设计模式?89.简单工厂和抽象工厂有什么区别?十、Spring/Spring MVC90.为什么要使用 spring?91.解释一下什么是 aop?92.解释一下什么是 ioc?93.spring 有哪些主要模块?94.spring 常用的注入方式有哪些?95.spring 中的 bean 是线程安全的吗?96.spring 支持几种 bean 的作用域?97.spring 自动装配 bean 有哪些方式?98.spring 事务实现方式有哪些?99.说一下 spring 的事务隔离?100.说一下 spring mvc 运行流程?101.spring mvc 有哪些组件?102.@RequestMapping 的作用是什么?103.@Autowired 的作用是什么?十一、Spring Boot/Spring Cloud104.什么是 spring boot?105.为什么要用 spring boot?106.spring boot 核心配置文件是什么?107.spring boot 配置文件有哪几种类型?它们有什么区别?108.spring boot 有哪些方式可以实现热部署?109.jpa 和 hibernate 有什么区别?110.什么是 spring cloud?111.spring cloud 断路器的作用是什么?112.spring cloud 的核心组件有哪些?十二、Hibernate113.为什么要使用 hibernate?114.什么是 ORM 框架?115.hibernate 中如何在控制台查看打印的 sql 语句?116.hibernate 有几种查询方式?117.hibernate 实体类可以被定义为 final 吗?118.在 hibernate 中使用 Integer 和 int 做映射有什么区别?119.hibernate 是如何工作的?120.get()和 load()的区别?121.说一下 hibernate 的缓存机制?122.hibernate 对象有哪些状态?123.在 hibernate 中 getCurrentSession 和 openSession 的区别是什么?124.hibernate 实体类必须要有无参构造函数吗?为什么?十三、Mybatis125.mybatis 中 #{}和 ${}的区别是什么?126.mybatis 有几种分页方式?127.RowBounds 是一次性查询全部结果吗?为什么?128.mybatis 逻辑分页和物理分页的区别是什么?129.mybatis 是否支持延迟加载?延迟加载的原理是什么?130.说一下 mybatis 的一级缓存和二级缓存?131.mybatis 和 hibernate 的区别有哪些?132.mybatis 有哪些执行器(Executor)?133.mybatis 分页插件的实现原理是什么?134.mybatis 如何编写一个自定义插件?十四、RabbitMQ135.rabbitmq 的使用场景有哪些?136.rabbitmq 有哪些重要的角色?137.rabbitmq 有哪些重要的组件?138.rabbitmq 中 vhost 的作用是什么?139.rabbitmq 的消息是怎么发送的?140.rabbitmq 怎么保证消息的稳定性?141.rabbitmq 怎么避免消息丢失?142.要保证消息持久化成功的条件有哪些?143.rabbitmq 持久化有什么缺点?144.rabbitmq 有几种广播类型?145.rabbitmq 怎么实现延迟消息队列?146.rabbitmq 集群有什么用?147.rabbitmq 节点的类型有哪些?148.rabbitmq 集群搭建需要注意哪些问题?149.rabbitmq 每个节点是其他节点的完整拷贝吗?为什么?150.rabbitmq 集群中唯一一个磁盘节点崩溃了会发生什么情况?151.rabbitmq 对集群节点停止顺序有要求吗?十五、Kafka152.kafka 可以脱离 zookeeper 单独使用吗?为什么?153.kafka 有几种数据保留的策略?154.kafka 同时设置了 7 天和 10G 清除数据,到第五天的时候消息达到了 10G,这个时候 kafka 将如何处理?155.什么情况会导致 kafka 运行变慢?156.使用 kafka 集群需要注意什么?十六、Zookeeper157.zookeeper 是什么?158.zookeeper 都有哪些功能?159.zookeeper 有几种部署模式?160.zookeeper 怎么保证主从节点的状态同步?161.集群中为什么要有主节点?162.集群中有 3 台服务器,其中一个节点宕机,这个时候 zookeeper 还可以使用吗?163.说一下 zookeeper 的通知机制?十七、MySql164.数据库的三范式是什么?165.一张自增表里面总共有 7 条数据,删除了最后 2 条数据,重启 mysql 数据库,又插入了一条数据,此时 id 是几?166.如何获取当前数据库版本?167.说一下 ACID 是什么?168.char 和 varchar 的区别是什么?169.float 和 double 的区别是什么?170.mysql 的内连接、左连接、右连接有什么区别?171.mysql 索引是怎么实现的?172.怎么验证 mysql 的索引是否满足需求?173.说一下数据库的事务隔离?174.说一下 mysql 常用的引擎?175.说一下 mysql 的行锁和表锁?176.说一下乐观锁和悲观锁?177.mysql 问题排查都有哪些手段?178.如何做 mysql 的性能优化?十八、Redis179.redis 是什么?都有哪些使用场景?180.redis 有哪些功能?181.redis 和 memcache 有什么区别?182.redis 为什么是单线程的?183.什么是缓存穿透?怎么解决?184.redis 支持的数据类型有哪些?185.redis 支持的 java 客户端都有哪些?186.jedis 和 redisson 有哪些区别?187.怎么保证缓存和数据库数据的一致性?188.redis 持久化有几种方式?189.redis 怎么实现分布式锁?190.redis 分布式锁有什么缺陷?191.redis 如何做内存优化?192.redis 淘汰策略有哪些?193.redis 常见的性能问题有哪些?该如何解决?十九、JVM194.说一下 jvm 的主要组成部分?及其作用?195.说一下 jvm 运行时数据区?196.说一下堆栈的区别?197.队列和栈是什么?有什么区别?198.什么是双亲委派模型?199.说一下类加载的执行过程?200.怎么判断对象是否可以被回收?201.java 中都有哪些引用类型?202.说一下 jvm 有哪些垃圾回收算法?203.说一下 jvm 有哪些垃圾回收器?204.详细介绍一下 CMS 垃圾回收器?205.新生代垃圾回收器和老生代垃圾回收器都有哪些?有什么区别?206.简述分代垃圾回收器是怎么工作的?207.说一下 jvm 调优的工具?208.常用的 jvm 调优的参数都有哪些?面试答案由于文章篇幅问题,关注微信公众号「王磊的博客」,后续逐步更新答案。 ...

March 4, 2019 · 2 min · jiezi

「前端面试题系列8」数组去重(10 种浓缩版)

前言这是前端面试题系列的第 8 篇,你可能错过了前面的篇章,可以在这里找到:JavaScript 中的事件机制(从原生到框架)理解函数的柯里化ES6 中箭头函数的用法this 的原理以及用法伪类与伪元素的区别及实战如何实现一个圣杯布局?今日头条 面试题和思路解析前端面试中经常会问到数组去重的问题。因为在平时的工作中遇到复杂交互的时候,需要知道该如何解决。另外,我在问应聘者这道题的时候,更多的是想考察 2 个点:对 Array 方法的熟悉程度,还有逻辑算法能力。一般我会先让应聘者说出几种方法,然后随机抽取他说的一种,具体地写一下。这里有一个通用的面试技巧:自己不熟悉的东西,千万别说!我就碰到过几个应聘者,想尽可能地表现自己,就说了不少方法,随机抽了一个,结果就没写出来,很尴尬。ok,让我们马上开始今天的主题。会介绍 10 种不同类型的方法,一些类似的方法我做了合并,写法从简到繁,其中还会有 loadsh 源码中的方法。10 种去重方法假设有一个这样的数组: let originalArray = [1, ‘1’, ‘1’, 2, true, ’true’, false, false, null, null, {}, {}, ‘abc’, ‘abc’, undefined, undefined, NaN, NaN];。后面的方法中的源数组,都是指的这个。1、ES6 的 Set 对象ES6 提供了新的数据结构 Set。它类似于数组,但是成员的值都是唯一的,没有重复的值。Set 本身是一个构造函数,用来生成 Set 数据结构。let resultArr = Array.from(new Set(originalArray));// 或者用扩展运算符let resultArr = […new Set(originalArray)];console.log(resultArr);// [1, “1”, 2, true, “true”, false, null, {…}, {…}, “abc”, undefined, NaN]Set 并不是真正的数组,这里的 Array.from 和 … 都可以将 Set 数据结构,转换成最终的结果数组。这是最简单快捷的去重方法,但是细心的同学会发现,这里的 {} 没有去重。可是又转念一想,2 个空对象的地址并不相同,所以这里并没有问题,结果 ok。2、Map 的 has 方法把源数组的每一个元素作为 key 存到 Map 中。由于 Map 中不会出现相同的 key 值,所以最终得到的就是去重后的结果。const resultArr = new Array();for (let i = 0; i < originalArray.length; i++) { // 没有该 key 值 if (!map.has(originalArray[i])) { map.set(originalArray[i], true); resultArr.push(originalArray[i]); }}console.log(resultArr);// [1, “1”, 2, true, “true”, false, null, {…}, {…}, “abc”, undefined, NaN]但是它与 Set 的数据结构比较相似,结果 ok。3、indexOf 和 includes建立一个新的空数组,遍历源数组,往这个空数组里塞值,每次 push 之前,先判断是否已有相同的值。判断的方法有 2 个:indexOf 和 includes,但它们的结果之间有细微的差别。先看 indexOf。const resultArr = [];for (let i = 0; i < originalArray.length; i++) { if (resultArr.indexOf(originalArray[i]) < 0) { resultArr.push(originalArray[i]); }}console.log(resultArr);// [1, “1”, 2, true, “true”, false, null, {…}, {…}, “abc”, undefined, NaN, NaN]indexOf 并不没处理 NaN。再来看 includes,它是在 ES7 中正式提出的。const resultArr = [];for (let i = 0; i < originalArray.length; i++) { if (!resultArr.includes(originalArray[i])) { resultArr.push(originalArray[i]); }}console.log(resultArr);// [1, “1”, 2, true, “true”, false, null, {…}, {…}, “abc”, undefined, NaN]includes 处理了 NaN,结果 ok。4、sort先将原数组排序,生成新的数组,然后遍历排序后的数组,相邻的两两进行比较,如果不同则存入新数组。const sortedArr = originalArray.sort();const resultArr = [sortedArr[0]];for (let i = 1; i < sortedArr.length; i++) { if (sortedArr[i] !== resultArr[resultArr.length - 1]) { resultArr.push(sortedArr[i]); }}console.log(resultArr);// [1, “1”, 2, NaN, NaN, {…}, {…}, “abc”, false, null, true, “true”, undefined]从结果可以看出,对源数组进行了排序。但同样的没有处理 NaN。5、双层 for 循环 + splice双层循环,外层遍历源数组,内层从 i+1 开始遍历比较,相同时删除这个值。for (let i = 0; i < originalArray.length; i++) { for (let j = (i + 1); j < originalArray.length; j++) { // 第一个等于第二个,splice去掉第二个 if (originalArray[i] === originalArray[j]) { originalArray.splice(j, 1); j–; } }}console.log(originalArray);// [1, “1”, 2, true, “true”, false, null, {…}, {…}, “abc”, undefined, NaN, NaN]splice 方法会修改源数组,所以这里我们并没有新开空数组去存储,最终输出的是修改之后的源数组。但同样的没有处理 NaN。6、原始去重定义一个新数组,并存放原数组的第一个元素,然后将源数组一一和新数组的元素对比,若不同则存放在新数组中。let resultArr = [originalArray[0]];for(var i = 1; i < originalArray.length; i++){ var repeat = false; for(var j=0; j < resultArr.length; j++){ if(originalArray[i] === resultArr[j]){ repeat = true; break; } } if(!repeat){ resultArr.push(originalArray[i]); }}console.log(resultArr);// [1, “1”, 2, true, “true”, false, null, {…}, {…}, “abc”, undefined, NaN, NaN]这是最原始的去重方法,很好理解,但写法繁琐。同样的没有处理 NaN。7、ES5 的 reducereduce 是 ES5 中方法,常用于值的累加。它的语法:arr.reduce(callback[, initialValue])reduce 的第一个参数是一个 callback,callback 中的参数分别为: Accumulator(累加器)、currentValue(当前正在处理的元素)、currentIndex(当前正在处理的元素索引,可选)、array(调用 reduce 的数组,可选)。reduce 的第二个参数,是作为第一次调用 callback 函数时的第一个参数的值。如果没有提供初始值,则将使用数组中的第一个元素。利用 reduce 的特性,再结合之前的 includes(也可以用 indexOf),就能得到新的去重方法:const reducer = (acc, cur) => acc.includes(cur) ? acc : […acc, cur];const resultArr = originalArray.reduce(reducer, []);console.log(resultArr);// [1, “1”, 2, true, “true”, false, null, {…}, {…}, “abc”, undefined, NaN]这里的 [] 就是初始值(initialValue)。acc 是累加器,在这里的作用是将没有重复的值塞入新数组(它一开始是空的)。 reduce 的写法很简单,但需要多加理解。它可以处理 NaN,结果 ok。8、对象的属性每次取出原数组的元素,然后在对象中访问这个属性,如果存在就说明重复。const resultArr = [];const obj = {};for(let i = 0; i < originalArray.length; i++){ if(!obj[originalArray[i]]){ resultArr.push(originalArray[i]); obj[originalArray[i]] = 1; }}console.log(resultArr);// [1, 2, true, false, null, {…}, “abc”, undefined, NaN]但这种方法有缺陷。从结果看,它貌似只关心值,不关注类型。还把 {} 给处理了,但这不是正统的处理办法,所以 不推荐使用。9、filter + hasOwnPropertyfilter 方法会返回一个新的数组,新数组中的元素,通过 hasOwnProperty 来检查是否为符合条件的元素。const obj = {};const resultArr = originalArray.filter(function (item) { return obj.hasOwnProperty(typeof item + item) ? false : (obj[typeof item + item] = true);});console.log(resultArr);// [1, “1”, 2, true, “true”, false, null, {…}, “abc”, undefined, NaN]这 貌似 是目前看来最完美的解决方案了。这里稍加解释一下:hasOwnProperty 方法会返回一个布尔值,指示对象自身属性中是否具有指定的属性。typeof item + item 的写法,是为了保证值相同,但类型不同的元素被保留下来。例如:第一个元素为 number1,第二第三个元素都是 string1,所以第三个元素就被去除了。obj[typeof item + item] = true 如果 hasOwnProperty 没有找到该属性,则往 obj 里塞键值对进去,以此作为下次循环的判断依据。如果 hasOwnProperty 没有检测到重复的属性,则告诉 filter 方法可以先积攒着,最后一起输出。看似 完美解决了我们源数组的去重问题,但在实际的开发中,一般不会给两个空对象给我们去重。所以稍加改变源数组,给两个空对象中加入键值对。let originalArray = [1, ‘1’, ‘1’, 2, true, ’true’, false, false, null, null, {a: 1}, {a: 2}, ‘abc’, ‘abc’, undefined, undefined, NaN, NaN];然后再用 filter + hasOwnProperty 去重。然而,结果竟然把 {a: 2} 给去除了!!!这就不对了。所以,这种方法有点去重 过头 了,也是存在问题的。10、lodash 中的 .uniq灵机一动,让我想到了 lodash 的去重方法 .uniq,那就尝试一把:console.log(.uniq(originalArray));// [1, “1”, 2, true, “true”, false, null, {…}, {…}, “abc”, undefined, NaN]用法很简单,可以在实际工作中正确处理去重问题。然后,我在好奇心促使下,看了它的源码,指向了 baseUniq 文件,它的源码如下:function baseUniq(array, iteratee, comparator) { let index = -1 let includes = arrayIncludes let isCommon = true const { length } = array const result = [] let seen = result if (comparator) { isCommon = false includes = arrayIncludesWith } else if (length >= LARGE_ARRAY_SIZE) { const set = iteratee ? null : createSet(array) if (set) { return setToArray(set) } isCommon = false includes = cacheHas seen = new SetCache } else { seen = iteratee ? [] : result } outer: while (++index < length) { let value = array[index] const computed = iteratee ? iteratee(value) : value value = (comparator || value !== 0) ? value : 0 if (isCommon && computed === computed) { let seenIndex = seen.length while (seenIndex–) { if (seen[seenIndex] === computed) { continue outer } } if (iteratee) { seen.push(computed) } result.push(value) } else if (!includes(seen, computed, comparator)) { if (seen !== result) { seen.push(computed) } result.push(value) } } return result}有比较多的干扰项,那是为了兼容另外两个方法,.uniqBy 和 .uniqWith。去除掉之后,就会更容易发现它是用 while 做了循环。当遇到相同的值得时候,continue outer 再次进入循环进行比较,将没有重复的值塞进 result 里,最终输出。另外,.uniqBy 方法可以通过指定 key,来专门去重对象列表。.uniqBy([{ ‘x’: 1 }, { ‘x’: 2 }, { ‘x’: 1 }], ‘x’);// => [{ ‘x’: 1 }, { ‘x’: 2 }].uniqWith 方法可以完全地给对象中所有的键值对,进行比较。var objects = [{ ‘x’: 1, ‘y’: 2 }, { ‘x’: 2, ‘y’: 1 }, { ‘x’: 1, ‘y’: 2 }];_.uniqWith(objects, _.isEqual);// => [{ ‘x’: 1, ‘y’: 2 }, { ‘x’: 2, ‘y’: 1 }]这两个方法,都还挺实用的。总结从上述的这些方法来看,ES6 开始出现的方法(如 Set、Map、includes),都能完美地解决我们日常开发中的去重需求,关键它们还都是原生的,写法还更简单。所以,我们提倡拥抱原生,因为它们真的没有那么难以理解,至少在这里我觉得它比 lodash 里 _.uniq 的源码要好理解得多,关键是还能解决问题。PS:欢迎关注我的公众号 “超哥前端小栈”,交流更多的想法与技术。 ...

March 4, 2019 · 4 min · jiezi

7 个开放式的 CSS 面试题及回答策略

翻译:疯狂的技术宅原文:https://www.indeed.com/hire/i…不管你是面试官还是求职者,里面的思路都能让你获益匪浅。你用 CSS 多久了?解析:这个问题可以让面试官了解求职者使用 CSS 的经验。它还可以告诉面试官该人是否将该技能作为业余爱好或在学术或专业环境中学习。面试官想知道些什么:求职者使用 CSS 的经验对创建网站的兴趣是否持续关注新技术参考答案:我在高中时就开始用 CSS 了。当时我和一些朋友为我们喜欢的电视节目搭了一个网站。不过这些页面很差劲,因为它们没有统一的风格。每个页面的字体和颜色都不一样。后来我学会了 CSS,这样就可以很轻松的使页面风格统一。你最喜欢的 CSS 功能是什么?解析:求职者最喜欢 CSS 的哪些功能并不重要。通过这个人是否能够熟练的地谈论 CSS,能够看出他 CSS 了解多少。面试官想知道些什么:求职者对 CSS 的经验对 CSS 的了解如何用 CSS 节省时间参考答案:我非常喜欢用 CSS 改变页面布局这个功能,因为这样可以适配不同的屏幕尺寸。现在有各种尺寸和形状的设备。如果你能正确的使用 CSS,在任何设备上访问你网站的体验都会很好,不管是用 iPhone 还是用 PC 浏览你的网站都没关系。你有没有把 CSS 与其他语言结合使用?解析:Web开发人员通常依赖好几种语言来构建动态网站。通过面试者的答案可以得知他都知道哪些开发语言。如果面试官单独提出某种语言,还可知道面试者对该语言的理解有多深。面试官想知道些什么:对前端设计的兴趣其他语言技能是否具有创造性解决问题的能力参考答案:通常我用 CSS 来创建网站页面的总体布局并进行美化。另外我还将用 HTML 为页面提供一些结构。当我需要向网站添加交互式元素、动画、音乐或其他功能时,更喜欢使用 JavaScript,因为它能够很方便的实现一些逻辑。“你能告诉我一个让你学到新 CSS 技术的项目吗?解析:这个问题鼓励面试者反思他们使用CSS的经历。答案可能会揭示一些事情,例如面试者对 CSS 有多少经验,以及他们如何使用批判性思维来解决困难问题。面试官想知道些什么:对 CSS 的了解使用 CSS 的经验是否具有批判性思维参考答案:我在大学里做过一个项目,这个项目能帮助图书管理员和工程师团队对内容进行数字化。我的小团队打算把图书馆里收藏的大量手写诗集数字化。花费精力最多的一个功能是:写了一个能够容纳各种大小和形状的诗词稿件的样式。你最喜欢的一个项目是什么?This question gives you some insight into what the interviewee enjoys. The answer says more about the applicant’s personality than programming skills. 通过这个问题可以深入了解求职者的经验。答案更多地揭示了求职者的个性而不是编程技巧。面试官想知道些什么:了解 CSS 的好处能够创造性的解决问题面试者如何应对挑战参考答案:我帮一位朋友写了一个发布文章、照片、音乐和各种媒体的在线杂志,这个项目并不难,几个小时后,他的网站从 OK 变为优秀。它让我想起为什么了解 CSS,JavaScript 和其他常用语言非常重要。如果没有一个好的网站来展示它,你所做的都没有什么意义。当你忘记某个 CSS 的功能时,是怎么解决的?解析:程序员不能把所有东西都放在脑子里,所以当他们能想起特定的功能和术语时,会依赖一些资源。这个问题的答案能够知道求职者有没有可靠的资源,这些资源在未来开展项目时可能会派上用场。面试官想知道些什么深入了解 CSS 资源在哪里去查找 CSS 功能的资料Insider knowledge of CSS是否懂得 CSS 更深层次的知识参考答案:Mozilla开发者网络,简称 MDN,为我节省了大量的时间。这是一个在线资源,其中包含有 CSS、HTML和JavaScript 的资料。我也喜欢 w3schools.com 上的 CSS 教程。你最不喜欢 CSS 的哪些东西?解析:所有人都有不喜欢的工作。对这个问题的诚实回答可以帮助面试官为求职者提供合适的团队。例如,如果一个人喜欢在 CSS 文件中发现错误,那么你可能希望聘请这个人与一个喜欢专注于大局的创意人一起工作。面试官想知道些什么:了解 CSS 的优缺点了解求职者可以扮演什么角色了解求职者的其他兴趣参考答案:CSS 使网页设计变得更加容易,但它仍然感觉有点单调乏味。理想情况下,我想用动态语言让用户与网站进行互动。但就目前而言,我很乐意磨练自己的 CSS 技能并向更有经验的人学习。欢迎继续阅读本专栏其它高赞文章:12个令人惊叹的CSS实验项目世界顶级公司的前端面试都问些什么CSS Flexbox 可视化手册过节很无聊?还是用 JavaScript 写一个脑力小游戏吧!从设计者的角度看 ReactCSS粘性定位是怎样工作的一步步教你用HTML5 SVG实现动画效果程序员30岁前月薪达不到30K,该何去何从第三方CSS安全吗?谈谈super(props) 的重要性本文首发微信公众号:jingchengyideng欢迎扫描二维码关注公众号,每天都给你推送新鲜的前端技术文章 ...

March 4, 2019 · 1 min · jiezi

PHP面试之MySQL数据库部分基础知识

你好,是我琉忆,PHP程序员面试笔试系列图书的作者。本周(2019.3.4至3.8)的一三五更新的文章如下:周一:PHP面试MySQL数据库的基础知识周三:PHP面试MySQL数据库的索引周五:PHP面试MySQL数据库的面试真题自己整理了一篇“一二三四范式有何区别?”的文章,关注公众号:“琉忆编程库”,回复:“范式”,我发给你。以下内容来自《PHP程序员面试笔试宝典》如需转载请注明出处。一、几款开源数据库的对比和介绍二、SQL语言的功能有哪些?SQL是结构化查询语言(Structured Query Language)的缩写,其功能包括数据查询、数据操纵、数据定义和数据控制四个部分。数据查询是数据库中最常见的操作,通过select语句可以得到所需的信息。SQL语言的数据操纵语句(Data Manipulation Language,DML)主要包括插入数据、修改数据以及删除数据三种语句。SQL语言使用数据定义语言(Data Definition Language,DDL)实现数据定义功能,可对数据库用户、基本表、视图、索引进行定义与撤销。数据控制语句(Data Control Language,DCL)用于对数据库进行统一的控制管理,保证数据在多用户共享的情况下能够安全。基本的SQL语句有select、insert、update、delete、create、drop、grant、revoke等。其具体使用方式见下表。自己整理了一篇“一二三四范式有何区别?”的文章,关注公众号:“琉忆编程库”,回复:“范式”,我发给你。三、什么是事务?事务是数据库中一个单独的执行单元(Unit),它通常由高级数据库操作语言(例如SQL)或编程语言(例如C++、Java等)书写的用户程序的执行所引起。当在数据库中更改数据成功时,在事务中更改的数据便会提交,不再改变;否则,事务就取消或者回滚,更改无效。例如网上购物,其交易过程至少包括以下几个步骤的操作:1)更新客户所购商品的库存信息。2)保存客户付款信息。3)生成订单并且保存到数据库中。4)更新用户相关信息,如购物数量等。在正常的情况下,这些操作都将顺利进行,最终交易成功,与交易相关的所有数据库信息也成功地更新。但是,如果遇到突然掉电或是其他意外情况,导致这一系列过程中任何一个环节出了差错,例如在更新商品库存信息时发生异常、顾客银行账户余额不足等,都将导致整个交易过程失败。而一旦交易失败,数据库中所有信息都必须保持交易前的状态不变,例如最后一步更新用户信息时失败而导致交易失败,那么必须保证这笔失败的交易不影响数据库的状态,即原有的库存信息没有被更新、用户也没有付款、订单也没有生成。否则,数据库的信息将会不一致,或者出现更为严重的不可预测的后果,数据库事务正是用来保证这种情况下交易的平稳性和可预测性的技术。事务必须满足四个属性,即原子性(Atomicity)、一致性(Consistency)、隔离性(Isolation)、持久性(Durability),即ACID四种属性。(1)原子性事务是一个不可分割的整体,为了保证事务的总体目标,事务必须具有原子性,即当数据修改时,要么全执行,要么全都不执行,即不允许事务部分地完成,避免了只执行这些操作的一部分而带来的错误。原子性要求事务必须被完整执行。(2)一致性一个事务执行之前和执行之后数据库数据必须保持一致性状态。数据库的一致性状态应该满足模式锁指定的约束,那么在完整执行该事务后数据库仍然处于一致性状态。为了维护所有数据的完整性,在关系型数据库中,所有的规则必须应用到事务的修改上。数据库的一致性状态由用户来负责,由并发控制机制实现,例如银行转账,转账前后两个账户金额之和应保持不变,由于并发操作带来的数据不一致性包括丢失数据修改、读“脏”数据、不可重复读和产生幽灵数据。(3)隔离性隔离性也被称为独立性,当两个或多个事务并发执行时,为了保证数据的安全性,将一个事物内部的操作与事务的操作隔离起来,不被其他正在进行的事务看到。例如对任何一对事务T1、T2,对T1而言,T2要么在T1开始之前已经结束,要么在T1完成之后再开始执行。数据库有四种类型的事务隔离级别:不提交的读、提交的读、可重复的读和串行化。因为隔离性使得每个事务的更新在它被提交之前,对其他事务都是不可见的,所以,实施隔离性是解决临时更新与消除级联回滚问题的一种方式。(4)持久性持久性也被称为永久性,事务完成以后,数据库管理系统(DBMS)保证它对数据库中的数据的修改是永久性的,当系统或介质发生故障时,该修改也永久保持。持久性一般通过数据库备份与恢复来保证。严格来说,数据库事务属性(ACID)都是由数据库管理系统来进行保证的,在整个应用程序运行过程中应用无须去考虑数据库的ACID实现。一般情况下,通过执行COMMIT或ROLLBACK语句来终止事务,当执行COMMIT语句时,自从事务启动以来对数据库所做的一切更改就成为永久性的了,即被写入磁盘,而当执行ROLLBACK语句时,自动事务启动以来对数据库所做的一切更改都会被撤销,并且数据库中内容返回到事务开始之前所处的状态。无论什么情况,在事务完成时,都能保证回到一致状态。四、什么是触发器?触发器是一种特殊类型的存储过程,它由事件触发,而不是程序调用或手工启动,当数据库有特殊的操作时,对这些操作由数据库中的事件来触发,自动完成这些SQL语句。使用触发器可以用来保证数据的有效性和完整性,完成比约束更复杂的数据约束。具体而言,触发器与存储过程的区别见下表。根据SQL语句的不同,触发器可分为两类:DML触发器和DLL触发器。DML触发器是当数据库服务器发生数据操作语言事件时执行的存储过程,有After和Instead Of这两种触发器。After触发器被激活触发是在记录改变之后进行的一种触发器。Instead Of触发器是在记录变更之前,去执行触发器本身所定义的操作,而不是执行原来SQL语句里的操作。DLL触发器是在响应数据定义语言事件时执行的存储过程。具体而言,触发器的主要作用表现为如下几个方面:1)增加安全性。2)利用触发器记录所进行的修改以及相关信息,跟踪用户对数据库的操作,实现审计。3)维护那些通过创建表时的声明约束不可能实现的复杂的完整性约束以及对数据库中特定事件进行监控与响应。4)实现复杂的非标准的数据库相关完整性规则、同步实时地复制表中的数据。5)触发器是自动的,它们在对表的数据做了任何修改之后就会被激活,例如可以自动计算数据值,如果数据的值达到了一定的要求,则进行特定的处理。以某企业财务管理为例,如果企业的资金链出现短缺,并且达到某种程度,则发送警告信息。下面是一个触发器的例子,该触发器的功能是在每周末进行数据表更新,如果当前用户没有访问WEEKEND_UPDATE_OK表的权限,那么需要重新赋予权限。CREATE OR REPLACE TRIGGER update_on_weekends_checkBEFORE UPDATE OF sal ON EMPFOR EACH ROW DECLAREmy_count number(4);BEGINSELECT COUNT(u_name) FROM WEEKEND_UPDATE_OK INTO my_count WHERE u_name = user_name; IF my_count=0 THEN RAISE_APPLICATION_ERROR(20508, ‘Update not allowed’); END IF; END;五、触发器分为事前触发和事后触发,二者有什么区别?语句级触发和行级触发有什么区别?事前触发发生在事件发生之前验证一些条件或进行一些准备工作;事后触发发生在事件发生之后,做收尾工作,保证事务的完整性。而事前触发可以获得之前和新的字段值。语句级触发器可以在语句执行之前或之后执行,而行级触发在触发器所影响的每一行触发一次。预告:本周三(3.6)将更新PHP面试MySQL数据库的索引,敬请期待。以上内容摘自《PHP程序员面试笔试宝典》书籍,目前本书没有电子版,可到各大电商平台购买纸质版。更多PHP相关的面试知识、考题可以关注公众号获取:琉忆编程库对本文有什么问题或建议都可以进行留言,我将不断完善追求极致,感谢你们的支持。

March 4, 2019 · 1 min · jiezi

循环中的异步&&循环中的闭包

原文链接在这之前先要了解一下for循环中let 和var的区别var 是函数级作用域或者全局作用域,let是块级作用域看一个例子 function foo() { for (var index = 0; index < array.length; index++) { //..循环中的逻辑代码 } console.log(index);//=>5 } foo() console.log(index)//Uncaught ReferenceError: index is not definedfoo函数下的index输出5,全局下的index不存在现在我们把var 换为let function foo() { for (let index = 0; index < array.length; index++) { //..循环中的逻辑代码 } console.log(index)//Uncaught ReferenceError: index is not defined } foo()报错了,index不在foo函数作用域下,当然肯定也不会再全局下因为var和let的这个区别(当然var和let的区别不止于此)所以导致了下面的这个问题关于var的 const array = [1, 2, 3, 4, 5] function foo() { for (var index = 0; index < array.length; index++) { setTimeout(() => { console.log(index); }, 1000); } } foo()看下输出关于let的 const array = [1, 2, 3, 4, 5] function foo() { for (let index = 0; index < array.length; index++) { setTimeout(() => { console.log(index); }, 1000); } } foo()看下输出因为var和let 在作用域上的差别,所以到这了上面的问题使用var 定义变量的时候,作用域是在foo函数下,在for循环外部,在整个循环中是全局的,每一次的循环实际上是为index赋值,循环一次赋值一次,5次循环完成,index最后的结果赋值就为5;就是被最终赋值的index,就是5;let的作用局的块级作用局,index的作用域在for循环内部,即每次循环的index的作用域就是本次循环,下一次循环重新定义变量index;所以index每次循环的输出都不同这里还有另外一个问题,setTimeout,这是一个异步,这就是我们今天要讨论的循环中的异步setTimeout(func,time)函数运行机制setTimeout(func,time)是在time(毫秒单位)时间后执行func函数。浏览器引擎按顺序执行程序,遇到setTimeout会将func函数放到执行队列中,等到主程序执行完毕之后,才开始从执行队列(队列中可能有多个待执行的func函数)中按照time延时时间的先后顺序取出来func并执行。即使time=0,也会等主程序运行完之后,才会执行。一个需求,一个数组array[1,2,3,4,5],循环打印,间隔1秒上面的let是循环打印了12345,但是不是间隔1s打印的,是在foo函数执行1s后,同时打印的方式一 放弃for循环,使用setInterval function foo(){ let index = 0; const array = [1, 2, 3, 4, 5] const t = setInterval(()=>{ if (index < array.length) { console.log(array[index]); } index++; }, 1000); if (index >= array.length) { clearInterval(t); } } foo()我们上面说到,当for循环遇到了var,变量index的作用域在foo函数下,循环一次赋值一次,5次循环完成,index最后的结果赋值就为5;就是被最终赋值的index,就是5;方式二,引入全局变量代码执行顺序是,先同步执行for循环,再执行异步队列,在for循环执行完毕后,异步队列开始执行之前,index经过for循环的处理,变成了5。所以我们引入一个全局变量j,使j在for循环执行完毕后,异步队列开始执行之前,依然是0,在异步执行时进行累加 var j = 0; for (var index = 0; index < array.length; index++) { setTimeout(() => { console.log(j); j++; }, 1000 * index) }方式三 for循环配合setTimeout(常规思路,不赘述,面试必备技能) const array = [1, 2, 3, 4, 5] function foo() { for (let index = 0; index < array.length; index++) { setTimeout(() => { console.log(index); }, 1000*index); } } foo()方式四,通过闭包实现开始讨论方式四之前我推荐先阅读一遍我之前写过一篇文章谈一谈javascript作用域我们对上面的问题再次分析,for循环同步执行,在for循环内部遇到了setTimeout,setTimeout是异步执行的,所以加入了异步队列,当同步的for循环执行完毕后,再去执行异步队列,setTimeout中有唯一的一个参数数index方式三可行,是因为let是块级作用域,每次for执行都会创建新的变量index,for循环执行完毕后,异步执行之前,创建了5个独立的作用域,5个index变量,分别是0,1,2,3,4,相互独立,互不影响,输出了预期的结果如果说每次循环都会生成一个独立的作用域用来保存index,问题就会得到解决,所以,我们通过闭包来实现 const array = [1, 2, 3, 4, 5] function foo() { for (var index = 0; index < array.length; index++) { function fun(j) { setTimeout(function () { console.log(j); }, 1000 * j); } fun(index) } } foo()setTimeout中的匿名回调函数中引用了函数fun中的局部变量j,所以当fun执行完毕后,变量j不会被释放,这就形成了闭包当然我们可以对此进行一下优化 const array = [1, 2, 3, 4, 5] function foo() { for (var index = 0; index < array.length; index++) { (function(j) { setTimeout(function () { console.log(j); }, 1000 * j); })(index) } } foo()将foo函数改为匿名的立即执行函数,结果是相同的总结for循环本身是同步执行的,当在for循环中遇到了异步逻辑,异步就会进入异步队列,当for循环执行结束后,才会执行异步队列当异步函数依赖于for循环中的索引时(一定是存在依赖关系的,不然不会再循环中调动异步函数)要考虑作用域的问题,在ES6中使用let是最佳的选择,当使用var时,可以考虑再引入一个索引来替代for循环中的索引,新的索引逻辑要在异步中处理也可以使用闭包,模拟实现let在实际开发过程中,循环调用异步函数,比demo要复杂,可能还会出现if和else判断等逻辑,具体的我们下次再续参考通过for循环每隔两秒按顺序打印出arr中的数字setTimeOut和闭包《你不知道了JavaScript》上卷 ...

March 4, 2019 · 2 min · jiezi

我所知道的面试手册

达内 Java 企业面试题精选达内 20 本教材里面的最后一本,偏 Java 语言和 Web 框架。Github 下载Gitee 下载传智播客面试宝典传智播客总结的面试宝典,里面有 Android、C++、Java、PHP、大数据五种。CSDN 下载数据结构与算法/leetcode/lintcode题解本文档为数据结构和算法学习笔记,全文大致分为以下三大部分: Part I为数据结构和算法基础,介绍一些基础的排序/链表/基础算法 Part II为 OJ 上的编程题目实战,按题目的内容分章节编写,主要来源为 https://leetcode.com/ 和 http://www.lintcode.com/. Part III 为附录部分,包含如何写简历和其他附加材料。在线阅读/下载牛客网名企春招实习备战攻略 2019 技术篇牛客网总结的实习攻略,最新一期,全部都是算法题。Github 下载CSDN 下载系统设计入门几乎每个大公司的面试中都有系统设计问题,如果你是应届生或者没有经验,就会很头疼。在线阅读Cyc2018 CS 笔记Cyc2018 在 Github 上整理的笔记,偏语言和系统。在线阅读JavaGuide - Java 学习/面试指南一份涵盖大部分Java程序员所需要掌握的核心知识。由 SnailClimb 在 Github 上整理。在线阅读机器学习面试 1000 题系列由著名机器学习创业者 july 整理的机器学习面试题。在线阅读深度学习500问深度学习500问,以问答形式对常用的概率知识、线性代数、机器学习、深度学习、计算机视觉等热点问题进行阐述,以帮助自己及有需要的读者。 全书分为18个章节,近30万字。由于水平有限,书中不妥之处恳请广大读者批评指正。 未完待续…………在线阅读Glassdoor/看准网如果是一些非技术的问题,面经中可能并没有提及,这时候就需要它们了。Glassdoor看准网程序员找工作黑名单最后是一份黑名单。换工作和当技术合伙人需谨慎啊。在线阅读

March 4, 2019 · 1 min · jiezi

前端实习面试记录(一)

前端实习面试记录(2019.2.27)初次面试 惨败CSS部分1.利用border的特性实现CSS实现三角形JS部分1.数组乱序arr.sort(function(){ return Math.random() - 0.5;})最优算法:Fisher–Yates shuffle 洗牌算法2.for in 和 for of for infor ofArray遍历当前实例以及其原型链上所有可枚举的key只遍历当前实例中的所有value,不涉及原型链Object遍历当前实例以及其原型链上所有可枚举的key, 通常需要Object.hasOwnProperty过滤。也可以直接用Object.keys(),该函数不会遍历到原型链报错 typeErrorfor in 遍历的是键,for of 遍历的是值, for in 一般用于对象的遍历,不会用在数组上。Js中for in 和for of的区别3.promisePromise构造函数接收的函数中,调用resolve()之后的代码仍然会执行,它在被new的时候是同步执行的,然后再考虑微任务的问题console.log(’test start’)var promise = new Promise(function(resolve, reject){ console.log(‘promise start’); resolve(’testing’); console.log(‘promise end’);})promise.then(function(value){ console.log(value);})console.log(’test end’);4.监听一段时间内用户对我方网页的所有操作到现在我也想不到如何监听document的所有事件,总不能让我把所有事件种类都监听吧。在这里只写一个click事件document.addEventListener(‘click’, function(e){ e = e || window.event; // firefox下window.event为null, IE下event为null stopBubble(e); preventDefault(e); console.log(e.target);}, false);function stopBubble(e) { // 组织冒泡 if(e && e.stopPropagetion){ e.stopPropagation(); // w3c } else { window.event.cancelBubble = true; }}function preventDefault(e){ // 组织浏览器默认行为 if(e && e.preventDefault) { // w3c e.preventDefault() } else { // ie window.event.returnValue = false; } return false; }综合方面1.图片懒加载和预加载懒加载也可称作延迟加载懒加载就是先将页面内的所有图片的真实地址放在一个属性(data-url)里, 并将src先设置为1px的小图片,当触发某些条件(图片进入视野)时,再将图片的src换成真实地址。使页面加载速度快、可以减轻服务器的压力,节约了流量,用户体验好。预加载提前加载图片,当用户需要查看时可直接从本地缓存中渲染,可以使用css或js实现。详细解释:懒加载和预加载参考文章CSS实现三角形Fisher–Yates shuffle 洗牌算法Js中for in 和for of的区别懒加载和预加载谢谢面后想了想,其实这些题也都挺简单的,只怪我基础不牢,也太紧张了,一道题没答上后面脑子就不好使了,希望下次能够沉稳点。在此感谢面试我的面试官,让我认识到了自己的不足,谢谢。 ...

March 1, 2019 · 1 min · jiezi

适合实习生的一个面试准备指南

从前端学习到找到一份合适的工作,大大小小的面试必不可少,春招的实习面试,以及秋招的校招面试,从面试内容到技术的深度,要求都是不一样的。先说春招的实习面试,面试内容主要还是考察前端的基本功,如果是科班出身,对于计算机基础学科要求还是比较多的,比如会考察操作系统,编译原理,计算机网络相关基本知识。总体来说,考察的方向大致有一下几种:HTML/HTML5CSS/CSS3布局相关,如双飞翼/圣杯定位清除浮动BFCcss3动画,过渡,形变各种花式垂直水平居中flex弹性布局=如何理解css中的流等等太多了JavaScriptecmascript语法基本功,如字符串拼接,数据类型,对象等js的作用域,作用域链,执行上下文闭包DOM操作,什么是DOM,什么是BOM事件模型thisES6常用的新增特性JS网络相关(都是重点)什么是跨域,常见的跨域方式有哪些,是否知道nginx配置代理进行跨域CORS是什么什么是同源策略什么是Ajax(xhr)(比较重点)常见的存储方式有哪些,即localstorage sessionstorage的场景以及区别Promise是什么,和setTimeout/setInterval的区别,再深一点,宏任务和微任务的区别。什么是模块化,是否了解CMD,以及ES6的module/import垃圾回收机制重要-事件循环机制计算机网络post/get的区别浏览器输入url到页面加载完毕发生了什么启动浏览器网络线程根据是否是有缓存浏览器进行自己的请求规则决定是否发出请求dns查询tcp建立连接接到数据,开始页面渲染,启动浏览器渲染引擎(渲染引擎与js引擎互斥,也就是只能运行一个)解析HTML构建DOM树构建css树解析html过程中如果遇到script标签,要停止渲染,启动js引擎执行script中的代码,如果script中是src格式,那么要启动网络线程,加载js脚本,加载完毕开始执行js脚本,页面渲染一直会被阻塞。当DOM构建完毕,css树也构建完毕,浏览器进入layout阶段合成渲染树render-tree下一步进入painting阶段,即页面开始绘制结束协商缓存vs强缓存cookie/sessionhttp1.0/http1.1/http2/httpstcp模型常见的状态码算法各种花式排序其他的还是看下面给的链接吧框架相关(我擅长vue,其他还没有投入过学习)你的技术栈vue的典型问题,见下面的vue源码揭秘vue-router如何实现,hash/history两种路由的区别vuex,状态管理是什么组件间如何通信数据如何实现双向绑定组件间数据单向流动是啥compute/watch的区别说一下你知道的js开发模式,实现一下观察者模式webpack有了解吗,为什么需要构建工具vue3.0 有了解吗性能相关如何提高页面响应性能(主要是从网络层面思考)减少网络请求图片合图,减少请求的资源总量使用CDN托管静态资源将js脚本标签放在html的最后,避免阻塞渲染,提高首屏加载速度避免使用不必要的同步请求页面图片懒加载,滑动到可是区域,才发起图片请求图片要压缩,现在推荐webp格式,gif推荐animated webP如何提高页面运行时性能避免js操作DOM,因为可能会造成页面的回流或者重绘代码层面(可以参考高性能JavaScript)多次使用类似于a.b.c这样的变量,最佳实践是将其保存在变量中,避免每次都要向上查询,比如a.b.c表示页面节点a的属性b的属性c。多次使用会一直遍历DOM节点,耗时太大。合理使用闭包,避免垃圾回收无法回收实际已经不在使用的内存在合适的情景下释放掉绑定的事件,事件程序本身占用内存,事件太多,要不就及时释放点,要不就研究一下能不能使用事件委托。查找DOM节点时,推荐使用querySelector/querySelectorAll。对于同一个节点的查询,这两个方法要比getElementById/getElementByClass快很多。js中避免直接操作css,代替它的是使用切换类名。后面再慢慢补充总结内容还不是很完善,但是已经完全适合实习生的面试以及自己第一阶段的学习需求了,更详细的内容可以看一下下面贴出来的面试图谱,范围广并且兼顾了一定的深度,可以研究研究。资料链接:面试图谱-真面试宝典,但是我觉得对于实习生性价比最高JavaScript基础进阶–讲道理,这个多看几遍,面试官会觉得你这个实习生蛮强的,其实里面有很多东西,我发现多年从事前端的同学也未必掌握结合CSS3的布局新特征谈谈常见布局方法ES6常用语法从输入URL到页面加载的过程?如何由一道题完善自己的前端知识体系!cookie、Sessionstorage、Localstorage快速上手前端面试之CSS总结(上)浏览器工作原理 (一) : 浏览器渲染原理 & 浏览器内核【CSS基础】Flex弹性布局

March 1, 2019 · 1 min · jiezi

【Leetcode】104. 二叉树的最大深度

题目给定一个二叉树,找出其最大深度。二叉树的深度为根节点到最远叶子节点的最长路径上的节点数。说明: 叶子节点是指没有子节点的节点。示例:给定二叉树 [3,9,20,null,null,15,7], 3 / \ 9 20 / \ 15 7返回它的最大深度 3 。题解求最大深度,和深度相关,我们很容易想到用层序遍历。每遍历一层,就深度加1, 怎么记录是第几层我们之前的文章中讲过了。/** * Definition for a binary tree node. * public class TreeNode { * int val; * TreeNode left; * TreeNode right; * TreeNode(int x) { val = x; } * } */class Solution { public int maxDepth(TreeNode root) { if (root == null) { return 0; } int depth = 0; LinkedList<TreeNode> queue = new LinkedList<>(); queue.add(root); while (!queue.isEmpty()) { int size = queue.size(); while (size > 0) { TreeNode current = queue.poll(); if (current.left != null) { queue.add(current.left); } if (current.right != null) { queue.add(current.right); } size–; } depth++; } return depth; }}这道题用递归解代码比较简单.递归的结束条件: 当节点为叶子节点的时候.递归的子问题: 当前最大深度 = 左右子树最大深度的较大者 + 1代码实现就很简单了。class Solution { public int maxDepth(TreeNode root) { if (root == null) { return 0; } return Math.max(maxDepth(root.left), maxDepth(root.right)) + 1; }}热门阅读技术文章汇总【Leetcode】103. 二叉树的锯齿形层次遍历【Leetcode】102. 二叉树的层次遍历【Leetcode】101. 对称二叉树【Leetcode】100. 相同的树【Leetcode】98. 验证二叉搜索树 ...

March 1, 2019 · 1 min · jiezi

PHP面试之网络协议面试题

你好,是我琉忆。本篇是本周(2019.2-25至3-1)的最后一篇文章。之前的文章可以关注我的“PHP面试”专栏获取,或者关注我的公众号“琉忆编程库”查看历史获取。我正在segmentfault社区送两本书:《PHP程序员面试笔试真题解析》 ,今晚(3月1日)19点开奖,欢迎参与。明天就是周末了,提前祝各位周末愉快。面试时如何给自己添金?没接触过支付功能?为此特意准备了一篇“新手零基础对接境外支付paypal”的文章,为你面试添金,关注公众号:“琉忆编程库”,回复:“pay”,我发给你。以下正文的内容来自《PHP程序员面试笔试真题解析》书籍,如果转载请保留出处:【真题1】如何利用PHP解决HTTP的无状态本质?答案:可以使用Session和Cookie记住用户,监控是否同一个用户在操作。使用Session前需要在页面使用session_start()开启Session。然后使用$_SESSION来存储Session变量。使用Cookie时,需使用set_cookie()设置Cookie,然后使用$_COOKIE取Cookie值。【真题2】HTTP的Keep-Alive作用是什么?答案:HTTP的Keep-Alive作用如下:Keep-Alive使客户端到服务器端的连接持续有效,当出现对服务器的后继请求时,Keep-Alive功能避免了建立或者重新建立连接。Web服务器基本上都支持HTTP Keep-Alive。缺点:对于提供静态内容的网站来说,这个功能通常很有用。但是,对于负担较重的网站来说,虽然为客户保留打开的连接有一定的好处,但它同样影响了性能,因为在暂停使用的期间,本来可以释放的资源仍旧被占用。所以当Web服务器和应用服务器在同一台机器上运行时,Keep-Alive功能对资源利用的影响尤其突出。解决:Keep-Alive:timeout=5,max=100timeout:过期时间5秒(对应httpd.conf里的参数是:KeepAliveTimeout),max是最多请求一百次,强制断掉连接。在timeout时间内又有新的连接过来时,max会自动减1,直到为0,强制断掉。【真题3】如何快速下载一个远程HTTP服务器上的图片文件到本地?答案:先通过fopen()函数打开要下载的图片路径,然后使用fread()函数读取图片,再在本地打开指定存储图片的目录,将读取到的图片信息写入到该目录内的文件中,即可远程下载一个图片到本地。实现代码如下:<?php $file=“http://static.nipic.com/images/originalNewtip.png"; $fp=fopen($file,‘rb’); $img=fread($fp,100000); $dir=”./"; $local=fopen($dir.’/’.basename($file),‘w’); fwrite($local,$img);?>【真题4】请简述HTTP中POST、GET、PUT、DELETE方式的区别。答案:HTTP定义了与服务器交互的不同的方法,最基本的方法是POST、GET、PUT和DELETE,URL全称是资源描述符,可以认为一个URL地址用于描述一个网络上的资源,而对应的HTTP中的四种方法GET、POST、PUT、DELETE对应这个资源的查,改,增,删四个操作。它们的具体功能为:(1)根据HTTP规范,GET用于信息获取,信息应该是安全的;(2)根据HTTP规范,POST表示可以修改服务器上的资源的请求;(3)PUT和POST极为相似,都是向服务器发送数据,但它们之间有一个重要区别,PUT通常指定了资源的存放位置,而POST则没有,POST的数据存放位置由服务器决定;(4)DELETE的功能是删除某一个资源。【真题5】如何理解HTTP的gzip压缩传输?答案:HTTP下的gzip是一种流行的文件压缩算法,在Linux平台上使用十分广泛,gzip压缩传输能更加有效节约带宽流量。它先把文本压缩为.gz然后传输给浏览器,最后由浏览器负责解压缩呈现给用户,可以降低网络传输的字节数,加快网页的加载速度。面试时如何给自己添金?没接触过支付功能?为此特意准备了一篇“新手零基础对接境外支付paypal”的文章,为你面试添金,关注公众号:“琉忆编程库”,回复:“pay”,我发给你。【真题6】如何给动态文件(如PHP页面)做缓存?(即第二次刷新浏览器时,返回304状态)答案:304状态的意思是当客户端向服务端请求时,服务端告诉客户端缓存的文档没有过期可以继续使用。可以通过以下方法作浏览器缓存:(1)当用户首次请求该文件的时候,通过HTTP HEAD的Last-Modified字段将该文件的最后修改日期发送到客户端,让客户端知道该文件的版本,例如:Last-Modified: Tue, 08 Apr 2008 14:48:05 GMT(2)在浏览器再次请求该文件的时候,会自动将该时间作为请求的HTTP HEAD的If-Modified-Since字段内容,例如: If-Modified-Since: Tue, 08 Apr 2008 14:48:05 GMT(3)服务端根据If-Modified-Since字段的内容(如果存在该字段)来判断客户端的文件是否已经过期,如果已经过期,那么重新返回新的文件,如果没有,那么只需要返回304状态码,就可结束输出,这样代表浏览器端的文件版本是最新的,不需要返回文件内容。【真题7】真题478、请写出HTTP和HTTPS的区别。【真题8】IP是否可以伪造?PHP中如何拿到真实IP?答案:IP地址是可以伪造的,客户端可以伪造IP地址或使用代理IP导致IP地址不是用户的真实IP地址。REMOTE_ADDR代表客户端的IP地址,HTTP_X_FORWARDED_FOR是代理服务器的IP地址,存放真实的IP和各级代理IP。实现代码如下:<?php function getIP(){ $unknown = ‘unknown’; if(isset($_SERVER[‘HTTP_X_FORWARDED_FOR’])&&$_SERVER[‘HTTP_X_FORWARDED_FOR’]&&strcasecmp($_SERVER[‘HTTP_X_FORWARDED_FOR’],$unknown)){ $IP = $_SERVER[‘HTTP_X_FORWARDED_FOR’]; }elseif(isset($_SERVER[‘REMOTE_ADDR’])&&$_SERVER[‘REMOTE_ADDR’]&&strcasecmp($_SERVER[‘REMOTE_ADDR’],$unknown)){ $IP = $_SERVER[‘REMOTE_ADDR’]; } if (false !== strpos($IP,’,’)) $IP = reset(explode(’,’,$IP)); return $IP; } ?>【真题9】在HTTP 1.0中,状态码 401 的含义是( );如果返回“找不到文件”的提示,则可用 header 函数,其语句为( )。参考答案:客户端在授权头信息中没有有效的身份信息时访问受到密码保护的页面;header(“HTTP/1.0 404 Not Found”);。【真题10】下列关于HTTP协议的说法中,错误的是( )。A.如果本地开启了Cookie,那么每打开一个网址,HTTP请求就会把相应的Cookie传给Web服务器B.HTTP响应的状态码为301,意思是暂时地把内容转移到一个新的URL,但是老的URL还没有废除C.HTTP是一个基于请求与响应模式的、无状态的、应用层的协议,绝大多数的Web开发都是基于HTTP协议D.绝大多数的Web开发离不开Cookie,如果禁用Cookie导致Session失效,那么可以通过URL来传递sessionID参考答案:B。分析:301表示请求的网页已被永久移动到新位置而不是暂时转移内容到新的URL。所以,选项B说法错误。预告:下周(3.4-3.8)将更新PHP面试之MySQL数据库知识点,敬请期待。以上内容摘自《PHP程序员面试笔试真题解析》书籍,该书已在天猫、京东、当当等电商平台销售。更多PHP相关的面试知识、考题可以关注公众号获取:琉忆编程库对本文有什么问题或建议都可以进行留言,我将不断完善追求极致,感谢你们的支持。

March 1, 2019 · 1 min · jiezi

Javascript 面试中经常被问到的三个问题!

本文不是讨论最新的 JavaScript 库、常见的开发实践或任何新的 ES6 函数。相反,在讨论 JavaScript 时,面试中通常会提到三件事。我自己也被问到这些问题,我的朋友们告诉我他们也被问到这些问题。然,这些并不是你在面试之前应该学习的唯一三件事 - 你可以通过多种方式更好地为即将到来的面试做准备 - 但面试官可能会问到下面是三个问题,来判断你对 JavaScript 语言的理解和 DOM 的掌握程度。让我们开始吧!注意,我们将在下面的示例中使用原生的 JavaScript,因为面试官通常希望了解你在没有 jQuery 等库的帮助下对JavaScript 和 DOM 的理解程度。问题 1: 事件委托代理在构建应用程序时,有时需要将事件绑定到页面上的按钮、文本或图像,以便在用户与元素交互时执行某些操作。如果我们以一个简单的待办事项列表为例,面试官可能会告诉你,当用户点击列表中的一个列表项时执行某些操作。他们希望你用 JavaScript 实现这个功能,假设有如下 HTML 代码:<ul id=“todo-app”> <li class=“item”>Walk the dog</li> <li class=“item”>Pay bills</li> <li class=“item”>Make dinner</li> <li class=“item”>Code for one hour</li></ul>你可能想要做如下操作来将事件绑定到元素:document.addEventListener(‘DOMContentLoaded’, function() { let app = document.getElementById(’todo-app’); let times = app.getElementsByClassName(‘item’); for (let item of items) { item.addEventListener(‘click’, function(){ alert(‘you clicked on item: ’ + item.innerHTML); }) }})虽然这在技术上是可行的,但问题是要将事件分别绑定到每个项。这对于目前 4 个元素来说,没什么大问题,但是如果在待办事项列表中添加了 10,000 项(他们可能有很多事情要做)怎么办?然后,函数将创建 10,000 个独立的事件侦听器,并将每个事件监听器绑定到 DOM ,这样代码执行的效率非常低下。在面试中,最好先问面试官用户可以输入的最大元素数量是多少。例如,如果它不超过 10,那么上面的代码就可以很好地工作。但是如果用户可以输入的条目数量没有限制,那么你应该使用一个更高效的解决方案。如果你的应用程序最终可能有数百个事件侦听器,那么更有效的解决方案是将一个事件侦听器实际绑定到整个容器,然后在单击它时能够访问每个列表项, 这称为 事件委托,它比附加单独的事件处理程序更有效。下面是事件委托的代码:document.addEventListener(‘DOMContentLoaded’, function() { let app = document.getElementById(’todo-app’); app.addEventListener(‘click’, function(e) { if (e.target && e.target.nodeName === ‘LI’) { let item = e.target; alert(‘you clicked on item: ’ + item.innerHTML) } })})问题 2: 在循环中使用闭包闭包常常出现在面试中,以便面试官衡量你对 JS 的熟悉程度,以及你是否知道何时使用闭包。闭包基本上是内部函数可以访问其范围之外的变量。 闭包可用于实现隐私和创建函数工厂, 闭包常见的面试题如下:编写一个函数,该函数将遍历整数列表,并在延迟3秒后打印每个元素的索引。经常不正确的写法是这样的:const arr = [10, 12, 15, 21];for (var i = 0; i < arr.length; i++) { setTimeout(function() { console.log(‘The index of this number is: ’ + i); }, 3000);}如果运行上面代码,3 秒延迟后你会看到,实际上每次打印输出是 4,而不是期望的 0,1,2,3 。为了正确理解为什么会发生这种情况,了解为什么会在 JavaScript 中发生这种情况将非常有用,这正是面试官试图测试的内容。原因是因为 setTimeout 函数创建了一个可以访问其外部作用域的函数(闭包),该作用域是包含索引 i 的循环。 经过 3 秒后,执行该函数并打印出 i 的值,该值在循环结束时为 4,因为它循环经过0,1,2,3,4并且循环最终停止在 4。实际上有多处方法来正确的解这道题:const arr = [10, 12, 15, 21];for (var i = 0; i < arr.length; i++) { setTimeout(function(i_local){ return function () { console.log(‘The index of this number is: ’ + i_local); } }(i), 3000)}const arr = [10, 12, 15, 21];for (let i = 0; i < arr.length; i++) { setTimeout(function() { console.log(‘The index of this number is: ’ + i); }, 3000);}问题 3:事件的节流(throttle)与防抖(debounce)有些浏览器事件可以在短时间内快速触发多次,比如调整窗口大小或向下滚动页面。例如,监听页面窗口滚动事件,并且用户持续快速地向下滚动页面,那么滚动事件可能在 3 秒内触发数千次,这可能会导致一些严重的性能问题。如果在面试中讨论构建应用程序,出现滚动、窗口大小调整或按下键等事件请务必提及 防抖(Debouncing) 和 函数节流(Throttling)来提升页面速度和性能。这两兄弟的本质都是以闭包的形式存在。通过对事件对应的回调函数进行包裹、以自由变量的形式缓存时间信息,最后用 setTimeout 来控制事件的触发频率。Throttle: 第一个人说了算throttle 的主要思想在于:在某段时间内,不管你触发了多少次回调,都只认第一次,并在计时结束时给予响应。这个故事里,‘裁判’ 就是我们的节流阀, 他控制参赛者吃东西的时机, “参赛者吃东西”就是我们频繁操作事件而不断涌入的回调任务,它受 “裁判” 的控制,而计时器,就是上文提到的以自由变量形式存在的时间信息,它是 “裁判” 决定是否停止比赛的依据,最后,等待比赛结果就对应到回调函数的执行。总结下来,所谓的“节流”,是通过在一段时间内无视后来产生的回调请求来实现的。只要 裁判宣布比赛开始,裁判就会开启计时器,在这段时间内,参赛者就尽管不断的吃,谁也无法知道最终结果。对应到实际的交互上是一样一样的:每当用户触发了一次 scroll 事件,我们就为这个触发操作开启计时器。一段时间内,后续所有的 scroll 事件都会被当作“参赛者吃东西——它们无法触发新的 scroll 回调。直到“一段时间”到了,第一次触发的 scroll 事件对应的回调才会执行,而“一段时间内”触发的后续的 scroll 回调都会被节流阀无视掉。现在一起实现一个 throttle:// fn 是我们需要包装的事件回调, interval 是时间间隔的阈值function throttle(fn, interval) { // last 为上一次触发回调时间 let last = 0 // 将 throttle 处理结果当然函数返回 return function () { // 保留调用时的 this 上下文 let context = this // 保留调用时传入的参数 let args = arguments // 记录本次触发回调的时间 let now = +new Date() // 判断上次触发的时间和本次触发的时间差是否小于时间间隔的阈值 if (now - last >= interval) { // 如果时间间隔大于我们设定的时间间隔阈值,则执行回调 last = fn.apply(context, args) } }}// 用 throttle来包装 scroll 的回调const better_scroll = throttle(() => { console.log(‘触发了滚动事件’)}, 1000)document.addEventListener(‘scroll’, better_scroll)Debounce: 最后一个参赛者说了算防抖的主要思想在于:我会等你到底。在某段时间内,不管你触发了多少次回调,我都只认最后一次。继续大胃王比赛故事,这次换了一种比赛方式,时间不限,参赛者吃到不能吃为止,当每个参赛都吃不下的时候,后面10分钟如果没有人在吃,比赛结束,如果有人在10分钟内还能吃,则比赛继续,直到下一次10分钟内无人在吃时为止。对比 throttle 来理解 debounce: 在 throttle 的逻辑里, ‘裁判’ 说了算,当比赛时间到时,就执行回调函数。而 debounce 认为最后一个参赛者说了算,只要还能吃的,就重新设定新的定时器。现在一起实现一个 debounce:// fn 是我们需要包装事件回调,delay 是每次推迟执行的等待时间function debounce(fn, delay) { // 定时器 let timer = null // 将 debounce 处理结果当作函数返回 return function () { // 保留调用时的 this 上下文 let context = this // 保留调用时传入的参数 let args = arguments // 每次事件被触发时,都去清除之前的旧定时器 if (timer) { clearTimeout(timer) } // 设立新定时器 timer = setTimeout(function() { fn.apply(context, args) }, delay) }}// 用 debounce 来包装 scroll 的回调const better_scroll = debounce(() => { console.log(‘发了滚动事件’)}, 1000)document.addEventListener(‘scroll’, better_scroll)用 Throttle 来优化 Debouncedebounce 的问题在于它“太有耐心了”。试想,如果用户的操作十分频繁——他每次都不等 debounce 设置的 delay 时间结束就进行下一次操作,于是每次 debounce 都为该用户重新生成定时器,回调函数被延迟了不计其数次。频繁的延迟会导致用户迟迟得不到响应,用户同样会产生“这个页面卡死了”的观感。为了避免弄巧成拙,我们需要借力 throttle 的思想,打造一个“有底线”的 debounce——等你可以,但我有我的原则:delay 时间内,我可以为你重新生成定时器;但只要delay的时间到了,我必须要给用户一个响应。这个 throttle 与 debounce “合体”思路,已经被很多成熟的前端库应用到了它们的加强版 throttle 函数的实现中:// fn是我们需要包装的事件回调, delay是时间间隔的阈值function throttle(fn, delay) { // last为上一次触发回调的时间, timer是定时器 let last = 0, timer = null // 将throttle处理结果当作函数返回 return function () { // 保留调用时的this上下文 let context = this // 保留调用时传入的参数 let args = arguments // 记录本次触发回调的时间 let now = +new Date() // 判断上次触发的时间和本次触发的时间差是否小于时间间隔的阈值 if (now - last < delay) { // 如果时间间隔小于我们设定的时间间隔阈值,则为本次触发操作设立一个新的定时器 clearTimeout(timer) timer = setTimeout(function () { last = now fn.apply(context, args) }, delay) } else { // 如果时间间隔超出了我们设定的时间间隔阈值,那就不等了,无论如何要反馈给用户一次响应 last = now fn.apply(context, args) } }}// 用新的throttle包装scroll的回调const better_scroll = throttle(() => console.log(‘触发了滚动事件’), 1000)document.addEventListener(‘scroll’, better_scroll)参考:Throttling and Debouncing in JavaScriptThe Difference Between Throttling and DebouncingExamples of Throttling and DebouncingRemy Sharp’s blog post on Throttling function calls前端性能优化原理与实践原文:https://medium.freecodecamp.o…你的点赞是我持续分享好东西的动力,欢迎点赞!一个笨笨的码农,我的世界只能终身学习!更多内容请关注公众号《大迁世界》! ...

February 28, 2019 · 3 min · jiezi

7个开放式的前端面试题

翻译:疯狂的技术宅原文:https://www.indeed.com/hire/i…不管你是面试官还是求职者,里面的思路都能让你获益匪浅。如何保证你的网站或 Web 应用的可访问性?解析:前端开发人员在为最终用户设计体验时需要充分掌控其可用性和可访问性。这个问题让面试官知道求职者对这些标准和最佳实践的了解。面试官想要知道些什么:实施可用性和可访问性标准的第一手经验保证可访问性的手段求职者是否曾经在项目中对可用性和可访问性做过实施参考答案:我在各种浏览器和设备上测试我的 Web 应用,检查在所有设备上的体验是否可用。我还使用流行的屏幕阅读软件和其他辅助解决方案来查看是否所有潜在用户都可以访问它。”你最喜欢 HTML5 的哪些功能,以及如何在前端项目中实现?解析:前端开发会在 HTML5 上花费大量时间,他们整合 Web 设计并实现各种功能,从而节省开发时间并改进最终产品。面试官想要知道些什么:深入了解 HTML5 功能能够清楚的解释求职者在过去的项目中是如何使用HTML5的对 HTML5 的创造性应用参考答案:与以前的版本相比,我喜欢 HTML5 对多媒体的支持。我使用此功能开发时,会把视频和音频作为用户体验的重要组成部分,而不必担心它无法正确加载或破坏页面。如何对 CSS 和 JavaScript 代码进行组织,以使其他开发人员更容易使用?解析:前端开发人员经常会处理由以前的员工创建的代码,或作为团队的一员工作。这个问题让面试官了解求职者能不能创建其他员工可以理解的网站或 Web 应用。面试官想要知道些什么:具有组织代码和注释代码的经验如果没有对代码进行恰当的注释会发生什么后果愿意让开发团队其他成员更轻松参考答案:我把站点的每个组件都分拆出了独立的代码。每个代码片段都有注释,以便其他开发人员可以更改它。你能解释一下 CSS float 的概念并举例吗?解析:该技术问题考察面试者对常见 CSS 元素的理解。这个问题用来筛选求职者,并确保他们不只是能够熟练使用 CSS 的好办法。面试官想要知道些什么:CSS float 的具体定义是什么求职者是怎样在他们的代码中使用此元素的是否能够向任何人解释这一技术概念参考答案:CSS float 告诉浏览器将特定元素放在容器的右侧或左侧。当我开发基于用户分辨率动态调整大小的页面时,使用float。”你是怎样处理特定浏览器的渲染问题的?你是否发现某个浏览器比其他浏览器更具挑战性?解析:基于每个浏览器的渲染的方式,前端开发人员必须将特定于浏览器的 hack 引入Web 应用和页面。对于求职者来说,这个常见的过程应该是比较熟悉的。面试官想要知道些什么:针对特定浏览器的解决方法示例了解浏览器如何呈现应用和页面了解特定浏览器的特点参考答案:我经常需要修改 Microsoft Edge 网页上的 DIV 位置。对于我来说,这个浏览器是最有问题的,因为我几乎不会在 Chrome 或 Firefox 上遇到这类问题。你通过哪些途径来了解最新的前端技术?解析:前端开发中的技术和标准在不断变化。拥有“持续学习”能力的求职者可以通过不断的学习,来获取改进开发过程和改善最终用户体验的方法。面试官想要知道些什么:求职者都读过什么技术书籍是否参与开源项目或个人有什么开源项目是否参加继续教育或在线课程参考答案:我有订阅了几个前端开发方面的杂志,并经常访问 Stack Overflow 论坛。这些资源帮助我了解前端开发的最新技术。你最喜欢的前端项目是什么?为什么?解析:这个问题可让面试官深入了解前端开发人员的个人偏好。一些求职者可能更倾向网站项目,而其他人更喜欢参与Web 应用团队。面试官想要知道些什么:对他们最喜欢的发展方向的见解他们描述该项目时表现出的热情他们最喜欢的前端项目的案例参考答案:我最近为当地医院开发了一个网络应用。它简化了患者的求医过程,当我知道自己的工作能帮助患者更快地获得所需的帮助时非常开心。欢迎继续阅读本专栏其它高赞文章:12个令人惊叹的CSS实验项目世界顶级公司的前端面试都问些什么CSS Flexbox 可视化手册过节很无聊?还是用 JavaScript 写一个脑力小游戏吧!从设计者的角度看 ReactCSS粘性定位是怎样工作的一步步教你用HTML5 SVG实现动画效果程序员30岁前月薪达不到30K,该何去何从第三方CSS安全吗?谈谈super(props) 的重要性本文首发微信公众号:jingchengyideng欢迎扫描二维码关注公众号,每天都给你推送新鲜的前端技术文章

February 28, 2019 · 1 min · jiezi

vue组件之间8种组件通信方式总结

对于vue来说,组件之间的消息传递是非常重要的,下面是我对组件之间消息传递的各种方式的总结,总共有8种方式。1.props和$emit父组件向子组件传递数据是通过prop传递的,子组件传递数据给父组件是通过$emit触发事件来做到的。Vue.component(‘child’,{ data(){ return { mymessage:this.message } }, template: &lt;div&gt; &lt;input type="text" v-model="mymessage" @input="passData(mymessage)"&gt; &lt;/div&gt; , props:[‘message’],//得到父组件传递过来的数据 methods:{ passData(val){ //触发父组件中的事件 this.$emit(‘getChildData’,val) } } }) Vue.component(‘parent’,{ template: &lt;div&gt; &lt;p&gt;this is parent compoent!&lt;/p&gt; &lt;child :message="message" v-on:getChildData="getChildData"&gt;&lt;/child&gt; &lt;/div&gt; , data(){ return { message:‘hello’ } }, methods:{ //执行子组件触发的事件 getChildData(val){ console.log(val) } } }) var app=new Vue({ el:’#app’, template: &lt;div&gt; &lt;parent&gt;&lt;/parent&gt; &lt;/div&gt; })在上面的例子中,有父组件parent和子组件child。 1).父组件传递了message数据给子组件,并且通过v-on绑定了一个getChildData事件来监听子组件的触发事件; 2).子组件通过props得到相关的message数据,最后通过this.$emit触发了getChildData事件。2.$attrs和$listeners第一种方式处理父子组件之间的数据传输有一个问题:如果父组件A下面有子组件B,组件B下面有组件C,这时如果组件A想传递数据给组件C怎么办呢? 如果采用第一种方法,我们必须让组件A通过prop传递消息给组件B,组件B在通过prop传递消息给组件C;要是组件A和组件C之间有更多的组件,那采用这种方式就很复杂了。Vue 2.4开始提供了$attrs和$listeners来解决这个问题,能够让组件A之间传递消息给组件C。Vue.component(‘C’,{ template: &lt;div&gt; &lt;input type="text" v-model="$attrs.messagec" @input="passCData($attrs.messagec)"&gt; &lt;/div&gt; , methods:{ passCData(val){ //触发父组件A中的事件 this.$emit(‘getCData’,val) } } }) Vue.component(‘B’,{ data(){ return { mymessage:this.message } }, template: &lt;div&gt; &lt;input type="text" v-model="mymessage" @input="passData(mymessage)"&gt; &lt;!-- C组件中能直接触发getCData的原因在于 B组件调用C组件时 使用 v-on 绑定了$listeners 属性 --&gt; &lt;!-- 通过v-bind 绑定$attrs属性,C组件可以直接获取到A组件中传递下来的props(除了B组件中props声明的) --&gt; &lt;C v-bind="$attrs" v-on="$listeners"&gt;&lt;/C&gt; &lt;/div&gt; , props:[‘message’],//得到父组件传递过来的数据 methods:{ passData(val){ //触发父组件中的事件 this.$emit(‘getChildData’,val) } } }) Vue.component(‘A’,{ template: &lt;div&gt; &lt;p&gt;this is parent compoent!&lt;/p&gt; &lt;B :messagec="messagec" :message="message" v-on:getCData="getCData" v-on:getChildData="getChildData(message)"&gt;&lt;/B&gt; &lt;/div&gt; , data(){ return { message:‘hello’, messagec:‘hello c’ //传递给c组件的数据 } }, methods:{ getChildData(val){ console.log(‘这是来自B组件的数据’) }, //执行C子组件触发的事件 getCData(val){ console.log(“这是来自C组件的数据:"+val) } } }) var app=new Vue({ el:’#app’, template: &lt;div&gt; &lt;A&gt;&lt;/A&gt; &lt;/div&gt; })3.中央事件总线上面两种方式处理的都是父子组件之间的数据传递,而如果两个组件不是父子关系呢?这种情况下可以使用中央事件总线的方式。新建一个Vue事件bus对象,然后通过bus.$emit触发事件,bus.$on监听触发的事件。Vue.component(‘brother1’,{ data(){ return { mymessage:‘hello brother1’ } }, template: &lt;div&gt; &lt;p&gt;this is brother1 compoent!&lt;/p&gt; &lt;input type="text" v-model="mymessage" @input="passData(mymessage)"&gt; &lt;/div&gt; , methods:{ passData(val){ //触发全局事件globalEvent bus.$emit(‘globalEvent’,val) } } }) Vue.component(‘brother2’,{ template: &lt;div&gt; &lt;p&gt;this is brother2 compoent!&lt;/p&gt; &lt;p&gt;brother1传递过来的数据:{{brothermessage}}&lt;/p&gt; &lt;/div&gt; , data(){ return { mymessage:‘hello brother2’, brothermessage:’’ } }, mounted(){ //绑定全局事件globalEvent bus.$on(‘globalEvent’,(val)=>{ this.brothermessage=val; }) } }) //中央事件总线 var bus=new Vue(); var app=new Vue({ el:’#app’, template: &lt;div&gt; &lt;brother1&gt;&lt;/brother1&gt; &lt;brother2&gt;&lt;/brother2&gt; &lt;/div&gt; })4.provide和inject父组件中通过provider来提供变量,然后在子组件中通过inject来注入变量。不论子组件有多深,只要调用了inject那么就可以注入provider中的数据。而不是局限于只能从当前父组件的prop属性来获取数据,只要在父组件的生命周期内,子组件都可以调用。Vue.component(‘child’,{ inject:[‘for’],//得到父组件传递过来的数据 data(){ return { mymessage:this.for } }, template: &lt;div&gt; &lt;input type="tet" v-model="mymessage"&gt; &lt;/div&gt; }) Vue.component('parent',{ template: <div> <p>this is parent compoent!</p> <child></child> </div> , provide:{ for:'test' }, data(){ return { message:'hello' } } }) var app=new Vue({ el:'#app', template: <div> <parent></parent> </div> })5.v-model父组件通过v-model传递值给子组件时,会自动传递一个value的prop属性,在子组件中通过this.$emit(‘input’,val)自动修改v-model绑定的值Vue.component('child',{ props:{ value:String, //v-model会自动传递一个字段为value的prop属性 }, data(){ return { mymessage:this.value } }, methods:{ changeValue(){ this.$emit('input',this.mymessage);//通过如此调用可以改变父组件上v-model绑定的值 } }, template: <div> <input type=“text” v-model=“mymessage” @change=“changeValue”> </div> }) Vue.component(‘parent’,{ template: &lt;div&gt; &lt;p&gt;this is parent compoent!&lt;/p&gt; &lt;p&gt;{{message}}&lt;/p&gt; &lt;child v-model="message"&gt;&lt;/child&gt; &lt;/div&gt; , data(){ return { message:‘hello’ } } }) var app=new Vue({ el:’#app’, template: &lt;div&gt; &lt;parent&gt;&lt;/parent&gt; &lt;/div&gt; })6.$parent和$childrenVue.component(‘child’,{ props:{ value:String, //v-model会自动传递一个字段为value的prop属性 }, data(){ return { mymessage:this.value } }, methods:{ changeValue(){ this.$parent.message = this.mymessage;//通过如此调用可以改变父组件的值 } }, template: &lt;div&gt; &lt;input type="text" v-model="mymessage" @change="changeValue"&gt; &lt;/div&gt; }) Vue.component('parent',{ template: <div> <p>this is parent compoent!</p> <button @click=“changeChildValue”>test</button > <child></child> </div> , methods:{ changeChildValue(){ this.$children[0].mymessage = 'hello'; } }, data(){ return { message:'hello' } } }) var app=new Vue({ el:'#app', template: <div> <parent></parent> </div> ` })7.boradcast和dispatchvue1.0中提供了这种方式,但vue2.0中没有,但很多开源软件都自己封装了这种方式,比如min ui、element ui和iview等。 比如如下代码,一般都作为一个mixins去使用, broadcast是向特定的父组件,触发事件,dispatch是向特定的子组件触发事件,本质上这种方式还是on和on和emit的封装,但在一些基础组件中却很实用。function broadcast(componentName, eventName, params) { this.$children.forEach(child => { var name = child.$options.componentName; if (name === componentName) { child.$emit.apply(child, [eventName].concat(params)); } else { broadcast.apply(child, [componentName, eventName].concat(params)); } });}export default { methods: { dispatch(componentName, eventName, params) { var parent = this.$parent; var name = parent.$options.componentName; while (parent && (!name || name !== componentName)) { parent = parent.$parent; if (parent) { name = parent.$options.componentName; } } if (parent) { parent.$emit.apply(parent, [eventName].concat(params)); } }, broadcast(componentName, eventName, params) { broadcast.call(this, componentName, eventName, params); } }};8.vuex处理组件之间的数据交互如果业务逻辑复杂,很多组件之间需要同时处理一些公共的数据,这个时候才有上面这一些方法可能不利于项目的维护,vuex的做法就是将这一些公共的数据抽离出来,然后其他组件就可以对这个公共数据进行读写操作,这样达到了解耦的目的。 详情可参考:https://vuex.vuejs.org/zh-cn/ ...

February 28, 2019 · 3 min · jiezi

Vue生命周期的理解

当面试官问:“谈谈你对vue的生命周期的理解”,听到这句话你是不是心里暗自窃喜:这也太容易了吧,不就是beforeCreate、created、beforeMount、mounted、beforeUpdate、updated、beforeDestroy、destroyed 这几个钩子函数么,创建=>挂载=>更新=>销毁,So easy !!!非也非也。如果你只是简单罗列出这几个钩子函数的名称,不具体深入阐述的话,你这样的回答很难令面试官满意。如何才能以点带面深入阐述自己对vue的生命周期理解,从而让面试官对你留下好印象呢?别急,闰土大叔来告诉你,下次再碰到这个问题,你可以直接甩给面试官下面这张Image就OK了~当然,甩张Image给面试官这句话肯定是开玩笑的(适度幽默,缓解紧张气氛)。不过这张流程图还是有用的,因为它是我从Vue官网上拷贝下来的,只要你能理解了这张图,也就对Vue的生命周期有了一个大致的了解。那么接下来,闰土大叔将手摸手教你如何深入浅出地说出令面试官满意的、有亮点的回答。在谈到Vue的生命周期的时候,我们首先需要创建一个实例,也就是在 new Vue ( ) 的对象过程当中,首先执行了init(init是vue组件里面默认去执行的),在init的过程当中首先调用了beforeCreate,然后在injections(注射)和reactivity(反应性)的时候,它会再去调用created。所以在init的时候,事件已经调用了,我们在beforeCreate的时候千万不要去修改data里面赋值的数据,最早也要放在created里面去做(添加一些行为)。当created完成之后,它会去判断instance(实例)里面是否含有“el”option(选项),如果没有的话,它会调用vm.$mount(el)这个方法,然后执行下一步;如果有的话,直接执行下一步。紧接着会判断是否含有“template”这个选项,如果有的话,它会把template解析成一个render function ,这是一个template编译的过程,结果是解析成了render函数:render (h) { return h(‘div’, {}, this.text)}解释一下,render函数里面的传参h就是Vue里面的createElement方法,return返回一个createElement方法,其中要传3个参数,第一个参数就是创建的div标签;第二个参数传了一个对象,对象里面可以是我们组件上面的props,或者是事件之类的东西;第三个参数就是div标签里面的内容,这里我们指向了data里面的text。使用render函数的结果和我们之前使用template解析出来的结果是一样的。render函数是发生在beforeMount和mounted之间的,这也从侧面说明了,在beforeMount的时候,$el还只是我们在HTML里面写的节点,然后到mounted的时候,它就把渲染出来的内容挂载到了DOM节点上。这中间的过程其实是执行了render function的内容。在使用.vue文件开发的过程当中,我们在里面写了template模板,在经过了vue-loader的处理之后,就变成了render function,最终放到了vue-loader解析过的文件里面。这样做有什么好处呢?原因是由于在解析template变成render function的过程,是一个非常耗时的过程,vue-loader帮我们处理了这些内容之后,当我们在页面上执行vue代码的时候,效率会变得更高。beforeMount在有了render function的时候才会执行,当执行完render function之后,就会调用mounted这个钩子,在mounted挂载完毕之后,这个实例就算是走完流程了。后续的钩子函数执行的过程都是需要外部的触发才会执行。比如说有数据的变化,会调用beforeUpdate,然后经过Virtual DOM,最后updated更新完毕。当组件被销毁的时候,它会调用beforeDestory,以及destoryed。这就是vue实例从新建到销毁的一个完整流程,以及在这个过程中它会触发哪些生命周期的钩子函数。那说到这儿,可能很多童鞋会问,钩子函数是什么意思?钩子函数,其实和回调是一个概念,当系统执行到某处时,检查是否有hook,有则回调。说的更直白一点,每个组件都有属性,方法和事件。所有的生命周期都归于事件,在某个时刻自动执行。其实,当你跟面试官阐述到这儿的时候,面试官基本上已经满意你的回答了,隐约看到了你的技术功底。当然,如果你还想更进一步,让面试官对你刮目相看,达到加分的效果,你还可以这样说:在这个过程当中,Vue为我们提供了renderError方法,这个方法只有在开发的时候它才会被调用,在正式打包上线的过程当中,它是不会被调用的。它主要是帮助我们调试render里面的一些错误。renderError (h, err) { return h(‘div’, {}, err.stack)}有且只有当render方法里面报错了,才会执行renderError方法。所以我们主动让render函数报个错:render (h) { throw new TypeError(‘render error’)}如图所示,渲染出来的就是Error信息了。还有一点,renderError只有在本组件的render方法报错的情况下它才会被调用。

February 28, 2019 · 1 min · jiezi

JS核心知识点梳理——数据篇

JS核心知识点梳理——数据篇看了一些资料,结合ES6、高程和MDN,对JS核心知识点进行了梳理。由于篇幅有限,这里只对我认为重要的知识做了介绍。一些常识性的东西可以参考高程,另外一些核心知识点的扩展可以参考我其他的文章。本文适合作JS知识点复习/突击用,亦可作为前端面试指导。7种数据类型基础数据类型:存储到栈内存中,操作的是值null:空指针,所以typeof null ==>Objectundefined:定义了未赋值Number:数字String:字符串Symbol:一种实例是唯一且不可改变的数据类型。Boolean:布尔值引用数据类型:储存在堆内存种,操作的是空间地址Object:具体可以是Array,Function,RegExp,Date判断数据类型(方法,优劣)typeof:只能判断基础类型中的非Null,不能判断引用数据类型(因为全部为object)它是操作符typeof ’’ // ==> stringtypeof 1 //==> numbertypeof true //==>booleantypeof undefined //==>undefinedlet b = Symbol() ; typeof b //==>symbol—————–下面的判断不了实际类型了———————–typeof function fn(){} //==>functiontypeof null //==>objecttypeof [1,2,3] //==>objecttypeof {} //==>objectinstanceof:用于测试构造函数的prototype属性是否出现在对象的原型链中的任何位置,可以用它来判断Array但是不够优雅且有一定风险let arr = [1,2,3]arr instanceof Array //==>truearr instanceof Object //==>trueinstanceof操作符的问题在于,它只有一个全局执行环境,如果网页有多个框架,那实际上就存在两个以上的不同的全局执行环境,从而存在两个以上不同版本的Array构造函数。如果从一个框架想另外一个框架传入一个数组,那么传入的数组与在第二个框架中原生创建的数组分别具备各自不同的构造函数 —-高程page88 (笔者白话问翻译一下:风险来至原型链的重写)constructor:原理也是基于原型链,风险同样来之于原型链的重写,比如当你在多个frame中来回穿梭的时候,这两种方法就亚历山大了。由于每个iframe都有一套自己的执行环境,跨frame实例化的对象彼此是不共享原型链的,因此导致上述检测代码失效!isNaN:这个方法会先调用Number,所以不是很好用 console.log(isNaN(“1px”)); //先调用Number(‘1px’),返回NaN,然后再调用isNaN(NaN)返回true //燃鹅 ‘1px’客观并不是NaN [1,2,3,1].constructor === Array; // true———————————————–比较好的方法——————————–Object.prototype.toString.call() Object.prototype.toString.call(null) // ==> [object Null] Object.prototype.toString.call([]) // ==> [object Array]————————————————-优雅的方法———————如果需要单独判断ArrayArray.isArray([]) //==>true如果需要单独判断nulllet a = nullObject.is(a , null) //==>true6种声明变量的方法ES5 只有两种声明变量的方法:var命令和function命令。ES6 除了添加let和const命令,后面章节还会提到,另外两种声明变量的方法:import命令和class命令。所以,ES6 一共有 6 种声明变量的方法。 –es6var:变量提升,没有块级作用域说到var肯定要提变量提升:当前作用域,js(函数)执行前,浏览器会把带var或者function进行提前声明和定义变量只声明,函数是声明+赋值,自执行函数定义和执行一起完成了不受逻辑判断条件影响return 下面的也提升,但是return 里面的不提升重复的声明可以,重新赋值即可,但是变量和方法名字不能冲突const: 常量,地址不变,但是属性可以变let: 块作用域,暂时性死区(TDZ),不进行变量提升,不允许重复声明//只要块级作用域内存在let命令,它所声明的变量就“绑定”(binding)这个区域,不再受外部的影响。所以下面代码不报错,外层作用域和里层作用域都有一个tmplet tmp = 123; if (true) { let tmp =123; }//ES6 明确规定,如果区块中存在let和const命令,这个区块对这些命令声明的变量,从一开始就形成了封闭作用域。凡是在声明之前就使用这些变量,就会报错。 let tmp = 123; if (true) { tmp = ‘abc’; // ReferenceError let tmp; }import:es6模块化解决方案class:es6继承解决方案类型转化这一块内容太多,太杂了,其实我不怎么想写,因为很少有人会这么写代码。但是这块太重要了,面试必考。建议大家掌握这块的核心内容以及原则,不要关注奇淫巧技。1.自动装包三种包装类型:Number,Boolean,Stringlet s1 = ‘123’let s2 = s1.slice(2) // a是基本类型,它是没有slice方法的这里实际上后台完成了一个自动装包—下面是实际发生的事———let s1 = new string(‘123’)let s2 = s1.slice(2) s1 = null //注意这里用完就销毁了//所以如果添加某个属性后面是调用不出来的let s1 = ‘123’s1.color = ‘red’console.log(s1.color) // ==> undefind这些类型(构造函数)基本都重写了它们的tostring方法2.强行转化为数字Number :将其他数据类型的值强制转换成number类型; console.log(Number({}));//NaN console.log(Number(null));// 0 console.log(Number(undefined));// NaN console.log(Number([]));// 0 console.log(Number(""));// 0 console.log(Number(true));// 1 console.log(Number(false));parseInt :经常用于字符串提取数字的方法; 把字符串中从左到右依次识别,直到遇到一个非有效数字,停止,把找到的数字返回; console.log(parseInt(“12px12”));// 12 console.log(parseInt(“12.666.777px12”));// 12 console.log(parseInt(“px12.666px12”));// NaN console.log(parseInt(""));// NaN console.log(parseInt(true));// NaN console.log(parseInt({}));// NaN console.log(parseInt([]));// NaN console.log(parseInt(null));// NaN console.log(parseInt(undefined));// NaNtoFixed : 保留小数点位数的方法,返回值是一个字符串; console.log(Number(“1px”)); //==> NAN console.log(parseInt(“1px”)); //==> 1 console.log(parseInt(“p1px”)); //==> NaN3.-转化会先把字符串转换成数字(Number),然后再进行计算,注意NaN,undifined参与的任何计算都是NaN console.log(“6” - 2);//==> 4 console.log(“5px”-“4”)//==> NaN (NaN-4还是NaN) 4.+转化具体调用string还是number请看下表 || undefined | null | boolean | number | string | object |========================================================================= undefined || number | number | number | number | string | string | null || number | number | number | number | string | string | boolean || number | number | number | number | string | string | number || number | number | number | number | string | string | string || string | string | string | string | string | string | object || string | string | string | string | string | string | //字符串和任何类型相加都是调用String var a = typeof 10 + true + [] + null + undefined+{}; console.log(a); //==>numbertruenullundefined[object Object],{} console.log(“6px”+undefined); ==> 6pxundefined console.log(NaN+“undefined”);==> NaNundefined //经典面试题 [1,2]+[2,1] //==>都调用toString ‘1,2’+‘2,1’===>‘1,22,1'5.布尔值Boolean其他数据类型转布尔类型是false有且只有五个值: 0 "" NaN null undefined 所以boolean({}) 或者boolean([]) 都是真6.==和======是全等,==是类型转化后再判断,规则比较复杂。这里我认为除了准备面试需要看看,平时基本不会用,所以这个知识性价比非常低,学了用不到也会忘,大家自己把握,详细规则可以搜我其他文章平时除了判断a是否是undefined或者是null(jq源码里面都用法)都时候其他情况下都用===console.log(null==undefined) // trueconsole.log(undefined==undefined) // true总结本期文章估计大家看的很烦,无奈,我写起来也非常枯燥。因为不仅得讲规则,还得写用例。还是那句话,建议大家掌握核心知识点,细枝末节的东西就随意啦。这一期文章还有许多东西可以展开来讲,篇幅有限,我将会在后面的文章中一一涉及。感谢大家的阅读,本人水平有限,如果有不对的地方请在评论区指出。 ...

February 27, 2019 · 2 min · jiezi

“寒冬”下的金三银四跳槽季来了,帮你客观分析一下局面

如果第二次看到我的文章,欢迎下方扫码订阅我的个人公众号(跨界架构师)哟本文长度为5723字,建议阅读15分钟。坚持原创,每一篇都是用心之作~这是一篇以程序员视角写的文章,但是内容是互联网行业通用的。文章虽然有点长,但是这里的读者大部分还是互联网行业的,所以我认为值得你看完,应该会对你有所帮助。嗅觉比较灵敏的人在17年就感受到了互联网蓬勃发展的气息在明显减弱。直到17年下半年开始,越来越多的声音在喊互联网寒冬潮来了。到目前来看,势头好像越演越烈,还没有见顶的迹象。在这样的一个大环境下我想还是肯定有不少人,想找找机会。毕竟想着,危机危机,危中带机嘛。真实的大环境首先我们不得不承认一个事实,过去的互联网20多年,是一个开疆辟土的野蛮生长期,各方诸侯为了在一大片蓝海中抢占更多的地盘,竞争可谓激烈。所谓,竞争会带来局部溢价。竞争本事是个非理性的事情,因为此时有很大原因是为了竞争而竞争,战胜竞争对手的重要性远大于投入产出比的合理性。所以这会导致,越靠近核心链路的资源投入超配。其实我们生活中的很多事情就是如此,比如在学生时代拼读书成绩,那么一个家庭在教育上的投入肯定是超配的。为了成绩比其他人更好,可以投入很多成本进去,时间、金钱等等。程序员这个群体就是随着互联网时代的蓬勃发展开始快速增长。根据国家统计局的数据来看,完整体现了这个趋势。▲截图来源于国家统计局,点击图片查看大图可以看到这几年间,从业人员的涨幅,那可是所有行业的No1。而且,在2003年以前,这个群体还只是被划分在了“其他”里面。为什么涨那么快呢?正如前面所说,因为这行相对其他行业是有溢价的,而人又是逐利的,自然就越来越多的人向这个方向而来。所以从这个角度来说,程序员目前的薪资相对其他行业来说被“外部因素”干预是属于比较大的,包括运营,产品经理等这些互联网新起的岗位都是如此。当然了,归根到底这是一个市场的供需关系导致的,一个愿意来到这个行业一个愿意要你。但是我们要想一下,在寒冬之下,这个供需关系会有什么样的变化呢?我想告诉大家的一个信息是,如果放眼到整个中国社会来看,其实互联网行业并没进入什么寒冬。至少说对我们互联网从业人员的影响并算不上寒冬。可能你会问,那为什么会有那么多人在喊寒冬呢?瞎喊也不可能这么多人啊。没错,但是感官只是我们对身边所见的局部信息的一种结果认定,并且不代表全局。假如我们的视野仅限于传统的虚拟经济产业的话,目前的确进入到了一个转折点。出现了明显的瓶颈,甚至是负增长。从51job公布的招聘需求情况来看,也是如此。▲截图来源于51job但是,放眼于整个中国社会来看,国家正在推动供给侧改革,在供给侧的很多传统实体企业都需要进行数字化转型。这对我们老互联网人来说其实是开辟了一个新的大陆。所以,从整体来看,我们每一位互联网人不但没进入寒冬,反而获得了很多如20年前一样的新机会。最大的不同在于,过去的20年相当于重新建立了一个平行世界,而接下去的一段时间则是让旧世界变的更好。艾瑞咨询恰巧在这周一(2019.2.18)发布了一份最新的互联网就业相关报告(文末附下载连接),可以看到传统行业对互联网人才的需求度情况。▲截图来源于艾瑞咨询《中国互联网就业洞察白皮书》另外,从总量上来说,18年几个过去长期基于互联网发展的头部行业对人才的需求还是有明显增长的。▲截图来源于艾瑞咨询《中国互联网就业洞察白皮书》当然,毕竟这是年度数据,不排除有些纯线上的企业反应比较迟钝,在下半年才对招聘踩了刹车。所以,今年纯线上的行业对人才需求数涨幅可能相比18年会缩小一些。从18年的季度变化中也能预见这点。WED指标进入到一个平台区,变化开始变小。这份数据同样新鲜,来源于上周六BOSS直聘发布的报告(同样文末附下载连接)。▲截图来源于BOSS直聘《2019人才资本趋势报告》但是从JMPI指标来看,整个行业的蛋糕还是在快速扩大的,并无瓶颈迹象。值得注意的是,不同岗位在不同地区的供需情况差异还是比较大。如果对于选择去哪个城市工作还存在纠结的可以参考一下(数值越小,代表市场竞争越小)。▲截图来源于艾瑞咨询《中国互联网就业洞察白皮书》好了,其他的大家在文末下载这2份报告自己看吧,包括薪资什么的。该如何准备跳槽了解清楚大环境之后,如果你选择跳槽的话可以继续看下去。暂时没这打算的也可以先收藏一下,后续再来翻~准备跳槽,主要要做3件事:找公司、写简历,备面试。找公司大公司、小公司的选择是我们经常讨论的。一般来说公司越大知名度越大,意味着他的“品牌效应”越明显,可以给你的履历加分,相当于给自己带个buff。不过,一个公司当下的规模大小仅代表他过去的成果,并不等于未来也是这样。大公司轰然倒下的案例也不在少数。一个公司的主营业务必然也是依赖着背后的一个大环境。所以,业务所面向的市场是一个增量市场还是存量市场,也是格外重要的。通过这2个维度,一个公司可以划分到4个区间之一。右上和左下两个部分,大家的认知都一样,就不多说了。关于另外2块怎么选呢?针对大部分人,Z哥给你的普适性建议需要你先扪心自问搞清楚2个问题。对自己当下所具备的能力是否拥有自信?从长远来看,自己有没有强烈的意愿追求一些个人成就。选择安逸还是激情?ok,第一个问题答案如果是「那必须的」,同时自己没有大公司的工作履历,那么建议你先选择「低风险低潜力」的大公司。不管第二个问题的答案是什么。为什么呢?为了先获得一个buff,这个好比你打游戏里获得的buff一样,越早获得,给你带来的「加成时长」越长,总的收益越大。个人英雄主义毕竟还是小概率事件,我们要面对现实。那么哪些公司属于这个范畴呢?传统行业中的大企业、部分细分行业(行业规模中小型)里的头部企业。这些企业相比bat、tmd之类企业入职难度自然会低一些,只要大家全力以赴去准备,希望还是很大的。如果第一个问题的答案是「没自信」,那么直接进入第二个问题的选择。如果第二个问题选择的是「激情」,那么当你在大公司见过2、3年的世面之后就可以跳出来了,去选择那些「高风险高潜力」的公司拼一把。这个时候,你不但眼界比大多数人广,还有实际的“大企业品牌背书”,会让你在小公司中颇受青睐。相当于钻石段位的人去黄金段位找人组队,抢着要的人肯定多的是。当然了,薪资一般就要不到大公司那么高了,但是你的职业生涯发展空间更大。如果第二个问题选择的是「安逸」,那么就好好呆在大企业吧。但要在做好本职工作之余开始培养自己的副业,任何可以增加你被动收入的副业,而不是靠一份时间换一份钱的那种。好了,接下来专门聊聊第一题答案是「没自信」的。说句实话,一般来说,没自信的话,第二题基本上也是选择安逸的多。如果真的突然自己顿悟了,下定决心准备“重新做人”,充满激情的,参照上面的「自信+激情」发展路线。区别是,需要花一些时间将你“逝去的能力”追回来。如此一来,就只剩下没自信 + 安逸了。这部分小伙伴的出路在哪里呢?首先,出路不在这个四象限里直接体现。有句话说,“钱多事少离家近“最多只能满足其二,选择了「安逸」意味着选择了“事少”,那就剩下“钱多”和“离家近”二选一了。只要选择公司的时候,确保他的主营业务不是「夕阳行业」就行。说真的,现在闷声发财的公司真的不少,只是社会上的关注点都在那些明星公司上,但是小团体发点小财,也是很滋润的日子。这里Z哥不太建议你选择热点行业的公司,为什么呢?还是如前面所说,在风口上,资源是会溢出的。但溢出不是一个可持续的事情,迟早是要需要还的。万一还不上,公司就没了。如此反而变得更动荡了,不安逸了。当然了,你认为自己运气特别好或者眼光特别好,那可以忽略这点。不过Z哥觉得,人活着,找点激情的事情做,生活才有意义啊!哪怕就其中几年都行,所以尽量不要让你自己走到「没自信 + 安逸」的地步吧。至此,可以选好一些符合目标的公司,接下来就是准备简历了。写简历面试这个事我自己做过4年多时间,看过上千封简历。大多数人写简历的风格是尽可能多的陈列自己大大小小的“伟绩”上去。但是我很负责任的告诉你,在简历方面,多 ≠ 好。写简历要秉持「少即是多」的原则,这个和做PPT一样,如何更快更深入的给人留下印象,才是本质目的。想象一下,摆在你面前的有20篇作文,10分钟内找3篇认为最好的出来。这个就是做简历筛选的hr或者面试官的视角。「少即是多」的第一层意思就是:不要把自己描述的太完美。因为只要是个外人都知道这是在吹牛,只是很多时候自己容易陷入自我感觉良好的错觉里去写简历。那么该怎么把握呢?建议按照你的工作年限来把握内容的侧重点。比如3年以下的话多突出自己的「可塑性」强。3年~6年多突出自己的深度能力或者广度能力。不要再写什么学习能力强、积极主动什么的了,反而显得很初级。6年以上多以塑造自己是某方面小V的人设来写。这个只是Z哥给你的建议和思路,具体内容因人而异,自己把握。「少即是多」另外一层意思是,「独特性」。别人没有,你有的。或者很少人有的。比如程序员的话,可以把自己的github地址写上去,也可以特别强调一下star数比较高的项目。如果平时有自己写东西的习惯的话,可以将自己的博客、公众号等信息写上去,并且将效果好的几篇内容做一下概述。尽可能突出这些「独特性」的价值。最差最差,这些独特的东西能够帮你“抢占”简历筛选者的时间,让你获得简历筛选者更多的「印象空间」。那么具体的文字性内容我应该怎么写呢?比如项目经历、个人github上的项目、以及自己写的文章。Z哥给你一个技巧是用「两点一线」去写具体内容。因为,「线性思维」对我们人类来说是最好理解的。给一件事定义一个起点,就是它开始的时候是怎么样的。再给他定义一个终点,最终通过你的努力它变成怎么样了。最后,在中间补充你做的具体事情。尽量将起点和终点描述的「可衡量」、「易懂」。因为「终点」 - 「起点」 = 「价值」,你要让更多的人能看明白才行。不要写一堆专业术语,可能恰巧这位hr对这块并不是很专业,无法快速理解就跳过去了。补充中间做的具体事情也很有讲究,不要什么都写。仔细看对方的招聘需求,做到投其所好。除此之外的就不要写了,可以用一个词“等等”或者“其他”带过。潜在意思是,一份简历尽量不要海投。你随意的对待他人,他人自然也会随意的对待你。对了,包括自己的一些荣誉也是,不要列一堆。列几个重点,最好是与对方招聘需求相关的就行。写简历有2个硬伤一定要避免。一是不能出现错别字,因为这会给你建立一个“粗心”的人设。这个问题可以借助一些错别字检测工具(避免打广告,大家自己网上搜吧,借助word之类的也行)来避免。第二个是如果你是通过邮箱投递简历的话,切记,尽量要通用的文件格式,比如pdf之类的。这样可以避免在不同的电脑端打开出现变形(想象一下你好不容易投给心仪企业的简历,对方hr打开是乱码。。)。简历搞定了就等面试通知了。备面试不管是电话面试还是现场面试,本质都是在一段时间内将你对自己的认知输出给面试官的过程。就像将水从一个杯子倒往另一个杯子里。这里的“水”包括技术能力、沟通能力、情绪管理能力、应变能力等等。最终,面试官会根据他的水杯里接收到的水位判断你是否符合要求。那么怎么做就很好理解了,要么在固定时间内让信息的传输量更大。要么想办法拉长交谈时间,争取更多的信息输出机会。但是我们要搞清楚,掌握信息、拥有“水”的人是你自己。所以,自己一定要尽量掌握主动权,而不是被动的等着面试官提问。另外,你对面试官来说是陌生的,他对你是否有初步的了解,取决于他有没有看你的简历,还有你的简历写的如何。所以,很多时候面试的开局是让你自我介绍,或者让你自己说一个最体现自己能力的项目经历。这时候大部分小伙伴会以流水账的形式啪啦啪啦一顿说。Z哥认为这个开头很关键,抢得主动权就靠这个了。不管你啪啦啪啦说什么,在结尾或者中间要着重反复突出1到2点核心点,以此引导面试官的提问方向。只要突出的核心点是对方招聘需求中的东西,会被接下去提问的概率非常大。然后就将自己准备好的最拿手的东西讲出来就好了。可能你会问,如果面试官不按这个套路出牌,直接根据简历发问怎么办呢?或者途中话锋一转换了一个我不熟悉的话题怎么办呢?这个时候,保持不卑不亢,知道什么就说什么。模糊不清的不要抱着侥幸心理去蒙,不知道就不知道。但是可以多输出一些自己的思路,哪怕不知道怎么实现也没关系。记住,原则上来说,思路比具体的方式更有价值,更重要,因为它是一个抽象的事物,更具延展性。当然了,如果有机会还是可以间接引导到自己熟悉的领域里去。这需要你抓住机会在结尾处做一下补充语句,以引导后续的交流方向。毕竟招聘需求所要求的项有很多,对面试官来说,在有限的时间里只能抽样一部分内容来验证。所以只要你的引导方向在清单范围内,我觉得一般不太会刻意刁难你,故意避开你熟悉的领域。当然了,你也不要表现的“用力过猛”,体现你在刻意引导提问方向。整个面试过程的大部分时间应该都会花在一些细枝末节上面。不过,对面试官来说,他的最终诉求无非就是2个:你说的这个事情,或者你简历里写的这个事情是不是真的。你解决这个问题或者这类问题的能力到底在什么程度。所以多说说你是怎么做的,怎么解决的,而不要只是说做了什么。what并不能体现你的能力,只有多说how和why才是体现你能力的地方。特别特别特别要注意的一点是,有不少小伙伴喜欢用大量的内容阐述背后的工作环境,公司是做什么业务的啊,这个是其中的什么子业务啊。这些其实对面试官来说是不太感冒的,要避免。我们讲述环境信息的尺度把握在能说清楚这是一个什么问题就够了。总结总结一下,本文Z哥先带你分析了下当前的就业大环境。并且在找公司、写简历、备面试上给你一些参考建议。找公司的核心就是,对自己要有深刻实际的认识,然后通过四象限来筛选公司。找到匹配自己规划的目标公司,写简历的核心就是,突出独特性,并且投其所好。避免错别字,避免用非通用的文件格式。备面试的核心就是,掌握信息输出的主动权,通过多讲述how和why,体现自己解决问题的能力,而不是“陈列成绩”。好了,希望此文对你有所帮助。最后附上3份相关的市场报告,希望能帮大家更多的了解当下的大环境。在我微信后台回复关键字:「19就业」获取。相关文章:你知识焦虑了吗?什么技巧能「一日千里」?作者:Zachary出处:https://www.cnblogs.com/Zacha…如果你喜欢这篇文章,可以点一下文末的「赞」。这样可以给我一点反馈。: )谢谢你的举手之劳。▶关于作者:张帆(Zachary,个人微信号:Zachary-ZF)。坚持用心打磨每一篇高质量原创。欢迎扫描下方的二维码。定期发表原创内容:架构设计丨分布式系统丨产品丨运营丨一些思考。如果你是初级程序员,想提升但不知道如何下手。又或者做程序员多年,陷入了一些瓶颈想拓宽一下视野。欢迎关注我的公众号「跨界架构师」,回复「技术」,送你一份我长期收集和整理的思维导图。如果你是运营,面对不断变化的市场束手无策。又或者想了解主流的运营策略,以丰富自己的“仓库”。欢迎关注我的公众号「跨界架构师」,回复「运营」,送你一份我长期收集和整理的思维导图。

February 27, 2019 · 1 min · jiezi

【Leetcode】103. 二叉树的锯齿形层次遍历

题目给定一个二叉树,返回其节点值的锯齿形层次遍历。(即先从左往右,再从右往左进行下一层遍历,以此类推,层与层之间交替进行)。例如:给定二叉树 [3,9,20,null,null,15,7], 3 / \ 9 20 / \ 15 7返回锯齿形层次遍历如下:[ [3], [20,9], [15,7]]题解这道题要求用z字型,就是要求知道深度。因为知道深度我们就可以根据深度的奇偶来判断如何打印。首先相到的就是层序遍历,然后记录是第几层。层序遍历用队列的代码我们已经很熟悉了。/** * Definition for a binary tree node. * public class TreeNode { * int val; * TreeNode left; * TreeNode right; * TreeNode(int x) { val = x; } * } /class Solution { public List<List<Integer>> zigzagLevelOrder(TreeNode root) { List<List<Integer>> res = new LinkedList<>(); if (root == null) { return res; } LinkedList<TreeNode> queue = new LinkedList<>(); queue.add(root); int depth = 0; while (!queue.isEmpty()) { int size = queue.size(); LinkedList<Integer> currentRes = new LinkedList<>(); // 当前层一直出队. while (size > 0) { TreeNode current = queue.poll(); TreeNode left = current.left; TreeNode right = current.right; if (left != null) { queue.add(left); } if (right != null) { queue.add(right); } // 奇数层,从头添加; 偶数层从尾部添加. if (depth % 2 != 0) { currentRes.add(0, current.val); } else { currentRes.add(current.val); } size–; } // 把当前层遍历的结果加入到结果中. res.add(currentRes); depth++; } return res; }}同之前一样,我们想一想有没有递归的解法.我们可以采用先序遍历的方式,先遍历节点,然后递归的遍历左子树和右子树。稍作改动的是,需要在遍历左子树和右子树的时候带上深度的信息,才能知道是加在列表头还是列表尾部。递归的结束条件就是遇到空节点。/* * Definition for a binary tree node. * public class TreeNode { * int val; * TreeNode left; * TreeNode right; * TreeNode(int x) { val = x; } * } */class Solution { public List<List<Integer>> zigzagLevelOrder(TreeNode root) { List<List<Integer>> res = new LinkedList<>(); if (root == null) { return res; } helper(res, root, 0); return res; } public void helper(List<List<Integer>> res,TreeNode root, int depth) { if (root == null) { return; } // 注意这里new List, 说明当前节点递归进入了新的层. if (res.size() <= depth) { res.add(new LinkedList<>()); } if (depth % 2 != 0) { res.get(depth).add(0, root.val); } else { res.get(depth).add(root.val); } helper(res, root.left, depth + 1); helper(res, root.right, depth + 1); }}热门阅读技术文章汇总【Leetcode】101. 对称二叉树【Leetcode】100. 相同的树【Leetcode】98. 验证二叉搜索树 ...

February 27, 2019 · 2 min · jiezi

金三银四,你可以行动了

为什么会离职又要交房租了,工资怎么还不发?每个月总觉得钱不够花,工资总是跟不上消费,什么时候能涨个工资呢?现在上班犹如上坟一样,已经找不到半点成就感,每天各种烦心事,各种琐事永远做不到。我们为什么会离职?无非就是以上两种原因:钱没给够,干的不开心,想换个环境。如果你已经动了换工作的心思,趁着金三银四,可以行动准备起来了。2018 年经历了互联网寒冬,今年还会更加糟糕,有读者告知上班的第一天就被 cai ,也就在前几天,滴滴官宣过冬,将 cai 员 15 %,波及员工超 2000 人,因此跳槽也要谨慎,如何跳槽拿到心仪的 offer 呢?简历简历是第一印象,好的简历会大大增加面试的可能性,程序员如何写好自己的简历呢,GitHub 上有个开源项目,分享了程序员简历模板,来写得一手漂亮的简历:https://github.com/geekcompan… ,另外再分享一个具体的 Android 工程师简历,公众号「吴小龙同学」后台回复关键字:「Android 简历」获得。面试题这个似乎没什么说的,自己多做准备,不反对背题,但最终还是需要平时的积累,分享一些面试题:Android 名企面试题及涉及知识点整理:https://github.com/helen-x/An…Android 面试BAT大厂:https://www.jianshu.com/p/0f8…如何选公司问:有多家 offer,应该如何抉择,条件都差不多?答:厂大选大的 ,厂小选公司有钱的,都差不多选加班少的,加班都多,选钱多的。具体如何判断公司好坏,可以参考我之前写的文章:如何判断一家软件公司的好坏:https://mp.weixin.qq.com/s/s1… ,还有跳槽,如何找一家靠谱的公司?:https://www.jianshu.com/p/172… 。职业规划到这步,想必你已经拿到心仪的 offer 了,之后就安心好好上班工作,推荐一篇酷壳文章,技术人员的发展之路:https://coolshell.cn/articles… 。平时我们要多问问自己,不撸代码还能做什么。最后金三银四,不要慌,只要技术在手,哪里都有饭吃,最后千万别裸辞。推荐阅读写给年轻程序员的建议和HR来场约会漫画:越挫越勇公众号我的公众号:吴小龙同学,欢迎关注交流~

February 26, 2019 · 1 min · jiezi

工作三年,我要如何提升Java技术 | 粉丝提问

本博客 猫叔的博客,转载请申明出处前言感谢粉丝提问,由于我最近工作较忙,所以今晚抽空回复问题,可能有点仓促,回答的不是很全,不过希望能对你有所帮助。整理自己的技术栈首先,我需要你整理出自己一份满意的简历,我希望程序员可以每个季度整理一次简历,在更新的过程中,你也将发现你的提升与不足,当然有些东西是厚积薄发,不过业余时间还是需要不断的研究与深入学习。将自己整理出来的技术栈与常规招聘网上的技术岗位要求对比一下,看看自身还有什么不足?专注于某个核心技术这是我个人一点深刻的想法,三年的java程序员已经开始走向中级Java的层次,已经积累了一定量的工作经验与编码实战能力,这些程序员基本上都是什么都学习的过程,大家都在不停的学习,各种各样的知识,但是在这些工作与学习中,你会经常性的触及一些核心知识点,比如缓存、多线程、消息中间件、分布式等等,你需要把握时间,掌握一项对应的核心技术,比如我目前最喜欢的netty。我在GitHub的一个项目与经常写文章的习惯让我偶然得到一次阿里内推的资格,面试过程都是关于netty的一切知识与源码的理解。这个岗位好像没有提及netty,不过对应的项目负责人刚好需要使用到netty这门框架,所以希望找一个对netty熟悉的程序员。最后五年工作经验以上。所以我希望大家也可以深入某项核心技术去好好研究。阅读源码个人觉得阅读源码的能力是检验你对编程或Java相关的能力映射。好的程序员都会阅读源码,而且喜欢阅读源码,我希望你能培养出这样的喜欢,并最好有对应的笔记去记录自己的理解。产出自己的开源作品参与开源,去GitHub上划划水,看看别人是怎么写框架的,是如何做项目的,输出自己的第一个核心的项目,它可以是与你的核心技术相关的,也可以是一个Demo。最近比较少在GitHub上更新自己的项目了,不过InChat仅从18年8月至今,已经发展到这个程度,这对于我来说是莫大的动力,因为我也是偶然机会下的想法,并将它转为框架,其实一开始它只是一个Demo。写文章不要看中短期效益,你要重视的是你的长期效应,人活着,眼前其实很多时候不一定是最重要的。写文章是一件长期收益很高的项目。这也在锻炼你的表达能力与书面能力。相信我,未来综合能力强的程序员会更加受欢迎的。看架构相关、管理类书籍如果说一定要看什么书的话,那么请看一些架构相关、或者管理类的书籍,不要再去随随便便敲代码,而是培养自己的思维架构,然后去写一个针对某个问题的Demo,在业务时间我希望程序员不要去写业务代码,这是我很抗拒的事情,这也导致我最近真的很少这些代码。最后好在,最近朋友创业,我兼顾了技术负责人,我想又可以写代码了,而且有一些新的业务创新,这对我来说是挑战也是兴趣点。这个话题没那么简单,我简单说了我一时想到的几个点,希望大家能不断的深化并找到属于自己的学习方式,突破困惑期。公众号:Java猫说现架构设计(码农)兼创业技术顾问,不羁平庸,热爱开源,杂谈程序人生与不定期干货。

February 26, 2019 · 1 min · jiezi

【Leetcode】102. 二叉树的层次遍历

题目给定一个二叉树,返回其按层次遍历的节点值。 (即逐层地,从左到右访问所有节点)。例如:给定二叉树: [3,9,20,null,null,15,7], 3 / \ 9 20 / \ 15 7返回其层次遍历结果:[ [3], [9,20], [15,7]]题解我们数据结构的书上教的层序遍历,就是利用一个队列,不断的把左子树和右子树入队。但是这个题目还要要求按照层输出。所以关键的问题是: 如何确定是在同一层的。我们很自然的想到:如果在入队之前,把上一层所有的节点出队,那么出队的这些节点就是上一层的列表。由于队列是先进先出的数据结构,所以这个列表是从左到右的。/** * Definition for a binary tree node. * public class TreeNode { * int val; * TreeNode left; * TreeNode right; * TreeNode(int x) { val = x; } * } /class Solution { public List<List<Integer>> levelOrder(TreeNode root) { List<List<Integer>> res = new LinkedList<>(); if (root == null) { return res; } LinkedList<TreeNode> queue = new LinkedList<>(); queue.add(root); while (!queue.isEmpty()) { int size = queue.size(); List<Integer> currentRes = new LinkedList<>(); // 当前队列的大小就是上一层的节点个数, 依次出队 while (size > 0) { TreeNode current = queue.poll(); if (current == null) { continue; } currentRes.add(current.val); // 左子树和右子树入队. if (current.left != null) { queue.add(current.left); } if (current.right != null) { queue.add(current.right); } size–; } res.add(currentRes); } return res; }}这道题可不可以用非递归来解呢?递归的子问题:遍历当前节点, 对于当前层, 遍历左子树的下一层层,遍历右子树的下一层递归结束条件: 当前层,当前子树节点是null/* * Definition for a binary tree node. * public class TreeNode { * int val; * TreeNode left; * TreeNode right; * TreeNode(int x) { val = x; } * } /class Solution { public List<List<Integer>> levelOrder(TreeNode root) { List<List<Integer>> res = new LinkedList<>(); if (root == null) { return res; } levelOrderHelper(res, root, 0); return res; } /* * @param depth 二叉树的深度 */ private void levelOrderHelper(List<List<Integer>> res, TreeNode root, int depth) { if (root == null) { return; } if (res.size() <= depth) { // 当前层的第一个节点,需要new 一个list来存当前层. res.add(new LinkedList<>()); } // depth 层,把当前节点加入 res.get(depth).add(root.val); // 递归的遍历下一层. levelOrderHelper(res, root.left, depth + 1); levelOrderHelper(res, root.right, depth + 1); }}热门阅读技术文章汇总【Leetcode】101. 对称二叉树【Leetcode】100. 相同的树【Leetcode】98. 验证二叉搜索树 ...

February 26, 2019 · 2 min · jiezi

Javascript基础之Array数组API(待完善)

Javascript数组原型提供的方法非常之多,主要分为三种:直接修改原数组原数组不变,返回新数组数组遍历方法直接修改原数组的APIpop()删除一个数组中的最后的一个元素,并且返回这个元素push()添加一个或者多个元素到数组末尾,并且返回数组新的长度shift()删除数组的第一个元素,并返回这个元素unshift()在数组开始处插入一些元素,并返回数组新的长度splice()从数组中添加/删除一些元素,然后返回被删除的元素reverse()颠倒数组中元素的顺序sort()用于对数组的元素进行排序fill() – ES6新增将数组指定区间内的元素替换为某个值copyWithin() – ES6新增数组内元素之间的替换返回新数组的APIcontact()将传入的数组或者元素与原数组合并,组成一个新的数组并返回slice()连接两个或多个数组join()将数组中的所有元素连接成一个字符串indexOf()用于查找元素在数组中第一次出现时的索引,如果没有,则返回-1lastIndexOf()用于查找元素在数组中最后一次出现时的索引,如果没有,则返回-1includes() – ES7新增用来判断当前数组是否包含某个指定的值,如果是,则返回 true,否则返回false数组遍历方法forEach()指定数组的每项元素都执行一次传入的函数,返回值为undefinedmap()遍历数组,使用传入函数处理每个元素,并返回函数的返回值组成的新数组filter()使用传入的函数测试所有元素,并返回所有通过测试的元素组成的新数组every()使用传入的函数测试所有元素,每一个元素经传入的函数处理都返回true该方法才返回true,否则返回false(验证是否每一个元素都满足测试函数)some()使用传入的函数测试所有元素,只要有一个元素经传入的函数处理返回true该方法就返回true,否则返回false(验证是否有元素都满足测试函数)reduce()接收一个方法作为累加器,数组中的每个值(从左至右) 开始合并,最终为一个值reduceRight()接收一个方法作为累加器,数组中的每个值(从右至左) 开始合并,最终为一个值find() – ES6新增返回数组中第一个满足条件的元素(如果有的话), 如果没有,则返回 undefinedfindIndex() – ES6新增返回数组中第一个满足条件的元素的索引(如果有的话), 如果没有,则返回 -1keys() – ES6新增返回一个数组索引的迭代器values() – ES6新增返回一个数组迭代器对象,该对象包含数组中每个索引的值entries() – ES6新增返回一个数组迭代器对象,该对象包含数组中每个索引的键值对

February 26, 2019 · 1 min · jiezi

前端安全问题及解决办法

一、随着前端的快速发展,各种技术不断更新,但是前端的安全问题也值得我们重视,不要等到项目上线之后才去重视安全问题,到时候被黑客攻击的时候一切都太晚了。二、本文将讲述前端的六大安全问题,是平常比较常见的安全问题,当然如果还有其他必要重要的安全问题大家可以帮忙补充:1、XSS(Cross-Site Scripting)脚本攻击漏洞;2、CSRF(Cross-sit request forgery)漏洞;3、iframe安全隐患问题;4、本地存储数据问题;5、第三方依赖的安全性问题;6.HTTPS加密传输数据;下面将对这些问题进行分享说明。三、XSS(Cross-Site Scripting)脚本攻击漏洞XSS是前端谈论最多的安全问题,是通过在你的输入文本当中或者这HTML标签当中插入js脚本进行攻击,比如会在你的a标签或者img标签之前插入一些脚本文件就能攻击到你的网站,所有在用HTML去切入到div的时候一定要注意,或者长串的字符串嵌入到a标签的时候。解决办法:1:如果要使用HTML进行转换内容的时候,写代码时改为innerText而不用innerHTML,或者把<script><iframe>等标签替换掉; var HtmlUtil = { /1.用浏览器内部转换器实现html转码/ htmlEncode:function (html){ //1.首先动态创建一个容器标签元素,如DIV var temp = document.createElement (“div”); //2.然后将要转换的字符串设置为这个元素的innerText(ie支持)或者textContent(火狐,google支持) (temp.textContent != undefined ) ? (temp.textContent = html) : (temp.innerText = html); //3.最后返回这个元素的innerHTML,即得到经过HTML编码转换的字符串了 var output = temp.innerHTML; temp = null; return output; }, /2.用浏览器内部转换器实现html解码/ htmlDecode:function (text){ //1.首先动态创建一个容器标签元素,如DIV var temp = document.createElement(“div”); //2.然后将要转换的字符串设置为这个元素的innerHTML(ie,火狐,google都支持) temp.innerHTML = text; //3.最后返回这个元素的innerText(ie支持)或者textContent(火狐,google支持),即得到经过HTML解码的字符串了。 var output = temp.innerText || temp.textContent; temp = null; return output; } };2.对一些切入标签的字符串进行转义:var HtmlUtil = { /1.用正则表达式实现html转码/ htmlEncodeByRegExp:function (str){ var s = “”; if(str.length == 0) return “”; s = str.replace(/&/g,"&amp;"); s = s.replace(/</g,"&lt;"); s = s.replace(/>/g,"&gt;"); s = s.replace(/ /g,"&nbsp;"); s = s.replace(/'/g,"&#39;"); s = s.replace(/"/g,"&quot;"); return s; }, /2.用正则表达式实现html解码/ htmlDecodeByRegExp:function (str){ var s = “”; if(str.length == 0) return “”; s = str.replace(/&amp;/g,"&"); s = s.replace(/&lt;/g,"<"); s = s.replace(/&gt;/g,">"); s = s.replace(/&nbsp;/g," “); s = s.replace(/&#39;/g,”'"); s = s.replace(/&quot;/g,"""); return s; } };四、CSRF(Cross-sit request forgery)漏洞CSRF也称为跨站请求伪造,其实就是对网站中的一些表单提交行为被黑客利用。比如你的网站登录的时候存到cookie的一些个人信息,当你访问黑客的网站有一段相同代码隐藏div,但你点击的时候就会导致你的网站被登出或者被登录,就是在对别的网站就行操作的时候会对你之前访问的网站发送请求。解决办法:1.增加token验证.因为cookie发送请求的时候会自动增加上,但是token却不会,这样就避免了攻击2.Referer验证。页面来源的判断五、iframe安全隐患问题有时候前端页面为了显示别人的网站或者一些组件的时候,就用iframe来引入进来,比如嵌入一些广告等等。但是有些iframe安全性我们无法去评估测试,有时候会携带一些第三方的插件啊,或者嵌入了一下不安全的脚本啊,这些都是值得我们去考虑的。解决办法:1.使用安全的网站进行嵌入;2.在iframe添加一个叫sandbox的属性,浏览器会对iframe内容进行严格的控制,详细了解可以看看相关的API接口文档。六、本地存储数据问题很多开发者为了方便,把一些个人信息不经加密直接存到本地或者cookie,这样是非常不安全的,黑客们可以很容易就拿到用户的信息,所有在放到cookie中的信息或者localStorage里的信息要进行加密,加密可以自己定义一些加密方法或者网上寻找一些加密的插件,或者用base64进行多次加密然后再多次解码,这样就比较安全了。七、第三方依赖安全隐患现如今的项目开发,很多都喜欢用别人写好的框架,为了方便快捷,很快的就搭建起项目,自己写的代码不到20%,过多的用第三方依赖或者插件,一方面会影响性能问题,另一方面第三方的依赖或者插件存在很多安全性问题,也会存在这样那样的漏洞,所以使用起来得谨慎。解决办法:手动去检查那些依赖的安全性问题基本是不可能的,最好是利用一些自动化的工具进行扫描过后再用,比如NSP(Node Security Platform),Snyk等等。八、HTTPS加密传输数据在浏览器对服务器访问或者请求的过程中,会经过很多的协议或者步骤,当其中的某一步被黑客拦截的时候,如果信息没有加密,就会很容易被盗取。所以接口请求以及网站部署等最好进行HTTPS加密,这样防止被人盗取数据。前端安全问题先分享到这里,后续再慢慢补充,喜欢的可以点关注,谢谢! ...

February 25, 2019 · 1 min · jiezi

一次性理清JavaScript变量等高难度面试问题

主要是解决JavaScript中比较难懂的部分,当然了,这部分经常在面试题中露面,这篇文章主要是讲解解题思路,对新手会有很大帮助(如果你仔细看的话)。书籍方面,我看的是《你不知道的javascript》,精髓就在里面喽。文章会持续更新( 如果能看到比较刁钻的题目的话, 也欢迎大家将自己遇到的异常难于理解的JavaScript题目分享出来喔 )第0种 解析顺序 function a() { b = function () { console.log(1) } return this; } var b = function () { console.log(2) } function b() { console.log(3) } b(); a().b(); b();1.首先,你要明白的是3永远不可能打印出来。这涉及到函数的声明方式和执行顺序问题。直接用function的方式声明的函数,函数可以在function声明之前被调用,这说明在代码运行之前就已经调用了。再次声明的函数var b会替代原有的函数b2.第二点: 第一个函数中的this指向。一个点:函数中的this是在运行时候决定的,而不是函数定义时, 具体就看谁调用了this.在第一个函数中,没有调用对象,在浏览器中,this就指向window3.第三点: 还是第一个函数中,不使用var标识符直接定义的变量都属于全局变量,也就是说第一个函数中的b是挂载到window上的。第1种 变量提升(function(){ a = 5; alert(window.a); var a = 10; alert(a);})();喜闻乐见!看见一堆的变量a就知道了,这是变量提升的问题了,只需要自己模拟出执行顺序就行了.等同于:var a; // 第一步:变量提升a = 5; // 后面的就直接拿上来alert(window.a);a = 10;alert(a);同类型题目:var a=1;function test(){ console.log(a); var a=1;}test();看见var就把它提升到最前面就行了,一样画瓢解决:var a;a = 1;function test() { var a; console.log(a); a = 1;}还有同类型的题目一样可以依葫芦画瓢解决: var foo = ‘hello’; (function (foo) { console.log(foo); var foo = foo || ‘world’; console.log(foo); })(foo); console.log(foo);等。解决这些问题的关键是:不要想当然,动手一步一步模拟执行顺序,模拟变量提升情况第二种 面向对象问题或高阶函数( 等待更新 ) ...

February 25, 2019 · 1 min · jiezi

轻松理解JS基本包装对象

今天来讨论一下JS中的基本包装对象(也叫基本包装类型),之前刚学到这里的时候,自己也是一头雾水,不明白这个基本包装对象到底是个什么鬼,后来找了很多资料,终于看清了它的真面目。首先呢,我们现在复习一下JS的数据类型,JS数据类型被分为了两大门派,基本类型和引用类型。 基本类型:Undefined,Null,Boolean,Number,String 引用类型:Object,Array,Date,RegExp等,说白了就是对象。。。我们都知道,引用类型有方法和属性,但是基本类型是木有的,但是你一定见过这样的代码var str = ‘hello’; //string 基本类型var s2 = str.charAt(0);alert(s2); // h毫无疑问上面的string是一个基本类型,但是它却能召唤出一个charAt()的方法,这是什么原因呢?主要是因为在基本类型中,有三个比较特殊的存在就是:String Number Boolean,这三个基本类型都有自己对应的包装对象。并且随时等候召唤。包装对象呢,其实就是对象,有相应的属性和方法。至于这个过程是怎么发生呢,其实是在后台偷偷发生的。来看个栗子//我们平常写程序的过程:var str = ‘hello’; //string 基本类型var s2 = str.charAt(0); //在执行到这一句的时候 后台会自动完成以下动作 ://相当于:( var str = new String(‘hello’); // 1 找到对应的包装对象类型,然后通过包装对象创建出一个和基本类型值相同的对象 var s2 = str.chaAt(0); // 2 然后这个对象就可以调用包装对象下的方法,并且返回结给s2. str = null; // 3 之后这个临时创建的对象就被销毁了, str =null; ) `alert(s2);//h alert(str);//hello 注意这是一瞬间的动作 实际上我们没有改变字符串本身的值。就是做了下面的动作.这也是为什么每个字符串具有的方法并没有改变字符串本身的原因。由此我们可以知道,引用类型和基本包装对象的区别在于:生存期引用类型所创建的对象,在执行的期间一直在内存中,而基本包装对象只是存在了一瞬间。所以我们无法直接给基本类型添加方法:举个栗子var str = ‘hello’;str.number = 10; //假设我们想给字符串添加一个属性number ,后台会有如下步骤// 相当于{ var str = new String(‘hello’); // 1 找到对应的包装对象类型,然后通过包装对象创建出一个和基本类型值相同的对象 str.number = 10; // 2 通过这个对象调用包装对象下的方法 但结果并没有被任何东西保存 str =null; // 3 这个对象又被销毁 }alert(str.number); //undefined 当执行到这一句的时候,因为基本类型本来没有属性,后台又会重新重复上面的步骤//相当于{ var str = new String(‘hello’); // 1 找到基本包装对象,然后又新开辟一个内存,创建一个值为hello对象 str.number = undefined // 2 因为包装对象下面没有number这个属性,所以又会重新添加,因为没有值,所以值是未定 ;然后弹出结果 str =null; // 3 这个对象又被销毁 }那么我们怎么才能给基本类型添加方法或者属性呢?答案是在基本包装对象的原型下面添加,每个对象都有原型。来看个栗子//给字符串添加方法 要写到对应的包装对象的原型下才行var str = ‘hello’;String.prototype.last= fuction(){ return this.charAt(this.length);}; str.last(); // 5 执行到这一句,后台依然会偷偷的干这些事//相当于{ var str = new String(‘hello’);// 找到基本包装对象,new一个和字符串值相同的对象, str.last(); // 通过这个对象找到了包装对象下的方法并调用 str =null; // 这个对象被销毁}看注释相信能看出创建在基本包装对象原型下面的方法和属性才能被保存。 ...

February 25, 2019 · 1 min · jiezi

JS中数据类型、内置对象、包装类型对象、typeof关系

平时在复习JS基础知识时,经常会遇到JS数据类型、基础数据类型、内置对象、包装类型对象,检测数据类型时,用到的typeof值,感觉都差不多,但是又有差异。今天特地整理下,方便理解。JS数据类型基础数据类型和引用数据类型JS数据类型分为 基础数据类型 和 引用数据类型基础数据类型又分为undefined null boolean number string引用数据类型即object为什么编程语言要有数据类型 概念一句话概括的话,就是JS数据类型,是对外的,是面向JS编译器的,定义编译器对相应类型的处理方式;具体参考 为什么编程语言的都要定义数据类型JS内置对象JS内置对象 包含 Boolean String Number Array Function Date Math Object RegExp Error Global包装类型对象JS内置对象包含包装类型对象,包装类型对象 指的是 Boolean String Number三个内置对象为什么会有JS内置对象 概念一句话概括的话,就是JS内置对象,是对内的,是指这个语言自带的一些对象,供开发者使用,这些对象提供了一些常用的或是最基本而必要的功能。typeof值typeof是用来判断数据类型的,其中它的值有undefined boolean number string function symbol object;JS内置对象和JS数据类型关系JS数据类型是对外的,面对编译器;JS内置对象是对内的,日常编程与我们打交道最多的,其实是JS内置对象。至于包装数据类型对象的特点,可以参考 轻松理解JS基本包装对象,打个比方就是:JS就像一个国家,平时对外交流(与JS编译器交流),由6位副总统(JS基本数据类型,undefined null boolean number string object)去操作。其中,undefined副总统、null副总统年老体衰,平时掌管的国家事务不多。object副总统能力比较强,下设了11位部长(JS内置对象),平时国内治理(前端日常开发)由这些部长执行。另外,boolean副总统、number副总统、string副总统,平时施政时,必须由Boolean部长、Number部长、String部长三位部长(包装类型对象)辅助,命令才能正常实行。当然,Boolean部长、Number部长、String部长三位部长也可以单独执行,绕过三位副总统。typeof值与JS数据类型关系对比下图,即可知typeof值 相较于 JS基础数据类型 少null多function

February 25, 2019 · 1 min · jiezi

面试题·HashMap和Hashtable的区别(转载再整理)

原文链接: Javarevisited 翻译: ImportNew.com - 唐小娟译文链接: http://www.importnew.com/7010...HashMap和Hashtable的比较是Java面试中的常见问题,用来考验程序员是否能够正确使用集合类以及是否可以随机应变使用多种思路解决问题。HashMap的工作原理、ArrayList与Vector的比较以及这个问题是有关Java 集合框架的最经典的问题。Hashtable是个过时的集合类,存在于Java API中很久了。在Java 4中被重写了,实现了Map接口,所以自此以后也成了Java集合框架中的一部分。Hashtable和HashMap在Java面试中相当容易被问到,甚至成为了集合框架面试题中最常被考的问题,所以在参加任何Java面试之前,都不要忘了准备这一题。这篇文章中,我们不仅将会看到HashMap和Hashtable的区别,还将看到它们之间的相似之处。HashMap和Hashtable的区别HashMap和Hashtable都实现了Map接口,但决定用哪一个之前先要弄清楚它们之间的分别。主要的区别有:线程安全性,同步(synchronization),以及速度。HashMap几乎可以等价于Hashtable,除了HashMap是非synchronized的,并可以接受null(HashMap可以接受为null的键值(key)和值(value),而Hashtable则不行)。HashMap是非synchronized,而Hashtable是synchronized,这意味着Hashtable是线程安全的,多个线程可以共享一个Hashtable;而如果没有正确的同步的话,多个线程是不能共享HashMap的。Java 5提供了ConcurrentHashMap,它是HashTable的替代,比HashTable的扩展性更好。另一个区别是HashMap的迭代器(Iterator)是fail-fast迭代器,而Hashtable的enumerator迭代器不是fail-fast的。所以当有其它线程改变了HashMap的结构(增加或者移除元素),将会抛出ConcurrentModificationException,但迭代器本身的remove()方法移除元素则不会抛出ConcurrentModificationException异常。但这并不是一个一定发生的行为,要看JVM。这条同样也是Enumeration和Iterator的区别。由于Hashtable是线程安全的也是synchronized,所以在单线程环境下它比HashMap要慢。如果你不需要同步,只需要单一线程,那么使用HashMap性能要好过Hashtable。HashMap不能保证随着时间的推移Map中的元素次序是不变的。要注意的一些重要术语:sychronized意味着在一次仅有一个线程能够更改Hashtable。就是说任何线程要更新Hashtable时要首先获得同步锁,其它线程要等到同步锁被释放之后才能再次获得同步锁更新Hashtable。Fail-safe和iterator迭代器相关。如果某个集合对象创建了Iterator或者ListIterator,然后其它的线程试图“结构上”更改集合对象,将会抛出ConcurrentModificationException异常。但其它线程可以通过set()方法更改集合对象是允许的,因为这并没有从“结构上”更改集合。但是假如已经从结构上进行了更改,再调用set()方法,将会抛出IllegalArgumentException异常。结构上的更改指的是删除或者插入一个元素,这样会影响到map的结构。我们能否让HashMap同步?HashMap可以通过下面的语句进行同步:Map m = Collections.synchronizeMap(hashMap);结论Hashtable和HashMap有几个主要的不同:线程安全以及速度。仅在你需要完全的线程安全的时候使用Hashtable,Hashtable是java 4时代的过时产物,ConcurrentHashMap是它的替代品。而如果你使用Java 5或以上的话,请使用ConcurrentHashMap吧。

February 25, 2019 · 1 min · jiezi

为什么编程语言的都要定义数据类型

对于一个程序员来讲,写代码的第一件是请,恐怕就是需要定义一些数据类型。而程序本身,就是对这些数据类型进行操作,有没有人思考过,为什么每种语言编写的程序,开始都需要定义数据类型呢?以下面的C代码为例,我们来说明这个问题:#include<stdio.h>int main(){ int a=100; int b=200; double a1=10.1; double b1=10.2; a=a+b; a1=a1+b1; getchar(); return 0;}这段代码非常简单,定义了四个数据,两个类型。大家看,a=a+b;和a1=a1+b1;这两个语句,几乎一样,那么,我问你,这两个语句,在编译时,编译器会用同一段代码来替换这两个语句吗?显然不可是同一段代码,因为浮点数和整数,在计算机里面,使用了不同的处理器,整数使用普通的CPU,而浮点数必须使用浮点运算器。所以,这两句话,产生的机器代码完全不同!那么问题来了,编译器如何知道,在碰到两个数相加的时候,是使用浮点运算器的机器指令,还是使用普通CPU的机器指令?此时,编译器就会检查进行加法操作的两个加数的数据类型,根据他们的数据类型,来确定到底使用哪一个运算器的机器代码。此时,数据类型定义的意义就凸显出来了。总结:通过上述简单的描述,我们就能够清楚,任何编程语言(除了汇编,汇编只规定数据的字长),都会有自己的数据类型,数据类型背后,隐藏的是编译器或者解释器对数据处理方式的定义。知道了这个以后,我们在定义数据类型的时候,就应该知道,我们定义的这种数据类型,可以进行哪些操作,这些操作的规则是什么,这样我们才算真正掌握了这个数据类型。更高级的语言,例如C++可以定义自己的数据类型和数据类型的算法,类的重载操作符就是一个例子。

February 25, 2019 · 1 min · jiezi

JS中的call、apply、bind方法详解

bind 是返回对应函数,便于稍后调用;apply 、call 则是立即调用 。apply、call在 javascript 中,call 和 apply 都是为了改变某个函数运行时的上下文(context)而存在的,换句话说,就是为了改变函数体内部 this 的指向。JavaScript 的一大特点是,函数存在「定义时上下文」和「运行时上下文」以及「上下文是可以改变的」这样的概念。function fruits() {} fruits.prototype = { color: “red”, say: function() { console.log(“My color is " + this.color); }} var apple = new fruits;apple.say(); //My color is red但是如果我们有一个对象banana= {color : “yellow”} ,我们不想对它重新定义 say 方法,那么我们可以通过 call 或 apply 用 apple 的 say 方法:banana = { color: “yellow”}apple.say.call(banana); //My color is yellowapple.say.apply(banana); //My color is yellow所以,可以看出 call 和 apply 是为了动态改变 this 而出现的,当一个 object 没有某个方法(本栗子中banana没有say方法),但是其他的有(本栗子中apple有say方法),我们可以借助call或apply用其它对象的方法来操作。apply、call 区别对于 apply、call 二者而言,作用完全一样,只是接受参数的方式不太一样。例如,有一个函数定义如下:var func = function(arg1, arg2) { };就可以通过如下方式来调用:func.call(this, arg1, arg2);func.apply(this, [arg1, arg2])其中 this 是你想指定的上下文,他可以是任何一个 JavaScript 对象(JavaScript 中一切皆对象),call 需要把参数按顺序传递进去,而 apply 则是把参数放在数组里。 为了巩固加深记忆,下面列举一些常用用法:apply、call实例数组之间追加var array1 = [12 , “foo” , {name:“Joe”} , -2458]; var array2 = [“Doe” , 555 , 100]; Array.prototype.push.apply(array1, array2); // array1 值为 [12 , “foo” , {name:“Joe”} , -2458 , “Doe” , 555 , 100] 获取数组中的最大值和最小值var numbers = [5, 458 , 120 , -215 ]; var maxInNumbers = Math.max.apply(Math, numbers), //458 maxInNumbers = Math.max.call(Math,5, 458 , 120 , -215); //458number 本身没有 max 方法,但是 Math 有,我们就可以借助 call 或者 apply 使用其方法。验证是否是数组(前提是toString()方法没有被重写过)functionisArray(obj){ return Object.prototype.toString.call(obj) === ‘[object Array]’ ;}类(伪)数组使用数组方法var domNodes = Array.prototype.slice.call(document.getElementsByTagName(”"));Javascript中存在一种名为伪数组的对象结构。比较特别的是 arguments 对象,还有像调用 getElementsByTagName , document.childNodes 之类的,它们返回NodeList对象都属于伪数组。不能应用 Array下的 push , pop 等方法。但是我们能通过 Array.prototype.slice.call 转换为真正的数组的带有 length 属性的对象,这样 domNodes 就可以应用 Array 下的所有方法了。面试题定义一个 log 方法,让它可以代理 console.log 方法,常见的解决方法是:function log(msg) { console.log(msg);}log(1); //1log(1,2); //1上面方法可以解决最基本的需求,但是当传入参数的个数是不确定的时候,上面的方法就失效了,这个时候就可以考虑使用 apply 或者 call,注意这里传入多少个参数是不确定的,所以使用apply是最好的,方法如下:function log(){ console.log.apply(console, arguments);};log(1); //1log(1,2); //1 2接下来的要求是给每一个 log 消息添加一个"(app)“的前辍,比如:log(“hello world”); //(app)hello world该怎么做比较优雅呢?这个时候需要想到arguments参数是个伪数组,通过 Array.prototype.slice.call 转化为标准数组,再使用数组方法unshift,像这样:function log(){ var args = Array.prototype.slice.call(arguments); args.unshift(’(app)’); console.log.apply(console, args);};bind在讨论bind()方法之前我们先来看一道题目:var altwrite = document.write;altwrite(“hello”);结果:Uncaught TypeError: Illegal invocationaltwrite()函数改变this的指向global或window对象,导致执行时提示非法调用异常,正确的方案就是使用bind()方法:altwrite.bind(document)(“hello”)当然也可以使用call()方法:altwrite.call(document, “hello”)绑定函数bind()最简单的用法是创建一个函数,使这个函数不论怎么调用都有同样的this值。常见的错误就像上面的例子一样,将方法从对象中拿出来,然后调用,并且希望this指向原来的对象。如果不做特殊处理,一般会丢失原来的对象。使用bind()方法能够很漂亮的解决这个问题:this.num = 9; var mymodule = { num: 81, getNum: function() { console.log(this.num); }};mymodule.getNum(); // 81var getNum = mymodule.getNum;getNum(); // 9, 因为在这个例子中,“this"指向全局对象var boundGetNum = getNum.bind(mymodule);boundGetNum(); // 81bind() 方法与 apply 和 call 很相似,也是可以改变函数体内 this 的指向。MDN的解释是:bind()方法会创建一个新函数,称为绑定函数,当调用这个绑定函数时,绑定函数会以创建它时传入 bind()方法的第一个参数作为 this,传入 bind() 方法的第二个以及以后的参数加上绑定函数运行时本身的参数按照顺序作为原函数的参数来调用原函数。直接来看看具体如何使用,在常见的单体模式中,通常我们会使用 _this , that , self 等保存 this ,这样我们可以在改变了上下文之后继续引用到它。 像这样:var foo = { bar : 1, eventBind: function(){ var _this = this; $(’.someClass’).on(‘click’,function(event) { / Act on the event / console.log(_this.bar); //1 }); }}由于 Javascript 特有的机制,上下文环境在 eventBind:function(){ } 过渡到 $(’.someClass’).on(‘click’,function(event) { }) 发生了改变,上述使用变量保存 this 这些方式都是有用的,也没有什么问题。当然使用 bind() 可以更加优雅的解决这个问题:var foo = { bar : 1, eventBind: function(){ $(’.someClass’).on(‘click’,function(event) { / Act on the event */ console.log(this.bar); //1 }.bind(this)); }}在上述代码里,bind() 创建了一个函数,当这个click事件绑定在被调用的时候,它的 this 关键词会被设置成被传入的值(这里指调用bind()时传入的参数)。因此,这里我们传入想要的上下文 this(其实就是 foo ),到 bind() 函数中。然后,当回调函数被执行的时候, this 便指向 foo 对象。再来一个简单的栗子:var bar = function(){console.log(this.x);}var foo = {x:3}bar(); // undefinedvar func = bar.bind(foo);func(); // 3这里我们创建了一个新的函数 func,当使用 bind() 创建一个绑定函数之后,它被执行的时候,它的 this 会被设置成 foo , 而不是像我们调用 bar() 时的全局作用域。偏函数(Partial Functions)Partial Functions也叫Partial Applications,这里截取一段关于偏函数的定义:Partial application can be described as taking a function that accepts some number of arguments, binding values to one or more of those arguments, and returning a new function that only accepts the remaining, un-bound arguments.这是一个很好的特性,使用bind()我们设定函数的预定义参数,然后调用的时候传入其他参数即可:function list() { return Array.prototype.slice.call(arguments);}var list1 = list(1, 2, 3); // [1, 2, 3]// 预定义参数37var leadingThirtysevenList = list.bind(undefined, 37);var list2 = leadingThirtysevenList(); // [37]var list3 = leadingThirtysevenList(1, 2, 3); // [37, 1, 2, 3]和setTimeout一起使用function Bloomer() { this.petalCount = Math.ceil(Math.random() * 12) + 1;}// 1秒后调用declare函数Bloomer.prototype.bloom = function() { window.setTimeout(this.declare.bind(this), 100);};Bloomer.prototype.declare = function() { console.log(‘我有 ’ + this.petalCount + ’ 朵花瓣!’);};var bloo = new Bloomer();bloo.bloom(); //我有 5 朵花瓣!注意:对于事件处理函数和setInterval方法也可以使用上面的方法绑定函数作为构造函数绑定函数也适用于使用new操作符来构造目标函数的实例。当使用绑定函数来构造实例,注意:this会被忽略,但是传入的参数仍然可用。function Point(x, y) { this.x = x; this.y = y;}Point.prototype.toString = function() { console.log(this.x + ‘,’ + this.y);};var p = new Point(1, 2);p.toString(); // ‘1,2’var emptyObj = {};var YAxisPoint = Point.bind(emptyObj, 0/x/);// 实现中的例子不支持,// 原生bind支持:var YAxisPoint = Point.bind(null, 0/x/);var axisPoint = new YAxisPoint(5);axisPoint.toString(); // ‘0,5’axisPoint instanceof Point; // trueaxisPoint instanceof YAxisPoint; // truenew Point(17, 42) instanceof YAxisPoint; // true捷径bind()也可以为需要特定this值的函数创造捷径。例如要将一个类数组对象转换为真正的数组,可能的例子如下:var slice = Array.prototype.slice;// …slice.call(arguments);如果使用bind()的话,情况变得更简单:var unboundSlice = Array.prototype.slice;var slice = Function.prototype.call.bind(unboundSlice);// …slice(arguments);实现上面的几个小节可以看出bind()有很多的使用场景,但是bind()函数是在 ECMA-262 第五版才被加入;它可能无法在所有浏览器上运行。这就需要我们自己实现bind()函数了。首先我们可以通过给目标函数指定作用域来简单实现bind()方法:Function.prototype.bind = function(context){ self = this; //保存this,即调用bind方法的目标函数 return function(){ return self.apply(context,arguments); };};考虑到函数柯里化的情况,我们可以构建一个更加健壮的bind():Function.prototype.bind = function(context){ var args = Array.prototype.slice.call(arguments, 1), self = this; return function(){ var innerArgs = Array.prototype.slice.call(arguments); var finalArgs = args.concat(innerArgs); return self.apply(context,finalArgs); };};这次的bind()方法可以绑定对象,也支持在绑定的时候传参。继续,Javascript的函数还可以作为构造函数,那么绑定后的函数用这种方式调用时,情况就比较微妙了,需要涉及到原型链的传递:Function.prototype.bind = function(context){ var args = Array.prototype.slice(arguments, 1), F = function(){}, self = this, bound = function(){ var innerArgs = Array.prototype.slice.call(arguments); var finalArgs = args.concat(innerArgs); return self.apply((this instanceof F ? this : context), finalArgs); }; F.prototype = self.prototype; bound.prototype = new F(); return bound;};这是《JavaScript Web Application》一书中对bind()的实现:通过设置一个中转构造函数F,使绑定后的函数与调用bind()的函数处于同一原型链上,用new操作符调用绑定后的函数,返回的对象也能正常使用instanceof,因此这是最严谨的bind()实现。对于为了在浏览器中能支持bind()函数,只需要对上述函数稍微修改即可:Function.prototype.bind = function (oThis) { if (typeof this !== “function”) { throw new TypeError(“Function.prototype.bind - what is trying to be bound is not callable”); } var aArgs = Array.prototype.slice.call(arguments, 1), fToBind = this, fNOP = function () {}, fBound = function () { return fToBind.apply( this instanceof fNOP && oThis ? this : oThis || window, aArgs.concat(Array.prototype.slice.call(arguments)) ); }; fNOP.prototype = this.prototype; fBound.prototype = new fNOP(); return fBound; };有个有趣的问题,如果连续 bind() 两次,亦或者是连续 bind() 三次那么输出的值是什么呢?像这样:var bar = function(){ console.log(this.x);}var foo = { x:3}var sed = { x:4}var func = bar.bind(foo).bind(sed);func(); //? var fiv = { x:5}var func = bar.bind(foo).bind(sed).bind(fiv);func(); //?答案是,两次都仍将输出 3 ,而非期待中的 4 和 5 。原因是,在Javascript中,多次 bind() 是无效的。更深层次的原因, bind() 的实现,相当于使用函数在内部包了一个 call / apply ,第二次 bind() 相当于再包住第一次 bind() ,故第二次以后的 bind 是无法生效的。apply、call、bind比较那么 apply、call、bind 三者相比较,之间又有什么异同呢?何时使用 apply、call,何时使用 bind 呢。简单的一个栗子:var obj = { x: 81,}; var foo = { getX: function() { return this.x; }} console.log(foo.getX.bind(obj)()); //81console.log(foo.getX.call(obj)); //81console.log(foo.getX.apply(obj)); //81三个输出的都是81,但是注意看使用 bind() 方法的,他后面多了对括号。也就是说,区别是,当你希望改变上下文环境之后并非立即执行,而是回调执行的时候,使用 bind() 方法。而 apply/call 则会立即执行函数。再总结一下:apply 、 call 、bind 三者都是用来改变函数的this对象的指向的;apply 、 call 、bind 三者第一个参数都是this要指向的对象,也就是想指定的上下文;apply 、 call 、bind 三者都可以利用后续参数传参;bind 是返回对应函数,便于稍后调用;apply 、call 则是立即调用 。 ...

February 25, 2019 · 4 min · jiezi

【Leetcode】101. 对称二叉树

题目给定一个二叉树,检查它是否是镜像对称的。例如,二叉树 [1,2,2,3,4,4,3] 是对称的。 1 / \ 2 2 / \ / \3 4 4 3但是下面这个 [1,2,2,null,3,null,3] 则不是镜像对称的: 1 / \ 2 2 \ \ 3 3题解还记得我们上几次说过,二叉树的题目,大多数可以用递归解决。而递归主要确定两点:递归的子问题是什么;递归的结束条件是什么这个题这两点都不难找到,直接看代码吧。/** * Definition for a binary tree node. * public class TreeNode { * int val; * TreeNode left; * TreeNode right; * TreeNode(int x) { val = x; } * } */class Solution { public boolean isSymmetric(TreeNode root) { return root == null || isSymmetricHelp(root.left, root.right); } private boolean isSymmetricHelp(TreeNode left, TreeNode right) { if (left == null || right == null) return left == right; if (left.val != right.val) return false; return isSymmetricHelp(left.left, right.right) && isSymmetricHelp(left.right, right.left); }}这个题目还要求用非递归的方式去解.一种很直观的想法就是利用层序遍历,看它是不是对称的.入队顺序依次是, 左子树的左儿子,右子树的右儿子左子树的右儿子,右子树左右儿子。这样出队的时候两两检查是不是对称。public boolean isSymmetric(TreeNode root) { Queue<TreeNode> q = new LinkedList<TreeNode>(); if(root == null) return true; q.add(root.left); q.add(root.right); while(!q.isEmpty()){ TreeNode left = q.poll(); TreeNode right = q.poll(); // 叶子节点. if(left== null&& right == null) continue; // 其中一个为null 肯定不是 if(left == null ^ right == null) return false; // 值不相同 if(left.val != right.val) return false; q.add(left.left); q.add(right.right); q.add(left.right); q.add(right.left); } return true; }热门阅读技术文章汇总【Leetcode】100. 相同的树【Leetcode】98. 验证二叉搜索树【Leetcode】95~96 不同的二叉搜索树 ...

February 25, 2019 · 1 min · jiezi