![](https://img-blog.csdnimg.cn/20201017160919781.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3FxXzQwNDEyNDU2,size_16,color_FFFFFF,t_70)
前言
最近正在找工作,将面试官的提问记录一下。吐槽南京外包公司太多!!!我们注定要被外包的命运!!!
开始
// 用 Object.assign();
<script>
var obj1 = {name:'小A',sex:'女'};
var obj2 = {age:'30',job:'程序猿'};
Object.assign(obj1,obj2);
console.log(obj1);
</script>
========================================
//使用JQuery的extend方法
o3 = $.extend(o1, o2) // 合并 o1 和 o2, 将结果返回给 o3. 注意: 此时,o1 == o3! 即 o1 被修改
// 或
o3 = $.extend({}, o1, o2) // 合并 o1 和 o2, 将结果返回给 o3. 注意: 此时,o1 != o3! 即 o1 没有被修改
========================================
//遍历赋值法
var extend=function(o,n){
for (var p in n){
if(n.hasOwnProperty(p) && (!o.hasOwnProperty(p) ))
o[p]=n[p];
}
};
// 1.for循环
使用临时变量,将长度缓存起来,避免重复获取数组长度,当数组较大时优化效果才会比较明显。
for(j = 0,len=arr.length; j < len; j++) {}
===================================================
//2.foreach循环
遍历数组中的每一项,没有返回值,对原数组没有影响,不支持IE
//没有返回值
arr.forEach((item,index,array)=>{
//执行代码
})
//参数:value数组中的当前项, index当前项的索引, array原始数组;
//数组中有几项,那么传递进去的匿名回调函数就需要执行几次;
===================================================
//3.map循环
有返回值,可以return出来
map的回调函数中支持return返回值;return的是啥,相当于把数组中的这一项变为啥(并不影响原来的数组,只是相当于把原数组克隆一份,把克隆的这一份的数组中的对应项改变了);
var ary = [12,23,24,42,1];
var res = ary.map(function (item,index,ary ) {
return item*10;
})
console.log(res);//-->[120,230,240,420,10]; 原数组拷贝了一份,并进行了修改
console.log(ary);//-->[12,23,24,42,1]; 原数组并未发生变化
===================================================
//4.for...of遍历
可以正确响应break、continue和return语句
for (var value of myArray) {
console.log(value);
}
===================================================
//5.filter遍历
不会改变原始数组,返回新数组
var arr = [
{ id: 1, text: 'aa', done: true },
{ id: 2, text: 'bb', done: false }
]
console.log(arr.filter(item => item.done))
===================================================
//6.every遍历
every()是对数组中的每一项运行给定函数,如果该函数对每一项返回true,则返回true。
var arr = [ 1, 2, 3, 4, 5, 6 ];
console.log( arr.every( function( item, index, array ){
return item > 3;
}));
===================================================
//7.some遍历
some()是对数组中每一项运行指定函数,如果该函数对任一项返回true,则返回true。
var arr = [ 1, 2, 3, 4, 5, 6 ];
console.log( arr.some( function( item, index, array ){
return item > 3;
}));
===================================================
//8.reduce
reduce() 方法接收一个函数作为累加器(accumulator),数组中的每个值(从左到右)开始缩减,最终为一个值。
var total = [0,1,2,3,4].reduce((a, b)=>a + b); //10
reduce接受一个函数,函数有四个参数,分别是:上一次的值,当前值,当前值的索引,数组
[0, 1, 2, 3, 4].reduce(function(previousValue, currentValue, index, array){
return previousValue + currentValue;
});
===================================================
//9.reduceRight
reduceRight()方法的功能和reduce()功能是一样的,不同的是reduceRight()从数组的末尾向前将数组中的数组项做累加。
var arr = [0,1,2,3,4];
arr.reduceRight(function (preValue,curValue,index,array) {
return preValue + curValue;
}); // 10
===================================================
//10.find
find()方法返回数组中符合测试函数条件的第一个元素。否则返回undefined
stu.find((element) => (element.name == '李四'))
===================================================
//11.findIndex
对于数组中的每个元素,findIndex 方法都会调用一次回调函数(采用升序索引顺序),直到有元素返回 true。只要有一个元素返回 true,findIndex 立即返回该返回 true 的元素的索引值。如果数组中没有任何元素返回 true,则 findIndex 返回 -1。
findIndex 不会改变数组对象。
[1,2,3].findIndex(function(x) { x == 2; });
===================================================
//12.keys,values,entries
ES6 提供三个新的方法 —— entries(),keys()和values() —— 用于遍历数组。它们都返回一个遍历器对象,可以用for...of循环进行遍历,唯一的区别是keys()是对键名的遍历、values()是对键值的遍历,entries()是对键值对的遍历
for (let index of ['a', 'b'].keys()) {
console.log(index);
}
// 0
// 1
for (let elem of ['a', 'b'].values()) {
console.log(elem);
}
// 'a'
// 'b'
for (let [index, elem] of ['a', 'b'].entries()) {
console.log(index, elem);
}
// 0 "a"
// 1 "b"
// 浅拷贝
function simpleCopy(){
var obj2 = Array.isArray(obj1)?[]:{};
for(let i in obj1){
obj2[i] = obj1[i]
}
return obj2
}
var obj1 = {
a: 1,
b: 2,
c: {
d: 3
}
}
var obj2 = simpleCopy(obj1);
obj2.a = 3;
obj2.c.d = 4;
alert(obj1.a); // 1
alert(obj2.a); // 3
alert(obj1.c.d); // 4
alert(obj2.c.d); // 4
=================================================
var obj = {
a: 1,
b: 2
}
var obj1 = Object.assign(obj);
obj1.a = 3;
console.log(obj.a) // 3
//深拷贝
1、通过JSON对象来实现深拷贝
function deepClone2(obj) {
var _obj = JSON.stringify(obj),
objClone = JSON.parse(_obj);
return objClone;
}
=================================================
2、采用递归去拷贝所有层级属性
function deepClone(obj){
let objClone = Array.isArray(obj)?[]:{};
if(obj && typeof obj==="object"){
for(key in obj){
if(obj.hasOwnProperty(key)){
//判断ojb子元素是否为对象,如果是,递归复制
if(obj[key]&&typeof obj[key] ==="object"){
objClone[key] = deepClone(obj[key]);
}else{
//如果不是,简单复制
objClone[key] = obj[key];
}
}
}
}
return objClone;
}
let a=[1,2,3,4],
b=deepClone(a);
a[0]=2;
console.log(a,b);
// 父组件
<template>
<div @click="fatherMethod">
<child ref="child"></child>
</div>
</template>
<script>
import child from '~/components/dam/child.vue';
export default {
components: {
child
},
methods: {
fatherMethod() {this.$refs.child.childMethods();
}
}
};
</script>
=============================================
// 子组件
<template>
<div>{{name}}</div>
</template>
<script>
export default {
data() {
return {
name: '测试'
};
},
methods: {
childMethods() {
console.log(this.name);
}
}
};
</script>
在父组件中, this.$refs.child 返回的是一个vue实例,可以直接调用这个实例的方法
var 没有块级作用域,支持变量提升。
let 有块级作用域,不支持变量提升。不允许重复声明,暂存性死区。不能通过window.变量名进行访问.
const 有块级作用域,不支持变量提升,不允许重复声明,暂存性死区。声明一个变量一旦声明就不能改变,改变报错。
原型链:每个被实例对象都有__proto__对象,它指向了构造该对象的构造函数的prototype属性。、
同时该对象可以通过__proto__对象来寻找不属于自身的属性,
原型:就是实现继承过程中产生的一个概念。
继承:复制父类的属性和方法来重写子类的原型对象
原型继承
构造函数继承
组合继承
寄生继承
寄生组合继承
class
等等
闭包就是有权访问一个函数内部变量的函数,也就是常说的函数内部嵌套函数,
内部函数访问外部函数变量,从而导致垃圾回收机制没有将当前变量回收掉。
这样的操作,有可能会带来内存泄漏。好处就是可以设计私有的方法和变量。
浅拷贝
通常需要拷贝的对象内部只有一层的这种对象。
常用的方法
Object.assign方法来实现
扩展运算符 ...obj
深拷贝
通常是嵌套二层或以上的复杂对象
常用方法
JSON.parse(JSON.stringfy(object)); 该方法忽略掉undefined、忽略Symbol、忽略function。只适合简单深拷贝
手写递归方法去实现。
通过第三方库提供的深拷贝实现。
防抖函数:将多次触发变成最后一次触发;
function debounce(fn,wait){
let timer = null;
return function (){
let arg = arguments;
if(timer){
clearTimeout(timer);
timer = null;
}
timer = setTimeout(()=>{
fn.apply(this,arg)
},wait)
}
}
function clg(){
console.log('clg')
}
window.addEventListener('resize',debounce(clg,1000))
节流函数:将多次执行变成每隔一个时间节点去执行的函数
function throttle(fn,time){
let lastTime = null;
return function(){
let nowTime = Date.now();
if(nowTime - lastTime > time || !lastTime){
fn();
last = nowTime
}
}
}
function sayHi(){
console.log('hi')
}
setInterval(throttle(sayHi,1000),500)
相同点:都是重定向this指针的方法。
不同点:call和apply的第二个参数不相同,call是若干个参数的列表。apply是一个数组
bind方法是直接返回一个新的函数,需要手动去调用才能执行。
目前暂时已知的跨域方法是:
jsonp跨域,原理:script标签没有跨域限制的漏洞实现的一种跨域方法,只支持get请求。安全问题会受到威胁。
cors跨域,通过后端服务器实现,Access-Control-Allow-Origin。
postMessage window的一个属性方法。
websocket
nginx反向代理
iframe跨域
- 简述cookie、localstorage、seesionstorage
名称 | 大小 | 网络请求 | 生命周期 |
---|
cookie | 4kb左右 | 每次都会携带在HTTP头中,如果使用cookie保存过多数据会带来性能问题 | 默认是关闭浏览器后失效, 但是也可以设置过期时间 |
localstorage | 5M | 仅在浏览器中保存,不参与和服务器的通信 | 除非手动被清除,否则永久保存 |
SessionStorage | 5M | 仅在浏览器中保存,不参与和服务器的通信 | 仅在当前会话(窗口)下有效,关闭窗口或浏览器后被清除, 不能设置过期时间 |
Object.defineProperty缺点:
无法监控数组下标的变化,导致直接通过数组的下标给数组设置值。不能事实响应。vue内部通过数组的一些方法来监听。
只能劫持对象的属性,因此要对每个对象的属性进行遍历。 vue2.x版本之后是通过递归和遍历实现对data对象的数据监控。
proxy:
可以劫持整个对象,并返回一个新的对象
有多种劫持操作
1、vue使用的是template模版编写。react使用的是jsx语法。
2、状态管理:react中的状态全部存入state中,通常修改的时候需要用到setState方法来更新状态。
vue中的3、state对象不是必须,vue是通过data属性在vue对象中进行管理
4、监听数据的变化,vue劫持一些函数,能精确的知道数据变化。
react中默认是通过比较引用的方式去进行,如果不优化使用
shouldComponentUpdate/PureComponent方法优化,那会导致大量的虚拟dom重新渲染
5、数据流不同:vue可以进行组件与dom之间v-modle双向绑定。react从始至终都只有单向数据流
vue中使用的是mixins。react使用的是Hoc高阶组件
link | import |
---|
页面被加载,link会同时被加载 | @import引用的css会等到页面被加载完成之后再加载。 |
只适用与2.1之后的版本 | link是没有任何兼容问题的。 |
支持使用js去控制dom改变样式 | 不支持 |
| 只能加载css |
get | post |
---|
参数长度有限制 | 参数长度无限制 |
get会把请求的数据附加在url上 | post请求会把数据附加在请求体中 |
get是明文传输 | post不是明文传输 |
请求能缓存 | 不能缓存 |
http | https |
---|
80端口 | 443端口 |
无需申请证书 | 需要申请证书 |
超文本传输协议 | ssl加密协议 |
快 | 慢(因为会有一个ssl包需要传输) |
setState只是在合成事件和生命周期函数中是异步更新
在settimeout、原生事件、async函数中是同步更新。
react的异步请求,放入componentDidMount中才是正确操作。
因为WillMount中请求发送,react的执行机制是不会等到数据返回之后才继续往下执行,
而是继续向下执行并render。不会‘暂停’以等待数据到达。
问题:服务器渲染时,如果在WillMount中请求数据,fetch data会执行两次,
一次在服务端一次在客户端。造成了多余的请求,而且在16版本之后,WillMount可能在一次渲染中多次调用。
父传子:父组件通过props的方式传递。
子传父:props+回调函数方式。
兄弟组件:找到这两个共同的父节点,结合props和回调函数进行通信
跨层级通信:context通信
store
单项数据流,只能通过数据层的变化去影响视图层变化。
数据驱动视图。无需关注dom,只用关注数据即可
渲染过程,生命周期函数
diff算法。对照两次dom不同的部分渲染
// 性能优化方法
dns预解析
浏览器缓存,强缓存和协商缓存
预加载 将一些不影响首屏但重要的文件延后加载 preload
预渲染 prerender
懒加载
文件优化
webpack优化 使用到tree shaking。各种loader等等
1、声明命令let和const
2、模板字符串(Template String)
3、函数的扩展
4、对象的扩展
5、for...of 循环
6、import和export
7、Promise对象(Promise是异步编程的一种解决方案,将异步操作以同步操作的流程表达出来,
避免了层层嵌套的回调函数。)
8、解构赋值
9、set数据结构
10、箭头函数
11、class
200:请求被正常处理
204:请求被受理但没有资源可以返回
206:客户端只是请求资源的一部分,服务器只对请求的部分资源执行GET方法,相应报文中通过Content-Range指定范围的资源。
301:永久性重定向
302:临时重定向
303:与302状态码有相似功能,只是它希望客户端在请求一个URI的时候,能通过GET方法重定向到另一个URI上
304:发送附带条件的请求时,条件不满足时返回,与重定向无关
307:临时重定向,与302类似,只是强制要求使用POST方法
400:请求报文语法有误,服务器无法识别
401:请求需要认证
403:请求的对应资源禁止被访问
404:服务器无法找到对应资源
500:服务器内部错误
503:服务器正忙
1、建立TCP连接
2、Web浏览器向Web服务器发送请求行
3、Web浏览器发送请求头
4、Web服务器应答
5、Web服务器发送应答头
6、Web服务器向浏览器发送数据
7、Web服务器关闭TCP连接
GET: 用于请求访问已经被URI(统一资源标识符)识别的资源,可以通过URL传参给服务器
POST:用于传输信息给服务器,主要功能与GET方法类似,但一般推荐使用POST方式。
PUT: 传输文件,报文主体中包含文件内容,保存到对应URI位置。
HEAD: 获得报文首部,与GET方法类似,只是不返回报文主体,一般用于验证URI是否有效。
DELETE:删除文件,与PUT方法相反,删除对应URI位置的文件。
OPTIONS:查询相应URI支持的HTTP方法。
由于浏览器的 同源策略,在出现 域名、端口、协议有一种不一致时,
就会出现跨域,属于浏览器的一种安全限制。
1、资源压缩合并,减少http请求
2、非核心代码异步加载 --> 异步加载的方式 --> 异步加载的区别
3、利用浏览器缓存 --> 缓存的分类 --> 缓存的原理
4、使用CDN
5、DNS预解析
1、css文件加载需要一些时间,在加载的过程中页面是空白的。 解决:可以考虑将css代码前置和内联。
2、首屏无实际的数据内容,等待异步加载数据再渲染页面导致白屏。 解决:在首屏直接同步渲染html,
后续的滚屏等再采用异步请求数据和渲染html。
3、首屏内联js的执行会阻塞页面的渲染。 解决:尽量不在首屏html代码中放置内联脚本。(来自翔歌)
解决方案
根本原因是客户端渲染的无力,因此最简单的方法是在服务器端,使用模板引擎渲染所有页面。同时
1减少文件加载体积,如html压缩,js压缩
2加快js执行速度 比如常见的无限滚动的页面,可以使用js先渲染一个屏幕范围内的东西
3提供一些友好的交互,比如提供一些假的滚动条
4使用本地存储处理静态文件。
优点:
1、具有桌面应用的即时性、网站的可移植性和可访问性。
2、用户体验好、快,内容的改变不需要重新加载整个页面。
3、基于上面一点,SPA相对对服务器压力小。
4、良好的前后端分离。SPA和RESTful架构一起使用,后端不再负责模板渲染、输出页面工作,
web前端和各种移动终端地位对等,后端API通用化。
5、同一套后端程序代码,不用修改就可以用于Web界面、手机、平板等多种客户端;
缺点:
1、不利于SEO。(如果你看中SEO,那就不应该在页面上使用JavaScript,你应该使用网站而不是Web应用)
2、初次加载耗时相对增多。
3、导航不可用,如果一定要导航需要自行实现前进、后退。
refAge: {
type: Number,
default: 0
},
refName: {
type: String,
default: ''
},
hotDataLoading: {
type: Boolean,
default: false
},
hotData: {
type: Array,
default: () => {
return []
}
},
getParams: {
type: Function,
default: () => () => {}
},
meta: {
type: Object,
default: () => ({})
}
- 怎么定义vue-router的动态路由?怎么获取传过来的动态参数?
在router目录下的index.js文件中,对path属性加上/:id。 使用router对象的params.id 例如 : this.$route.params.id;
vue用来写路由一个插件。router-link、router-view
var str ='http://p1.ifengimg.com/fck/2017_21/2c1b05acd3d0b73_w600_h344.jpg';
str.substring(str.lastIndexOf('/')+1); //2c1b05acd3d0b73_w600_h344.jpg