2020-10-26 | 面试合集 | UNLOCK | 更新时间:2020-10-26 15:6

前端面试题的收集和总结(JS 篇)

HTTP常见的请求方式?

目前常见的请求方式有:GET POST PUT DELETE get一般用于请求数据,post一般用于提交数据,put一般用于更新数据,delete一般用于删除数据;不常见的有HEAD CONNECT OPTIONS TRACE PATCH; head 和get 类似,但没有响应体。 connect 一般用于代理服务器,开发中不会用到。options 一般用于CORS向服务器发送预检,检测实际请求是否被接受。trace 一般用于检测和诊断,patch 是对put 的补充,一般用于数据局部更新。

POST 和 GET 的区别

  • GET 请求的参数放在URL上,而POST请求的参数放在请求体里,所以GET 比POST 更不安全,参数暴露在url上,不能用来传递敏感数据
  • GET 请求参数会被保存在历史记录和书签上,GET请求只能支持URL编码,POST支持多种编码
  • GET 请求会被浏览器默认缓存,POST则需要主动设置,GET在浏览器回退时是无害,而POST会再次请求

讲一下HTTP的握手过程?

第一次握手:客户端发送syn包(syn=j)到服务器,并进入SYN_SEND状态,等待服务器确认;
第二次握手:服务器收到syn包,必须确认客户的SYN(ack=j+1),同时自己也发送一个SYN包(syn=k),即SYN+ACK包,此时服务器进入SYN_RECV状态;
第三次握手:客户端收到服务器的SYN+ACK包,向服务器发送确认包ACK(ack=k+1),此包发送完毕,客户端和服务器进入ESTABLISHED状态,完成三次握手。
握手完成后,才会开始发送数据

介绍下HTTPS的握手过程?

第一步,客户端给出协议版本号、一个客户端生成的随机数(Client random),以及客户端支持的加密方法。
第二步,服务端确认双方使用的加密方法,并给出数字证书、以及一个服务器生成的随机数(Server random)。
第三步,客户端确认数字证书有效,然后生成一个新的随机数(Premaster secret),并使用数字证书中的公钥,加密这个随机数,发给服务端。
第四步,服务端使用自己的私钥,获取客户端发来的随机数(即Premaster secret)。
第五步,客户端和服务端根据约定的加密方法,使用前面的三个随机数,生成”对话密钥”(session key),用来加密接下来的整个对话过程。

Http1.x和2.0的区别?

新的二进制格式(Binary Format),HTTP1.x的解析是基于文本。基于文本协议的格式解析存在天然缺陷二进制则不同,只认0和1的组合。基于这种考虑HTTP2.0的协议解析决定采用二进制格式,实现方便且健壮。
多路复用(MultiPlexing),即连接共享,即多个请求可同时在一个连接上并行执行。某个请求任务耗时严重,不会影响到其它连接的正常执行
header压缩,如上文中所言,对前面提到过HTTP1.x的header带有大量信息,而且每次都要重复发送,HTTP2.0使用encoder来减少需要传输的header大小,通讯双方各自cache一份header fields表,既避免了重复header的传输,又减小了需要传输的大小。
服务端推送(server push),服务端推送能把客户端所需要的资源伴随着index.html一起发送到客户端,省去了客户端重复请求的步骤。正因为没有发起请求,建立连接等操作,所以静态资源通过服务端推送的方式可以极大地提升速度

如何中断ajax请求?

一种是设置超时时间让ajax自动断开,另一种是手动停止ajax请求,其核心是调用XML对象的abort方法,ajax.abort()

cookie,sessionStorage和localStorage

cookies:存在大小限制,由于http是无状态协议,cookie在被设计成每次请求都会自动附带,用于后台验证
sessionStorage:存在于窗口会话阶段,当窗口关闭既清除,一般大小为5M或者更大,
localStorage:除非清除,否则一直存在,大小同样为5M或更大,
三者都存在安全信息的问题,不建议保存安全密钥

src和href的区别?

