前端面试题 个人归纳

前端面试复习导图

1、 说出下面运行的结果,解释原因。

function test(person) {
  person.age = 26
  person = {
    name: 'hzj',
    age: 18
  }
  return person
}
const p1 = {
  name: 'fyq',
  age: 19
}
const p2 = test(p1)
console.log(p1) // -> ?
console.log(p2) // -> ?

结果:

p1:{name: “fyq”, age: 26}
p2:{name: “hzj”, age: 18}

原因:

在函数传参的时候传递的是对象在堆中的内存地址值,test函数中的实参person是p1对象的内存地址,通过调用person.age
= 26确实改变了p1的值,但随后person变成了另一块内存空间的地址,并且在最后将这另外一份内存空间的地址返回,赋给了p2。

2、对于已知是对象的数据类型判断,采用instanceof会更好,instanceof的原理是基于原型链的查询,只要处于原型链中,判断永远为true。

const Person = function() {}
const p1 = new Person()
p1 instanceof Person // true

var str1 = 'hello world'
str1 instanceof String // false

var str2 = new String('hello world')
str2 instanceof String // true

3、手动实现一个 instanceof 功能

  function myinstanceof (left,right) {
  	if(typeOf(left) !== object || left !== null) return false
  	let proto = Object.getPrototypeOf(left)
    while(true){
    	if(proto == null) return false
    	if(proto == right.prototype) return true
    	proto = Object.getPrototypeOf(proto)
    }
  }

4、类型转换规则
在这里插入图片描述
隐式转换的规则:

两边的类型是否相同,相同的话就比较值的大小,例如1==2,返回false
判断的是否是null和undefined,是的话就返回true
判断的类型是否是String和Number,是的话,把String类型转换成Number,再进行比较
判断其中一方是否是Boolean,是的话就把Boolean转换成Number,再进行比较
如果其中一方为Object,且另一方为String、Number或者Symbol,会将Object转换成字符串,再进行比较

5、什么是闭包:

闭包是指有权访问另外一个函数作用域中的变量的函数,就是当前的执行函数中引用了另一个函数的变量。

6、如何解决下列循环问题:

for(var i = 1; i <= 5; i ++){
  setTimeout(function timer(){
    console.log(i)
  }, 0)
} // 6,6,6,6,6

解法一:

for (let i = 0;i <= 5; i++){
	setTimeout(function timer(){
    console.log(i)
  }, 0)
}

解法二:

for(let i = 0;i <= 5; i++){
	setTimeout(function timer(i){
    console.log(i)
  }, 0,i)
}

解法三:

for (let i= 0;i<=5; i++){
	(function(j){
		setTimeout(function timer(){
    	console.log(j)
 		 }, 0)
	})(i)
}

7、js中实现继承的最推荐使用方法:

 function Parent5 () {
    this.name = 'parent5';
    this.play = [1, 2, 3];
  }
  function Child5() {
    Parent5.call(this);
    this.type = 'child5';
  }
  Child5.prototype = Object.create(Parent5.prototype);
  Child5.prototype.constructor = Child5;

8、类数组怎么转化为数组
方法一:

function sum(a, b) {
  let args = Array.prototype.slice.call(arguments);
  console.log(args.reduce((sum, cur) => sum + cur));//args可以调用数组原生的方法啦
}
sum(1, 2);//3

方法二:

function sum(a, b) {
  let args = Array.from(arguments);
  console.log(args.reduce((sum, cur) => sum + cur));//args可以调用数组原生的方法啦
}
sum(1, 2);//3

方法三:

function sum(a, b){
	let args = [...arguments]
	console.log(args.reduce((sum, cur) => sum + cur));//args可以调用数组原生的方法啦
}
sum(1, 2);//3
}

方法四:

function sum(a, b) {
  let args = Array.prototype.concat.apply([], arguments);//apply方法会把第二个参数展开
  console.log(args.reduce((sum, cur) => sum + cur));//args可以调用数组原生的方法啦
}
sum(1, 2);//3

