前端面试

xiaoxiao2021-02-28  118

Q: CSS 有哪些样式可以给子元素继承!

可继承的:font-size,font-weight,line-height,color,cursor等不可继承的一般是会改变盒子模型的:display,margin、border、padding、height等

更加全面的可以到引擎找


Q: CSS3实现一个扇形

思路跟画实体三角形一个道理,只不过多了一个圆角属性 <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <meta http-equiv="X-UA-Compatible" content="ie=edge"> <title>扇形</title> <style> .sector { width: 0; height: 0; border-width: 50px; border-style: solid; border-color: #f00 transparent transparent; border-radius: 50px; } </style> </head> <body> <div class="sector"></div> </body> </html>

Q: box-sizing常用的属性有哪些? 分别有啥作用?

box-sizing有两个值:content-box(W3C标准盒模型),border-box(怪异模型),

这个css 主要是改变盒子模型大小的计算形式

可能有人会问padding-box,这个之前只有 Firefox 标准实现了,目前50+的版本已经废除;

用一个栗子来距离,一个div的宽高分别100px,border为5px,padding为5px

<style> .test { box-sizing: content-box; border: 5px solid #f00; padding:5px; width: 100px; height: 100px; } </style> <div class="test"></div> <!-- content-box的计算公式会把宽高的定义指向 content,border和 padding 另外计算, 也就是说 content + padding + border = 120px(盒子实际大小) 而border-box的计算公式是总的大小涵盖这三者, content 会缩小,来让给另外两者 content(80px) + padding(5*2px) + border(5*2px) = 100px -->

Q: 清除浮动的方式有哪些?比较好的是哪一种?

常用的一般为三种.clearfix, clear:both,overflow:hidden;

比较好是 .clearfix,伪元素万金油版本...后两者有局限性..等会再扯

.clearfix:after { visibility: hidden; display: block; font-size: 0; content: " "; clear: both; height: 0; } <!-- 为毛没有 zoom ,_height 这些...IE6,7这类需要 csshack 不再我们考虑之内了 .clearfix 还有另外一种写法... --> .clearfix:before, .clearfix:after { content:""; display:table; } .clearfix:after{ clear:both; overflow:hidden; } .clearfix{ zoom:1; } <!--display:table 是为了避免外边距margin重叠导致的margin塌陷, 内部元素默认会成为 table-cell 单元格的形式 -->

clear:both:若是用在同一个容器内相邻元素上,那是贼好的...有时候在容器外就有些问题了,比如相邻容器的包裹层元素塌陷

overflow:hidden:这种若是用在同个容器内,可以形成 BFC避免浮动造成的元素塌陷


Q: CSS 中transition和animate有何区别? animate 如何停留在最后一帧!

这种问题见仁见智,我的回答大体是这样的..待我捋捋.

transition一般用来做过渡的, 没时间轴的概念, 通过事件触发(一次),没中间状态(只有开始和结束)

而animate则是做动效,有时间轴的概念(帧可控),可以重复触发和有中间状态;

过渡的开销比动效小,前者一般用于交互居多,后者用于活动页居多;

至于如何让animate停留在最后一帧也好办,就它自身参数的一个值就可以了

animation-fill-mode: forwards; <!--backwards则停留在首帧,both是轮流-->

让我们来举个栗子....自己新建一个 html 跑一下....

<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <meta http-equiv="X-UA-Compatible" content="ie=edge"> <title>Box-sizing</title> <style> .test { box-sizing: border-box; border: 5px solid #f00; padding: 5px; width: 100px; height: 100px; position:absolute; /* 简写的姿势排序 @keyframes name : 动画名 duration 持续时间 timing-function 动画频率 delay 延迟多久开始 iteration-count 循环次数 direction 动画方式,往返还是正向 fill-mode 一般用来处理停留在某一帧 play-state running 开始,paused 暂停 .... 更多的参数去查文档吧..我就不一一列举了 */ animation: moveChangeColor ease-in 2.5s 1 forwards running; } @keyframes moveChangeColor { from { top:0%; left:5%; background-color:#f00 } to{ top:0%; left:50%; background-color:#ced; } } </style> </head> <body> <div class="test"></div> </body> </html>

Q: 块级元素水平垂直居中的方法

我们要考虑两种情况,定宽高和不定宽高的;

方案 N 多种,我记得我很早写过这类的笔记

传送门:网页元素居中攻略记


Q: 说说样式权重的优先级;

!important > 行内样式 > id > class > tag

样式权重可以叠加, 比如 id>class


Q: 对HTML语义化的理解

简言之:就是不滥用标签(比如 DIV)/随意嵌套(比如 span>div) ,

类的命名要合理, 利于浏览器解析乃至引擎收录,也利于团队协作和维护


Q: JS有几种数据类型,其中基本数据类型有哪些!

七种数据类型

BooleanNullUndefinedNumberStringSymbol (ECMAScript 6 新定义)Object

(ES6之前)其中5种为基本类型:string,number,boolean,null,undefined,

ES6出来的Symbol也是原始数据类型 ,表示独一无二的值

Object 为引用类型(范围挺大),也包括数组、函数,


Q: null和undefined的差异

大体说一下,想要知其所以然请引擎搜索

相同点:

在 if判断语句中,值都默认为 false大体上两者都是代表无,具体看差异

差异:

null转为数字类型值为0,而undefined转为数字类型为 NaN(Not a Number)undefined是代表调用一个值而该值却没有赋值,这时候默认则为undefinednull是一个很特殊的对象,最为常见的一个用法就是作为参数传入(说明该参数不是对象)设置为null的变量或者对象会被内存收集器回收

Q: 谈谈你对ajax 的理解,以及用原生 JS 实现有哪些要点需要注意;

ajax全称是异步 javascript 和 XML,用来和服务端进行数据交互的,让无刷新替换页面数据成了可能;

至于有哪些要要点,来一个简短的ajax请求

var xhr = new XMLHttpRequest(); // 声明一个请求对象 xhr.onreadystatechange = function(){ if(xhr.readyState === 4){ // readyState 4 代表已向服务器发送请求 if(xhr.status === OK){ // // status 200 代表服务器返回成功 console.log(xhr.responseText); // 这是返回的文本 } else{ console.log("Error: "+ xhr.status); // 连接失败的时候抛出错误 } } } xhr.open('GET', 'xxxx'); // 如何设置请求头? xhr.setRequestHeader(header, value); xhr.setRequestHeader('Content-Type', 'application/json'); xhr.send(null); // get方法 send null(亦或者不传,则直接是传递 header) ,post 的 send 则是传递值

更为详细的可以阅读此处;

<<ajax 概念 by 阮一峰>><<XMLHttpRequest2 用法指南>>

Q: JS 实现一个闭包函数,每次调用都自增1;

这里主要考察了闭包,函数表达式以及 IIFE(立即执行表达式)

var add = (function() { // 声明一变量,由于下面 return所以变量只会声明一次 var count = 0; return function() { return console.log(count++); }; })(); add(); // 0 add(); // 1 add(); // 2

Q: 对数组 ['2018-03-05', '2013-06-12','2019-03-12','2018-03-05','2014-02-22'] 去重且排序

我这里用的是结合 ES6的,代码量很短

//很好理解, Set 具有值唯一性(但不是所有值,等会我抛出我的另外一篇文章) // 结合...解构,可以把可迭代(比如 arguments/nodelist 等)的转为数组 // sort 里面传入 两个值比较,返回-1和1是因为1代表这个数大排后(相对),-1代表小(相对),0为相等 let arr = [...new Set(['2018-03-05', '2013-06-12','2019-03-12','2018-03-05','2014-02-22'])].sort(function(a,b){ return a<b ? -1:1; // 这里返回的是升序的,降序改下返回值就好了.所以是相对 }) // ["2013-06-12", "2014-02-22", "2018-03-05", "2019-03-12"]

对于数组去重的,有兴趣的可以看下我这篇水文:

JS数组去重!!!一篇不怎么靠谱的"深度"水文

Q: 对数组[1,2,3,4,5,'6',7,'8','a','b','z']进行乱序

// 我们依旧可以用上面的 sort 的原理实现乱序 let tempArr = [1,2,3,4,5,'6',7,'8','a','b','z'].sort(function(){ return Math.random() > 0.5 ? -1 : 1; }) // 因为里面有随机数,所以答案没有标准答案,我这边跑了一次是输出这个 //["6", "z", 3, "b", 5, 2, 7, "8", "a", 1, 4]

上面和这道题逗涉及到数组顺序的问题,想了解下为什么 a-b,a>b这类可以更改排序

可以看看知乎对于这块的探讨: 传送门:javascript排序return a-b?


Q: 求[1, 10, 11, -1,'-5',12, 13, 14, 15, 2, 3, 4, 7, 8, 9]内最大值与最小值之差

// 来一个很粗糙的版本,只当传入是数组且可以隐性转为数字的 function MaxMinPlus(arr) { // 返回最大值与最小值之差 return Array.isArray(arr) ? Math.max.apply(Math, arr) - Math.min.apply(Math, arr) : console.log('传入的不是数组亦或者未能解决的错误') } // 结果是 20 // 若是要完善的话,要考虑传入的是非数组, //传入字符串的时候要判断,然后切割为数组.. // 都要考虑进去代码量不短

Q: 请给Array实现一个方法,去重后返回重复的字符(新数组)

var testArr = [1,6,8,3,7,9,2,7,2,4,4,3,3,1,5,3]; Array.prototype.extraChar = function(){ var cacheExtraChar = []; // 缓存重复出现的字符 var that = this; // 缓存 this; this.map(function(item,index){ // 怎么理解这段代码呢? // 就是向前往后查找一遍和从后往前查找一遍,不等就是没有重复 // 为什么还要判断一遍缓存,是过滤缓存数组内多次写入 (that.indexOf(item) !== that.lastIndexOf(item)) && cacheExtraChar.indexOf(item) === -1 ? cacheExtraChar.push(item) : -1; }); return cacheExtraChar; } testArr.extraChar(); // [1, 3, 7, 2, 4] // 若是还需要排序就再排序下 [1,6,8,3,7,9,2,7,2,4,4,3,3,1,5,3] .extraChar() .sort(function(a,b){return a-b}) // [1, 2, 3, 4, 7]

Q: 一个数组中 par中存放了多个人员的信息,每个人员的信息由 name 和 age 构成({name:'张三',age:15}).请用 JS 实现年龄从小到大的排序;

var par = [{age:5,name:'张三'},{age:3,name:'李四'},{age:15,name:'王五'},{age:1,name:'随便'}] var parSort = par.sort(function(a,b){ return a.age - b.age; })

Q: 判断一个回文字符串和同字母异序字符串

回文字符串就是正序倒序都是一样的;同字母异序字符串则是字符串都一样,但是位置可能不一定一样,比如abcefd和dceabf=>return true

后者的思路就是用排序把异序扭正..

普通版

// 回文判断 , 比如用 abcba var isPalindromes = function(params){ params = params.toString().toLowerCase() return params === params.split('').reverse().join(''); } // 同字母异序判定,比如`abcefd`和`dceabf` var isAnagram = function(str1, str2) { str1 = str1.toString().toLowerCase(); str2 = str2.toString().toLowerCase(); return str1.split('').sort().join('') === str2.split('').sort().join('') }

进阶版:多一些特殊字符

若是我们要去除所有非字母数字的字符,则需要用到正则

// 进阶版: isPalindromes('abc_ &b #@a') var isPalindromes = function(params){ // 传入参数先转为字符串且全部转为小写,最后去除多余字符比较 params = params.toString().toLowerCase().replace(/[\W_\s]/g,''); console.log(params) return params === params.split('').reverse().join(''); } // 进阶版同字母异序: isAnagram('ab *&cef#d','!d@ce^abf') var isAnagram = function(str1, str2) { str1 = str1.toString().toLowerCase().replace(/[\W_\s]/g,''); str2 = str2.toString().toLowerCase().replace(/[\W_\s]/g,''); return str1.split('').sort().join('') === str2.split('').sort().join('') }

Q: JS 实现String.trim()方法;

// 原生是有 trim()方法的.我们要模拟一个; String.prototype.emuTrim = function(){ // 这条正则很好理解,就是把头部尾部多余的空格字符去除 return this.replace(/(^\s*)|(\s*$)/g,''); } ' fsaf fsdaf f safl lllll '.emuTrim(); //"fsaf fsdaf f safl lllll"

Q: 实现对一个数组或者对象的浅拷贝和"深度"拷贝

浅拷贝就是把属于源对象的值都复制一遍到新的对象,不会开辟两者独立的内存区域;

深度拷贝则是完完全全两个独立的内存区域,互不干扰

浅拷贝 // 这个 ES5的 function shallowClone(sourceObj) { // 先判断传入的是否为对象类型 if (!sourceObj || typeof sourceObj !== 'object') { console.log('您传入的不是对象!!') } // 判断传入的 Obj是类型,然后给予对应的赋值 var targetObj = sourceObj.constructor === Array ? [] : {}; // 遍历所有 key for (var keys in sourceObj) { // 判断所有属于自身原型链上的 key,而非继承(上游 )那些 if (sourceObj.hasOwnProperty(keys)) { // 一一复制过来 targetObj[keys] = sourceObj[keys]; } } return targetObj; } // ES6 可以用 Object.assign(targeObj, source1,source2,source3) 来实现对象浅拷贝 深度拷贝 // 就是把需要赋值的类型转为基本类型(字符串这些)而非引用类型来实现 // JOSN对象中的stringify可以把一个js对象序列化为一个JSON字符串,parse可以把JSON字符串反序列化为一个js对象 var deepClone = function(sourceObj) { if (!sourceObj || typeof sourceObj !== 'object') { console.log('您传入的不是对象!!'); return; } // 转->解析->返回一步到位 return window.JSON ? JSON.parse(JSON.stringify(sourceObj)) : console.log('您的浏览器不支持 JSON API'); }; 深拷贝的考虑点实际上要复杂的多,详情看看知乎怎么说

Q: this对象的理解

简言之:谁调用指向谁,运行时的上下文确定,而非定义的时候就确定;

强行绑定 this的话,可以用 call,apply,bind,箭头函数....来修改this的指向

这类的文章太多,自行搜索吧....

Q: 看到你说到 bind,能用 JS简单的模拟个么?

Function.prototype.emulateBind = function (context) { var self = this; return function () { return self.apply(context); } }

这个实现很粗糙...更为详细全面,考虑周全的(比如参数的处理什么的)...自行谷歌.


Q:JS 的作用域是什么?有什么特别之处么?

作用域就是有它自身的上下文区域(比如函数内),内部会有变量声明提升,函数声明提升这些;

函数声明提升优于变量声明提升..

作用域有全局作用域和块级作用域(局部,比如用 let 或者单纯花括号的);

作用域会影响this的指向

坐等补充,我回答的时候,面试大佬只是 嗯..恩...恩...也不知道具体如何


Q: 怎么解决跨域问题,有哪些方法...

我一般用这三种,cors,nginx反向代理,jsonp

jsonp : 单纯的 get 一些数据,局限性很大...就是利用script标签的src属性来实现跨域。nginx 反向代理: 主要就是用了nginx.conf内的proxy_pass http://xxx.xxx.xxx,会把所有请求代理到那个域名,有利也有弊吧..cors的话,可控性较强,需要前后端都设置,兼容性 IE10+ ,比如 Access-Control-Allow-Origin: http://foo.example // 子域乃至整个域名或所有域名是否允许访问Access-Control-Allow-Methods: POST, GET, OPTIONS // 允许那些行为方法Access-Control-Allow-Headers: X-PINGOTHER, Content-Type // 允许的头部字段Access-Control-Max-Age: 86400 // 有效期

Q: 对于想携带一些鉴权信息跨域如何走起?比如cookie!

需要配置下 header Access-Control-Allow-Credentials:true ,具体用法看下面的nginxdemo

当然cros的配置不仅仅这些,还有其他一些,具体引擎吧....

若是我们要用 nginx或者 express 配置cors应该怎么搞起? 来个简易版本的

nginx location / { # 检查域名后缀 add_header Access-Control-Allow-Origin xx.xx.com; add_header Access-Control-Allow-Methods GET,POST,OPTIONS; add_header Access-Control-Allow-Credentials true; add_header Access-Control-Allow-Headers DNT,X-CustomHeader,Keep-Alive,User-Agent,X-Requested-With,If-Modified-Since,Cache-Control,Content-Type; add_header Access-Control-Max-Age 86400; } express, 当然这货也有一些别人封装好的 cors中间件,操作性更强... let express = require('express'); let app = express(); //设置所有请求的头部 app.all('*', (req, res, next) => { res.header("Access-Control-Allow-Origin", "xx.xx.com"); res.header("Access-Control-Allow-Headers", "DNT,X-CustomHeader,Keep-Alive,User-Agent,X-Requested-With,If-Modified-Since,Cache-Control,Content-Type"); res.header("Access-Control-Allow-Credentials","true") res.header("Access-Control-Allow-Methods","PUT,POST,GET,DELETE,OPTIONS"); next(); });

有些还会跟你死磕,,除了这些还有其他姿势么...我说了一个HTML5的postMessage....

..因为真心没用过,只是以前查阅的时候了解了下..只能大体点下

这货用于iframe 传递消息居多, 大体有这么两步步

window打开一个实例,传递一个消息到一个x域名x 域名下监听message事件,获取传递的消息

这货的兼容性没那么好,而且没考虑周全下容易遭受 CSRF 攻击


Q: 对于XSS 和 CSRF 如何防范

这里就不说概念性的东西了

XSS的防范

我能想到的就是转义<>这些造成代码直接运行的的标签..轮询或者正则替换 而面试官说这种的效率最低下,我回来仔细找了找相关资料好像没有更优方案...有的留言...若是有用到 cookie,设置为http-only,避免客户端的篡改

CSRF的防范一般这几种

验证码,用户体验虽然不好,,但是很多场合下可以防范大多数攻击验证 HTTP Referer 字段,判断请求来源token加密解密,这种是目前很常用的手段了...

任何防范都有代价的,比如验证码造成的体验不好,token滥用造成的性能问题,轮询替换造成的响应时间等


Q: 描述下cookie,sessionStorage,localSotrage的差异..

cookie : 大小4KB 左右,跟随请求(请求头),会占用带宽资源,但是若是用来判断用户是否在线这些挺方便sessionStorage和localStorage大同小异,大小看浏览器支持,一般为5MB,数据只保留在本地,不参与服务端交互. sessionStorage的生存周期只限于会话中,关闭了储存的数据就没了.localStorage则保留在本地,没有人为清除会一直保留

Q: javascript的原型链你怎么理解?

原型链算是 JS 内一种独有的机制,

所有对象都有一个内置[[proto]]指向创建它的原型对象(prototype)

原型链的基本用来实现继承用的


Q: javascript里面的继承怎么实现,如何避免原型链上面的对象共享

我在写的时候,用了两种,一个是 ES5和 ES6的方案

ES5:寄生组合式继承:通过借用构造函数来继承属性和原型链来实现子继承父。 function ParentClass(name) { this.name = name; } ParentClass.prototype.sayHello = function () { console.log("I'm parent!" + this.name); } function SubClass(name, age) { //若是要多个参数可以用apply 结合 ...解构 ParentClass.call(this, name); this.age = age; } SubClass.prototype = Object.create(ParentClass.prototype); SubClass.prototype.constructor = SubClass; SubClass.prototype.sayChildHello = function (name) { console.log("I'm child " + this.name) } let testA = new SubClass('CRPER') // Object.create()的polyfill /* function pureObject(o){ //定义了一个临时构造函数 function F() {} //将这个临时构造函数的原型指向了传入进来的对象。 F.prototype = obj; //返回这个构造函数的一个实例。该实例拥有obj的所有属性和方法。 //因为该实例的原型是obj对象。 return new F(); } */ ES6: 其实就是ES5的语法糖,不过可读性很强.. class ParentClass { constructor(name) { this.name = name; } sayHello() { console.log("I'm parent!" + this.name); } } class SubClass extends ParentClass { constructor(name) { super(name); } sayChildHello() { console.log("I'm child " + this.name) } // 重新声明父类同名方法会覆写,ES5的话就是直接操作自己的原型链上 sayHello(){ console.log("override parent method !,I'm sayHello Method") } } let testA = new SubClass('CRPER')

到这里就结束了么...不,这只是笔试,

Q: 问的时候你用过静态方法,静态属性,私有变量么?

静态方法是ES6之后才有这么个玩意,有这么些特点

方法不能给 this引用,可以给类直接引用静态不可以给实例调用,比如 let a = new ParentClass => a.sayHello() 会抛出异常父类静态方法,子类非static方法没法覆盖父类静态方法可以给子类继承静态属性可以继承也可以被修改

看下面的代码..

class ParentClass { constructor(name) { this.name = name; } static sayHello() { console.log("I'm parent!" + this.name); } static testFunc(){ console.log('emm...Parent test static Func') } } class SubClass extends ParentClass { constructor(name) { super(name); } sayChildHello() { console.log("I'm child " + this.name) } static sayHello() { console.log("override parent method !,I'm sayHello Method") } static testFunc2() { console.log(super.testFunc() + 'fsdafasdf'); } } ParentClass.sayHello(); // success print let a = new ParentClass('test'); a.sayHello() // throw error SubClass.sayHello(); // 同名 static 可以继承且覆盖 SubClass.testFunc2(); // 可以继承 let testA = new SubClass('CRPER');

私有变量这个我没答出来,只是说了下没有private这个关键字和基本用下划线的人为区分

所以回来只是找了下相关的资料,发现有一个比较好的模拟方案,就是WeakMap;

WeakMap可以避免内存泄露,当没有被值引用的时候会自动给内存寄存器回收了.

const _ = new WeakMap(); // 实例化,value 必须为对象,有 delete,get,has,set四个方法,看名字都知道了 class TestWeakMap { constructor(id, barcode) { _.set(this, { id,barcode }); } testFunc() { let { id,barcode } = _.get(this); // 获取对应的值 return { id,barcode }; } }

Q: 谈谈你对 Promise 的理解? 和 ajax 有关系么?

Promise和ajax没有半毛钱直接关系.promise只是为了解决"回调地狱"而诞生的;

平时结合 ajax是为了更好的梳理和控制流程...这里我们简单梳理下..

Promise有三种状态,Pending/resolve()/reject();

一些需要注意的小点,如下

在 Pending 转为另外两种之一的状态时候,状态不可在改变..Promise的 then为异步.而(new Promise())构造函数内为同步Promise的catch不能捕获任意情况的错误(比如 then 里面的setTimout内手动抛出一个Error)Promise的 resolve若是传入值而非函数,会发生值穿透的现象Promise的catch还是then,return的都是一个新的 Promise(在 Promise 没有被中断的情况下)

Promise 还有一些自带的方法,比如race,all,前者有任一一个解析完毕就返回,后者所有解析完毕返回...

实现一个延时的 promise 函数, 可以用async和await

const delay = (time)=> new Promise((resolve,reject)=>{ setTimeout(resolve,time) }) // test let testRun = async function(){ console.log(1); await delay(2000); console.log('我两秒后才触发',3) } // 1 => Promise = > 3 <<Promise 必知必会(十道题)>>: 有助于你更加深刻的了解 promise 的运行情况关于 Promise 的 9 个提示更多的Promise 详情可以参考<<JavaScript Promise迷你书(中文版)>>;

Q: 谈谈你对 TCP 的理解;

Q: TCP 是在哪个OSI 的哪个层!通讯过程是全双工还是半双工(单工)?

A: 传输层,全双工

Q: TCP的通讯的过程是怎么样的!

A: 整个过程是三次握手,四次挥手..

Q: 你说的没错,说说整个过程如何?

A: 举个栗子,我把 TCP 比做两个人用对讲机沟通(大白话)..三次握手就是.A1(吼叫方,客户端)想要呼叫 A2(控制室的某某,服务端)..

A1对着对讲机说"over over ,听到请回答"(第一次,请求应答) ...

A2收到回应"收到收到,你说"(第二次,确认应答)

A1开始巴拉巴拉个不停而 A2没拒绝(第三次,通讯建立)


而四次挥手则是两者确认互相倾述完毕的过程..

A1说:"控制室,报告完毕了"(第一次挥手)

A2说:"知道了...那么你废话说完就好好听我指挥....巴拉巴拉.."(第二次挥手)

A1此时等待控制室说完毕,而控制室等回应(第三次挥手)

等到 A1回馈控制室确认都知道完毕了..(第四次挥手)...

以上都是瞎掰,可能有些地方描述不当,笑笑就好了

TCP没有百分百建立成功的,会造成链接失败的情况有很多..

比如长时间没应答(A1吼了半天没有反应或者 A2应答了而 A1不再鸟它)..亦或者丢包(对讲机也没了);

TCP 协议相关的文章网上很多,若是要更加全面的了解该协议请自行引擎..

我建议阅读<<TCP-IP详解卷1~卷3>>,这个是网络圣经...很厚...我只看了一丢丢..


Q: TCP 你了解了,那么 OSI 七层协议和五层网络架构应该知道吧?

对于这类的问题我也只能大体点了下,毕竟不是专攻网络这块的...

OSI 七层涵盖:物理层,数据链路层,网络层,传输层,会话层,表示层,应用层;

五层模型就是"会话,表示,应用层"同为一层;

Q: DNS 的大体的执行流程了解么,属于哪个层级?工作在哪个层级?

DNS 属于应用层协议, 至于TCP/UDP哪一层上面跑,看情况 , 大体的执行流程是这样的;

优先读取浏览器缓存其次系统的缓存都没有的情况下,找本地hosts文件(比如你写了映射关系优先寻找)再没有的情况找最近的域名解析服务器再没有则扩大访问,最终找到根服务器,还是没有就失败了..

DNS 的解析的几个记录类型需要了解:

A: 域名直接到 IPCNAME: 可以多个域名映射到一个主机,类似在 Github Page就用 CNAME 指向MX: 邮件交换记录,用的不多,一般搭建邮件服务器才会用到NS: 解析服务记录,可以设置权重,指定谁解析TTL: 就是生存时间(也叫缓存时间),一般的域名解析商都有默认值,也可以人为设置TXT: 一般指某个主机名或域名的说明

回来我找下相关的资料,有兴趣的可以深入了解下,传送门如下:

梳理Linux下OSI七层网络与TCP/IP五层网络架构TCP/IP(六)应用层(DNS和HTTP协议)DNS域名解析解剖

Q: HTTP 和 HTTPS 有何差异? 听说过 SPDY 么?

我只是粗浅的回答了下...

HTTP相对于 HTTPS来说,速度较快且开销较小(没有 SSL/TSL) 对接,默认是80端口;

HTTP容易遭受域名劫持,而HTTPS相对来说就较为安全但开销较大(数据以加密的形式传递),默认端口为443..

HTTP是明文跑在 TCP 上.而HTTPS跑在SSL/TLS应用层之下,TCP上的

Q: 那么 HTTPS中的TLS/SSL是如何保护数据的...

一般有两种形式,非对称加密,生成公钥和私钥,私钥丢服务器,公钥每次请求去比对验证;

更严谨的采用 CA(Certificate Authority),给密钥签名....

Q: 你说到对称加密和非对称加密,能说说整个流程如何运转的么(HTTPS)

对称加密: 双方都有同样的密钥,每次通讯都要生成一个唯一密钥,速度很快安全性较低且密钥增长的数量极快非对称加密(一般用 RSA) 安全性很高,对资源消耗很大(CPU),目前主流的加密算法(基本用于交换密钥或签名,而非所有通讯内容)CA(数字签名): 这个是为了防止中间人给偷换了造成数据被窃取而诞生的用一些权威机构颁布的算法来签名,权威机构做中间人,通讯过程都会跟机构核对一遍

懂得真心不多,回来找了下相关资料,有兴趣可以点击看看;

深入揭秘HTTPS安全问题&连接建立全过程深入理解 https 通信加密过程:口语化 " : 看了上面那篇文章来看下面,会清晰很多

Q: SPDY 听说过么.什么来的?

谷歌推行一种协议(HTTP 之下SSL之上[TCP]),可以算是HTTP2的前身,有这么些优点

压缩数据(HEADER)多路复用优先级(可以给请求设置优先级)

而这些优点基本 HTTP2也继承下来了..

Q: 你对 HTTP 的状态吗了解多少...

这里列举一丢丢常见的..

1XX: 一般用来判断协议更换或者确认服务端收到请求这些 100: 服务端收到部分请求,若是没有拒绝的情况下可以继续传递后续内容101: 客户端请求变换协议,服务端收到确认2xx: 请求成功,是否创建链接,请求是否接受,是否有内容这些 200: (成功)服务器已成功处理了请求。201: (已创建)请求成功并且服务器创建了新的资源。202: (已接受)服务器已接受请求,但尚未处理。204: (无内容)服务器成功处理了请求,但没有返回任何内容。3XX: 一般用来判断重定向和缓存 301: 所有请求已经转移到新的 url(永久重定向),会被缓存302: 临时重定向,不会被缓存304: 本地资源暂未改动,优先使用本地的(根据If-Modified-Since or If-Match去比对服务器的资源,缓存)4XX: 一般用来确认授权信息,请求是否出错,页面是否丢失 400: 请求出错401: 未授权,不能读取某些资源403: 阻止访问,一般也是权限问题404: 页面丢失,资源没找到408: 请求超时415: 媒介类型不被支持,服务器不会接受请求。5XX: 基本都是服务端的错误 500: 服务端错误502: 网关错误504: 网关超时

Q: HTTP的请求报文是怎么样的,能大体的说下么?

HTTP 的请求报文 = 请求行 + 请求头 + 请求体;

请求行: 这个好理解就是访问的方法+ 协议+ 访问的 URL 构成请求头: 这个也好理解,比如 accept,content-type,user-agent这类值键对,服务端可以直接读取的请求体: 比如 POST 提交的一个表单,我们编码后放在上面需要传递的

想深入了解的具体引擎搜索

Q: 请求报文知道,那你说说cookie是如何跟随请求的?

Cookie 就是保存在 HTTP 协议的请求或者应答头部(Cookie 是由服务端生成),这样一路漂泊...


Q: 浏览器缓存和服务端的缓存控制你了解多少,说说看?

Last-Modified: 第一次请求资源从服务器拉取的会自动带上该属性第二次请求会跟服务端比对If-Modified-Since的时间,没变动则使用本地的(状态304)结合Expires(过期时间:缓存的载止时间),跟随请求一起发出..资源没过期拿本地,否则重新请求Cache-control 是 HTTP1.1的东西,判断资源过期结合max-age来替代Expires[http 1.0]Etag: 第一次请求url 时候会给服务器上标记(一串字符串)第二次请求时候会比对服务端的If-None-Match,没有改动依旧拿缓存(304)

Q: 几个短而让我印象深刻的题

if(!("a" in window)){ var a = 10; } console.log(a); // undefined // !("a" i n window) , 返回 true // 留言小伙伴的刨析,非常感谢,还是涉及变量提升的问题 /* var a; if(!("a" in window)){ a = 10; } */ // 变种题 (function(){ var x = c = b = {a:1} })() console.log(x.a); // error , x is not defined console.log(c,b) // {a: 1} {a: 1} var count = 0; console.log(typeof count === "number"); // true , 这个不用解释了 console.log(!!typeof count === "number"); // false // 这里涉及到就是优先级和布尔值的问题 // typeof count 就是字符串"number" // !!是转为布尔值(三目运算符的变种),非空字符串布尔值为 true // 最后才=== 比较 , true === "number" , return false (function(){ var a = b = 3; })() console.log(typeof a === "undefined"); // true console.log(typeof b === "undefined"); // false // 这里涉及的就是立即执行和闭包的问题,还有变量提升,运算符执行方向(=号自左向右) // 那个函数可以拆成这样 (function() var a; /* 局部变量,外部没法访问*/ b = 3; /* 全局变量,so . window.b === 3 , 外部可以访问到*/ a = b; })() // 若是改成这样,这道题应该是对的 console.log(typeof b === "number" && b ===3 ); // true function foo(something){ this.a = something; } var obj1 = { foo:foo }; var obj2 = {}; obj1.foo(2) console.log(obj1.a) // 2 ,此时的 this 上下文还在 obj1内,若是 obj1.foo 先保存当做引用再执行传参,则上下文为 window obj1.foo.call(obj2,3); // 用 call 强行改变上下文为 obj2内 console.log(obj2.a); // 3 var bar = new obj1.foo(4); // 这里产生了一个实例 console.log(obj1.a); // 2 console.log(bar.a); // 4; new的绑定比隐式和显式绑定优先级更高 function fn(){ alert(a); var a = 200; alert(a); } fn(); // undefined / 200 ; 涉及变量提升 alert(a); // 100 var a; alert(a); // 100 var a = 300; alert(a); // 300 var obj1= { name:'obj1', fn:function(){ console.log(this.name); } }; var obj2 = {name:'obj2'}; var obj3 = {name:'obj3'}; // 这道题主要涉及的是 this 指向的问题.. obj1.fn(); // obj1 var newFn = obj1.fn; newFn(); // undefined, this 指向 window newFn.call(obj2);// obj2, this 指向 obj2 obj3.fn = newFn; /* ƒ (){ console.log(this.name); } */ obj3.fn(); // 这里指向的是 obj3 .所以输出 obj3 // 这道题来作为笔试题很绕,因为要回答的答案很多(脑海构思)..反正我是遇到了.. // 这道题主要考核的是对原型链继承这块的理解 function Parent(){ this.a = 1; this.b = [1,2,this.a]; this.c = {demo:5}; this.show = function(){ console.log(this.a + '' + this.c.demo + ':' + this.b) } } function Child(){ this.a = 2; this.change = function(){ this.b.push(this.a); this.a = this.b.length; this.c.demo = this.a++; } } Child.prototype = new Parent(); var parent = new Parent(); var child1 = new Child(); var child2 = new Child(); child1.a = 11; child2.a = 12; // 这前面几个还算简单,继续看下去 parent.show(); // 15:1,2,1 // 因为 Child 自身没有 show 的方法,所以往原型链的上游找; // 找到父类的,this 因为没更改,所以输出结果如下 child1.show(); // 115:1,2,1 child2.show(); // 125:1,2,1 child1.change(); // 改变一些数据,没有输出 child2.change(); // +1 parent.show(); // 15:1,2,1 child1.show(); // 55:1,2,1,11,12 child2.show(); // 65:1,2,1,11,12 // 这道题也很绕,函数递归调用的 function test(a,b){ console.log(b); return { test:function(c){ return test(c,a); } }; // 这道题的理解,拆成这样就好理解了 /*function test(a,b){ console.log("a:"+a,"b:"+b); return { test:function(c){ console.log("a:"+a,"b:"+b,"c"+c); return test(c,a); } } }*/ var a = test(100); // undefined, 这个是不言而喻的; a.test(200); // 100; a.test(300); // 100; var b = test(101).test(201).test(301); // undefined/101/201 var c = test(102).test(202); // undefined / 102 c.test(302); // 202

Q:有字符串 var test='abc345efgabcab'; 请根据提示实现对应要求

去掉字符串中的 a,b,c 字符 ,形成结果'345efg'; test.replace(/[abc]/g,''); // "345efg" 将字符串的数字用括号括起来, 形成结果: abc[3][4][5]efg....' test.replace(/\d/g,'[$&]'); // "abc[3][4][5]efgabcab" // 若是有分组则按照$1, $2, $3的形式进行引用,而 $& 则表示的是整个正则表达式匹配的内容。 将字符串中的每个数字的值分别乘以2,输出:'abc6810....' var temp = test.split('').map(function(item){ return /^\d$/.test(item) ? item * 2 : item; }).join(''); // "abc6810efgabcab"

Q: 使用不少于三种方式替换文本"dream"改成"package",提供字符串"I have a dream";

正则替换 // 这是最简单的代码量了.. var str = "I have a dream"; str.replace(/dream/g,"package"); // 不用正则也可以直接字符串替换 str.replace("dream","package") 数组遍历更改 // 很直白的大脑回路 var str = "I have a dream"; str.split(" ").map(function(item){ return item === "dream" ? item = "package":item; }).join(" "); 数组查询切割法 var str = "I have a dream"; var tempArr = str.split(" "); // ["I", "have", "a", "dream"] var removeIndex = tempArr.indexOf('dream'); // 3 tempArr.splice(removeIndex,1,"package"); var transStr = tempArr.join(" "); // "I have a package";

这类东东弄成数组还是挺好弄的

var str = "I have a dream";

Q: 还有一道题目是涉及事件循环,执行优先权的..

就是 macrotask和microtask 相关的, 具体记不起来了..那时候给了答案虽然对了.

要说出所以然,给秀了一脸..回来找了下相关的资料;

JavaScript 运行机制详解:再谈Event Loop深入理解事件循环和异步流程控制所有你需要知道的关于完全理解 Node.js 事件循环及其度量

Q: 你对基础算法这块掌握的如何....

来,这纸给你,写个快排试试...

// 快排的大体思路是这样的, // 找个中位值,从原数组切割出来, // 剩下的作为两个数组,每次都去比较; // 直到递归的结果出来, 平均复杂度O(nlog n) function quickSort(arr) { //如果数组长度<=1,则直接返回 if (arr.length <= 1) { return arr; } // 中间位(基准)取长度的一半向下取整 var pivotIndex = Math.floor(arr.length / 2); //把中间位从原数组切割出来, splice 会改变原数组!!!! var pivot = arr.splice(pivotIndex, 1)[0]; //定义两个空数组来存放比对后的值 var left = []; var right = []; //比基准小的放在left,比基准大的放在right for (var i = 0 , j = arr.length; i < j; i++) { if (arr[i] <= pivot) { left.push(arr[i]); } else { right.push(arr[i]); } } //递归下去 arr = [ left , pivot , right] // 怎么个递归法,就是比对后的数组还是会重复之前的取基准再切开比较..直到最后没有可以切了 return quickSort(left).concat([pivot], quickSort(right)); }

Q: 写一个二分法查找

// 二分法跟快排的思路差不多,对半比较 function binSearch(target,arr,start,end) { var start = start || 0; // 允许从什么位置开始,下标 var end = end || arr.length-1; // 什么位置结束,下标 var mid = parseInt(start+(end-start)/2); // 中位下标 if(target==arr[mid]){ return mid; // 找到直接返回下标 }else if(target>arr[mid]){ //目标值若是大于中位值,则下标往前走一位 return binSearch(target,arr,mid+1,end); }else{ 若是目标值小于中位值,则下标往后退一位 return binSearch(target,arr,start,mid-1); } return -1; // 没有找到 } // binSearch(5,[1,2,3,4,5,6,7,8]) => 4

这类的文章很多,有兴趣的可以阅读下面的一些文章

传送门:

<<十大经典排序算法总结(JavaScript描述>>JavaScript数据结构和算法javascript 常见排序算法

Q: 设计模式你了解多少?

Javascript常用的设计模式详解js-design-pattern

Q: 思维拓展题: 你有两个玻璃球,有个100米的高楼,求玻璃球在哪个楼层扔下会碎(用的次数最少);

问题的要点: 玻璃球碎(有限个数) ,确定楼层数 , 最少次数 => 就是求最优的公式

在这道题上给秀的一脸,我的第一次的思路

先折半,就变成[1-50][51-100], 那就是 1+50 = 51次 ...

面试大佬说,你用了快排的思路就肯定不是最优的..

憋了许久,想到开平方 , 这样的话,最多只要20次

然后又说给我三个球,在1000米的高楼,判断多少次...但是根据我上面的话,

开立方, , 那最多不超过30次;

至于第一次丢球的位置如何确定, 就是开平之后的值作为一个区间.

若 N 个球和 M 米的大厦...第一次丢球的高度区间就是这个了

面试大佬说这个还可以...那就暂且告一段落

...回来用万能的搜索引擎找了下..最优方案+最少次数需要考虑的东西很多,没那么简单

传送门: 知乎有人讨论了这个问题;

但是高数还老师了..这种帖子看的一脸懵逼....抽空再好好研究下


Q: 你对优化这块了解多少?

大体常见的手段了解.

比如从客户端着手的:

压缩代码(JS/CSS),压缩图片合并一些小图片(css sprite)若是打包的代码尽可能切割成多个 chunk,减少单一 chunk过大静态文件采用 cdn 引入HTTP的缓存头使用的合理减小第三方库的依赖对于代码应该考虑性能来编写,比如使用requestAnimationFrame绘制动画,尽可能减少页面重绘(DOM 改变)渐进升级,引入preload这些预加载资源看情况用service worker来缓存资源(比如移动端打算搞 PWA)

比如从服务端着手:

带宽,域名解析, 多域名解析等页面做服务端渲染,减小对浏览器的依赖(不用客户端解析)渐进升级,比如引入 HTTP2(多路复用,头部压缩这些可以明显加快加载速度)

当然,这是这些都是很片面的点到...实际工作中去开展要复杂的多;

比如我们要多个维度去考虑的话,要去优化 DOM 的绘制时间,资源的加载时间,域名解析这些;

要全面的优化一个项目是一个大工程...


Q: MySQL有哪些索引类型? 索引的数据结构储存方式? MySQL和 MongoDB的差异

MySQL索引类型:

普通索引: 就普通的类型唯一索引: 代表索引的值唯一不重复(允许有空值),相对于上面多了个UNIQUE主键索引:(创建表的跟随创建,唯一索引,不允许有空值)组合索引(就是将多个字段都建立到一个索引)

索引有利有弊,用的好加快查询速度,滥用索引会造成大量磁盘空间占用,维护性也会增多;索引不会包含null的列;

索引的数据结构储存方式,我只简单了解过B-Tree

至于MySQL 和 MongoDB的差异;

前者是关系型数据库, 后者非关系型数据库(数据是以文档的方式储存,值为 key-value);

MySQL应用层面很广,有事务系统这些,链表查询这些都很方便.经常作为很多系统的主力数据库

而MongoDB作为NoSQL,虽然有些层面不如 MySQL,但是应用层面也挺广,比如结合前端做一些用户的概要信息的维护,一些缓存信息的维护.

em....后端了解不多,也能点到即止....大学的时候学过一些..都差不多还给老师....



结语

还有一些题目记不起来了,就没辙了...还有一些题目是看你个人发挥的,没法写,比如

Q: 让你来为公司的一个项目做技术选型,你会怎么做,为什么?Q: MVVM 和 MVC 的差异? 听说过 MVP?Q: React,Angular,Vue的比较?Q: 说说你对 VNode的理解,diff的过程;Q: Vue-Router的两种模式主要依赖什么实现的Q: 小程序以及React Native的差异..等等

有些问题回答的一团糟(自我感觉), 我一直在思考,什么叫系统化学习;

我这种的感觉更多的给人叫做"野路子",折腾这折腾那,但是又没有很深入一些知识点;

但面试的过程中磕磕碰碰才能发现自身的很多不足和需要去努力的方向.

此篇文章暂未停更,随时会更新,因为作为技术渣的我还木有找到工作;

在找到工作之前会一直更新面试碰到问到的问题;

有不对之处请留言,会及时跟进修正,谢谢各位大佬

作者:CRPER 链接:https://juejin.im/post/5a998991f265da237f1dbdf9 来源:掘金 著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。
转载请注明原文地址: https://www.6miu.com/read-2614779.html

最新回复(0)