2019上半年面试总结

该博客围绕前端面试知识点展开,涵盖JS、CSS、webpack、react、vue、小程序/公众号等方面。JS涉及闭包、原型链、跨域等;CSS有水平垂直居中等;webpack介绍配置项;react和vue包含生命周期、组件传参等;小程序介绍登录授权流程。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

JS
1.闭包以及闭包的使用场合

function fn(){
    var arr=[];
    //i为fn函数中的局部变量。
    for(var i=0;i<3;i++){
        arr.push(function(){
            return i;
        });//此匿名函数没有立即执行,当在调用匿名函数的时候,通过闭包获得的i已经是3了
    }
    return arr;
}
var b=fn();
for(var i=0;i<b.length;i++){
    console.log(b[i]());//3
}

在for循环中,数组中的匿名函数没有自我执行,当在调用的时候通过闭包获得的i已经是3了
想要输出结果为0,1,2,做如下调整
function fn(){
    var arr=[];
    //i为fn函数中的局部变量。
    for(var i=0;i<3;i++){
        arr.push((function(num){
        	// 将立即执行函数返回的匿名函数放到数组中
        	// num为函数的参数,因此有自己独立的作用域
            return function() {
				return num
			}
        })(i));
    }
    return arr;
}
var b=fn();
for(var i=0;i<b.length;i++){
    console.log(b[i]());//3
}

react中的高阶组件也是一个闭包

2.原型链

3.构造函数继承和原型继承的方式

function Animal() {
	this.spcies = '动物'
}
function Cat(name,color) {
   this.name = name;
   this.color = color;
}
1.构造函数继承:
function Cat(name, color) {
	Animal.call(this)
	//或Animal.apply(this, arguments)
	this.name = name
	this.color = color
}
var cat1 = new Cat('dog', 'xx')
console.log(cat1.spcies) //动物
2.原型继承
Cat.prototype = new Animal() // 给Cat的prototype赋一个新的值,Cat的constructor指向Animal
Cat.prototype.constructor = Cat //修正Cat的constructor指向
var cat1 = new Cat('dog', 'xx')
console.log(cat1.spcies) //动物
每一个实例也有一个constructor属性,默认调用prototype对象的constructor属性
cat1.constructor == Cat.prototype.constructor //true
如果替换了prototype对象
o.prototype = {}
下一步必然是添加constructor属性,并将这个属性指回原来的构造函数
o.prototype.constructor = o
3.直接继承
不变的属性直接写在原型上
function Animal() {}
Animal.prototype.spcies = '动物'
Cat的prototype直接指向Animal的prototype,即完成继承
Cat.prototype = Animal.prototype 
Cat.prototype.constructor = Cat
缺点:修改Cat的prototype会影响Animal的prototype
4.利用空对象作为中介
function extend(Child, Parent) {
	var F = function() {}
	F.prototype = Parent.prototype
	Child.prototype = new F()
	Child.prototype.constructor = Cat
	Child.uber = Parent.prototype;//为子对象设一个uber属性,这个属性直接指向父对象的prototype属性。(uber是一个德语词,意思是"向上"、"上一层"。)这等于在子对象上打开一条通道,可以直接调用父对象的方法。这一行放在这里,只是为了实现继承的完备性,纯属备用性质。
}
extend(Cat, Animal)
var cat1 = new Cat("大毛","黄色");
alert(cat1.species); // 动物
5.拷贝继承
function Animal(){}
Animal.prototype.species = "动物";

function extend2(Child, Parent) {
   var p = Parent.prototype;
   var c = Child.prototype;
   for (var i in p) {
     c[i] = p[i];
   }
   c.uber = p;
 }

new一个构造函数之后发生了什么

1.创建一个新的对象
2.将构造函数的作用域赋给新对象(this就指向了新对象)
3.执行构造函数中的代码(为这个新对象添加属性)
4.返回新对象