5、请列举几个html5语义化标签,并说明语义化标签的优点

<center></center>
<footer></footer>
<header></hearder>
<artical></artical>

优点:有助于搜索引擎搜索,获取关键信息;
     有助于代码阅读;
     在没有CSS的情况下,语义化标签也能很好的展示内容结构、代码结构

6、请列举几种除了px外的CSS度量单位并解释其含义。

相对单位:
    px
    em:当用于指定字体大小时,em单位是指父元素的字体大小​
    rem:可以设置根源素的大小,批量设置整个页面的大小(文字)。也就是说相对HTML根元素​
绝对单位:
    pc
    mm

7、new操作符做了什么

1、创建一个对象
2、把这个对象的原型指向构造函数的 prototype
3、绑定this指向,执行构造函数
4、返回对象

8、网页中接收事件的顺序(事件流)有哪些?它们之间的区别是什么?

DOM2级事件”规定的事件流包括三个阶段:事件捕获、处于目标阶段和事件冒泡阶段。发生的顺序是事件捕获阶段==>目标阶段==>事件冒泡阶段
事件捕获:事件捕获的思想是越不具体的节点越早接收事件,最具体的节点最后接收事件。
事件冒泡:事件开始时有最具体的元素(文档中嵌套层次最深的那个节点)接收,然后逐级向上传播到较为不具体的节点,直到document对象。(即与事件捕获相反)

9、window的onload事件和domcontentloaded谁先谁后?

domcontentloaded 早于 window的onload事件,
window 的 onload事件要等到资源全部加载完成,才会触发;domcontentloaded 只要渲染完成,资源不一定加载完成(比如图片),即DOM树构建完成,就可以触发。

10、你之前遇到过跨域问题吗?是怎么解决的。

跨域产生的原因:
1、以下三种情况出现一种都属于跨域:协议不同、域名不同、端口号不同
2、跨域这个概念来自一个叫 “同源策略” 的东西。同源策略是浏览器(注意是浏览器,跟通信协议无关)上为了安全考虑实施的非常重要的安全机制。
3、Ajax 默认只能获取到同源的数据,对于非同源的数据,Ajax是获取不到的。
解决方法:
1、解决跨域的方法简单的有三种:反向代理,JSONP,CORS。
2、如果访问的别人服务器的资源,并且未设置JSONP,服务器也没有设置CORS接口,那么唯一的选择就是使用自己的服务器进行反向代理。
3、反向代理:就是使用自己的服务器,在后端请求目标服务器的数据,然后返回给客户端。反向代理服务器,最常用的就是Nginx。
但是作为前端代码实现的Node.js也可以搭建反向代理服务器。
比如我有一个后端接口:http://39.105.136.190:3000/zhuiszhu/goods/getList,可以获取一些商品列表数据,但是我运行的node项目是在 localhost:3000 下的,明显构成跨域。
我们根据项目使用的框架不同,处理的方式也不同。
我们经常在vue开发项目的时候,会用webpack作为前端自动化构建工具的话,也会使用到webpack-dev-server的插件,那么可以引用webpack-dev-server来进行配置跨域方案。
webpack-dev-server是一个小型的nodejs服务器,是基于express框架的,用于实时监听和打包编译静态资源。其中里面有一个属性是proxy,是专门来配置代理请求接口的。
你只需要在webpack.config.js中 devServer中如下设置即可:
devServer: {
port: 3000,
inline: true,
proxy: {
“/zhuiszhu”: {
target: “http://39.105.136.190:3000/”,
changeOrigin: true //必须配置为true,才能正确代理
}
}
},
4、JSONP
JSONP基本思想是,网页通过添加一个“<s c r i p t删除线格式 >”元素,向服务器请求JSON数
据,这种做法不受同源政策限制;服务器收到请求后,将数据作为参数放在一个指定名字的回调函数里传回来,这个回调函数的名字我们需要通过js定义好。
比如:当页面资源加载完毕时候,获取跨域的数据,并且制定回调函数的名字为为foo:
window.onload = function () {
var script = document.createElement(“script”);
script.setAttribute(“type”,“text/javascript”);
script.src = "http://bbb.com?callback=foo;
document.body.appendChild(script);
};
foo(data) {
console.log(data); // data即为跨域获取到的数据
}
使用JSONP需要注意:
必须后端配置相应回调函数。
只能发送GET请求。
5、CORS全称“ Cross-origin resource sharing ”(跨域资源共享),相比JSONP, CORS允许任何类型的请求 。
CORS需要浏览器和服务器同时支持。目前,所有浏览器都支持该功能,IE浏览器不能低于IE10。
整个CORS通信过程,都是浏览器自动完成,不需要用户参与。对于开发者来说,CORS通信与同源的AJAX通信没有差别,代码完全一样。浏览器一旦发现AJAX请求跨源,就会自动添加一些附加的头信息,有时还会多出一次附加的请求,但用户不会有感觉。
因此,实现CORS通信的关键是服务器。只要服务器实现了CORS接口,就可以跨源通信