src指向外部资源的位置,指向的内容将会嵌入到文档中当前标签所在位置;在请求src资源时会将其指向的资源下载并应用到文档内,例如js脚本,img图片和frame等元素。当浏览器解析到该元素时,会暂停其他资源的下载和处理,直到将该资源加载、编译、执行完毕,图片和框架等元素也如此
href指向网络资源所在位置,建立和当前元素(锚点)或当前文档(链接)之间的链接,浏览器识别该文档为css文件,就会并行下载资源并且不会停止对当前文档的处理。这也是为什么建议使用link方式来加载css,而不是使用@import方式

图片优化技术有哪些?

图片懒加载技术,在页面不可视区域,先不将图片元素进行带入,当页面滚动后再将页面图片进行加载显示
图片缩略显示,在图片加载之前,将先加载图片的缩略图片,等到图片下载完成后再进行替换图片

全局变量和局部变量的优缺点?

全局变量的优点是,减少变量的数量,降低参数数据传递的时间。缺点是全局变量存储在静态存储区,存放过多会导致消耗过多的内存。全局变量会破坏函数的封闭性和独立性,导致代码移植性差。当被多个函数调用的时候不利于调试
局部变量的优点是,程序动态分配内存,不会消耗太多内存。局部变量在函数内部可以保证函数的封闭和独立性。在不同函数命名相同名称的变量不会互相影响。缺点是局部函数在函数执行后就消失,不利于数据保存,且局部变量的作用域小,仅仅作用于函数内部

讲一下原型和原型链?

JS在创建对象的时候都会有一个 -proto- 的内置属性,用于指向创建它的函数对象的原型对象 prototype;而函数对象也会有也有一个 -proto- 指向创建它的函数对象的原型对象,直到指向Object.prototype.这一系列的指向就像一条链子,所以也叫原型链。

讲一下什么是闭包?

简单的讲,闭包就是一个函数嵌套一个函数,然后把里面的函数return出来,在js中,只有函数内的子函数才能读取函数的内部变量,闭包就是将函数内部和函数外部连接起来的桥梁,比如,函数a的内部函数b,被函数a外面的一个变量c引用的时候,这就叫创建了一个闭包;

闭包的优点和缺点是什么?

闭包的优点是:使变量私有化,保护函数内变量的安全,加强了函数的封装性,局部变量模拟全局变量一样,但是它只能被特定函数使用,不用担心类似全局变量的命名冲突
闭包的缺点是:内部函数会调用变量导致变量不会被回收,常驻变量,增大内存的使用量,使用不当会造成内存泄漏
可以通过手动将闭包设为null进行销毁,或删除dom元素

call 和 apply 的区别

fn.call(obj, arg1, arg2, …),调用一个函数, 具有一个指定的this值和分别地提供的参数(参数的列表)。

fn.apply(obj, [argsArray]),调用一个函数,具有一个指定的this值,以及作为一个数组(或类数组对象)提供的参数。

bind 和 call/apply 有一个很重要的区别,一个函数被 call/apply 的时候,会直接调用,但是 bind 会创建一个新函数。当这个新函数被调用时,bind() 的第一个参数将作为它运行时的 this,之后的一序列参数将会在传递的实参前传入作为它的参数

var person= {
    name: 'lily'
}

function func(age, job) {
    console.log(this.name +'的年龄是'+age);
    this.job=job
}

// 此时this指向person,[]作为func的参数传入
func.apply(person, [18, 'coder']);

// 和apply类似,只是参数形式变了
func.call(person, 18, 'student');

// 和call类似,但是需要手动赋予调用
var funca=func.bind(person,18, 'bind')
funca()

前端同步和异步,你是怎么理解

  • 同步就是指一个进程在执行某个请求的时候,若该请求需要一段时间才能返回信息,那么这个进程将会一直等待下去,直到收到返回信息才继续执行下去;
  • 异步是指进程不需要一直等下去,而是继续执行下面的操作,不管其他进程的状态。当有消息返回时系统会通知进程进行处理,这样可以提高执行的效率;

面向对象的概念是什么?

面向对象和面向过程都是一种思想,他们的区别在于,一个以对象为主,一个以过程为主,例如:冰箱装进大象要几步。面向过程是:打开(冰箱),放入(冰箱,大象),关上(冰箱);面向对象是:冰箱.打开(),冰箱.装进(大象),冰箱.关门()。