4.同源策略,跨域的解决方案
5.cors跨域的原理
参考:http://www.ruanyifeng.com/blog/2016/04/cors.html
6.http请求头常用的配置
7.localStorage如何存储对象
8.let和const
9.构造函数,new一个构造函数之后发生了什么
10.H5新增的canvas的拖拽怎么写
11.

function fn(a,b){
      return  {
             p1:a&b,
             p2:a||b
      }
}
console.log(0,2)   //0  2    &和 || 符号输出的类型肯定是相同的
console.log(1,2)    //2   1
console.log('a','b')    //a  b
console.log(true,'b')   //b   true

12.基本数据类型和引用数据类型

function fn(params) {
       params[0] = 1
}
let  str = 'abc'
let  nstr =  ['a','b','c']
fn(str)
console.log(str)     //abc
fn(nstr)
console.log(nstr)    //[1,'b','c']

数据类型的判断方法
基本数据类型
引用数据类型

13.浅拷贝,深拷贝

  let  arr = [{name:1,age:3}{name:2,age:4}]
  let  newArr = arr.concat(arr)
  newArr[0].name = 'a'
  console.log(newArr[0].name,newArr[2.name])
  // a   a
  concat是浅拷贝,slice对于单层结构的数组是深拷贝,多层的是浅拷贝
  JSON.stringify() 对函数类型,symbol,undefined类型的不能使用
  es6的解构赋值以及Object.assign()对于基本数据类型可以深拷贝
  深拷贝:
  function deepCopy(obj){
	var newObj = Array.isArray(obj) ? [] : {}
	console.log(newObj)
	for(var key in obj){
		if((typeof obj[key] === 'object' || typeof obj[key] === 'function') && typeof obj[key] !== null){
			//是引用类型
			newObj[key] = deepCopy(obj[key])
		} else {
			//是基本数据类型
			newObj[key] = obj[key]
		}
	}
	return newObj
 }

14.parseInt

  ['10','10','10'].map(parseInt)
  [10,NaN,2]
  parseInt有两个参数,string,radix
  string:要被解析的值
  radix:表示前边字符串的基数,数字2-36之前的整型。默认使用10,表示十进制。这个参数的意义是指把前面的字符看作是多少进制的数字,所谓的基数。
  parseInt('10',0)   //10    (parseInt的处理方式,这个地方item没有以"0x"或者"0X"开始,8和10这个基数由实现环境来定,ES5规定使用10来作为基数,因此这个0相当于传递了10)
  parseInt('10',1)    //NaN   (因为parseInt的定义,超出了radix的界限)
  parseInt('10',2)    //2    

15.http和https的区别
16.npm和node的关系,npm怎么安装(npm内置在node.j中,所以安装的时候一并安装了)
npm install之后发生了什么
npm是node.js的的包管理工具
node是js的运行环境,可以让js运行在服务端

17.sort()不传参数的时候默认怎么排序
按asii码排序,
(‘10’<’5’) //默认先比较第一个字符,即1<5

18.setTimeout

for(var i=0;i<5;i++){
     setTimeout(()=>{
           console.log(i)
     })
}
console.log(i)
//4
4
4
4
4
5
怎么打印出0 1 2 3 4
var改成了let
或者让setTimeout变成立即执行的函数,用到了闭包
写一个函数把setTimeout放进去,在循环中调用,把i传进去

19.对象在数据结构中属于什么类型(类似于hasmap)
数字作为key值怎么取值 obj[‘1’],虽然写的时候key值为数字,但是还是会把数字转换成字符串

20.一个http请求,从发起到请求返回response的过程
域名解析 --> 发起TCP的3次握手 --> 建立TCP连接后发起http请求 --> 服务器响应http请求,浏览器得到html代码 --> 浏览器解析html代码,并请求html代码中的资源(如js、css、图片等) --> 浏览器对页面进行渲染呈现给用户

21.cookie,localStorage,session的区别

22.一个页面同时有多个请求的解决办法
Promise.all()
promise.all怎么实现

