前端知识梳理与总结(一)--面试常见问题

最近我一直在准备系统的梳理一遍前端知识,为之后进大厂做准备。这个面试总结系列是我查看了多篇博客所累积的知识点,也是我之前没有认真去理解过的知识点,希望和小伙伴们一起分享,一起加油努力,希望这个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编译原理

  1. babylon 将 ES6/ES7 代码解析成 AST
  2. babel-traverse 对 AST 进行遍历转译,得到新的 AST
  3. 新 AST 通过 babel-generator 转换成 ES5

14.从输入URL到页面展示的过程

  1. 首先输入地址,浏览器会查本地的缓存
  2. DNS解析:
    先在本机的hosts文件下查找是否有和此域名对应的规则,若没有,浏览器会发起一个DNS请求到本地DNS服务器,若本地DNS服务器没有则请求根DNS服务器,根DNS提供域服务器的地址,DNS服务器继续向域服务器请求,返回域名的解析服务器地址,然后本地DNS服务器就收到了一个域名和IP地址的对应关系,返回给用户电脑,并保存在缓存中。
  3. 发起TCP连接(TCP三次握手),浏览器发送HTTP请求
  4. 服务器返回请求的文件(html)
  5. 浏览器进行渲染

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-SinceETag 和 If-None-Match(HTTP/1.1)
  1. 栈和队列的区别、用栈来实现队列
  • 栈的插入和删除操作都是在一端进行的,而队列的操作却是在两端进行的。
  • 队列先进先出,栈先进后出。
    使用两个栈来实现队列
    假如入队序列为1,2,3,4。出队时应该是1,2,3,4。
    首先将序列压入到栈1中,栈顶元素为4.之后再将栈1中的元素按顺序出栈再依次压入栈2中,栈2中的栈顶元素为1.最后依次再出栈,这就相当于是队列的先进先出了。
    前端知识梳理与总结(一)--面试常见问题_第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();
}

  1. 浏览器性能优化
  • 减少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属性值。它的作用是告诉浏览器加载下一页面可能会用到的资源。因此该方法的加载优先级非常低,也就是说该方式的作用是加速下一个页面的加载速度。

所以,对于当前页面很有必要的资源使用 preload,对于可能在将来的页面中使用的资源使用 prefetch。

你可能感兴趣的:(前端面试秘籍)