面向对象有三种特性:封装,继承,多态;

封装

对应Es5来说没有类这个概念,但由于存在函数作用域,就可以模拟类这个概念,(类是一个模板,它描述一类对象的行为和状态)在Es5中,类其实就是保存了一个函数的变量,这个函数有自己的属性和方法。将属性和方法组成一个类的过程就是封装。

通过构造函数来创建对象;构造函数其实就是普通的函数,只不过有以下的特点:首字母大写,内部使用this,使用new生成实例。

function Cat(name,color){   //构造函数
    var sex = 12;
    this.name = name;
    this.color = color;
}
Cat.prototype.type = "猫科动物";    //原型
Cat.prototype.eat = function(){console.log("吃老鼠")};

Cat.setTime = function () {    // 语法
    console.log('this is new time')
}

var cat1 = new Cat("大毛","黄色");
var cat2 = new Cat("二毛","黑色");
console.log(cat1.type); // 猫科动物
cat1.eat(); // 吃老鼠

通过构造函数、原型和. 语法三者都可以在类上添加属性和方法。但是三者是有一定的区别。
构造函数:通过this添加的属性和方法总是指向当前对象的,所以在实例化的时候,通过this添加的属性和方法都会在内存中复制一份,这样就会造成内存的浪费。但是这样创建的好处是即使改变了某一个对象的属性或方法,不会影响其他的对象(因为每一个对象都是复制的一份)。
原型:通过原型继承的方法并不是自身的,我们要在原型链上一层一层的查找,这样创建的好处是只在内存中创建一次,实例化的对象都会指向这个prototype 对象,但是这样做也有弊端,因为实例化的对象的原型都是指向同一内存地址,改动其中的一个对象的属性可能会影响到其他的对象
. 语法:在类的外部通过. 语法创建的属性和方法只会创建一次,但是这样创建的实例化的对象是访问不到的,只能通过类的自身访问

在函数作用域内的属性和变量都是私有变量,而通过this定义的属性和方法在实例时都会复制一遍,所以是公有变量,而原型也是公有变量。

继承

继承:子类可以使用父类的所有功能,并且对这些功能进行扩展。继承的过程,就是从一般到特殊的过程

类式继承:所谓的类式继承就是使用的原型的方式,将方法添加在父类的原型上,然后子类的原型是父类的一个实例化对象。子类实例化对象的属性和方法都指向父类的原型,子类之间可能会互相影响

//声明父类
var SuperClass = function () {
    var id = 1;
    this.name = ['javascript'];
    this.superValue = function () {
        console.log('superValue is true');
        console.log(id)
    }
};

//为父类添加共有方法
SuperClass.prototype.getSuperValue = function () {
    return this.superValue();
};

//声明子类
var SubClass = function () {
    this.subValue = function () {
        console.log('this is subValue ')
    }
};

//继承父类
SubClass.prototype = new SuperClass() ;         //关键语句

构造函数继承:SuperClass.call(this,id) ,每个实例化的子类互不影响,内存浪费

//声明父类
function SuperClass(id) {
    var name = 'javascript'
    this.books=['javascript','html','css'];
    this.id = id
}

//声明父类原型方法
SuperClass.prototype.showBooks = function () {
    console.log(this.books)
}

//声明子类
function SubClass(id) {
    SuperClass.call(this,id)        //关键语句;通过call 替换掉父级到this指向
}

多态

多态的基本概念:一个引用类型(变量)在不同情况下的多种状态。多态用途在于做面向对象开发时,需要有一个方法不变,但是它接收的参数类型是变化的,就可以使用多态

new 做了什么,如果让你写一个?

首先创建了一个空对象,然后,将这个空对象的 proto 属性指向构造函数的原型,接下来,改变 this 的指向,将其指向这个新的空对象,最后,根据构造函数的返回值,决定返回哪个对象。当然这只是加拿简化版的运行逻辑。的实际的 new 操作符还涉及更多细节,如原型链、构造函数的参数传递等。

function myNew(Con, ...args) {
    let obj = {};
    Object.setPrototypeOf(obj, Con.prototype);
    let res = Con.apply(obj, args);
    return res instanceof Object ? res : obj;
}