11、.知道什么是事件委托吗?

优点:
1.只需要将同类元素的事件委托给父级或者更外级的元素,不需要给所有的元素都绑定事件,减少内存占用空间,提升性能。
2.新增元素实现动态绑定事件
需要注意的点:
1、事件委托的实现依靠的冒泡,因此不支持事件冒泡的事件就不适合使用事件委托。
2、不是所有的事件绑定都适合使用事件委托,不恰当使用反而可能导致不需要绑定事件的元素也被绑定上了事件。

实现方式:
一、可用addEventListener(); //所有主流浏览器,除了IE8及更早IE版本。
1.语法:element.addEventListener(event, function, useCapture);
event:必须。字符串,指定事件名。 不加’on’,如click
function:必须。指定要事件触发时执行的函数。
useCapture:可选。布尔值,指定事件是否在捕获或冒泡阶段执行(true-事件句柄在捕获阶段执行;false-默认。事件句柄在冒泡阶段执行)。
2.移除事件监听:element.removeEventListener(event, function,useCapture)。移除时传入的参数与添加处理程序时使用的参数相同。这也意味着通过addEventListener()添加的匿名函数无法移除。
3.功能:可多次绑定同一个事件,并且不会覆盖上一个事件。
代码实例:给所有的li绑定点击事件,极为繁琐,这时候需要用到事件代理。
ul.addEventListener(“click”,function(e) {
if(e.target && e.target.nodeName.toLowerCase() == “li”) { // 检查事件源e.target是否为Li
console.log(“List item “,e.target.id.replace(“post-”,””)," was clicked!"); // 打印当前点击是第几个item
}
})
上面的实现事件委托可能会有一个问题,当ul下有4个li,每个li标签下又有一个span标签,这时候如果给li增加事件委托,点击li就不会触发事件,所以可以封装一个方法,在点击某个标签的时候递归遍历 span 的祖先元素看其中有没有 ul 里面的 li。

12、手写防抖、节流函数

function debounce(fn){ // 防抖函数
   	let timeOut = null
      return function(){
     		clearTimeout(timeOut)
 		timeOut = setTimeout(function(){
  			fn.apply(this,arguments)
 		},500)
  	}
  }	
 function throttle(fn){ // 节流函数
  	let canRun = true
  	return function(){
 		if(!canRun) return 
 		canRun = false
 		setTimeout(function(){
  			fn.apply(this, arguments)
  		}, 500)
 	}
  }

13、localstorage的使用方法
存:

window.localStorage.setItem('mydata',JSON.stringify(data.body.data))

存localStorage需要转换成字符串,不能直接存对象,切记。

取:

