年底研发组解散失业, 选择回去学车了,也顺利拿到了驾照,最近回归大深圳,开始踏上漫漫的找工做之路。javascript
拉勾上吊一百年不匹配, BOSS直聘日夜没反应。css
题目范围涵盖我最近遇到的笔试题和面谈的(CSS/JS/HTTP/Node/Hybrid/Vue/NG/React)html
emm,这里不列举哪些公司了, 如果你完整的阅读一遍,相信你有很多的收获,谢谢阅读前端
offer
,还有一些后续不清楚的font-size
,font-weight
,line-height
,color
,cursor
等display
,margin
、border
、padding
、height
等更加全面的能够到引擎找vue
input
,span
,a
,img
以及display:inline
的元素p
,div
,header
,footer
,aside
,article
,ul
以及display:block
这些br
,hr
<!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>
复制代码
box-sizing
经常使用的属性有哪些? 分别有啥做用?box-sizing
有两个值:content-box(W3C标准盒模型)
,border-box(怪异模型)
,html5
这个css 主要是改变盒子模型大小的计算形式java
可能有人会问padding-box
,这个以前只有 Firefox 标准实现了,目前50+的版本已经废除;node
用一个栗子来距离,一个div
的宽高分别100px
,border
为5px
,padding
为5px
webpack
<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 -->
复制代码
经常使用的通常为三种.clearfix
, clear:both
,overflow:hidden
;nginx
比较好是 .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
避免浮动形成的元素塌陷
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>
复制代码
咱们要考虑两种状况,定宽高和不定宽高的;
方案 N 多种,我记得我很早写过这类的笔记
传送门:网页元素居中攻略记
!important
> 行内样式 > id
> class
> tag
样式权重能够叠加, 好比 id>class
简言之:就是不滥用标签(好比 DIV)/随意嵌套(好比 span>div) ,
类的命名要合理, 利于浏览器解析乃至引擎收录,也利于团队协做和维护
七种数据类型
(ES6以前)其中5种为基本类型:string
,number
,boolean
,null
,undefined
,
ES6出来的Symbol
也是原始数据类型 ,表示独一无二的值
Object
为引用类型(范围挺大),也包括数组、函数,
null
和undefined
的差别大致说一下,想要知其因此然请引擎搜索
相同点:
if
判断语句中,值都默认为 false
差别:
null
转为数字类型值为0,而undefined
转为数字类型为 NaN(Not a Number)
undefined
是表明调用一个值而该值却没有赋值,这时候默认则为undefined
null
是一个很特殊的对象,最为常见的一个用法就是做为参数传入(说明该参数不是对象)null
的变量或者对象会被内存收集器回收document.getElementById/ClassName/Name/TagName 等,或者 querySelector
)// example
// get Node
var element = document.querySelector('#test');
// 追加
element.appendChild(Node);
// 删除
element.removeChild(Node);
// 查找
element.nextSibling // 获取元素以后的兄弟节点 , 会拿到注释文本,空白符这些
element.nextElementSibling // 等同, 获取标签(不会拿到注释文本这些)
element.previousSibling // 和上面同理,往前找兄弟节点
element.previousElementSibling
// 改动,好比 属性这些
element.setAttribute(name, value); // 增长属性
element.removeAttribute(attrName); //删除属性
// 来一个简易的练习题,随便一个网页追加插入一块DOM(非覆盖:不能 innerHTML);
/* <div id="test"> <span>Hello, World</span> </div> */
// 以上面的例子为例
var test = document.createElement('div'); // 建立一个块级元素
test.setAttribute("id","test"); // 设置其id 属性
var span = document.createElement('span'); // 建立一个 span
span.innerText = "Hello,world"; // 插入 span 的文本内容
test.appendChild(span); // 组合节点
element.appendChild(test); //追加到某个节点区域
复制代码
hasOwnProperty
,这个更多的是用来区分自身属性和原型链上的属性。
DOM
添加捕获和冒泡的两种写法的事件点击,谁先执行?分状况分析:
node.addEventListener('event',callback,bubble or capture)
; 谁先调用谁先执行stackoverflow 有相关的探讨:
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 则是传递值
复制代码
更为详细的能够阅读此处;
这里主要考察了闭包,函数表达式以及 IIFE(当即执行表达式)
var add = (function() {
// 声明一变量,因为下面 return因此变量只会声明一次
var count = 0;
return function() {
return console.log(count++);
};
})();
add(); // 0
add(); // 1
add(); // 2
复制代码
['1','2','3'].map(parseInt); // [1,NaN,NaN]
// 刨析
// map有三个参数:数组元素,元素索引,原数组自己
// parseInt有两个参数,元素自己以及进制
// 理清了这两个就好办了,
// ['1','2','3'].map(parseInt); 等于以下
['1','2','3'].map(function(item,index,array){
return parseInt(item,index); // 是否是一目了然
});
// parseInt("1",0); => 1
// parseInt("2",1); => NaN
// parseInt("3",2); => NaN
复制代码
WebSocket、localstorge、cookies
均可以。
要考虑浏览器无痕模式的话用WebSocket
会更好,否则功能基本失效或者报错。
最多见的就是轮询XHR
window
对象是指浏览器打开的窗口。
document
对象是HTML 文档对象的一个只读引用,window
对象的一个属性。
我这里用的是结合 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"]
复制代码
对于数组去重的,有兴趣的能够看下我这篇水文:
[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?
[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
// 如果要完善的话,要考虑传入的是非数组,
//传入字符串的时候要判断,而后切割为数组..
// 都要考虑进去代码量不短
复制代码
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]
复制代码
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;
})
复制代码
回文字符串
就是正序倒序都是同样的;
同字母异序字符串
字符串都同样,可是位置可能不必定同样,好比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('')
}
复制代码
String.trim()
方法;// 原生是有 trim()方法的.咱们要模拟一个;
String.prototype.emuTrim = function(){
// 这条正则很好理解,就是把头部尾部多余的空格字符去除
return this.replace(/(^\s*)|(\s*$)/g,'');
}
' fsaf fsdaf f safl lllll '.emuTrim(); //"fsaf fsdaf f safl lllll"
复制代码
for(var i=0;i<10;i++){
// TODO
}
复制代码
// 这道题涉及到做用域
for(var i=0;i<10;i++){
setTimeout((function(i){
return function(){
console.log(i);
}
})(i),1000);
}
复制代码
如果用到 ES6,那简直不能再简便了
for(let i=0;i<10;i++){
setTimeout(function(){
console.log(i);
},1000);
}
复制代码
浅拷贝就是把属于源对象的值都复制一遍到新的对象,不会开辟二者独立的内存区域;
深度拷贝则是完彻底全两个独立的内存区域,互不干扰
// 这个 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');
};
复制代码
简言之:谁调用指向谁,运行时的上下文肯定,而非定义的时候就肯定;
强行绑定 this
的话,能够用 call
,apply
,bind
,箭头函数来来改变this
的指向
这类的文章太多,自行搜索吧。
Q: 看到你说到 bind
,能用 JS简单的模拟个么?
Function.prototype.emulateBind = function (context) {
var self = this;
return function () {
return self.apply(context);
}
}
复制代码
这个实现很粗糙,更为详细全面,考虑周全的(好比参数的处理什么的),自行谷歌.
做用域就是有它自身的上下文区域(好比函数内),内部会有变量声明提高,函数声明提高这些;
函数声明提高优于变量声明提高..
做用域有全局做用域和块级做用域(局部,好比用 let 或者单纯花括号的);
做用域会影响this
的指向
坐等补充,我回答的时候,面试大佬只是 嗯..恩,恩,也不知道具体如何
我通常用这三种,cors
,nginx反向代理
,jsonp
jsonp
: 单纯的 get 一些数据,局限性很大,就是利用script标签的src属性来实现跨域。nginx 反向代理
: 主要就是用了nginx.conf
内的proxy_pass http://xxx.xxx.xxx
,会把全部请求代理到那个域名,有利也有弊吧..cors
的话,可控性较强,须要先后端都设置,兼容性 IE10+ ,好比
Q: 对于想携带一些鉴权信息跨域如何走起?好比cookie
!
须要配置下 header Access-Control-Allow-Credentials:true
,具体用法看下面的nginx
demo
固然cros
的配置不只仅这些,还有其余一些,具体引擎吧,.
如果咱们要用 nginx
或者 express
配置cors
应该怎么搞起? 来个简易版本的
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;
}
复制代码
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域名message
事件,获取传递的消息这货的兼容性没那么好,并且没考虑周全下容易遭受 CSRF
攻击
XSS
和 CSRF
如何防范这里就不说概念性的东西了
XSS的防范
<>
这些形成代码直接运行的的标签..轮询或者正则替换
cookie
,设置为http-only
,避免客户端的篡改CSRF的防范通常这几种
HTTP Referer
字段,判断请求来源token
加密解密,这种是目前很经常使用的手段了,任何防范都有代价的,好比验证码形成的体验很差,token
滥用形成的性能问题,轮询替换形成的响应时间等
cookie
,sessionStorage
,localStorage
的差别..cookie
: 大小4KB 左右,跟随请求(请求头),会占用带宽资源,可是如果用来判断用户是否在线这些挺方便sessionStorage
和localStorage
大同小异,大小看浏览器支持,通常为5MB,数据只保留在本地,不参与服务端交互.
sessionStorage
的生存周期只限于会话中,关闭了储存的数据就没了.localStorage
则保留在本地,没有人为清除会一直保留javascript
的原型链你怎么理解?原型链算是 JS 内一种独有的机制,
全部对象都有一个内置[[proto]]
指向建立它的原型对象(prototype
)
原型链的基本用来实现继承用的
javascript
里面的继承怎么实现,如何避免原型链上面的对象共享我在写的时候,用了两种,一个是 ES5和 ES6的方案
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(); } */
复制代码
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')
复制代码
class
/import
/export
/extends
)let
,const
async
/await
Array.inclueds
/String.padStart|String.padEnd
/Object.assign
let
会产生块级做用域,不会形成变量提高,没法从新声明(但能够从新赋值);const
async
和await
的用途?promise
的异步变成同步运行成了可能,await
能够等到 promise
执行完毕this
指向谁?确定不少小伙伴会说指向局部方法内!!答案是错误的,
箭头函数所改变的并不是把 this 局部化,而是彻底不把 this 绑定到里面去;
就是 this 是取自外部的上下级做用域(可是又不是常规 function
的语法糖)..
由于箭头函数里并不支持 var self = this
或者 .bind(this)
这样的写法。
静态方法是ES6以后才有这么个玩意,有这么些特色
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');
复制代码
私有变量能够用WeakMap
模拟,也能用语义化的下划线,亦或者symbol
,
因此回来只是找了下相关的资料,发现有一个比较好的模拟方案,就是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 };
}
}
复制代码
固然你也能够用Symbol
来实现一个私有变量,这也是一个好法子
Promise
和ajax
没有半毛钱直接关系.promise
只是为了解决"回调地狱"而诞生的;
平时结合 ajax
是为了更好的梳理和控制流程,这里咱们简单梳理下..
Promise
有三种状态,Pending/resolve()/reject();
一些须要注意的小点,以下
Pending
转为另外两种之一的状态时候,状态不可在改变..Promise
的 then
为异步.而(new Promise()
)构造函数内为同步Promise
的catch
不能捕获任意状况的错误(好比 then
里面的setTimout
内手动抛出一个Error
)Promise
的then
返回Promise.reject()
会中断链式调用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
复制代码
如下这段代码的运行结果是什么?
var test = new Promise((resolve,reject)=>{
resolve();
});
test
.then(data => {
// promise start
console.log('promise first then : ', data);
return Promise.resolve(1); // p1
})
.then(data => {
// promise p1
console.log('get parent(p1) resolve data : ', data);
return Promise.reject(new Error('哎呀,中断了,你能奈我何!')); // p2
})
.then(data => {
// promise p2
console.log('result of p2: ', data);
return Promise.resolve(3); // p3
})
.catch(err => {
console.log('err: ', err);
return false;
});
// promise first then : undefined
// get parent(p1) resolve data : 1
// err: Error: 哎呀,中断了,你能奈我何!
// 这里在 then 返回 Promise.reject()的时候已经中断了链式调用.直接给 catch捕获到
复制代码
别急,假如你无论有没有捕获到错误,最后再执行一个回调函数如何实现?
这里说的就是相似try..catch..finally
,给Promise
实现一个 finally;
// finally比较好加,按照如今社区的讨论,finally的特色以下:
// url : https://www.v2ex.com/t/205715
//1. 不接收任何参数,原来的value或者Error在finally里是收不到的
//2. 处理后不影响原Promise的状态,该reject仍是reject,该resolve仍是resolve
//3. 不影响Promise向后传递的传,resolve状态仍是传递原来的value,reject状态仍是传递原来的Error
Promise.prototype.finally = function (callback) {
let P = this.constructor; // 这里拿到的是 Promise 的构造函数
//无论前面的 Promise 是fulfilled仍是rejected,都会执行回调函数callback。
return this.then(
value => P.resolve(callback()).then(() => value),
reason => P.resolve(callback()).then(() => { throw reason })
);
};
// 用法很简单,就是能够传入一个回调函数..
// https://developers.google.com/web/updates/2017/10/promise-finally
// 这个 url 中说了 node 及 chrome 的哪些版本已经实现了 finally 及用法
// ES 2018已经把 finally 追加到 promise 的原型链中..
复制代码
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>>,这个是网络圣经,很厚,我只看了一丢丢..
对于这类的问题我也只能大致点了下,毕竟不是专攻网络这块的,
OSI
七层涵盖:物理层,数据链路层,网络层,传输层,会话层,表示层,应用层;
五层模型就是"会话,表示,应用层"同为一层;
Q: DNS
的大致的执行流程了解么,属于哪一个层级?工做在哪一个层级?
DNS
属于应用层协议, 至于TCP/UDP
哪一层上面跑,看状况 , 大致的执行流程是这样的;
DNS 默认端口是53,走 UDP
hosts
文件(好比你写了映射关系优先寻找)DNS 的解析的几个记录类型须要了解:
A
: 域名直接到 IPCNAME
: 能够多个域名映射到一个主机,相似在 Github Page
就用 CNAME
指向MX
: 邮件交换记录,用的很少,通常搭建邮件服务器才会用到NS
: 解析服务记录,能够设置权重,指定谁解析TTL
: 就是生存时间(也叫缓存时间),通常的域名解析商都有默认值,也能够人为设置TXT
: 通常指某个主机名或域名的说明回来我找下相关的资料,有兴趣的能够深刻了解下,传送门以下:
我只是粗浅的回答了下,
HTTP
相对于 HTTPS
来讲,速度较快且开销较小(没有 SSL/TSL) 对接,默认是80端口;
HTTP
容易遭受域名劫持,而HTTPS
相对来讲就较为安全(加密),默认端口为443。
HTTP
是明文跑在 TCP
上.而HTTPS
跑在SSL/TLS
应用层之下,TCP
上的
Q: 那么 HTTPS
中的TLS/SSL
是如何保护数据的,
通常有两种形式,非对称加密,生成公钥和私钥,私钥丢服务器,公钥每次请求去比对验证;
更严谨的采用 CA(Certificate Authority),给密钥签名,.
Q: 你说到对称加密和非对称加密,能说说整个流程如何运转的么(HTTPS)
懂得真心很少,回来找了下相关资料,有兴趣能够点击看看;
Q: SPDY
据说过么.什么来的?
谷歌推行一种协议(HTTP
之下SSL之上[TCP]),能够算是HTTP2的前身,有这么些优势
而这些优势基本 HTTP2也继承下来了..
Q: 你对 HTTP 的状态吗了解多少,
这里列举一丢丢常见的..
If-Modified-Since or If-Match
去比对服务器的资源,缓存)Q: HTTP的请求报文是怎么样的,能大致的说下么?
HTTP 的请求报文 = 请求行 + 请求头 + 请求体;
accept
,content-type
,user-agent
这类值键对,服务端能够直接读取的想深刻了解的具体引擎搜索
Q: 请求报文知道,那你说说cookie
是如何跟随请求的?
Cookie 就是保存在 HTTP 协议的请求或者应答头部(Cookie 是由服务端生成),这样一路漂泊,
Q: Cookie 隔离是什么,如何作;
cookie 隔离就是下降
header
的数据包含,以达到加快访问速度的目的
方案: 静态资源丢 CDN或者非主域来加载
Last-Modified
:
If-Modified-Since
的时间,没变更则使用本地的(状态304)Expires(过时时间:缓存的载止时间)
,跟随请求一块儿发出..资源没过时拿本地,不然从新请求Cache-control
是 HTTP1.1
的东西,判断资源过时结合max-age
来替代Expires[http 1.0]
Etag
:
If-None-Match
,没有改动依旧拿缓存(304)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"); // false
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); // undefined
var a;
alert(a); // undefined
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
复制代码
test.replace(/[abc]/g,''); // "345efg"
复制代码
test.replace(/\d/g,'[$&]'); // "abc[3][4][5]efgabcab"
// 如果有分组则按照$1, $2, $3的形式进行引用,而 $& 则表示的是整个正则表达式匹配的内容。
复制代码
var temp = test.split('').map(function(item){
return /^\d$/.test(item) ? item * 2 : item;
}).join('');
// "abc6810efgabcab"
复制代码
"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";
复制代码
这类东东弄成数组仍是挺好弄的
这个是留言区小伙伴提供的方法..大同小异,以下;
// 源代码
// 字符串也有数组的 slice 以及 concat 的方法..思路和数组差很少
var str = 'I haved a dream';
str.indexOf('dream') !== -1 ? str.slice(0,str.indexOf('dream')).concat('package'):str;
复制代码
就是 macrotask
和microtask
相关的, 具体记不起来了,那时候给了答案虽然对了。
要说出因此然,给秀了一脸,回来找了下相关的资料;
来,这纸给你,写个快排试试,
// 快排的大致思路是这样的,
// 找个中位值,从原数组切割出来,
// 剩下的做为两个数组,每次都去比较;
// 直到递归的结果出来, 平均复杂度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; // 什么位置结束,下标
start >= end ? -1 : ''; // 没有找到,直接返回-1
var mid = Math.floor((start + end) / 2); // 中位下标
if (target == arr[mid]) {
return mid; // 找到直接返回下标
} else if (target > arr[mid]) {
//目标值如果大于中位值,则下标往前走一位
return binSearch(target, arr, start, mid - 1);
} else {
//如果目标值小于中位值,则下标日后退一位
return binSearch(target, arr, mid + 1, end);
}
}
// binSearch(5,[1,2,3,4,5,6,7,8]) => 4
// 无序的数组则须要先排序好数组,不然会堆栈溢出(死循环)
复制代码
这类的文章不少,有兴趣的能够阅读下面的一些文章
传送门:
问题的要点: 玻璃球碎(有限个数) ,肯定楼层数 , 最少次数 => 就是求最优的公式
在这道题上给秀的一脸,个人第一次的思路
先折半,就变成[1-50][51-100], 那就是 1+50 = 51次 ,
面试大佬说,你用了快排的思路就确定不是最优的..
憋了许久,想到开平方
, 这样的话,最多只要20次
而后又说给我三个球,在1000米的高楼,判断多少次,可是根据我上面的话,
开立方,
, 那最多不超过30次;
至于第一次丢球的位置如何肯定, 就是开平以后的值做为一个区间.
若 N 个球和 M 米的大厦,第一次丢球的高度区间就是这个了
面试大佬说这个还能够,那就暂且告一段落
,回来用万能的搜索引擎找了下..最优方案+最少次数须要考虑的东西不少,没那么简单
传送门: 知乎有人讨论了这个问题;
可是高数还老师了..这种帖子看的一脸懵逼,.抽空再好好研究下
大致常见的手段了解.
css sprite
)chunk
,减小单一 chunk
过大requestAnimationFrame
绘制动画,尽量减小页面重绘(DOM 改变)preload
这些预加载资源service worker
来缓存资源(好比移动端打算搞 PWA)固然,这是这些都是很片面的点到,实际工做中去开展要复杂的多;
好比咱们要多个维度去考虑的话,要去优化 DOM 的绘制时间,资源的加载时间,域名解析这些;
要全面的优化一个项目是一个大工程,
MySQL索引类型:
UNIQUE
索引有利有弊,用的好加快查询速度,滥用索引会形成大量磁盘空间占用,维护性也会增多; 索引不会包含null
的列;
索引的数据结构储存方式,我只简单了解过B-Tree
至于MySQL 和 MongoDB的差别;
前者是关系型数据库, 后者非关系型数据库(数据是以文档的方式储存,值为 key-value
);
MySQL
应用层面很广,有事务系统这些,链表查询这些都很方便.常常做为不少系统的主力数据库
而MongoDB
做为NoSQL
,虽然有些层面不如 MySQL
,可是应用层面也挺广, 好比结合前端作一些用户的概要信息的维护,一些缓存信息的维护.
em,.后端了解很少,也能点到即止,.大学的时候学过一些..都差很少还给老师,.
给定一个时间段和步长,枚举该时间段内步长的划分
例如:时间段
3:00-5:00
,步长为20
分钟那么返回的数组为
['3:00-3:20', '3:20-3:40',.]
等
这类问题,通常都要先梳理好思路再来写;
// 这个东东个人小伙伴也写出来了.个人是在它的解答方式上加以注释和对参数的判断作了考虑
// 他的解法方案在他的 github 上 https://github.com/lyh2668/blog/issues/1 , by lyh2668
// 方便一些小伙伴的理解,如下代码包含ES6的姿式(参数默认值,剪头函数)
let inputDateRange = (date, step = 30, separator = '-') => {
let startTime, endTime; // 开始时间和结束时间
if (Object.prototype.toString.call(date) === '[object String]') {
date = date.trim(); // 去除两边的空格
var tempDate = '';
if (separator) {
tempDate = date.split(separator);
} else {
if (date.indexOf('-') !== -1) {
tempDate = date.split('-');
} else if (date.indexOf('~')) {
tempDate = date.split('~');
} else {
console.log('您传入的也许不是一个时间段!!!');
}
}
startTime = time2min(tempDate[0]); // 传入的开始时间
endTime = time2min(tempDate[1]); //传入的结束时间
} else if (Object.prototype.toString.call(date) === '[object Array]') {
if (date.length === 2) {
startTime = time2min(date[0]); // 传入的开始时间
endTime = time2min(date[1]); //传入的结束时间
}
} else {
console.log('您传入的也许不是一个时间段!!!');
}
// 传入的 step 是否为数字,不然截图数字部分转化
// 为何和 NaN 比较(自身不等性),如果传入的连正则都无法识别,那只能给默认值了
Object.prototype.toString.call(step) === '[object Number]'
? (step = parseInt(step, 10))
: parseInt(step.replace(/[W\s\b]/g, ''), 10) === NaN
? (step = parseInt(step.replace(/[W\s\b]/g, ''), 10))
: (step = 30);
// 如果开始时间大于结束时间则结束时间日后追加一天
startTime > endTime ? (endTime += 24 * 60) : '';
let transformDate = []; // 储存转换后的数组,时间分段
// 开始遍历判断,用 while
while (startTime < endTime) {
// 若是开始时间+步长大于结束时间,则这个分段结束,不然结束时间是步长递增
let right = startTime + step > endTime ? endTime : startTime + step;
transformDate.push(`${min2time(startTime)}-${min2time(right)}`);
startTime += step; // 步长递增
}
return transformDate;
};
// 时间转化为分钟
let time2min = time => {
// 获取切割的
time.indexOf(':') ? (time = time.trim().split(':')) : '';
return time[0] * 60 + parseInt(time[1]); // 返回转化的分钟
};
// 分钟转会字符串时间
let min2time = minutes => {
let hour = parseInt(minutes / 60); // 返回多少小时
let minute = minutes - hour * 60; // 扣除小时后剩余的分钟数
hour >= 24 ? (hour = hour - 24) : ''; // 如果大于等于24小时须要扣除一天获得所剩下的小时
minute < 10 ? (minute = '0' + minute) : ''; // 小于10的都要补零
hour < 10 ? (hour = '0' + hour) : ''; // 小于10的都要补零
return `${hour}:${minute}`;
};
// test ,支持字符串传入时间段
inputDateRange('3:00-5:00','20d'); // ["03:00-03:20", "03:20-03:40", "03:40-04:00", "04:00-04:20", "04:20-04:40", "04:40-05:00"]
// 亦或者数组传入
inputDateRange(['3:00','5:00'],'45df.3d'); // ["03:00-03:45", "03:45-04:30", "04:30-05:00"]
// step 支持数字亦或者带特殊字符的数字
inputDateRange(['6:00','8:00'],'55df.3d'); // ["06:00-06:55", "06:55-07:50", "07:50-08:00"]
inputDateRange('3:00-5:00',60); // ["03:00-04:00", "04:00-05:00"]
复制代码
Vue-Router
的两种模式主要依赖什么实现的hash
主要依赖location.hash
来改动 URL,达到不刷新跳转的效果.每次 hash
改变都会触发hashchange
事件(来响应路由的变化,好比页面的更换)history
主要利用了 HTML5
的 history
API 来实现,用pushState
和replaceState
来操做浏览历史记录栈这类的文章好多,三个开发模式的诞生都有先后,不是同时出现的.
传送门:
153=1^3+5^3+3^3
function threeWaterFlower(rangeStart, rangeEnd) {
var temp = [];
rangeStart = rangeStart || 100;
rangeEnd = rangeEnd || 999;
for (var i = rangeStart; i <= rangeEnd; i++) {
var t = i.toString().split('');
Math.pow(t[0], 3) + Math.pow(t[1], 3) + Math.pow(t[2], 3) == i
? temp.push(i)
: '';
}
return temp;
}
threeWaterFlower(100,999); // [153, 370, 371, 407]
threeWaterFlower(); // [153, 370, 371, 407]
复制代码
let manyWaterFlower = (rangeStart = 100, rangeEnd = 999, flower = 3) => {
let temp = [];
for (let i = rangeStart; i <= rangeEnd; i++) {
let t = i
.toString()
.split('')
.map(item => Math.pow(item, flower))
.reduce((cur,next)=> parseInt(cur)+parseInt(next));
let transformT = parseInt(t, 10);
transformT == i ? temp.push(i) : '';
}
return temp;
}
manyWaterFlower(); // [153, 370, 371, 407]
manyWaterFlower(100,10000,4); // [1634, 8208, 9474]
manyWaterFlower(100,10000,5); // [4150, 4151]
复制代码
这种是穷举遍历,如果要快一点呢(考虑的周全一点呢),以及传参范围的矫正
相信小伙伴都看得懂,我已经尽可能注释了..
let manyWaterFlower = (flower = 3,rangeStart, rangeEnd ) => {
let temp = [];// 缓存全部找到的花值
// 这一段就是填充开始循环的范围,处理完毕后转为数字,推荐的开始值
let flowerRecommandStart = Number(
''.padStart(flower, '0').replace(/^(\d{1})/g, '1')
);
let flowerRecommandEnd = Number(''.padStart(flower, '9'));
// 判断是否传入开始值
if (rangeStart) {
rangeStart > flowerRecommandStart
? (rangeStart = flowerRecommandStart)
: rangeStart;
} else {
rangeStart = flowerRecommandStart;
}
// 判断是否有传入结束值
if (rangeEnd) {
rangeEnd > flowerRecommandEnd ? (rangeEnd = flowerRecommandEnd) : rangeEnd;
} else {
rangeEnd = flowerRecommandEnd;
}
// 如果初始值大于结束值
if (rangeStart > rangeEnd) {
rangeEnd = flowerRecommandEnd;
}
for (let i = rangeStart; i <= rangeEnd; i++) {
let t = i
.toString()
.split('')
.map(item => Math.pow(item, flower))
.reduce((cur, next) => parseInt(cur) + parseInt(next));
let transformT = parseInt(t, 10);
transformT == i ? temp.push(i) : '';
}
return temp;
};
console.time('manyWaterFlower');
manyWaterFlower(4)
console.timeEnd('manyWaterFlower');
// VM34013:4 manyWaterFlower: 8.112060546875ms ,这个是跑出来的时间
用上个例子的代码,从100到9999的,咱们跑一下看看
console.time('manyWaterFlower');
manyWaterFlower(100,9999,4)
console.timeEnd('manyWaterFlower');
// VM3135:4 manyWaterFlower: 10.51904296875ms
// 个人 MBP 跑10花直接卡死,跑7花有点久,
console.time('7 flower')
manyWaterFlower(7);
console.timeEnd('7 flower')
// 7 flower: 6489.608154296875ms
// 8 花 CPU 的风扇狂叫,.
console.time('8 flower')
manyWaterFlower(8);
console.timeEnd('8 flower')
// VM644:3 8 flower: 68010.26489257812ms
// 对了咱们尚未考虑数值溢出的问题..由于正整数在 JS 的范围是有限的.
// 有兴趣的小伙伴能够自行完善
复制代码
好比console.log(findNode(['a1', 'b2'], data)) === data.a1.b2
// 请使用递归算法在 TODO 注释后实现经过节点 key 数组寻找 json 对象中的对应值
var data = {
a1: {
b1: 1,
b2: 2,
b3: {
b4: 5
}
},
a2: {
b1: 3,
b2: 4
}
};
function findNode(inPath, inData) {
// TODO
// 判断传入的是不是一个数组
if (Array.isArray(inPath)) {
// 当长度为1的时候寻找该 key 是否有值,有则返回,无则返回-1
if (inPath.length === 1) {
return inData[inPath[0]] ? inData[inPath[0]]: -1;
}else{
return findNode(inPath.slice(1), inData[inPath[0]]);
}
} else{
console.log('您传入的不是一个数组')
}
}
console.log(findNode(['a1', 'b2'], data)); // 2
console.log(findNode(['a1', 'b3','b4'], data)); // 5
复制代码
findNode('a1.b2',data)
?var data = {
a1: {
b1: 1,
b2: 2,
b3: {
b4: 5
}
},
a2: {
b1: 3,
b2: 4
}
};
// 判断格式
function isType(params) {
let type = Object.prototype.toString.call(params);
if (type === '[object String]') {
params = params.split('.');
return params;
}
if (type === '[object Array]') {
return params;
}
}
function findNode(inPath, inData) {
inPath = isType(inPath);
// 判断传入的是不是一个数组
if (Array.isArray(inPath)) {
// 当长度为1的时候寻找该 key 是否有值,有则返回,无则返回-1
if (inPath.length === 1) {
return inData[inPath[0]] ? inData[inPath[0]]: -1;
}else{
return findNode(inPath.slice(1), inData[inPath[0]]);
}
} else {
console.log('您传入的不是一个数组');
}
}
console.log(findNode(['a1', 'b2'], data)); // 2
console.log(findNode('a1.b3.b4', data)); // 5
复制代码
webpack 是一个资源处理工具,它的出现节省了咱们的人力和时间; 能够对资源打包,解析,区分开发模式等等,
常见的优化手段:
dll
happypack
commonChunkPlugin
UglifyJS
bundle chunk
的大小,好比ExtractTextPlugin
tree shaking
目前webpack3/4已经默认集成大致过程是这样的,想了解很细致的能够自行引擎;
props
on
+emit
on.sync
(语法糖)来的event bus
| vuex
Vuex
你怎么理解?vuex
是一个状态管理容器(你也能够理解为全局变量),数据的流向是是单向数据流,
且数据并不具备持久化的特性(默认状况下刷新就重置全部状态);
里面的一些数据乃至方法,能够大体理解为 vue 的一些特性,好比
Vuex | Vue |
---|---|
state | data |
getter | computed |
mutation/actions | methods |
至于单向数据流(全局单例模式)怎么理解
state
只能给mutation(同步操做)
改动, action
只能反馈给mutation
,能够进行异步操做(好比和后端交互拉取数据), state
能触发 render,action
能用dispatch
分发..如图
还有一些题目记不起来了,就没辙了,还有一些题目是看你我的发挥的,无法写,好比
React
,Angular
,Vue
的比较?VNode
的理解,diff
的过程;Vue
的双向绑定如何实现,用了什么模式(订阅模式),大致如何实现的。cmd
/amd
/commonjs
的差别React Native
的差别..等等面试的过程当中磕磕碰碰才能发现自身的不少不足和须要去努力的方向.
有不对之处请留言,会及时跟进修正,谢谢各位大佬
掘金技术征文活动连接: juejin.im/post/5aaf2a…