前言:小伙伴们,本篇文章是博主自己在面试过程中的一些面试题的记录,自己在总结回顾记录的同时希望也能帮助到你们,可以结合对应的知识点去理解和查看!有什么不对的地方欢迎伙伴们指正!大家一起学习
小提示重要!:简历上的内容(项目+技能)自己一定要准备好,防止被问起。
概念:SPA( single-page application )仅在 Web 页面初始化时加载相应的 HTML、JavaScript 和 CSS实现页面的渲染加载。一旦页面加载完成,SPA 不会因为用户的操作而进行页面的重新加载或跳转;取而代之的是利用路由机制实现 HTML 内容的变换,UI 与用户的交互、避免页面的重新加载。
优点:
缺点:
劣势解决:
前言:SPA单页面应用:在使用ajax与服务器数据交互过程中,实现局部数据的更新时,浏览器地址栏的url链接并没有发生改变,浏览器因为不会保存更新记录(浏览器会记录地址栏的url资源链接,如果url链接发生变化,浏览器会把该url链接保存到一个特殊的数据结构中,这样当用户点击返回与前进按钮时,会快速访问已经被记录的url链接资源。)。这时候再去返回就会出现非期待性的结果
- 解决方案:设置window.location.hash(location.hash)+ 监听window.onhashchange
//在ajax与服务器进行交互时,设置window.location.hash的值:
function updateView (attr){
$.ajax({
type:...,
url:...,
data:{attr : attr},
success:function(datas){
//设置hash值
window.location.hash = "#"+attr;
//do somthing
},
error:function(status){
//do somthing
}
});
}
//attr 这个值最好是该次请求所需的参数 设置onhashchange事件监听
window.onhashchange=function(){
var attr=window.location.hash.replace("#","");
updateView (attr);
}
//但是上述这样单纯的只要用户点击第一页的视图,ajax请求成功后,会主动改变hash值,这时候又触发onhashchange,又一次更新视图,两次访问服务器。
//解决办法
//设置一个全局变量,记录hash值的改变是怎样引起的:
var innerDocClick;
$('body').on('mouseleave',function(){
innerDocClick=false;//鼠标在页面外(点击了返回按钮)
});
$('body').on('mouseover',function(){
innerDocClick=true;//鼠标在页面内(没有点击返回按钮)
});
window.onhashchange=function(){
if(!innerDocClick)//若点击了返回按钮 加条件判断去更改hash值
{
var attr=window.location.hash.replace("#","");
updateView (attr);
}
}
第二种解决方法是在页面中嵌入一个隐藏 iframe,由于浏览器可以对 DOM 树中 iframe 节点的 src 属性进行历史记录跟踪,这样通过在逻辑上建立一条“页面 URL – 页面内 iframe URL – 页面状态”的对应链,同样可以在 IE 中建立片段标识符与页面状态的联系。
搜索引擎的规律:是靠搜索关键字来让自己的网站在搜索栏的第一页内,展示在用户的最前面,能让用户第一时间看到自己的网站。
补充
高质量网站有哪些特点
1.内容受众
2.时效性强
3.设计与布局整洁干净
4.无虚假违法信息
1.window对象
常用的方法:
1.alert()
2.定时器
2.location对象
常用的方法:
location.href = “http://www.666.com”; //页面跳转到该网址
location.assign(“http://www.666.com”); //页面跳转到该网址
location.hash用于设置页面的标签值
常用的方法:
navigator.cookicEnablcd:判断是否启用cookic
navigator.userAgent:判断浏览器的名称和版本号
navigator.plugins:保存浏览器中所有插件信息的集合
<script>
function hasPlugin(name) {
name = name ? name.toLocaleLowerCase() : '';
console.log('name = ' + name);
var plugins = navigator.plugins;
for (var i = 0; i < plugins.length; i++) {
//console.log(typeof plugins[i]);
console.log(plugins[i].name);
return plugins[i].name.toLocaleLowerCase().indexOf(name) !== -1 ? true : false;
}
}
var r = hasPlugin('pdf');
console.log(r);
常用方法:
1.前进:history.forward();history.go(1);
2.后退:history.back();history.go(-1);
3.获取记录个数:history.length:
let x = 1;
let y = 2;
[x, y] = [y, x];
- 2.从函数返回多个值
//返回一个数组,用结构赋值可以非常方便的取到对应值
function example() {
return [1, 2, 3];
}
let [a, b, c] = example();
//返回一个对象,获取对应的属性值
function example() {
return {
foo: 1,
bar: 2
};
}
let { foo, bar } = example();
- 3.取 JSON 数据
解构赋值对提取 JSON 对象中的数据
let jsonData = {
id: 42,
status: "OK",
data: [867, 5309]
};
let { id, status, data: number } = jsonData;
console.log(id, status, number);
// 42, "OK", [867, 5309]
// 上面代码可以快速提取 JSON 数据的值。
let arr = [a,b,c,d] = [1,2,3,4]
//解构嵌套数组,就是模式匹配,只要等号两边的层级结构相同,就可以拿到对应位置的值
const arr = [1, [2, 3, [4, 5, 6]]];
const [a, [b, c, [d, e, f]]] = arr;
let [a, b, c] = [1, 2, 3, 4]; // 1 2 3
let [a, b, c, d] = [1, 2, 3]; // 1 2 3 undefined
let [a, [b, c, [d, e]]] = [1, [2, 3, [4, 5, 6]]]; // 1 2 3 4 5
let [a = true] = [];
a // true
注意
数组的解构是根据它的位置(模式)对应的
解构操作允许有默认值,但一定是已经声明的。
如果等号的右边不是数组(或者严格地说,不是可遍历的结构)那么将会报错
展开运算:允许一个表达式在某处展开。展开运算符在多个参数(用于函数调用)或多个元素(用于数组字面量)或者多个变量(用于解构赋值)的地方可以使用。
在ES6之前将整个数组里面的元素依次作为实参传递给函数形参的时候使用Function.prototype.apply的特性
let arr = [1,2,3]
function test(a,b,c){}
test.apply(null,args) //通过apply特性将数值的形式转换为数组对应传递进去
ES6之后展开运算符
let arr = [1,2,3]
function test(a,b,c){}
test(..arr) //将数组展开进行传递参数
a.合并数组
let arr = [1,2,3]
let arr1 = [...arr,4,5,6] //1,2,3,4,5,6
b.展开运算符可以用于数组的一些方法中(push函数)
let arr = [1,2,3]
let arr1 = [4,5,6]
arr1.push(...arr) //4,5,6,1,2,3
c.类数组对象变成数组
let a=new Set([1,2,3,4,5,2,1]) // a : Set(5) {1, 2, 3, 4, 5}
let b=[...a] // (5) [1, 2, 3, 4, 5]
let [arg1,arg2,...arg3] = [1, 2, 3, 4]
arg1 //1
arg2 //2
arg3 //['3','4']
let {x,y,...z}={x:1,y:2,a:3,b:4};
x; //1
y; //2
z; //{a:3,b:4}
let z={a:3,b:4};
let n={x:1,y:2,...z};
n; //{x:1,y:2,a:3,b:4}
let a={x:1,y:2};
let b={z:3};
let ab={...a,...b};
ab //{x:1,y:2,z:3}
剩余参数:剩余参数语法允许我们将一个不定数量的参数表示为一个数组。
function test(a,b,...args){} //...args == [4,5,6]
test(1,2,3,4,5,6)
let [arg1,arg2,...arg3] = [1, 2, 3, 4]
arg1 //1
arg2 //2
arg3 //['3','4']
1.定义:arguments是一个对应传递给函数参数的类数组对象,arguments对象是所有非箭头函数都有的一个局部变量。你可以使用arguments对象在函数中引用函数的参数。此对象包含传递给函数的每个参数,第一个参数在索引0处。
2.特性:
const args = Array.from(arguments);
const args = [...arguments];
属性callee相当于调用自身函数,可以用作匿名函数的递归:
var sum = function (n) {
if (1 == n){
return 1;
} else {
return n + arguments.callee(n - 1); //6 5 4 3 2 1 } } alert(sum(6)); 输出结果:21
}
3.作用:
a.无需明确命名参数,就可以重写函数,在函数代码中,使用特殊对象 arguments,开发者无需明确指出参数名,就能访问它们
function sayHi(message) {
alert(arguments[0]); // 此处将打印message参数的值
}
b.检测参数个数( arguments.length )
function howManyArgs() {
alert(arguments.length);
}
howManyArgs("string", 45);
howManyArgs();
howManyArgs(12); // 上面这段代码将依次显示 "2"、"0" 和 "1"。
c.针对同一个方法被多处调用,但是参数数量不确定的情况下,可以更具arguments索引进行判断。
function func1() {
console.log(arguments[0]); // 1
console.log(arguments[1]); // 2
console.log(arguments[2]); // 3
}
func1(1, 2, 3)
d.模拟函数重载
用 arguments 对象判断传递给函数的参数个数,即可模拟函数重载
当只有一个参数时,doAdd() 函数给参数加 5。如果有两个参数,则会把两个参数相加,返回它们的和。所以,doAdd(10) 输出的是 “15”,而 doAdd(40, 20) 输出的是 “60”。
function doAdd() {
if(arguments.length == 1) {
alert(arguments[0] + 5);
} else if(arguments.length == 2) {
alert(arguments[0] + arguments[1]);
}
}
doAdd(10); //输出 "15"
doAdd(40, 20); //输出 "60"
2.拷贝数据的方式:
(1)直接赋值给一个变量(2)Object.assign() 浅拷贝
(3)Array.prototype.slice() 深拷贝
(4)Array.prototype.concat() 深拷贝
(5)JSON.parse(JSON.stringify()) 深拷贝
(6)Loadsh库中的API,如:.clone()、.cloneDeep()
(7)扩展运算符…arr 深拷贝
浅拷贝(对象,数组)
特点:拷贝的时候只是拷贝了一份引用,修改拷贝以后的数据会影响原来的数据。
浅拷贝只是拷贝一层,更深层次对象级别的值拷贝引用
如何浅拷贝:1.直接赋值 2.遍历赋值 3.ES6的语法糖,object.assign(给谁,拷贝谁)只要一层就没有问题
let a = {
age: 1,
color:[1,2,3],
like:{
sing:4,
dance:5
}
}
let b = {}
Object.assign(b, a);
b.like.sing = 2;
console.log(a.like.sing) // 原本是4现在变成了2这就是浅拷贝拷贝了复杂类型的地址
深拷贝(深度克隆)
特点:拷贝的时候会生成一份新的数据,修改拷贝以后的数据不会原数据。
拷贝多层,每一层的数据都会拷贝
let a = {
age: 1,
color:[1,2,3],
like:{
sing:4,
dance:5
}
}
let b = {}
var deepCopy = function(newObj,oldObj){
for(let k in oldObj){
// k是属性名,oldObj[k]是属性值
// 进行递归判断
let item = oldObj[k]
if(item instanceof Array){
// 设置新的对象的属性名并且声明类型
newObj[k] = []
// 再次调用
deepCopy(newObj[k],item)
}else if(item instanceof Object){
newObj[k] = {}
// 再次调用
deepCopy(newObj[k],item)
}else {
newObj[k] = item
}
}
}
deepCopy(b,a)
、、、简化代码
function deepClone(obj) {
var myObj = obj instanceof Array ? [] : {}
for (let key in obj) {
myObj[key] = typeof (obj[key]) =='object'?deepClone(obj[key]): obj[key]
}
return myObj
}
补充:深拷贝其他的实现方法
JSON.parse(JSON.stringify(obj))也可以实现深拷贝,但是弊端较多,例如无法识别undefined,symbol
lodash库里面的API_.cloneDeep(value).clone()
Array.prototype.slice() 深拷贝
Array.prototype.concat() 深拷贝
扩展运算符…arr 深拷贝
1、props和emit
2、Ref和 p a r e n t / parent/ parent/children
3、eventBus\emit\on 兄弟、父子组件通信
4、$attrs\listeners 隔代通信
5、Provide\inject 隔代组件通信
6、Vuex
7、Pubsub库
基本数据类型(简单数据类型)7种:number,string,boolean,null,undefined,symbol,bigInt
复杂数据类型:array,function,object
1.数组表示有序数据的集合,而对象表示无序数据的集合。如果数据的顺序很重要,就用数组,否则就用对象
2. 数组是种有顺序的链表,对象是无顺序的键值对。
3. 数组有length属性,对象没有
1.instanceof
2.对象的constructor属性
var arr = [1,2,3,1];
var obj = {
a:“A”,
c:“C”
}
console.log(arr.constructor === Array)//true
console.log(obj.constructor === Object) //true
3.Array.isArray([]) //true ES6语法
4.Object.prototype.toString.call(arr) 利用对象的toString可以准确判断是什么类型,call()改变this指向,这里是借用Object的方法
为什么不用Object.toString?
答:虽然所有对象的原型链最顶端都是Object.prototype,但同时Object本身也是一个构造函数,继承于Function,调用Object.toString最终通过原型链调用的是Function.prototype.toString
拓展:num.toString()//"123"1.把数字转换为字符串2.num.toString(2)//转换为二进制的字符串
1.let “” = x % n 2. x / n
200 成功状态码
202服务器已经接收到请求数据,但尚未进行处理
206 发送范围请求头Range抓取到资源的部分内容(大文件下载、分割多个部分同时下载等)
301 永久重定向(永久移动)
302 暂时重定向(临时移动)
304协商缓存(请求的资源未修改)
307临时重定向
400服务端无法理解(客户端语法错误)
401用户需要验证
403请求已成功,但被拒绝
404找不到资源,请求资源不存在
500服务器内部错误
505服务器不支持请求的HTTP协议版本
系统开发:修改端口server.port = 80 打包
服务器准备、环境配置:云服务,阿里云—云服务器ECS—实例—创建实例—购买服务器(cpu ,内存,硬盘,操作系统镜像)—网络配置(分配公网IPV4地址,带宽,安全组中Linux端口22,window端口3389)—会有公网和私网IP,用户名和密码
在操纵系统中安装目标程序所需要的环境:用FTP工具将本地上传到服务器,建立连接—主机:公网IP,协议:SFTP/SSH 端口: 用户名: 密码:
程序部署及运行:打包–上传(FTP工具)—启动
域名绑定:字母与IP绑定,访问域名相当于访问IP地址,购买域名—域名备案(ICP)—域名解析(解析,添加记录:记录值是IP地址,域名)
state, getters, mutations, actions, modules。
1.state:vuex的基本数据,用来存储变量
在vue中使用 this.$store.state.userId
2.geeter:从基本数据(state)派生的数据,相当于state的计算属性,具有返回值的方法
在vue中使用 this.$store.getters.userIdDouble
3.mutation:提交更新数据的方法,必须是同步的(如果需要异步使用action)。每个 mutation 都有一个字符串的 事件类型 (type) 和 一个 回调函数 (handler)。
commit 同步操作,写法: this.$store.commit(‘mutations方法名’,值)
4.action:和mutation的功能大致相同,不同之处在于 ==》1. Action 提交的是 mutation,而不是直接变更状态。 2. Action 可以包含任意异步操作。
dispatch:异步操作,写法: this.$store.dispatch(‘mutations方法名’,值)
5.modules:模块化vuex,可以让每一个模块拥有自己的state、mutation、action、getters,使得结构非常清晰,方便管理。
简单来说就是可以把以上的 state、mutation、action、getters 整合成一个user.js,然后放到store.js里面
meta标签用来描述一个HTML网页文档的属性
有四种属性:1.charset、2.name、3.content、4.http-equiv
name的参数有:
keywords(关键字)为搜索引擎提供网站的关键字
description(网站内容描述)为搜索引擎提供网站主要内容。
application-name(应用程序名称)
viewport(用于移动端页面控制)指定页面控制及缩放比例。content内容如下:
width:设置layout viewport 的宽度,为一个正整数,或字符串"width-device"
initial-scale:设置页面的初始缩放值,为一个数字,可以带小数,通常为1.0(1.0为正常大小)
minimum-scale:允许用户的最小缩放值,为一个数字,可以带小数
maximum-scale:允许用户的最大缩放值,为一个数字,可以带小数
http-equiv的参数有:refresh重定向,在规定的时间之后跳转到另一页面
<meta http-equiv="refresh" content="5;url=http://www.w3school.com.cn" />
DOCTYPE是document type (文档类型) 的缩写。声明位于文档的最前面,处于标签之前,它不是html标签。主要作用是告诉浏览器的解析器使用哪种HTML规范或者XHTML规范来解析页面。
把数据存到用户的浏览器里面
设置方便,易于读取,甚至刷新页面也不会丢失数据
只能储存字符串,可以将对象JSON.stringify()编码后存储
应用场景
1.定位 子绝父相
-1.1 margin设置为auto
.child {
top: 0px;
left: 0px;
bottom: 0px;
right: 0px;
margin: auto; }
.child {
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
2.flex布局
将父盒子设置成弹性盒容器
让子元素水平居中,垂直居中
.parent {
display: flex;
justify-content: center;
align-items: center;
}
.parent {
text-align: center;
line-height: 500px;
}
.child {
display: inline-block;
vertical-align: middle; vertical-align 属性设置一个元素的垂直对齐方式。middle把此元素放置在父元素的中部。
}
CSS盒子右边固定,左边自适应
1.固定盒子浮动+ 自适应盒子width: 100%;//宽度等于百分之百
.left {
float: left;
width: 200px;
height: 400px;
background: red;
}
.right {
width: 100%;//宽度等于百分之百
height: 400px;
margin-left: 200px;
background: blue;
}
2.flex布局父容器设置 display:flex;Right部分设置 flex:1
.box{ display: flex;}
.left{width:200px;background: red;}
.right{width:%;flex:1;background: blue;}/等于左边栏宽度/
3.左右两边都绝对定位
.left{width:200px;background: red; position: absolute;left:;}
.right{width:100%;background: blue;position: absolute;left:200px;}/等于左边栏宽度/
生命周期:每一个vue实例从创建到销毁的过程就是vue这个实例的生命周期
生命周期过程:开始创建、初始化数据、编译模板、挂载Dom、渲染→更新→渲染、卸载等一系列过程
使用场景:可以在此时加一些 loading 效果,在 created 时进行移除。
使用场景:需要异步请求数据的方法可以在此时执行,完成数据的初始化。
使用场景: 当需要操作 dom 的时候执行,可以配合$.nextTick 使用进行单一事件对数据的更新后更新dom。
使用场景: 当数据更新需要做统一业务处理的时候使用。
从数据类型上:
从使用场景上:
注意:NaN是一种特殊类型的的number,它与任何数据都不相等,包括自己本身,其他数据类型和NaN运算的结果都是NaN
vue底层的实现源码逻辑
get ,set
原文链接:详情
来源:解决内存的泄露,垃圾回收机制会定期(周期性)找出那些不再用到的内存(变量),然后释放其内存。
现在各大浏览器通常采用的垃圾回收机制有两种方法:1.标记清除,2.引用计数。
js中最常用的垃圾回收方式就是标记清除。垃圾回收机制在运行的时候会给存储再内存中的所有变量都加上标记(可以是任何标记方式),然后,它会去掉处在环境中的变量及被环境中的变量引用的变量标记(闭包)。而在此之后剩下的带有标记的变量被视为准备删除的变量,原因是环境中的变量已经无法访问到这些变量了。最后垃圾回收机制到下一个周期运行时,将释放这些变量的内存,回收它们所占用的空间。
工作原理:
当变量进入环境时,将这个变量标记为“进入环境”。当变量离开环境时,则将其标记为“离开环境”。标记“离开环境”的就回收内存。
工作流程:
垃圾回收器,在运行的时候会给存储在内存中的所有变量都加上标记; 去掉环境中的变量以及被环境中的变量引用的变量的标记;
被加上标记的会被视为准备删除的变量; 垃圾回收器完成内存清理工作,销毁那些带标记的值并回收他们所占用的内存空间。
到目前为止,IE、Firefox、Opera、Chrome、Safari的js实现使用的都是标记清除的垃圾回收策略或类似的策略,只不过垃圾收集的时间间隔互不相同。
语言引擎有一张"引用表",保存了内存里面所有资源(通常是各种值)的引用次数。如果一个值的引用次数是0,就表示这个值不再用到了,因此可以将这块内存释放。
工作原理:跟踪记录每个值被引用的次数。
工作流程:
声明了一个变量并将一个引用类型的值赋值给这个变量,这个引用类型值的引用次数就是1
同一个值又被赋值给另一个变量,这个引用类型值的引用次数加1;
当包含这个引用类型值的变量又被赋值成别的值了,那么这个引用类型值的引用次数减1; 当引用次数变成0时,说明没办法访问这个值了。
当垃圾收集器下一次运行时,它就会释放引用次数是0的值所占的内存;
扩展:
但是,为了减少内存垃圾,我们还是可以对javascript代码进行彻底检查,有些代码中存在明显的产生过多内存垃圾的问题代码,这些正是我们需要检查并且完善的。
type:
vue 双向数据绑定是通过 数据劫持 结合 发布订阅模式的方式来实现的, 也就是说数据和视图同步,数据发生变化,视图跟着变化,视图变化,数据也随之发生改变;
vue单向数据流:在vue中需要遵循单向数据流原则
在父传子的前提下,父组件的数据发生会通知子组件自动更新
子组件内部,不能直接修改父组件传递过来的props => props是只读的
vue调试工具的使用?
项目是否部署到服务器上,部署的整个过程
如何看待加班
如何学习一门新技术
周围朋友同学如何评价你
学过react没?
反问:
对加班如何看待效率会比较低的看法,待遇,福利(五险一金),转正,学习