window.localStorage.getItem('a') // 1 读取保存在storage对象里名为a的变量值
window.localStorage.b            // 2 读取保存在storage对象里名为b的变量值
window.localStorage['c']         // 3 读取保存在storage对象里名为c的变量值
window.localStorage.key(0)       // 4 根据key值读取数据,key(0)代表对象的第一条数据
window.localStorage.valueOf()    // 5 读取保存在storage对象上的全部数据

删除某个具体变量:

localStorage.removeItem('mydata')

删除所有数据:

localStorage.clear()

14、块级元素和内联元素

1、块级元素可以包含内联元素或某些块级元素,但内联元素不能包含块级元素,它只能包含其它内联元素。
2、有几个特殊的块级元素只能包含内联元素,不能包含块级元素。如h1,h2,h3,h4,h5,h6,p,dt
3、li内可以包含div
4、a标签可以包括任何元素,除了自身

15、元素属性的继承: 通常 font、文本、可见性的相关属性都具有继承性。

16、

1、在 box-sizing 为默认值时,width 属性只包含内容的宽度。
box-sizing有两个属性:
content-box,是默认值,width和height只包括内容的宽和高。
border-box,边框和内边距的值包含在width中,但不包括外边距。
2、div固定宽高,内容超出后会溢出,不设置宽高,超出后会自动拓展

17、canvas 和 svg 都可以使用 js 来绘制,可以给每个 svg 绑定不同的事件。

18、addEventListener第三个参数默认为fasle,为false时事件流为事件冒泡。事件开始由最具体的元素接受,即由最具体的元素执行事件。

19、关于浏览器的 cookie

一般我们都会说 “HTTP 是一个无状态的协议”,不过要注意这里的 HTTP 其实是指 HTTP 1.x,而所谓无状态协议,简单的理解就是即使同一个客户端连续两次发送请求给服务器,服务器也识别不出这是同一个客户端发送的请求,
cookie 可以解决客户端与服务端会话状态的问题,这个状态是指后端服务的状态而非通讯协议的状态。
维基上的解释:Cookie(复数形态Cookies),类型为「小型文本文件」,指某些网站为了辨别用户身份而储存在用户本地终端上的数据。(注意是 终端上是可以找到 储存 cookie 的本地文件,不是储存在浏览器客户端)。
cookie 的相关属性:

Name/Value:
用 JavaScript 操作 Cookie 的时候注意对 Value 进行编码处理。

Expires Expires 用于设置 Cookie 的过期时间。比如:

Set-Cookie: id=a3fWa; Expires=Wed, 21 Oct 2015 07:28:00 GMT;

当 Expires 属性缺省时,表示是会话性 Cookie
,像上图 Expires 的值为 Session,表示的就是会话性 Cookie。

当为会话性 Cookie 的时候,值保存在客户端内存中,并在用户关闭浏览器时失效。
与会话性 Cookie 相对的是持久性 Cookie,持久性 Cookies 会保存在用户的硬盘中,直至过期或者清除 Cookie。

这里值得注意的是,设定的日期和时间只与客户端相关,而不是服务端。

Max-Age
Max-Age 用于设置在 Cookie 失效之前需要经过的秒数。比如:

Set-Cookie: id=a3fWa; Max-Age=604800;

Max-Age 可以为正数、负数、甚至是 0。

如果 max-Age 属性为正数时,浏览器会将其持久化,即写到对应的 Cookie 文件中。

当 max-Age 属性为负数,则表示该 Cookie 只是一个会话性 Cookie。

当 max-Age 为 0 时,则会立即删除这个 Cookie。

假如 Expires 和 Max-Age 都存在,Max-Age 优先级更高。

Domain Domain 指定了 Cookie
可以送达的主机名。假如没有指定,那么默认值为当前文档访问地址中的主机部分(但是不包含子域名)。

