前端常见的知识点
1.简单说一下vue中的$nextTick的作用
当数据发生变化页面dom熏染不出来时可以使用,这个的意思是在页面渲染完成后执行。
2.当一个页面需要渲染的数据太多,处理不过来怎么解决
分页=》懒加载=》新线程
3.请介绍一下cache-control
每一个资源都可以通过cache-control HTTP头来定义自己的缓存策略,Cache-Control头在HTTP/1.1规范中定义,取代之前用来定义响应缓存策略的头。
4.JavaScript继承的6种方法
- 原型链继承
- 借用构造函数继承
- 组合继承(原型+借用构造)
- 原型式继承
- 寄生式继承
- 寄生组合式继承
5.简述同步和异步的区别
同步是阻塞模式,异步是非阻塞模式。
同步是指的是一个进程在执行某个请求的时候,若该请求需要一段时间才能返回信息,那么这个进程将会一直等待下去,直到收到返回信息才继续执行下去。
异步是指进程不需要一直等待下去,而是继续执行下面的操作,不管其他进程的状态。当有消息返回时系统会通知进程进行处理,这样就可以提高执行效率。
6.img的alt和title的异同?
alt是图片加载失败时,显示在网页上的代替文字;
title是鼠标放上面是显示的文字,title是图片的描述和进一步说明;
alt是img必要的属性,而title不是。
7.setTimeout与setInterval有何区别?
- setTimeout和setInterval的语法相同,他们都有两个参数。一个将要执行的代码字符串,还有一个是以毫秒为单位的时间间隔,当过了那个时间段之后将执行那段代码。
- 区别:setInterval在执行完一次代码之后,经过那个固定时间间隔,它还会自动重复执行代码。而setTimeout只执行一次那段代码。
8.px和em的区别
px的值是固定的,是绝对单位;em的值不是固定的,它是相对单位,em是指当前的默认字号大小的倍数。
9.深度数据拷贝的四种方法
1.递归方法进行数据深度拷贝
function deepclone(obj){
//首先是判断obj的类型是对象还是数组
var params = Array.isArray(obj)?{}:[];
for( key in obj )
{
if( obj(key) && typeof obj(key) == 'object' ){
params[key] = deeplone(obj[key]);
}else{
params[key] = obj[key]
}
}
return params;
}
a = {
id:1,
obj:{
name:'李白',
age:222
}
}
var b = deepclone(a);
a.obj.age = 100;
console.log(a);
console.log(b);
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-eYGJpjD5-1610201778340)(C:\Users\glzpcadm\AppData\Roaming\Typora\typora-user-images\1603703318042.png)]
2.JSON.对象的方法实现
function deepclone(obj){
var params =JSON.parse( JSON.Stringify(obj));
return params;
}
a = {
id:1,
obj:{
name:'李白',
age:222
}
}
var b = deepclone(a);
a.obj.age = 100;
console.log(a);
console.log(b);
3.用第三方插件实现Lodash实现深度拷贝
//先引js文件
<script src="https://cdn.bootcdn.net/ajax/libs/lodash.js/4.17.15/lodash.js"></script>
a = {
id:1,
obj:{
name:'李白',
age:222
}
}
b = ._cloneDeep(a,{
obj:{
name:'杜甫',
age:222
}
});
console.log(a);
console.log(b);
4.用jquery里面extend的方法
a = {
id:1,
obj:{
name:'李白',
age:222
}
},
b = $.extend(a,{
a.obj.name = '张三'
})
console.log(a);
console.log(b)
10.数据浅拷贝
//数据的浅拷贝就是增加查找堆中值的栈中的地址,通过栈的地址就能查找到堆中的值,堆的值不变。
let aa = { name:'李白'};
let bb = aa;
bb.name = '杜甫';
console.log(bb); // { name: '杜甫'}
console.log(aa); // { name: '杜甫'}
11.import 和require的区别
- require是AMD的规范引入方式,是运行时调用,所以require可以运行在代码的任何地方。require是赋值过程,require的结果就是对象、数字、字符串、函数等,在把require的结果赋值给某个变量。
- import是ES6的一个语法标准。import是编译时调用,所以必须放在文件头部 。import是解构的过程。
12.webpack中的loader的作用以及与plugin的区别
-
loader就是对于模块源的转换,loader描述了webpack如何处理非javaScript模块,并且在buld文件夹中引入这些依赖。
- 在webpack.config.js中指定loader,module.rules可以指定多个loader。loader从模板路径解析node_modules
-
plugin为loader更多的拓展性,目的是解决loader无法实现的其他事,从打包优化和压缩到重新编译环境变量,功能强大到用于处理各种各样的任务。
-
webpack4默认不需要配置文件,可以通过mode的选项为webpack指定了一些默认的配置,mode分为:development/production,默认是production.
在webpack4以下的版本需要通过plugin进行环境变量的配置。
13.vue打包后会生成哪些文件
- css文件
- js文件开发时调用js代码
- app.js里面存放的是项目的各个页面的逻辑代码
- manifest.js是webpack打包生成的一个配置文件
- vendor.js里面放的是各个页面的各个组件的代码
- index.html是前端代码入口的一个html文件
14.var let 和const的区别
-
声明过程
-
var:遇到有var的作用域,在任何语句执行器都已经完成了声明和初始化,也就是变量的提升并且拿到Undefined的原因。
-
let:解析器进入了一个块级作用域,发现let关键字,然后先声明变量,并没有到开始初始化,如果在此作用域提前使用,会报Undefined的错误,这就是暂时性死区的由来。等解析到有let那一行时,才会进入初始化的阶段,如果那一行时赋值操作,初始化和赋值同时进行。
const和let一样
-
-
内存分配
- var会直接在栈内存中预分配内存的空间,然后等到实际语句执行时,再存对应的变量
-
变量提升
- var声明的变量属于函数作用域,let和const声明的变量属于块级作用域
- var存在变量提升的现象,而let和const没有此现象
- var可以重复声明变量,而在同一块级作用域,let变量不能重新声明,const不能重新赋值。
for (var i = 0; i < 10; i++) {
setTimeout(function(){
console.log(i);
},100)
};
//结果打印出 10个10
for (let i = 0; i < 10; i++) {
setTimeout(function(){
console.log(i);
},100)
};
//打印出1-9
15.Vue的响应式原理
当一个vue实例被创建时,vue会遍历data选项的属性,用object.defineProperty将它们转为getter/setter并且在内部追踪相关依赖,在属性被访问和修改时通知变化。
每一个组件实例都有相应的watcher程序实例,它会在组件渲染的过程中把属性记录为依赖,之后当依赖项的setter被调用时,会通过watcher重新计算,从而使它相关的组件得以更新。
16.Html5的新元素有哪些?
-
用于绘画的canvas的元素
-
用于媒介回放的video和audio元素
-
对本地的存储有更好的存储
-
新的特殊内容元素,比如article、footer、header、nav、
-
新的表单控件:calendar、date、time、email、url、search
17.ajax请求过程
- 创建XMLHttpRequest对象
- 创建一个新的HTTP请求,并指定该HTTP的请求方法、URL以及验证信息
- 设置响应HTTP请求状态变化的函数
- 获取异步调用返回的数据
- 使用javascript和DOM实现局部刷新
18.JS数据类型
基本数据类型和引用数据类型
基本数据类型:Number、String、Boolean、Null、Undefined、Symbol
引用数据类型:Object({}、[])
19.vue引用echart的步骤以及配置
- 首先先安装echart包
npm install echart --save
- 然后在vue项目中的main.js中全局引用
import echarts from "echarts"
- 然后在项目中创建echarts模板
<div id="testechart"> </div>
- 初始化模板
20.让img在水平居中
<img src="" style="margin: 0.3rem auto;display: table-cell;vertical-align: middle;">
21.如何封装vue组件属于父子组件传值的问题
22.keep-alive作用
keep-alive是Vue提供的一个抽象组件,用来对组件进行缓存,从而节省性能,由于是一个抽象组件,所以在v页面渲染完毕后不会被渲染成一个DOM元素 。
当组件在keep-alive内被切换时组件的**activated、deactivated**这两个生命周期钩子函数会被执行
23.事件循环和回调队列宏观和微观任务执行顺序
- 考察的是事件循环和回调队列。注意以下几点:
- Promise 优先于 setTimeout 宏任务,所以 setTimeout 回调会最后执行
- Promise 一旦被定义就会立即执行
- Promise 的 resolve 和 reject 是异步执行的回调。所以 resolve() 会被放到回调队列中,在主函数执行完和 setTimeout 之前调用
- await 执行完后,会让出线程。async 标记的函数会返回一个 Promise 对象
async function async1() {
console.log('async1 start');
await async2();
console.log('async1 end');
}
async function async2() {
console.log('async2');
}
console.log('script start');
setTimeout(()=>{
console.log('setTimeout');
},0)
async1();
new Promise((resolve)=>{
console.log('promise1');
resolve();
}).then(()=>{
console.log('promise2');
});
console.log('script end');
结果:
script start
async1 start
async2
promise1
script end
async1 end
promise2
setTimeout
解析:
- 首先,事件循环从宏任务(macrostack)队列开始,这个时候,宏任务队列中,只有一个 script (整体代码)任务。从宏任务队列中取出一个任务来执行。
- 首先执行 console.log(‘script start’),输出 ‘script start’
- 遇到 setTimeout 把 console.log(‘setTimeout’) 放到 macrotask 队列中
- 执行 aync1() 输出 ‘async1 start’ 和 ‘async2’ ,把 console.log(‘async1 end’) 放到 micro 队列中
- 执行到 promise ,输出 ‘promise1’ ,把 console.log(‘promise2’) 放到 micro 队列中
- 执行 console.log(‘script end’),输出 ‘script end’
- macrotask 执行完成会执行 microtask ,把 microtask quene 里面的 microtask 全部拿出来一次性执行完,所以会输出 ‘async1 end’ 和 ‘promise2’
- 开始新一轮的事件循环,去除执行一个 macrotask 执行,所以会输出 ‘setTimeout’
24.this出现的场景分为四类
1、有对象就指向调用对象
2、没调用对象就指向全局对象:window是js中的全局对象
3、用new构造就指向新对象
4、通过apply或者call或bind来改变this的指向
25.箭头函数和普通函数的区别
- 样式不同,箭头函数时=>,普通函数是函数名 function
- 箭头函数不能作为构造函数使用,也就不能使用new的关键字
- 箭头函数不能绑定arguments,可以使用剩余参数来代替
- 箭头函数会捕获其所在上下文的this值,作为自己的this值,定义的时候就确定了
- 箭头函数没有原型属性
26.vue中的data为啥是函数
因为组件是可以复用的,JS里对象是引用关系,如果组件data是一个对象,没有子组件中的data属性值就会相互污染产生副作用,所以一个组件的data必须是一个函数。
27.promise和async-await有什么区别?
promise是一个异步编程解决方案,减少代码量,提高代码的可读性,有效的解决了回调地狱问题,可以链式调用。
async作为一个关键字放到函数的前面,调用该函数会返回一个promise对象,功能和promise差不多,但是async-await,await关键字只能放在async函数里面,它等待的是promise对象的执行完毕,并放回结果。
28.js事件循环是一种什么机制?
在js的运行机制中,把任务分为同步任务和异步任务,其中异步任务又分为宏任务和微任务。js
29.git添加ssh
//生成密匙
ssh-keygen -t rsa -C "1197235402@qq.com"
30.vue init webpack 项目名 和 vue create 项目名区别
vue init webpack 是vue-cli2.x的初始化方式,可以使用github上面的一些模板来初始化项目,webpack是官方推荐的标准模板名
vue create 是vue-cli3.x的初始化方式,目前模板是固定的,模板选项可自由配置,创建出来的是vue-cli3的项目,与cue-cli2项目结构不同,配置方法不同,具体配置方法参考官方文档。
31.状态码
400 | Bad Request | 客户端请求的语法错误,服务器无法理解 |
---|---|---|
302
已定义范围 | 分类 | |
---|---|---|
1XX | 100-101 | 信息提示 |
2XX | 200-206 | 成功 |
3XX | 300-305 | 重定向 |
4XX | 400-415 | 客户端错误 |
5XX | 500-505 | 服务器错误 |
200 OK 服务器成功处理了请求(这个是我们见到最多的) |
---|
301/302 Moved Permanently(重定向)请求的URL已移走。Response中应该包含一个Location URL, 说明资源现在所处的位置 |
304 Not Modified(未修改)客户的缓存资源是最新的, 要客户端使用缓存 |
404 Not Found 未找到资源 |
501 Internal Server Error服务器遇到一个错误,使其无法对请求提供服务 |
200 请求成功。一般用于GET与POST请求 |
204 无内容。服务器成功处理,但未返回内容。在未更新网页的情况下,可确保浏览器继续显示当前文档 |
206 部分内容。服务器成功处理了部分GET请求 |
301 永久移动。请求的资源已被永久的移动到新URI,返回信息会包括新的URI,浏览器会自动定向到新URI。今后任何新的请求都应使用新的URI代替 |
400 客户端请求的语法错误,服务器无法理解 |
403 服务器理解请求客户端的请求,但是拒绝执行此请求 |
405 客户端请求中的方法被禁止 |
500 服务器内部错误,无法完成请求 |
502 充当网关或代理的服务器,从远端服务器接收到了一个无效的请求 |
503 由于超载或系统维护,服务器暂时的无法处理客户端的请求。延时的长度可包含在服务器的Retry- After 头信息中 |
504 充当网关或代理的服务器,未及时从远端服务器获取请求 |
区别:axios是通过Promise实现对ajax技术的一种封装,就像jquery对ajax的封装一样,简单的来说ajax是实现数据的局部刷新,axios实现了对ajax的封装,axios有的ajxa都有,ajax有的axios不一定有。
总结:axios就是ajax,ajxa不只是axios.。
ajax:
1.本身是针对MVC编程,不符合前端的MVVM的浪潮
2.基于原生的XHR开发,XHR本身的架构不清晰,已经可fetch的替代方案,jquery整个项目太大,单纯使用的ajax却要引入整个jquery非常不合理
3.ajax不支持浏览器的back按钮
4.安全问题ajax暴露了与服务器交互的细节
5.对搜索引擎的支持比较弱
6.破坏了程序的异常机制
7.不容易调试
axios:
1.从node.js创建http请求
2.支持Promise API
3.客户端防止CSRF(网站恶意利用)
4.提供了一些并发请求的接口
33.jquery和vue的区别
jquery:只是针对原生JS的API选择器等等进行了封装,便于操作dom,本质还是操作DOM实现逻辑,数据和界面还是连载一起的。使用于需要操作dom的业务:动画,交互效果,页面特效。
VUE:MVVM模型,将数据层和视图层完全分离开,不仅对api进行封装,还提供可一系列的解决方案。事件的驱动机制,主要操作的是数据而不是频繁的操作DOM。
34.JavaScript是单线程还是多线程
多线程要考虑线程之间的资源抢占,死锁,冲突之类一系列问题。JavaScript作为一门客户端脚本,貌似没有多线程的一些列问题。那么JavaScript是单线程还是多线程?通过查资料总结了JavaScript运行的原理。如下:
一、为什么JavaScript是单线程?
JavaScript语言的一大特点就是单线程,也就是说,同一个时间只能做一件事。那么,为什么JavaScript不能有多个线程呢?这样能提高效率啊。
JavaScript的单线程,与它的用途有关。作为浏览器脚本语言,JavaScript的主要用途是与用户互动,以及操作DOM。这决定了它只能是单线程,否则会带来很复杂的同步问题。比如,假定JavaScript同时有两个线程,一个线程在某个DOM节点上添加内容,另一个线程删除了这个节点,这时浏览器应该以哪个线程为准?
为了利用多核CPU的计算能力,HTML5提出Web Worker标准,允许JavaScript脚本创建多个线程,但是子线程完
全受主线程控制,且不得操作DOM。所以,这个新标准并没有改变JavaScript单线程的本质。
二、任务队列
单线程就意味着,所有任务需要排队,前一个任务结束,才会执行后一个任务。如果前一个任务耗时很长,后一个任务就不得不一直等着。
如果排队是因为计算量大,CPU忙不过来,倒也算了,但是很多时候CPU是闲着的,因为IO设备(输入输出设备)很慢(比如Ajax操作从网络读取数据),不得不等着结果出来,再往下执行。
JavaScript语言的设计者意识到,这时主线程完全可以不管IO设备,挂起处于等待中的任务,先运行排在后面的任务。等到IO设备返回了结果,再回过头,把挂起的任务继续执行下去。
于是,所有任务可以分成两种,一种是同步任务(synchronous),另一种是异步任务(asynchronous)。同步任务指的是,在主线程上排队执行的任务,只有前一个任务执行完毕,才能执行后一个任务;异步任务指的是,不进入主线程、而进入”任务队列”(task queue)的任务,只有”任务队列”通知主线程,某个异步任务可以执行了,该任务才会进入主线程执行。
35.git将一个分支的代码移动到另一分支上
Git把当前分支上的修改转移到另一个分支上:
1.先在当前分支commit
2.获取本次commit的ID
(会获取到一个长id如:ae71cfaf9e865682e2c008aa867e8fbef7a19f7f)
git rev-parse HEAD
3.切换到新分支
git checkout -b fenzhi_name
4.在新分支上执行
git cherry-pick ae71cfaf9e865682e2c008aa867e8fbef7a19f7f
5.所有修改过的代码已提交到了当前新分支,git push 就好
36.推送远程分支
git push --set-upstream origin mengkun
37.请简述XMLHttpRequest、JSONP的适用场景,并且针对两种请求形式简述如何检测请求错误
1.XMLHttpRequest是浏览器端与服务器端进行数据请求时页面进行无刷新修改,支持get、post请求,一般用于非跨域场景。如果需要XMLHttpServer进行跨域请求数据,则需要通过CORS头支持。JSONP应用于跨域请求,只支持get请求。
2.XMLHttpRequst异常判断一般是通过该对象的readyState和状态码status来判断,JSONP的异常是通过onerror事件和超时timer来判断。
<html>
<head></head>
<body>
<div></div>
<p></p>
</body>
</html>
在页面中运行 getTags() 之后,函数就返回数组 [‘html’, ‘head’ ‘body’, ‘div’, ‘p’] (顺序不重要)。
注意:
1、标签名称使用小写
2、请使用ES5语法
3、答题时不要使用第三方插件
(function getTags () {
var list1 = document.getElementsByTagName("*"); //ES5语法,返回HTMLCollection
var list2 = document.querySelectorAll("*"); //ES6语法,返回nodeList
var tags = Array.prototype.slice.call(list1)
console.log(tags.map(function (item) {
return item.tagName.toLowerCase()
}))
})()
39.js统计数组里数据出现的次数
var names = ['Alice', 'Bob', 'Tiff', 'Bruce', 'Alice'];
var countedNames = names.reduce((obj, name) => {
if (name in obj) {
obj[name]++
} else {
obj[name]=1
}
return obj
}, {})
//reduce的第二个参数就是obj的初始值
console.log(countedNames)
// { 'Alice': 2, 'Bob': 1, 'Tiff': 1, 'Bruce': 1 }
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-IMLzjZIz-1610201778346)(C:\Users\glzpcadm\AppData\Roaming\Typora\typora-user-images\1608174358042.png)]
let data = ['a','b','a','b','c','c','d']
let datas = data.reduce((obj,data)=>{
if( data in obj){
obj[data] = true
}else{
obj[data] =false
}
return obj;
},{})
function getEleNums(data) {
var map = {}
for (i = 0; i < data.length; i++) {
var key = data[i]
if (map[key]) {
map[key] += 1
} else {
map[key] = 1
}
}
return map
}
var data = ['b','a','c','a','b','b','b','c','c','a','c','a','a','a','b','c']
console.log(getEleNums(data))
a: 6 b: 5 c: 5
function paddingNum(num){
return num.toLoaclString()
}
paddingNum(15246546.4654654)
true
false
undefined
number
function
object
a1
NaN
true
true
对于Object.assign()而言, 如果对象的属性值为简单类型(string, number),通过Object.assign({},srcObj);
得到的新对象为‘深拷贝’;如果属性值为对象或其它引用类型,那对于这个对象而言其实是浅拷贝的。
map[key] += 1
} else {
map[key] = 1
}
}
return map
}
var data = [‘b’,‘a’,‘c’,‘a’,‘b’,‘b’,‘b’,‘c’,‘c’,‘a’,‘c’,‘a’,‘a’,‘a’,‘b’,‘c’]
console.log(getEleNums(data))
a: 6 b: 5 c: 5
[外链图片转存中...(img-GqrO57AQ-1610201778349)]
```js
function paddingNum(num){
return num.toLoaclString()
}
paddingNum(15246546.4654654)
[外链图片转存中…(img-1kfE8D2q-1610201778354)]
true
false
undefined
number
function
object
a1
NaN
true
true
对于Object.assign()而言, 如果对象的属性值为简单类型(string, number),通过Object.assign({},srcObj);
得到的新对象为‘深拷贝’;如果属性值为对象或其它引用类型,那对于这个对象而言其实是浅拷贝的。