22.HTTP状态码
1** 信息,服务器收到请求,需要请求者继续执行操作
2** 成功,操作被成功接收并处理
3** 重定向,需要进一步的操作以完成请求
4** 客户端错误,请求包含语法错误或无法完成请求
5** 服务器错误,服务器在处理请求的过程中发生了错误

100:继续。客户端应继续其请求
200:请求成功
301:永久移动。请求的资源已被永久的移动到新URI,返回信息会包括新的URI,浏览器会自动定向到新URI。今后任何新的请求都应使用新的URI代替(只是域名后的链接地址做了变更)。
302:临时移动。与301类似。但资源只是临时被移动。客户端应继续使用原有URI。
304:未修改。所请求的资源未修改,服务器返回此状态码时,不会返回任何资源。客户端通常会缓存访问过的资源,通过提供一个头信息指出客户端希望只返回在指定日期之后修改的资源(跳转到了新域名)。
305:使用代理。所请求的资源必须通过代理访问。
307:临时重定向。与302类似。使用GET请求重定向。
400:客户端请求的语法错误,服务器无法理解。
401:当前请求需要用户验证,客户端提供了错误的证书或没有提供证书
403:禁止访问,服务器理解请求客户端的请求,但是拒绝执行此请求。
404:url错误,服务器无法根据客户端的请求找到资源(网页)。
405:客户端请求中的方法被禁止。
500:服务器内部错误,无法完成请求。
503:无法连接到服务器,由于超载或系统维护,服务器暂时的无法处理客户端的请求。延时的长度可包含在服务器的Retry-After头信息中。
504:充当网关或代理的服务器,未及时从远端服务器获取请求(请求超时)。

23.浏览器的缓存与304
https://blog.youkuaiyun.com/soonfly/article/details/50953814
①. Cache-Control和Expires(响应)
②. Last-Modified和If-Modified-Since(响应和请求)
③. ETag和If-None-Match(响应和请求)

24.HTTP1和HTTP2.0的区别

1.新的二进制格式(Binary Format),HTTP1.x的解析是基于文本。基于文本协议的格式解析存在天然缺陷,文本的表现形式有多样性,要做到健壮性考虑的场景必然很多,二进制则不同,只认0和1的组合。基于这种考虑HTTP2.0的协议解析决定采用二进制格式,实现方便且健壮。

2.多路复用(MultiPlexing),即连接共享,即每一个request都是是用作连接共享机制的。一个request对应一个id,这样一个连接上可以有多个request,每个连接的request可以随机的混杂在一起,接收方可以根据request的 id将request再归属到各自不同的服务端请求里面。

3.header压缩,如上文中所言,对前面提到过HTTP1.x的header带有大量信息,而且每次都要重复发送,HTTP2.0使用encoder来减少需要传输的header大小,通讯双方各自cache一份header fields表,既避免了重复header的传输,又减小了需要传输的大小。

4。服务端推送(server push),同SPDY一样,HTTP2.0也具有server push功能。

HTTPS与HTTP的区别:

1.HTTPS协议需要到CA申请证书,一般免费证书很少,需要交费。

2.HTTP协议运行在TCP之上,所有传输的内容都是明文,HTTPS运行在SSL/TLS之上,SSL/TLS运行在TCP之上,所有传输的内容都经过加密的。

3.HTTP和HTTPS使用的是完全不同的连接方式,用的端口也不一样,前者是80,后者是443。

4.HTTPS可以有效的防止运营商劫持,解决了防劫持的一个大问题。

25.一个页面最多可以有几个http请求

26.for in 和for of的区别

//for of只能遍历数组,key是数组元素
//for in既可以遍历数组也可以遍历对象,还能遍历到对象原型上的属性,key是属性key
var arr = [1,2,3]
Array.prototype.test = 'test'
for(var key in arr) {
	console.log(key)
}
0
1
2
test

27.sass与less的区别

