最近我一直在准备系统的梳理一遍前端知识,为之后进大厂做准备。这个面试总结系列是我查看了多篇博客所累积的知识点,也是我之前没有认真去理解过的知识点,希望和小伙伴们一起分享,一起加油努力,希望这个4月能有好消息,加油。
1.对语义化的理解
- 用正确的标签做正确的事
- 代码更容易阅读和维护
- 搜索引擎的爬虫依赖于标记来确定上下文和各个关键字,鲤鱼SEO
- html语义化可以使页面的内容结构化,便于浏览器搜索引擎解析
2.浏览器内核的理解
- 渲染引擎:取得网页的内容,整理信息,计算网页的显示方式。内核不同的浏览器对网页的语法解释不同,所以渲染出来的效果也不同。
- JS引擎:执行和解析JS代码,实现网页的动态效果。
- 由于JS引擎的独立,内核一般就指渲染引擎
3.cookies, sessionStorage 和localStorage 的区别
- cookie 是网站为了标示用户身份而储存在用户本地终端(Client Side) 上的数据(通 常经过加密),cookie数据会在浏览器和服务器之间来回传递,并且数据大小不超过4k
- sessionStorage 和 localStorage 不会自动把数据发给服务器, 仅在本地保存,它们的存储量比cookie大
- localStorage存储持久化数据,当浏览器关闭数据不会丢失,sessionStorage 数据在当前浏览器窗口关闭后自动删除,cookie 设置的 cookie 过期时间之前一直有效, 即使窗口或浏览器关闭
4.闭包的理解
- 闭包是指有权访问另一个函数作用域中的变量的函数
闭包法就是在要引用外部变量i的函数外面再包裹一个 用作块级作用域的匿名函数
(function(i){
//某某内部使用了i的函数
})(i);
5.作用域链、原型链的理解
原型链:每个函数都有prototype 属性(显式原型属性),除了 Function.prototype.bind()
,该属性指向原型。每个对象都有 __proto__
属性(隐式原型属性),指向了创建该对象的构造函数的原型。
总结
Object 是所有对象的爸爸,所有对象都可以通过 proto 找到它
Function 是所有函数的爸爸,所有函数都可以通过 proto 找到它
Function.prototype 和 Object.prototype 是两个特殊的对象,他们由引擎来创建
除了以上两个特殊对象,其他对象都是通过构造器 new 出来的
函数的 prototype 是一个对象,也就是原型
对象的 proto 指向原型, proto 将对象和原型连接起来组成了原型链
6.如何实现继承
一般我们把属性方法构造函数中,把方法挂在prototype下面,对象的
__proto__指向构造函数的prototype,原型链的终点为Object.prototype,再往上Object.prototype.proto=null
- 原型链继承
子类的原型指向父类的实例(子类.prototype=new 父类())
Child.prototype = new Parent("qy")
//为了不破坏原型链,将Child的constructor指向本身
Child.prototype.constructor=Child
子类与父类的关系为指向关系,实例是子类的实例,也是父类的实例 父类新增的原型方法或属性,子类都能访问。
子类不能拥有自己的属性,如果有多个实例时,其中一个实例修改了父类引用类型的值,那么所有的实例都会发生改变。
- 构造继承(call,apply继承)
把 父对象的所有属性和方法,拷贝进子对象
function Child(name){
Parent.call(this,name)
}
var child=new Child("qy")
解决了原型链继承中的子类共享父类属性的问题
创建的子类实例可以向父类传递参数
实例是子类的实例,不是父类的
只能继承父类的实例属性和方法,不能继承父类原型上的方法
无法实现函数复用,每个子类都有父类函数的属性和方法的副本,当child调用Parent上的方法时,Parent内部的this指向的是child,Parent内部的this上的属性和方法都被复制到了child上面
- 组合继承(原型链继承与构造继承)
function Child(name) {
Parent.call(this,name) //构造继承 ,第二次调用父类
}
//原型链继承
Child.prototype=new Parent()
Child.prototype.constructor=Child
var child=new Child("qy") //子类的实例向父类传递参数,第一次调用父类
console.log(child.name)
child.introduce()
child.hobby("sing")
console.log(child instanceof Parent) //true
console.log(child instanceof Child) //true
子类可向父类传参
实例既是子类的实例,也是父类的实例
多个实例之间不存在公用父类的引用属性的问题
实例可以继承父类实例的属性和方法,也可以继承原型上的属性和方法
缺点:这种方式调用了两次父类的构造函数,生成了两份实例,相同的属性既存在于实例中也存在于原型中
- 拷贝继承
function Child(name){
var parent = new Parent(name);
for(var key in parent){
Child.prototype[key] = parent[key];
}
}
支持多继承
效率很低,内存占用高
无法获取父类不可枚举的方法
- 寄生组合继承
通过借用构造函数来继承属性,通过原型链的混成形式来继承方法
function Child(name) {
Parent.call(this,name) //构造继承
}
(function(){
//创建一个临时的类
var Temp=function(){}
Temp.prototype=Parent.prototype
//子类的原型指向父类的实例
Child.prototype=new Temp() //原型链继承
})()
- ES6继承
class Parent{
constructor(name){
this.name=name
}
introduce(){
console.log("my name is " + this.name)
}
hobby(hobby){
console.log(this.name + " like " + hobby)
}
}
class Child extends Parent{
constructor(name,age){
super(name) //构造继承,可以继承Parent构造函数上的属性
this.age=age
}
}
在这个方法中使用extends 和 super 两个关键字,在子类的constructor方法中调用super方法,用来继承父类的this对象
实现原理:
先将父类实例 的属性方法加到this上(super),然后再用子类的构造函数修改this
7.对this的理解
- this总是指向函数的直接调用者
- new关键字出来的对象,this就指向这个对象
- 在事件中,this指向触发这个事件的对象
- 修改this的指向:
- call:function.call(target, 1, 2)
- apply: function.apply(target, [1, 2])
- bind: fn.bind(target)(1,2)
- call和apply都是为了改变this指向的,作用都是相同的,只是传参的方式不同。call可以接收一个参数列表,而apply只接收一个参数数组。
**new的过程:**
创建一个新对象: 如:var person = {};
链接到原型: 新对象的_proto_属性指向构造函数的原型对象
绑定this: this对象指向新对象
返回新对象
8.web安全
- sql注入:通过把sql命令插入到web表单递交或输入域名或页面请求的查询字符串,欺骗服务器执行恶意的SQL命令
- XSS:攻击者往web页面插入恶意的html标签或js代码(欺骗用户)
9.跨域问题的解决
- 两个页面具有相同的协议,域名,端口号,同源策略是浏览器的一个安全功能
- JSONP动态创建一个 script,通过 script 发出请求
- 根据 W3C 的跨源资源共享方案,在被调用方修改代码,加上字段(xhr.withCredentials = true;),告诉浏览器该网站支持跨域
- 使用 Nginx 反向代理,在 a 域名里面的的请求地址使用反向代理指向 b 域名,让浏览器以为一直在访问 a 网站,不触发跨域限制
- WebSocket protocol 是 HTML5 的一种新的协议。它实现了浏览器与服务器全双工通信,同时允许跨域通讯
10.CommonJS、AMD、ES6模块化的区别
- CommonJS规范主要用于服务端编程,加载模块是同步的,这并不适合在浏览器环境,因为同步意味着阻塞加载,浏览器资源是异步加载的,因此有了AMD
CMD解决方案。 - AMD规范在浏览器环境中异步加载模块,而且可以并行加载多个模块。不过,AMD规范开发成本高,代码的阅读和书写比较困难,模块定义方式的语义不顺畅。
- CMD规范与AMD规范很相似,都用于浏览器编程,依赖就近,延迟执行,可以很容易在Node.js中运行。不过,依赖SPM
打包,模块的加载逻辑偏重。
ES6 在语言标准的层面上,实现了模块功能,而且实现得相当简单,完全可以取代 CommonJS 和 AMD 规范,成为浏览器和服务器通用的模块解决方案。
11.对象的拷贝
- 深拷贝:完全拷贝一个新的对象,修改时原对象不受任何影响。方式:JSON.parse(JSON.stringify(obj)):当值为函数、undefined、或symbol时,无法拷贝
- 浅拷贝:以赋值的形式拷贝引用对象,仍指向同一个地址,修改时原对象也会受到影响。方式:展开运算符(…)、Object.assign
12.防抖与节流
- 防抖 (debounce): 将多次高频操作优化为只在最后一次执行,通常使用的场景是:用户输入
// 防抖函数
function debounce(fn) {
let timeout = null;
return function () {
clearTimeout(timeout);
timeout = setTimeout(() => {
fn.call(this, arguments);
}, 1000);
}
}
- 节流(throttle): 每隔一段时间后执行一次,也就是降低频率,将高频操作优化成低频操作,通常使用场景: 滚动条事件 或者 resize 事件,通常每隔 100~500 ms执行一次即可。
/*
* 节流函数生成器
* 传递事件处理函数和延迟时间
* 返回节流函数
*/
function throttleGen(fn, delay) {
let timer = null;
function throller() {
if (timer === null) {
timer = setTimeout(() => {
fn();
timer = null;
}, delay)
}
}
return throller;
}
- PS:防抖和节流的作用都是防止函数多次调用。区别在于,假设一个用户一直触发这个函数,且每次触发函数的间隔小于wait,防抖的情况下只会调用一次,而节流的 情况会每隔一定时间(参数wait)调用函数。
13.babel编译原理
- babylon 将 ES6/ES7 代码解析成 AST
- babel-traverse 对 AST 进行遍历转译,得到新的 AST
- 新 AST 通过 babel-generator 转换成 ES5
14.从输入URL到页面展示的过程
- 首先输入地址,浏览器会查本地的缓存
- DNS解析:
先在本机的hosts文件下查找是否有和此域名对应的规则,若没有,浏览器会发起一个DNS请求到本地DNS服务器,若本地DNS服务器没有则请求根DNS服务器,根DNS提供域服务器的地址,DNS服务器继续向域服务器请求,返回域名的解析服务器地址,然后本地DNS服务器就收到了一个域名和IP地址的对应关系,返回给用户电脑,并保存在缓存中。 - 发起TCP连接(TCP三次握手),浏览器发送HTTP请求
- 服务器返回请求的文件(html)
- 浏览器进行渲染
15.TCP三次握手
建立连接前,客户端和服务端需要通过握手来确认对方:
- 客户端发送 syn(同步序列编号) 请求,进入 syn_send 状态,等待确认
- 服务端接收并确认 syn 包后发送 syn+ack 包,进入 syn_recv 状态
- 客户端接收 syn+ack 包后,发送 ack 包,双方进入 established 状态
16.TCP四次挥手
- 客户端 – FIN --> 服务端, FIN—WAIT
- 服务端 – ACK --> 客户端, CLOSE-WAIT
- 服务端 – ACK,FIN --> 客户端, LAST-ACK
- 客户端 – ACK --> 服务端,CLOSED
17.强缓存和协商缓存(浏览器缓存)
- 实现强缓存可以通过两种响应头实现:Expires 和 Cache-Control 。强缓存表示在缓存期间不需要请求,state code 为 200。
- 如果缓存过期了,我们就可以使用协商缓存来解决问题。协商缓存需要请求,如果缓存有效会返回 304。Last-Modified 和 If-Modified-Since ,ETag 和 If-None-Match(HTTP/1.1)
- 栈和队列的区别、用栈来实现队列
- 栈的插入和删除操作都是在一端进行的,而队列的操作却是在两端进行的。
- 队列先进先出,栈先进后出。
使用两个栈来实现队列:
假如入队序列为1,2,3,4。出队时应该是1,2,3,4。
首先将序列压入到栈1中,栈顶元素为4.之后再将栈1中的元素按顺序出栈再依次压入栈2中,栈2中的栈顶元素为1.最后依次再出栈,这就相当于是队列的先进先出了。
var a=[],b=[]
function push(node)
{
a.push(node);
}
function pop()
{
var t=[]
//如果在a栈push到b栈之前,b栈中有元素,先要将b中元素倒出来放到t中,
//把a栈中元素全部倒入空的b栈中后,再把t中的元素倒回b栈中
if(b.length>0){
while(b.length>0){
t.push(b.pop())
}
}
while(a.length>0){
right.push(a.pop());
}
if(t.length>0){
while(t.length>0){
b.push(t.pop())
}
}
return b.pop();
}
- 浏览器性能优化
- 减少http请求,合理设置 HTTP缓存
减少http的主要手段是合并CSS、合并javascript、合并图片。将浏览器一次访问需要的javascript和CSS合并成一个文件,这样浏览器就只需要一次请求。 - 启用压缩
在服务器端对文件进行压缩,在浏览器端对文件解压缩,可有效减少通信传输的数据量。 - CSS Sprites和图片懒加载
- CSS放在页面最上部,javascript放在页面最下面
20.prefetch和preload
- preload 提供了一种声明式的命令,让浏览器提前加载指定资源(加载后并不执行),需要执行时再执行。
不带 “as” 属性的 preload 的优先级将会等同于异步请求。
使用 link 标签静态标记需要预加载的资源:
<link rel="preload" href="/path/to/style.css" as="style">
preload 不会阻塞 windows 的 onload 事件
对跨域的文件进行preload时,必须加上 crossorigin 属性
- prefetch是link元素中的rel属性值
<link rel=“prefetch”>
。它的作用是告诉浏览器加载下一页面可能会用到的资源。因此该方法的加载优先级非常低,也就是说该方式的作用是加速下一个页面的加载速度。
所以,对于当前页面很有必要的资源使用 preload,对于可能在将来的页面中使用的资源使用 prefetch。