像淘宝首页设置的 Domain 就是 .taobao.com,这样无论是 a.taobao.com 还是 b.taobao.com
都可以使用 Cookie。

在这里注意的是,不能跨域设置 Cookie,比如阿里域名下的页面把 Domain 设置成百度是无效的:

Set-Cookie: qwerty=219ffwef9w0f; Domain=baidu.com; Path=/; Expires=Wed, 30 Aug 2020 00:00:00 GMT

Path
Path 指定了一个 URL 路径,这个路径必须出现在要请求的资源的路径中才可以发送 Cookie 首部。比如设置 Path=/docs,/docs/Web/ 下的资源会带 Cookie 首部,/test 则不会携带 Cookie 首部。
Domain 和 Path 标识共同定义了 Cookie 的作用域:即 Cookie 应该发送给哪些 URL。

Domain 和 Path 标识共同定义了 Cookie 的作用域:即 Cookie 应该发送给哪些 URL。

Secure属性 标记为 Secure 的 Cookie 只应通过被HTTPS协议加密过的请求发送给服务端。使用 HTTPS
安全协议,可以保护 Cookie 在浏览器和 Web 服务器间的传输过程中不被窃取和篡改。

HTTPOnly 设置 HTTPOnly 属性可以防止客户端脚本通过 document.cookie 等方式访问 Cookie,有助于避免
XSS 攻击

SameSite
作用
我们先来看看这个属性的作用:
SameSite 属性可以让 Cookie 在跨站请求时不会被发送,从而可以阻止跨站请求伪造攻击(CSRF)。
属性值
SameSite 可以有下面三种值:
Strict 仅允许一方请求携带 Cookie,即浏览器将只发送相同站点请求的 Cookie,即当前网页 URL 与请求目标 URL 完全一致。
Lax 允许部分第三方请求携带 Cookie
None 无论是否跨站都会发送 Cookie
之前默认是 None 的,Chrome80 后默认是 Lax。
跨域和跨站
首先要理解的一点就是跨站和跨域是不同的。同站(same-site)/跨站(cross-site)」和第一方(first-party)/第三方(third-party)是等价的。但是与浏览器同源策略(SOP)中的「同源(same-origin)/跨域(cross-origin)」是完全不同的概念。
同源策略的同源是指两个 URL 的协议/主机名/端口一致。例如,https://www.taobao.com/pages/…,它的协议是 https,主机名是 www.taobao.com,端口是 443。
同源策略作为浏览器的安全基石,其「同源」判断是比较严格的,相对而言,Cookie中的「同站」判断就比较宽松:只要两个 URL 的 eTLD+1 相同即可,不需要考虑协议和端口。其中,eTLD 表示有效顶级域名,注册于 Mozilla 维护的公共后缀列表(Public Suffix List)中,例如,.com、.co.uk、.github.io 等。eTLD+1 则表示,有效顶级域名+二级域名,例如 taobao.com 等。
举几个例子,www.taobao.com 和 www.baidu.com 是跨站,www.a.taobao.com 和 www.b.taobao.com 是同站,a.github.io 和 b.github.io 是跨站(注意是跨站)。

Cookie 的作用
Cookie 主要用于以下三个方面:
会话状态管理(如用户登录状态、购物车、游戏分数或其它需要记录的信息)
个性化设置(如用户自定义设置、主题等)
浏览器行为跟踪(如跟踪分析用户行为等)

Cookie 的缺点
如果被问到话,可以从大小、安全、增加请求大小等方面回答。

20、如何提升 javascript 的变量存储性能

1、访问块级作用域变量较快,全局变量相对较慢。
2、访问字面量变量较快,访问数组内或对象内成员速度较慢
3、访问较深的原型链上的方法或属性速度较慢
4、通常来说,需要多次访问的数组元素、对象成员、跨作用域变量可以保存在局部变量当中来提升 js 的执行效率。

21、vue的运行机制或者实现原理

