2019-6-3 11:25:34
面试准备
- html语义化 — (不要全都是div) ===> html5 标签
- 查看成熟ui组件库源码
- github上多星的相关项目 看他们用什么
- 模拟数据mock.js
- 编写易读, 易维护的代码
- 前端构建环境 gulp
- 基础
- 算法
- 项目讲述演练
html5 标签
article - <h1>文本类 块模型</h1> => 类似body结构
aside - 侧边/左边 内容 侧边栏是 aside,aside 不一定是侧边栏。
nav - 导航内容
section - 中间内容
body>header+aside+section+footer
article>header+aside+section+footer
section>article
性能优化
从输入 URL 到页面加载完成,发生了什么?
浏览器进行DNS解析 -> TCP 网络连接 -> HTTP 请求 -> HTTP 响应里返回给客户端 -> 浏览器渲染
- 网络传输性能检测工具——chrome 网上应用商店 pagespeed
- 雪碧图生成插件webpack-spritesmith
配置
知识体系
javascript
- 为什么有的编程规范要求用 void 0 代替 undefined?
因为 JavaScript 的代码 undefined 是一个变量,而并非是一个关键字,这是 JavaScript 语言公认的设计失误之一,所以,我们为了避免无意中被篡改,我建议使用 void 0 来获取 undefined 值。
防止被重写外,还可以减少字节
简约写法
//取整
parseInt(a,10); //Before
Math.floor(a); //Before
a>>0; //Before
~~a; //After
a|0; //After
//四舍五入
Math.round(a); //Before
a+.5|0; //After
//内置值
undefined; //Before
void 0; //After, 快
0[0]; //After, 略慢
//内置值
Infinity;
1/0;
//布尔值短写法
true; //Before
!0; //After
//布尔值短写法
false; //Before
!1; //After
- 字符串有最大长度吗?
最大长度是 2^53 - 1, 指的是 字符串的 UTF16 编码,
- 0.1 + 0.2 不是等于 0.3 么?为什么 JavaScript 里不是这样的?
浮点数运算的精度问题导致等式左右的结果并不是严格相等,而是
相差了个微小的值。
ES6 新增 Number 属性:
- EPSILON: 表示 1 和比最接近 1 且大于 1 的最小 Number 之间的差别
- isInteger(): 用来判断给定的参数是否为整数。
正确的比较方法是使用 JavaScript 提供的最小精度值:
console.log( Math.abs(0.1 + 0.2 - 0.3) <= Number.EPSILON);
- ES6 新加入的 Symbol 是个什么东西?
一种新的原始数据类型Symbol,表示独一无二的值
- 为什么给对象添加的方法能用在基本类型上?
. 运算符提供了装箱操作,它会根据基础类型构造一个临时对象,使得我们能在基础类型上调用对应对象的方法。
JavaScript执行
1. Promise里的代码为什么比setTimeout先执行?
因为 Promise 产生的是 JavaScript 引擎内部的微任务,而 setTimeout 是浏览器 API,它产生宏任务。
setTimeout 把整个代码分割成了 2 个宏观任务,这里不论是 5 秒还是 0 秒,都是一样的。
闭包
闭包是一个绑定了执行环境的函数,这个函数并不是印在书本里的一条简单的表达式,闭包与普通函数的区别是,它携带了执行
的环境,就像人在外星中需要自带吸氧的装备一样,这个函数也带有在程序中生存的环境。
css
CSS 支持一批特定的计算型函数:
- calc()
- max()
- min()
- toggle()
- attr().
Dom
getElementById、getElementsByName、getElementsByTagName、getElementsByClassName,这几个 API 的性能高于 querySelector。
而 getElementsByName、getElementsByTagName、getElementsByClassName 获取的集合并非数组,而是一个能够动态更新的集合。
- 遍历 NodeIterator || TreeWalker
var iterator = document.createNodeIterator(document.body, NodeFilter.SHOW_TEXT | NodeFilter.SHOW_COMMENT, null, false);
var node;
while(node = iterator.nextNode())
{
console.log(node);
}
var walker = document.createTreeWalker(document.body, NodeFilter.SHOW_ELEMENT, null, false)
var node;
while(node = walker.nextNode())
{
if(node.tagName === "p")
node.nextSibling();
console.log(node);
}
链接
链接的家族中有 a 标签、area 标签和 link 标签。今天,我会逐一对它们进行介绍。
link 标签
- 超链接类
-
canonical 型 link => 会提示搜索引擎保留哪一个URL。
-
alternate 型 link …
- 外部资源类
外部资源型 link 标签会被主动下载,并且根据 rel 类型做不同的处理。外部资源型的标签包括:具有 icon 型的 link、预处理类 link、modulepreload 型的 link、
stylesheet、pingback。
浏览器:一个浏览器是如何工作的
- 状态码
- 1xx:临时回应,表示客户端请继续。
- 2xx:请求成功。
200:请求成功。
- 301&302:永久性与临时性跳转。
304:跟客户端缓存没有更新。
- 4xx:客户端请求错误。
403:无权限。
404:表示请求的页面不存在。s
418:It’s a teapot. 这是一个彩蛋,来自 ietf 的一个愚人节玩笑。(超文本咖啡壶控制协议)
- 5xx:服务端请求错误。
500:服务端错误。
503:服务端暂时性错误,可以一会再试。
对我们前端来说,1xx 系列的状态码是非常陌生的,原因是 1xx 的状态被浏览器 http 库直接处理掉了,不会让上层应用知晓。
2xx 系列的状态最熟悉的就是 200,这通常是网页请求成功的标志,也是大家最喜欢的状态码。
3xx 系列比较复杂,301 和 302 两个状态表示当前资源已经被转移,只不过一个是永久性转移,一个是临时性转移。实际上 301 更接近于一种报错,提示客户端下次
别来了。
304 又是一个每个前端必知必会的状态,产生这个状态的前提是:客户端本地已经有缓存的版本,并且在 Request 中告诉了服务端,当服务端通过时间或者 tag,发
现没有更新的时候,就会返回一个不含 body 的 304 状态。
- 请求头
- 响应头
HTTPS 有两个作用,一是确定请求的目标服务端身份,二是保证传输的数据不会被网络中间节点窃听或者篡改。
HTTPS 是使用加密通道来传输 HTTP 的内容。但是 HTTPS 首先与服务端建立一条 TLS 加密通道。TLS 构建于 TCP 协议之上,它实际上是对传输的内容做一次加
密,所以从传输内容上看,HTTPS 跟 HTTP 没有任何区别。
HTTP 2.0 最大的改进有两点,一是支持服务端推送,二是支持 TCP 连接复用。
浏览器CSSOM:如何获取一个元素的准确位置
getComputedStyle(el)
el.getClientRects()
el.getBoundingClientRect()
职位jd分析
gulp
实际演练
- 怎么让双核浏览器优先使用chrome内核
- 网站性能优化方法之一
// <meta>信息告诉浏览器,当前页面要做DNS预解析;https浏览器是默认不开启的(a标签)
<meta http-equiv="x-dns-prefetch-control" content="on" />
// 使用<link>标签来强制对DNS预解析;
<!--如果不确定是http还是https连接的话建议如下写法 -->
<link rel="dns-prefetch" href="//renpengpeng.com" />
- javascript实现异步的方法
什么是异步加载js?
使js文件脱离html解析的瀑布流加载,从而使js可以并行下载。
为什么要异步加载
一般写法将js放在head中,而且默认方式是同步加载,这就会导致在进行js加载的过程中,无法在其加载完成前对后续的内容进行操作,造成页面内阻塞,对用户体验很不友好。
- $(document).ready
- 标签的async=”async”属性 async 属性仅适用于外部脚本(只有在使用 src 属性时)。
- defer属性
async和defer看起来差不多呀?而且经常一起出现!来辨析一下:
(1)如果没有async和defer属性,那么浏览器会立即执行当前的JS脚本,阻塞后面的脚本;
(2)如果有async属性,加载和渲染后续文档的过程和当前JS的加载与执行并行进行(异步),它是乱序执行的,不管你声明的顺序如何,只要它加载完了就会执行;
(3)如果有defer属性,加载后续文档元素的过程和JS的加载是并行进行(异步)的,但是JS的执行在所有元素解析完成之后进行,而且它是按照加载顺序执行脚本的
-
动态创建script
-
原生的javascript 实现异步
1、延迟类型:setTimeout(setInterval也是可以的)、requestAnimationFrame、setImmediate(IE10及以上)
2、监听事件实现的类型:监听new Image加载状态、监听script加载状态、监听iframe加载状态、Message
3、带有异步功能类型 Promise、ajax( XMLHttpRequest、ActiveXObject)、Worker;
常见异步加载(Script DOM Element)
(function() {
var s = document.createElement('script');
s.type = 'text/javascript';
s.async = true;
s.src = 'http://yourdomain.com/script.js';
var x = document.getElementsByTagName('script')[0];
x.parentNode.insertBefore(s, x);
})();
这种加载方式在加载执行完之前会阻止 onload 事件的触发,而现在很多页面的代码都在 onload 时还要执行额外的渲染工作等,所以还是会阻塞部分页面的初始化处理
优化
// onload 时的异步加载
(function() {
function async_load(){
var s = document.createElement('script');
s.type = 'text/javascript';
s.async = true;
s.src = 'http://yourdomain.com/script.js';
var x = document.getElementsByTagName('script')[0];
x.parentNode.insertBefore(s, x);
}
if (window.attachEvent)
window.attachEvent('onload', async_load);
else
window.addEventListener('load', async_load, false);
})();
这和前面的方式差不多,但关键是它不是立即开始异步加载 js ,而是在 onload 时才开始异步加载。这样就解决了阻塞 onload 事件触发的问题。
自我介绍
自我陈述
- 自如谈兴趣(前端方面) 巧妙示实例(提前了解公司的技术栈,优化点,功能如何实现) 适时讨疑问(逐渐回答不上,含蓄地请教对方,讨要点学习资料,不过以后补充了知识再来面试)
- 节奏要适宜(谦虚,平稳地回答) 切忌小聪明(如实回答,不懂就适宜的问)
- 方向要对,过程要细(实现方案要多,细,不怕错,就怕不回答,)
- 胆子要大,心态要和(要有追求新知识的积极,不要自卑,这次发现知识体系薄弱点,不足.下次再来面试)
B/S结构和C/S结构是什么?它们之间有哪些区别和联系?
- B/S结构
B是英文单词“Browser”的首字母,即浏览器的意思;S是英文单词“Server”的首字母,即服务器的意思。B/S就是“Browser/Server”的缩写,即“浏览器/服务器”模式。
B/S结构是随着互联网的发展,web出现后兴起的一种网络结构模式。这种模式统一了客户端,让核心的业务处理在服务端完成。你只需要在自己电脑或手机上安装一个浏览器,就可以通过web Server与数据库进行数据交互
这种“B/S”结构有很多好处,维护和升级方式更简单,客户端是浏览器,基本不需要维护,只需要维护升级服务器端就可以
[外链图片转存失败(img-ctIGU3Zw-1564816566398)(https://pics5.baidu.com/feed/58ee3d6d55fbb2fb1c40b6c3189885a04423dccb.png?token=bdda3bf161ef987975b5026a48d4e81c&s=192A7433836E690B4C54D4DE0300C0B1)]
- C/S结构
“客户端/服务器”模式。
C/S结构是一种软件系统体系结构,也是生活中很常见的。这种结构是将需要处理的业务合理地分配到客户端和服务器端,这样可以大大降低通信成本,但是升级维护相对困难。比如我们手机中安装的微信、qq、王者荣耀等应用程序就是C/S结构。
[外链图片转存失败(img-ONptp6i1-1564816566398)(https://pics4.baidu.com/feed/9358d109b3de9c8267a6389b3453250e18d8433e.png?token=577184c0b3e27d4406e6240800dfe96a&s=98AA5C320B0A4C4B4C7464DE030080B1)]
- B/S结构和C/S结构的区别
- 硬件环境不同,C/S通常是建立在专用的网络上,小范围的网络环境。而B/S是建立在广域网上的,适应范围强,通常有操作系统和浏览器就行;
- C/结构比B/S结构更安全,因为用户群相对固定,对信息的保护更强;
- B/S结构维护升级比较简单,而C/S结构维护升级相对困难;
源码解读-分析
jQuery源码解读-事件分析
慕课网
vue源码
Object.defineProperty(obj, prop, descriptor)
obj
要在其上定义属性的对象。
prop
要定义或修改的属性的名称。
descriptor
将被定义或修改的属性描述符
返回值 被传递给函数的对象。
属性描述符
对象里目前存在的属性描述符有两种主要形式:数据描述符和存取描述符。数据描述符是一个具有值的属性,该值可能是可写的,也可能不是可写的。存取描述符是由getter-setter函数对描述的属性。描述符必须是这两种形式之一;不能同时是两者。
可编辑
configurable: 当且仅当该属性的 configurable 为 true 时,该属性描述符才能够被改变,同时该属性也能从对应的对象上被删除。默认为 false。
可枚举
enumberable: 当且仅当该属性的enumerable为true时,该属性才能够出现在对象的枚举属性中。默认为 false。
数据描述符同时具有以下可选键值:
value: 该属性对应的值。可以是任何有效的 JavaScript 值(数值,对象,函数等)。默认为 undefined。
writable: 当且仅当该属性的writable为true时,value才能被赋值运算符改变。默认为 false。
存取描述符同时具有以下可选键值:
get
一个给属性提供 getter 的方法,如果没有 getter 则为 undefined。当访问该属性时,该方法会被执行,方法执行时没有参数传入,但是会传入this对象(由于继承关系,这里的this并不一定是定义该属性的对象)。
默认为 undefined。
set
一个给属性提供 setter 的方法,如果没有 setter 则为 undefined。当属性值修改时,触发执行该方法。该方法将接受唯一参数,即该属性新的参数值。
默认为 undefined。
var obj = {}
// Object.defineProperty(obj, prop, descriptor)
// obj.o = '123'
// ==
Object.defineProperty(obj, 'o', {
value: '123',
configurable: true,
enumberable: true,
Writable: true
})
// 在对象中添加一个属性与存取描述符的示例
var bValue;
Object.defineProperty(obj, "b", {
get : function(){
console.log('get:' + bValue)
return bValue;
},
set : function(newValue){
console.log('set:' + newValue)
bValue = newValue;
},
enumerable : true,
configurable : true
});
obj.b = 38;
console.log(obj.o)
console.log(obj.b)
// 如果访问者的属性是被继承的,它的 get 和set 方法会在子对象的属性被访问或者修改时被调用。如果这些方法用一个变量存值,该值会被所有对象共享。
// 在 get 和 set 方法中,this 指向某个被访问和修改属性的对象。
function myclass() {
}
Object.defineProperty(myclass.prototype, "x", {
get() {
return this.stored_x;
},
set(x) {
this.stored_x = x;
}
});
var a = new myclass();
var b = new myclass();
a.x = 1;
console.log(b.x); // undefined
// 不像访问者属性,值属性始终在对象自身上设置,而不是一个原型。然而,如果一个不可写的属性被继承,它仍然可以防止修改对象的属性。
myclass.prototype.x = 1;
Object.defineProperty(myclass.prototype, "y", {
writable: false,
value: 1
});
var c = new myclass();
c.x = 2;
console.log(c.x); // 2
console.log(myclass.prototype.x); // 1
c.y = 2; // Ignored, throws in strict mode
console.log(c.y); // 1
console.log(myclass.prototype.y); // 1
双向数据绑定原理分析
数据与视图的绑定与同步,最终体现在对数据的读写处理过程中,也就是 Object.defineProperty() 定义的数据 set、get 函数中。Vue 中对于的函数为 defineReactive
数据响应式原理
Vue 的响应式,核心机制是 观察者模式。
数据是被观察的一方,发生改变时,通知所有的观察者,这样观察者可以做出响应,比如,重新渲染然后更新视图
初始化,在new vue()之后,首先在内部执行了一个初始化方法,它做的就是一些最基础的东西的初始化,比如说初始化生命周期,我们知道有很多生命周期的钩子,还有一些props,还有我们一些数据data的响应化等,其中最重要的是通过object.defineProperty设置getter和setter函数,用来实现响应式以及依赖收集。
在初始化之后调用 m o u n t 来 执 行 挂 载 函 数 , 我 们 知 道 V u e 的 初 始 化 就 是 通 过 mount来执行挂载函数,我们知道Vue的初始化就是通过 mount来执行挂载函数,我们知道Vue的初始化就是通过mount来实现的, m o u n t 其 实 就 是 要 指 定 一 个 挂 载 节 点 , 可 能 会 是 一 个 目 标 节 点 , 也 有 可 能 会 是 一 个 d o m 节 点 , 最 终 就 是 告 诉 我 们 v u e 将 把 那 些 写 好 的 模 板 通 过 编 译 以 后 达 到 更 新 以 后 这 个 最 新 的 东 西 我 到 底 要 显 示 在 什 么 地 方 , 就 是 mount其实就是要指定一个挂载节点,可能会是一个目标节点,也有可能会是一个dom节点,最终就是告诉我们vue将把那些写好的模板通过编译以后达到更新以后这个最新的东西 我到底要显示在什么地方,就是 mount其实就是要指定一个挂载节点,可能会是一个目标节点,也有可能会是一个dom节点,最终就是告诉我们vue将把那些写好的模板通过编译以后达到更新以后这个最新的东西我到底要显示在什么地方,就是mount最终指定的那个目标,然后$mount会启动这个编译器compile.
这个编译器最重要的事情就是对我们的Template里的东西进行一遍扫描,做parse optimize generate这三件事,compile在这个阶段会生成一些渲染函数或者也可以叫更新函数,会生成一颗树,我们叫虚拟节点树,将来在做数据更新的时候,其实我们改变的数据并不是真正的dom操作,而是这个虚拟dom上的数值,当我们准备更新之前我们会做一个diff算法的比较,通过最新的值和之前的老值进行比较,从而计算出我们应该做的最小的dom更新,然后我们才开始执行到这个patch步骤来打补丁做界面更新.
这样儿做的目的是用js里面的计算时间来换dom操作时间,我们知道浏览器的瓶颈在对页面操作这一块儿比较耗时间,Vue的核心在于减少页面渲染的次数和数量,compile除了编译渲染函数之外,还会做一个依赖收集的工作,通过这个依赖收集我们可以知道当页面数据发生变化的时候我应该去更新页面中的那一个dom节点,这也就是将来这个数据发生变化的时候,我们可以通过这个watcher观察者来知道数据发生变化,这时候调用更新渲染函数来打补丁。
- Vue的渲染逻辑——Render函数
三种渲染模式,自定义Render函数、template、el均可以渲染页面
这三种渲染模式最终都是要得到Render函数。只不过用户自定义的Render函数省去了程序分析的过程,等同于处理过的Render函数,而普通的template或者el只是字符串,需要解析成AST,再将AST转化为Render函数。
浏览器的事件循环(Event Loop)机制 ==> Vue 异步更新
上图中,主线程运行的时候,产生堆(heap)和栈(stack),栈中的代码调用各种外部API,它们在"任务队列"中加入各种事件(click,load,done)。只要栈中的代码执行完毕,主线程就会去读取"任务队列",依次执行那些事件所对应的回调函数。
执行栈中的代码(同步任务),总是在读取"任务队列"(异步任务)之前执行。请看下面这个例子。
var req = new XMLHttpRequest();
req.open('GET', url);
req.onload = function (){};
req.onerror = function (){};
req.send();
上面代码中的req.send方法是Ajax操作向服务器发送数据,它是一个异步任务,意味着只有当前脚本的所有代码执行完,系统才会去读取"任务队列"。所以,它与下面的写法等价。
var req = new XMLHttpRequest();
req.open('GET', url);
req.send();
req.onload = function (){};
req.onerror = function (){};
也就是说,指定回调函数的部分(onload和onerror),在send()方法的前面或后面无关紧要,因为它们属于执行栈的一部分,系统总是执行完它们,才会去读取"任务队列"。
Worker多线程
js是基于单线程的,而这个线程就是浏览器的js引擎。
假如我们要执行一些耗时的操作,比如加载一张很大的图片,我们可能需要一个进度条来让用户进行等待,在等待的过程中,整个js线程会被阻塞,后面的代码不能正常运行,这可能大大的降低用户体验,这时候我们就期望拥有一个工作线程来处理这些耗时的操作。在传统的html时代是基本不可能实现的,而现在,我们拥有一种叫做worker的东西。它是js里的一个类,而我们只需要创建它的实例就可以使用它。
var worker = new Worker(js file path);
构造函数的参数填上你的js文件的路径,这个js文件将会在浏览器新开的线程里运行,而与原先的js引擎的线程并不影响。
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<title></title>
</head>
<body>
<input type="text" name="ipt" id="ipt" value="" />
<button id="start">start</button>
<button id="stop">stop</button>
<button id="ale">alert</button>
<script type="text/javascript">
var ipt = document.getElementById("ipt");
var stop = document.getElementById("stop");
var start = document.getElementById("start");
var ale = document.getElementById("ale");
var worker = new Worker("js/test22.js");
worker.onmessage = function(){
ipt.value = event.data;
};
stop.addEventListener("click",function(){
//用于关闭worker线程
worker.terminate();
});
start.addEventListener("click",function(){
//开起worker线程
worker = new Worker("js/test22.js");
});
ale.addEventListener("click",function(){
alert("i'm a dialog");
});
</script>
</body>
</html>
test22.js
var i = 0;
function mainFunc(){
i++;
//把i发送到浏览器的js引擎线程里
postMessage(i);
}
var id = setInterval(mainFunc,1000);
运行起来我们会发现
虽然dialog的弹出会阻塞js引擎线程,但是并不影响worker线程的运行,所以,在我们点击确定后,只是在js引擎线程上更新了新的内容,而数值是一直在跑动的,这就说明worker线程和原本的js线程互不影响.
那么既然互不影响,两个线程之间要怎么来联系呢,答案其实已经在代码里了,那就是onPostMessage 和 onmessage这两个函数,其中onPostMessage(data)的参数是你要传递的数据,而onmessage是一个回调函数,只有在接受到数据时,onmessage会被回调,onmessage有一个隐藏的参数,那就是event,我们可以用event.data获取到传递过来的数据来更新主线程。
使用worker线程应注意的是,所有js里集成的对象都在js线程里,而并非worker线程。
var a = document.getElementById("a"); // Error
尽量把需要的东西都写到主线程里,而只把耗时的操作写到worker线程里。
开发环境
git
网络git服务器 codeing.net
js模块化
打包工具
上线回滚的流程