1.编译环境:sass是基于ruby环境的,在服务端处理,而less是基于js的,需要引入less.js来处理less代码输出css到浏览器。
2.变量符:less是@,sass是$
3.sass支持条件语句if{}else{},for{}等,less不支持
4.输出设置:less没有输出设置,sass提供四项输出设置:nested,compact,compressed,expaned
5.引用外部css文件:sass引用外部的文件名必须以_开头,文件名如果以下划线_开头的话,Sass会认为该文件是一个引用文件,不会将其编译为css文件。
@import "_test1.scss";
Less引用外部文件和css中的@import没什么差异。

sass:
引用父选择符: &

28.数组去重

方法1:
function fn(arr) {
	var newarr = []
	arr.forEach((item) => {
		if(newarr.indexOf(item) <0) {
			newarr.push(item)
		}
	})
	return newarr
}
方法2:看当前的索引值是否是第一次出现
function fn(arr) {
	var newarr = []
	arr.forEach((item, index) => {
		if(arr.indexOf(item) === index) {
		    //当前位置的元素出现的位置是当前的index,就push,否则就不是第一次出现
			newarr.push(item)
		}
	})
	return newarr
}
方法3:先排序,再比较相邻的,不同的就push
function fn(arr) {
	var newarr = [arr[0]]
	arr.sort()
	arr.forEach((item, index) => {
		if (item !== newarr[newarr.length - 1]) {
			newarr.push(item)
		}
	})
	return newarr
}
优势就是排序后的比较次数变少
方法4:双层循环
function fn(arr){
  var newarr=[];
  for (var i = 0; i < arr.length; i++) {
    for (var j = i+1; j < arr.length; j++) {
      if(arr[i]===arr[j]){
        ++i;
      }
    }
      newarr.push(arr[i]);
  }
  return newarr;
}
方法5:对象存放,哈希算法(映射)判断
function fn(arr) {
    // n为hash表,r为临时数组
    var n = {}, r = [];
    for (var i = 0; i < arr.length; i++) {
        // 如果hash表中没有当前项
        if (!n[arr[i]]) {
            // 存入hash表
            n[arr[i]] = true;
            // 把当前数组的当前项push到临时数组里面
            r.push(arr[i]); 
        }
    }
    return r;
}
但从耗时的角度来讲,这是最优的一种解决方式。但是从内存占用角度来说,这并不是最优的,因为多了一个hash表。这就是所谓的空间换时间
方法6:
//es5
function unique (arr) {
    return arr.filter((v, i) => arr.indexOf(v) === i)
}
// es6简化版
Array.prototype.unique = function() {
    return Array.from(new Set(this));
}
// 或
Array.prototype.unique = function() {
    return [...new Set(this)];
}

29.数组排序

30.获取浏览器的高度

网页可见区域宽: document.body.clientWidth
网页可见区域高: document.body.clientHeight
网页可见区域宽: document.body.offsetWidth (包括边线的宽)
网页可见区域高: document.body.offsetHeight (包括边线的高)
网页正文全文宽: document.body.scrollWidth
网页正文全文高: document.body.scrollHeight
网页被卷去的高: document.body.scrollTop
网页被卷去的左: document.body.scrollLeft
网页正文部分上: window.screenTop
网页正文部分左: window.screenLeft
屏幕物理分辨率的高: window.screen.height
屏幕物理分辨率的宽: window.screen.width
屏幕可用工作区高度: window.screen.availHeight

屏幕可用工作区宽度: window.screen.availWidth

屏幕缩放因子:window.devicePixelRatio

屏幕逻辑分辨率:window.screen.width * window.devicePixelRatio (缩放因子与物理分辨率的乘积)

css
1.div水平垂直居中

定宽高:
1.absolute + 负margin
{
	position: absolute;;
    top: 50%;
    left: 50%;
    margin-left: -50px;
    margin-top: -50px;
}
2.absolute + margin auto
{
	position: absolute;;
    top: 0;
    left: 0;
    right: 0;
    bottom: 0;
    margin: auto;
}
3.absolute + calc
{
	position: absolute;;
    top: calc(50% - 50px);
    left: calc(50% - 50px);
}
不定宽高:
1.absolute + transfrom
{
	position: absolute;
    top: 50%;
    left: 50%;
    transform: translate(-50%, -50%);
}
2.flex
{
	display: flex;
    justify-content: center;
    align-items: center;
}
3.lineheight
4.table
5.css-table
.wp {
    display: table-cell;
    text-align: center;
    vertical-align: middle;
}
.box {
    display: inline-block;
}
6.grid