这里简单的描述一下 Vue 2.x 的运行机制(需要注意分析的是 Runtime + Compiler 的 Vue.js)。
初始化流程:
创建 Vue 实例对象
init过程会初始化生命周期,初始化事件中心,初始化渲染、执行beforeCreate周期函数、初始化 data、props、computed、watcher、执行created周期函数等。
初始化后,调用$mount方法对Vue实例进行挂载(挂载的核心过程包括模板编译、渲染以及更新三个过程)。
如果没有在 Vue 实例上定义render方法而是定义了template,那么需要经历编译阶段。需要先将template 字符串编译成 render function,template 字符串编译步骤如下 :

parse正则解析template字符串形成 AST(抽象语法树,是源代码的抽象语法结构的树状表现形式)
optimize标记静态节点跳过 DIFF 算法(DIFF 算法是逐层进行比对,只有同层级的节点进行比对,因此时间的复杂度只有 O(n)。如果对于时间复杂度不是很清晰的,可以查看我写的文章ziyi2/algorithms-javascript/渐进记号)
generate将 AST 转化成render function字符串

编译成render function 后,调用$mount的mountComponent方法,先执行beforeMount钩子函数,然后核心是实例化一个渲染Watcher,在它的回调函数(初始化的时候执行,以及组件实例中监测到数据发生变化时执行)中调用updateComponent方法(此方法调用render方法生成虚拟 Node,最终调用update方法更新 DOM)。
调用render方法将render function渲染成虚拟的Node(真正的 DOM 元素是非常庞大的,因为浏览器的标准就把 DOM 设计的非常复杂。如果频繁的去做 DOM 更新,会产生一定的性能问题,而 Virtual DOM 就是用一个原生的 JavaScript 对象去描述一个 DOM 节点,所以它比创建一个 DOM 的代价要小很多,而且修改属性也很轻松,还可以做到跨平台兼容),render方法的第一个参数是createElement(或者说是h函数),这个在官方文档也有说明。
生成虚拟 DOM 树后,需要将虚拟 DOM 树转化成真实的 DOM 节点,此时需要调用update方法,update方法又会调用pacth方法把虚拟 DOM 转换成真正的 DOM 节点。需要注意在图中忽略了新建真实 DOM 的情况(如果没有旧的虚拟 Node,那么可以直接通过createElm创建真实 DOM 节点),这里重点分析在已有虚拟 Node 的情况下,会通过sameVnode判断当前需要更新的 Node节点是否和旧的 Node 节点相同(例如我们设置的key属性发生了变化,那么节点显然不同),如果节点不同那么将旧节点采用新节点替换即可,如果相同且存在子节点,需要调用patchVNode方法执行 DIFF 算法更新 DOM,从而提升 DOM 操作的性能。

需要注意在初始化阶段,没有详细描述数据的响应式过程,这个在响应式流程里做说明。
响应式流程:

在init的时候会利用Object.defineProperty方法(不兼容 IE8)监听Vue实例的响应式数据的变化从而实现数据劫持能力(利用了 JavaScript 对象的访问器属性get和set,在未来的 Vue3 中会使用 ES6 的Proxy来优化响应式原理)。在初始化流程中的编译阶段,当render function被渲染的时候,会读取Vue实例中和视图相关的响应式数据,此时会触发getter函数进行依赖收集(将观察者Watcher对象存放到当前闭包的订阅者Dep的subs中),此时的数据劫持功能和观察者模式就实现了一个 MVVM 模式中的 Binder,之后就是正常的渲染和更新流程。
当数据发生变化或者视图导致的数据发生了变化时,会触发数据劫持的setter函数,setter会通知初始化依赖收集中的Dep中的和视图相应的Watcher,告知需要重新渲染视图,Wather就会再次通过update方法来更新视图。

可以发现只要视图中添加监听事件,自动变更对应的数据变化时,就可以实现数据和视图的双向绑定了。

  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值