2.box-sizing的两个属性值:content-box,border-box
3.css写一个三角形
4.z-index
(1)Z-index仅对定位元素有效(如position:relative\absolute\float);

(2)Z-index只可比较同级元素。这也许是大家很容易忽视的问题,我就卡在了这里。也就是说,Z-index只能对同级元素进行分层展示;

(3)Z-index的作用域:假设A和B两个元素都设置了定位(相对定位、绝对定位、一个相对一个绝对定位都可以),且是同级元素,样式为:.boxA{z-index:4}
.boxB{z-index:5}
于是,不难看出,元素B的层级要高于元素A,在此需要指出的是,A元素下面的子元素的层级也同样都低于B元素里的子元素,即使你将A元素里的子元素设为z-index:9999;同理元素B里的子元素,即使是设的z-index:1它照样比元素A的层级要高;

(4)这个属性不会作用于窗口控件,如select对象。

(5)在父元素的子元素中设置Z-index的值,可以改变子元素之间的层叠关系

(6)子元素的Z-index值不管是高于父元素还是低于父元素,

只要他们的z-index值是大于等于0的数,他们都会显示在父元素之上,即压在父元素上。

只要他们的值是小于0的数,则显示在父元素之下!

webpack
1.webpack的几个配置项
entry,output,models里边的loaders,plugins,dev-server
2.配置sass需要几个loader,顺序是什么
css-loader,style-loader,sass-loader

react
1.虚拟dom的原理,怎么创建虚拟dom
2.diff算法
key值的作用
3.react中的高阶组件
4.react中资源的按需加载
5.生命周期
constructor, componentWillMount, render, componentDidMount, componentWillReceiveProps, componentWillUpdate, componentDidUpdate, shouldComponentUpdate, componentWillUnmount
请求数据在didMount中进行,不会多次渲染,如果在willMount中进行可能会多次渲染

6.组件之间传参的方式
父子组件:
非父子组件:发布订阅者模式,bus总线,观察者模式
发布订阅者模式参考:https://blog.youkuaiyun.com/sinat_38757616/article/details/85707510

vue
1.vue的生命周期
2.组建之间怎么传参
父子组件传值:
非父子组件传值:bus总线,发布订阅者模式,观察者模式
bus总线方式:

首先是在Vue的protptype上挂载了一个名字叫做bus的属性,Vue.prototype.bus = new Vue(),这个属性指向一个Vue的实例,只要以后调用new Vue()或者创建组件的时候每一个组件上都会有一个bus属性。
//~ 在main.js中加入
Vue.prototype.bus = new Vue()
下面的组件A和组件B可以是项目中任意两个组件:
//~ 组件A中,监听事件
this.bus.$on('change', function(data) {
    console.log(data)  // data就是触发update事件带过来的数据
})
//~ 组件B中,触发事件
this.bus.$emit('change', data)  // data就是触发update事件要带走的数据

3.vuex中action和mutation的区别
4.双向数据绑定的原理
双向绑定如何实现:v-model指令
5.虚拟dom
6.watch与computed的区别
watch:检测属性的值有没有发生变化
computed:里边定义的方法可以自动调用,不能用在事件绑定上,里边的值没有发生变化时函数不会执行,依赖缓存机制,而method里边的不管有没有变化,函数都会执行,method里边的方法不能自动调用

小程序/公众号
登录授权:
1 第一步:用户同意授权,获取code

2 第二步:通过code换取网页授权access_token

3 第三步:刷新access_token(如果需要)

4 第四步:拉取用户信息(需scope为 snsapi_userinfo)

5 附:检验授权凭证(access_token)是否有效

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值