目录
· JavaScript的三种类型检测typeof,instanceof,toString比较
· script 标签中 async 和 defer 的区别
· Vue生命周期什么阶段才能调用DOM?什么阶段能发起请求?
· 讲一下 TCP 的三次握手和四次挥手 (HTTP 传输过程)
· 垂直居中
<div class="parent">
<div class="children"></div>
</div>
1. flex布局
.parent{
display: flex;
align-items: center;
justify-content: center;
}
2. 子元素是单行文本
设置父元素的 text-align
和 line-height = height
.parent{
height: 100px;
line-height: 100px;
text-align: center;
}
3. absolute
+ transform
.parent {
position: relative;
}
.children{
position: absolute;
left: -50%;
top: -50%;
transform: translate(-50%, -50%);
}
4. grid
布局(将容器划分为行和列,产生单元格,然后指定项目所在的单元格,可以看作是二维布局)
.parent{
display: grid;
}
.children {
justify-self: center;
align-self: center;
}
5. 绝对定位和 margin:auto
.children{
position: absolute;
left: 0;
top: 0;
right: 0;
bottom: 0;
margin: auto;
}
.parent{
position: relative;
}
· 左侧固定、右侧自适应
- flex-basis: 100px; flex-shrink: 0; / flex-grow: 1;
- grid-template-columns: 100 px 1fr;
· 如何判断一个值是数组
- Array.isArray:Array.isArray(arr)
- toString:prototype.toString.call
(arr)
- instanceof(有局限性):arr instanceof Array
· bigint 最大安全整数
-2^63 到 2^63-1 ,存储大小为 8 个字节
· 如何判断某个字符串以 abc 开头
- 正则表达式
- .slice:切操作
- indexOf / startsWith
· 进程和线程的区别
- 定义不一样,进程是执行中的一段程序,而一个进程中执行中的每个任务即为一个线程
- 一个线程只可以属于一个进程,但一个进程能包含多个线程
- 线程无地址空间,它包括在进程的地址空间里
- 线程的开销或代价比进程的小
· tcp 与 udp
-
tcp 是一种面向有连接的传输层协议,能够对自己提供的连接实施控制。适用于要求可靠传输的应用,例如文件传输。面向字节流,传输慢
-
udp是一种面向无连接的传输层协议,不会对自己提供的连接实施控制。适用于实时应用,例如:IP电话、视频会议、直播等。以报文的方式传输,效率高
-
HTTP 请求的幂等概念的理解以及常见请求的幂等性常见请求的幂等性(HTTP幂等方法,是指无论调用这个url多少次,都不会有不同的结果的HTTP方法,也就是不管你调用1次还是调用100次,结果都是一样的)
-
HTTP请求方法
-
HTTP请求方法的幂等性(如果一个方法重复执行多次,产生的效果是一样的,那么这个方法就是幂等的)
· 跨域问题的几种解决方案
- cors技术(服务器在返回响应报文的时候,在响应头中设置一个允许的header)(后端修改)
//服务器设置CORS,允许浏览器跨域
res.setHeader('Access-Control-Allow-Origin', '*')
- 2-express使用中间件cors(自动给每一个res设置默认请求头)
2. 原生jsonp(通过动态创建script标签,通过script标签的src,向一个不同源的接口发送一个get请求)
3. jQuery中的jsonp
dataType:'jsonp'
4. 谷歌浏览器设置跨域
5. 区别
- CORS
- 服务器返回响应头,前端无需任何处理
- 简单快捷,支持所有请求方式
- JSONP
- 浏览器:自定义响应回调函数,使用script标签的src请求
- 利用浏览器的src属性没有跨域这一限制特点
- 服务器:接收callback参数,返回函数调用
- 处理复杂,并且只支持get请求
- 原因:get请求参数直接在url后面拼接,而post请求参数是放在请求体中
· option 预请求
options 请求就是预检请求,可用于检测服务器允许的 http 方法。当发起跨域请求时,由于安全原因,触发一定条件时浏览器会在正式请求之前自动先发起 OPTIONS 请求,即 CORS 预检请求,服务器若接受该跨域请求,浏览器才继续发起正式请求
- 简单请求:请求方式为GET、HEAD、POST时的请求
- 复杂请求:PUT、DELETE、CONNECT、OPTIONS、TRACE、PATCH
· 跨域的同时携带 cookie
- 前端请求时在
request
对象中配置"withCredentials": true
; - 服务端在
response
的header
中配置"Access-Control-Allow-Origin","http://xxx:${port}"
; - 服务端在
response
的header
中配置"Access-Control-Allow-Credentials", "true"
· 用 css 画一个扇形
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>扇形</title>
<style>
#box {
border: 200px solid transparent;
border-top-color: thistle;
width: 0;
border-radius: 200px;
}
</style>
</head>
<body>
<div id="box"></div>
</body>
</html>
· 多行超出显示省略号
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
· 实现多个页面之间通信
- localstorage(推荐)
// pageA.html
<body>
<h1>pageA</h1>
</body>
<script>
window.addEventListener("storage", (e) => {
console.info("localStorage发生变化:", e)
})
</script>
// pageB.html
<body>
<h1>pageB</h1>
<button id="btnB">添加数据到localStorage</button>
</body>
<script>
let btnB = document.getElementById("btnB");
let num = 0;
btnB.addEventListener("click", () => {
localStorage.setItem("num", num++)
})
</script>
2. websocket协议
3. SharedWorker
4. 使用cookie + setInterval
· cookie 和 localstorge 的区别
1. 容量
cookie容量在4K左右,且有条数限制。localStorage的容量有5M。
2. 是否向服务器发送数据
cookie会向服务器发送数据,localStorage不会往服务器发送数据。
3. 过期时间
cookie的有过期时间,在没有设置过期时间,关闭浏览器就消失,默认过期时间是一次会话。localStorage没有过期时间。
4. 路径限制
cookie有路径的限制。localStorage没有路径的限制。
5. 存储类型
cookie存储的是字符串,字符串,字符串。localStorage存储的是对象,对象,对象。
· cookie、sessionStorage、localStorage 区别
cookie、sessionStorage、localStorage都是浏览器本地存储
共同点:都是存储在浏览器本地的
区别:
- cookie是由服务器端写入的、而SessionStorage、LocalStorage都是由前端写入的
- cookie的生命周期是由服务端在写入的时候就设置好的,LocalStorage是写入就一直存在的,除非手动清除,SessionStorage是页面关时就会自动清除。
- cookie的存储空间比较小大概4KB,SessionStorage、LocalStorage存储空间比较大,大概5M
- Cookie、SessionStorage、LocalStorage存数据共享都遵循同源原则,SessionStorage还限制必须是同一个页面
- 在前端给后端发发送请求时会自动携带Cookie中的数据,但是SessionStorage、LocalStorage不会
由于以上区别,它们的应用场景也不同
- Cookie一般用于存储登录验证信息SessionID或者token
- LocalStorage常用于存储不易变动的数据,用于持久化的本地存储
- SessionStorage可以用来检测用户是否时刷新进入页面,如音乐播放器恢复播放进度条的功能
· call apply bind 的区别
1. 相同点:
- 都是改变this指向的
- 第一个参数都是this要指向的对象
- 都可以利用后续参数传参
2. 区别:
- call和bind的参数是依次传参,一一对应的
- 但apply只有两个参数,第二个参数为数组
- call和apply都是对函数进行直接调用,而bind方法返回的仍是一个函数
· 实现一个 bind(编程)
(function () {
Function.prototype.bind = function () {
let args = arguments;
return () => {
return this.apply(args[0], [...args].slice(1));
};
};
})();
· 最大连续子数组 (编程)
问题描述,给定一个数组A[0,1,...,n-1],求出A的连续数组,使得该子数组的和最大。
例如:数组A[1,-2,3,10,-4,7,2],则最大的子数组为[3,10,-4,7,2]
解法:动态规划
int maxSubArray_dongtaiguihua(int A[], int n) {
int result = A[0];
int sum = A[0];
for (int i = 0; i < n; i++) {
if (sum > 0)sum += A[i];
else sum = A[i];
if (sum > result)result = sum;
}
return result;
}
· 老虎吃羊的问题
问题
100只老虎和一只羊共同生活在一个只有草的魔法岛上。老虎可以吃草,但它们更喜欢吃羊。
假设:
A、每次一只老虎只能吃一只羊,并且吃完后老虎会变成羊;
B、所有的老虎都很精明,并且都想活下去。
问:
羊会被吃掉吗?
解答
对于数量较大的脑筋急转弯,可以先降低数量,简化问题。
这里100是个大数目,所以让我们再次把问题简化。如果只有一只老虎(m=1),它肯定会吃掉羊,因为它不用担心变成羊后会被吃掉。
如果有2只老虎呢?因为两只老虎都很精明,都清楚如果自己吃掉羊后变成羊,就会被另一只老虎吃掉,所以结果是,谁也不去吃羊。
如果有3只老虎呢?如果其中一只老虎吃掉一只羊后变身,剩下的两只老虎不会再继续吃羊,所以第一只老虎把羊吃掉。
如果有四只老虎,每只老虎都知道如果它吃了羊,它就会变成羊。还剩下3只老虎,它还是会被吃掉的。所以为了保证最大的生存可能性,没有老虎会吃羊肉。
同样的逻辑,我们自然可以证明,如果老虎的数量是偶数,羊就不会被吃掉。如果数字是奇数,羊就会被吃掉。对于100只老虎的情况,羊不会被吃掉。
· vue 中的 nextTick
nextTick:
将回调推迟到下一个 DOM 更新周期之后执行。在更改了一些数据以等待 DOM 更新后立即使用它。
nextTick会将其中的回调函数放入微任务队列中
· 宏任务和微任务
1. 什么是宏任务和微任务
- 宏任务(macrotask)
- 异步Ajax请求
- setTimeout 、setInterval
- 文件操作
- 其他宏任务
- 微任务(microtask)
- Promise.then、Promise.catch和Promise.finally
- process.nextTick
- 其他微任务
2. 宏任务和微任务的执行顺序
同步任务、同步宏任务、微任务、异步宏任务、同步宏任务、微任务、异步宏任务。。。
每一个宏任务执行完之后,都会检查是否存在待执行的微任务,
如果有,则执行完所有的微任务之后,再执行下一个宏任务。
宏任务和微任务交替执行。
3. 经典面试题
console.log('1');
setTimeout(function() {
console.log('2');
new Promise(function(resolve) {
console.log('3');
resolve()
}).then(function() {
console.log('4');
})
})
new Promise(function(resolve) {
console.log('5');
resolve()
}).then(function() {
console.log('6');
})
setTimeout(function() {
console.log('7');
new Promise(function(resolve) {
console.log('8');
resolve()
}).then(function() {
console.log('9');
})
})
正确输出顺序: 1 5 6 2 3 4 7 8 9
分析
- 第一行中,同步任务
- A块中,这个定时器的回调函数属于异步任务里面的宏任务,一整个放入宏任务队列等待
- B块中,五属于同步任务,六属于微任务
- C块中,和A块一样,一整个放入宏任务队列
- 所以,先执行同步任务 1 5
- 再执行微任务 6,现在只剩下宏任务队列的两个任务了
- 会先执行A块,执行 2 3 4
- 再执行 C块,执行 7 8 9
· 正则中的 i 具体做了什么事
正则表达匹配中是有区分大小写的,i 作用是忽略大小写,匹配。
· JavaScript的三种类型检测typeof,instanceof,toString比较
1. typeof:返回一个表达式的数据类型的字符串,返回结果只能为javascript中的基本数据类型,包括:number、boolean、string、object、undefined、function等6种数据类型。
2. instanceof:采用了另一种方式来判断对象类型:原型链。如 a instanceof b只要能在a对象的原型链上找到b,则认为a是b类型的一个对象。
3. toString:使用Object.prototype.toString.call(value) 方法去调用对象,得到对象的构造函数名。可以解决instanceof的跨框架问题
· script 标签中 async 和 defer 的区别
1. 共同点:
script放在<head>和放在<body>底部时没区别。script脚本会异步加载,在加载过程中不会阻塞HTML代码的解析和渲染;
2. 区别:
- defer要等到整个页面在内存中正常渲染结束(DOM 结构完全生成,以及其他脚本执行完成),才会执行;
- async一旦下载完,渲染引擎就会中断渲染,执行这个脚本以后,再继续渲染。
- defer是“渲染完再执行”,async是“下载完就执行”。
- 另外,如果有多个defer脚本,会按照它们在页面出现的顺序加载,而多个async脚本是不能保证加载顺序的;
注意:如果同时指定了两个属性,则会遵从async属性而忽略defer属性
· position 有哪几种?分别说一下对应的效果和用法
- relative:相对定位,经常配合absolute来实现垂直居中;
- absolute:绝对定位(脱离文档流的布局),起始位置为html文档,如果父元素为relative则相对于父元素;
- fixed:固定定位(类似于absolute),相对于浏览器窗口不随着滚动条而改变位置, 通常用于功能:回到顶部按钮;
- static:静态定位,元素在文档常规流中当前的布局位置。此时 top, right, bottom, left 和 z-index 属性无效。
· css 选择器优先级(由高到低)
- ! important
- 内联样式(行内样式:style=" 属性:属性值; ")
- id选择器(#myid)
- 类选择器(.myclassname)
- 标签选择器(div,h1,p)
- 子选择器(ul < li)
- 后代选择器(li a)
- 伪类选择器(a:hover,li:nth-child)
· js原型链
由于原型prototype本身是对象,因此他也有隐式原型,指向的规则不变,从某个对象出发,依次寻找隐式原型的指向,将形成一个链条,该链条叫做原型链。它也有一个’_ _ proto_ _'属性
fn._ _ proto _ _=== Foo.prototype;
Foo.prototype._ _ proto _ _=== Object.prototype;
· JavaScript创建对象的方式
1. 利用字面量创建对象
var obj = {
name: '猫猫没有猫耳朵',
sayHi: function () {
console.log('hi~');
}
}
2. 利用new Object创建对象
<script>
var obj = new Object();//O要大写
obj.name = '猫猫没有猫耳朵';
obj.sayHi = function () {
console.log('hi~');
}
obj.sayHi();
</script>
3. 利用构造函数创建对象
<script>
function 构造函数名() {
this.属性 = 值;
this.方法 = function () {
}
}
new 构造函数名();
</script>
· 使用JS实现一个new方法
function _new(constructer, ...arg) {
// 创建一个空的对象
let resultObj = {};
// 链接该对象到原型,这样新对象就能访问到原型上面的方法
resultObj.__proto__ = constructer.prototype;
// 然后实现步骤3,将新创建的对象作为this的上下文
let result = constructer.call(resultObj, ...arg);
// 实现步骤4:如果该函数没有返回对象(即result不是一个对象),则返回this(即resultObj)
return typeOf result === 'object' ? result : resultobj
}
new方法的效果
function Persion(name, age) {
this.name = name;
this.age = age;
}
let Persion1 = _new(Persion, 'lucy', '18');
console.log(Persion1.name); // 'lucy'
console.log(Persion1 instanceof Persion); // true
· MVVM 结构
MVVM是Model-View-ViewModel的简写。即模型-视图-视图模型。【模型】指的是后端传递的数据。【视图】指的是所看到的页面。【视图模型】mvvm模式的核心,它是连接view和model的桥梁。它有两个方向:一是将【模型】转化成【视图】,即将后端传递的数据转化成所看到的页面。实现的方式是:数据绑定。二是将【视图】转化成【模型】,即将所看到的页面转化成后端的数据。实现的方式是:DOM
· MVC结构
- View 传送指令到 Controller
- Controller 完成业务逻辑后,要求 Model 改变状态
- Model 将新的数据发送到 View,用户得到反馈
- 所有通信都是单向的
· MVVM 和 MVC 的区别
MVVM与MVC的最大区别就是:它实现了View和Model的自动同步,也就是当Model的数据改变时,我们不用再自己手动操作Dom元素,来改变View的显示,而是改变数据后该数据对应View层显示会自动改变。MVVM并不是用VM完全取代了C,ViewModel存在目的在于抽离Controller中展示的业务逻辑,而不是替代Controller,其它视图操作业务等还是应该放在Controller中实现。
- mvvm各部分的通信是双向的,而mvc各部分通信是单向的
- mvvm是真正将页面与数据逻辑分离放到js里去实现,而mvc里面未分离
· Vue 的生命周期
vue生命周期是指vue实例对象从创建之初到销毁的过程,vue所有功能的实现都是围绕其生命周期进行的,在生命周期的不同阶段调用对应的钩子函数实现组件数据管理和DOM渲染两大重要功能
· Vue生命周期什么阶段才能调用DOM?什么阶段能发起请求?
1. 调用DOM:
在钩子函数 mounted 被调用前,Vue 已经将编译好的模板挂载到页面上,所以在 mounted 中可以访问操作 DOM。
2. 发请求:
-
可以在钩子函数 created、beforeMount、mounted 中进行调用,因为在这三个钩子函数中,data 已经创建,可以将服务端端返回的数据进行赋值。
-
但是推荐在 created 钩子函数中调用异步请求,因为在 created 钩子函数中调用异步请求有以下优点:
- 能更快获取到服务端数据,减少页面loading 时间;
- ssr不支持 beforeMount 、mounted 钩子函数,所以放在 created 中有助于一致性;
· Vue 的组件通信(三种)
- 父子通信
- 父向子传递数据是通过 props,子向父是通过 events($ emit)
- 通过父链 / 子链也可以通信($ parent / $ children);
- ref 也可以访问组件实例;
- provide / inject API;
- $attrs/ $listeners
- 兄弟通信
- Bus
- Vuex
- 跨级通信
- Bus;
- Vuex;
- provide / inject API
- $attrs/ $listeners
· 讲一下 TCP 的三次握手和四次挥手 (HTTP 传输过程)
- 三次握手
- 客户端主机C说:“我可以给你发送数据吗?”
- 服务器S说:“可以的,不过我可能也会给你发送数据。”
- C:“好,那我开始互相发送数据吧。”
- 四次挥手
- 客户端主机C说:“我没有数据了,断开连接吧。 ”
- 服务器S说:“好,但是我还有数据(不断给C发送数据,此时C已经不能给S发送数据了,但是必须要就收S发来的数据)。”
- (当S给C发完数据后)S说:“我发完了,断开连接吧。”
- C说:“好,断开连接吧。”
· promise 有哪几种状态
- 初始态pending
- 成功态resolved(fulfilled)
- 失败态rejected
三种状态小结
- 状态是可转化
最初创建promise对象是,默认状态是pending,如果在函数体内部调用了第一个参数对应的函数,则状态变成了resolved;如果调用了第二个参数对应的函数,则状态变成了rejected
pending ----- resolve() --> resolved;
pending ----- reject() --> rejected;
- 状态转换是不可逆的
一旦从pending --> resolved(或者rejected),就不可能再回到pending,也不能由resolved变成rejected
· promise是什么与使用方法
Promise的作用:Promise是异步微任务,解决了异步多层嵌套回调的问题,让代码可读性更高,更容易维护Promise使用
- Promise的特点
- 对象的状态不受外界影响(Promise对象代表一个异步操作,有三种状态)
- 一旦状态改变,就不会再变,任何时候都可以得到这个结果
- resolve 方法的参数是then中回调函数的参数,reject 方法中的参数是catch中的参数
- then 方法和 catch方法 只要不报错,返回的都是一个fullfilled状态的promise
- Promise的其他方法
- Promise.resolve() :返回的Promise对象状态为fulfilled,并且将该value传递给对应的then方法。
- Promise.reject():返回一个状态为失败的Promise对象,并将给定的失败信息传递给对应的处理方法。
- Promise.all():返回一个新的promise对象,该promise对象在参数对象里所有的promise对象都成功的时候才会触发成功,一旦有任何一个iterable里面的promise对象失败则立即触发该promise对象的失败。
- Promise.any():接收一个Promise对象的集合,当其中的一个 promise 成功,就返回那个成功的promise的值。
- Promise.race():当参数里的任意一个子promise被成功或失败后,父promise马上也会用子promise的成功返回值或失败详情作为参数调用父promise绑定的相应句柄,并返回该promise对象。
· 手写promise
const p1 = new Promise((resolve, reject) => {
console.log('create a promise');
resolve('成功了');
})
console.log("after new promise");
const p2 = p1.then(data => {
console.log(data)
throw new Error('失败了')
})
const p3 = p2.then(data => {
console.log('success', data)
}, err => {
console.log('faild', err)
})
· 手写 Promise.All()
const PromiseAll = (iterator) => {
const promises = Array.from(iterator); // 对传入的数据进行浅拷贝,确保有遍历器
const len = promises.length; // 长度
let index = 0; // 每次执行成功+1,当等于长度时,说明所有数据都返回,则可以resolve
let data = []; // 用来存放返回的数据数组
return new Promise((resolve, reject) => {
for (let i in promises) {
promises[i]
.then((res) => {
data[i] = res;
if (++index === len) {
resolve(data);
}
})
.catch((err) => {
reject(err);
});
}
});
};
const promise1 = Promise.resolve('promise1');
const promise2 = new Promise(function (resolve, reject) {
setTimeout(resolve, 2000, 'promise2');
});
const promise3 = new Promise(function (resolve, reject) {
setTimeout(resolve, 1000, 'promise3');
});
PromiseAll([promise1, promise2, promise3]).then(function(values) {
console.log(values);
});
· 编程题(最长公共前缀)
题目内容
编写一个函数来查找字符串数组中的最长公共前缀。
如果不存在公共前缀,返回空字符串 “”。
示例 1:
输入:strs = [“flower”,“flow”,“flight”]
输出:“fl”
示例 2:
输入:strs = [“dog”,“racecar”,“car”]
输出:“”
import java.util.Arrays;
class Solution {
public String longestCommonPrefix(String[] strs) {
int l = strs.length;
StringBuffer sb =new StringBuffer(200);
Arrays.sort(strs);
int ml= Math.min(strs[0].length(),strs[l-1].length());
if(ml==0)
{
return "";
}
else{
for(int i= 0;i<ml;i++){
if(strs[0].charAt(i)==strs[l-1].charAt(i))
{
sb.append(strs[0].charAt(i));
}
else
{
return sb.toString();
}
}}
return sb.toString();
}
}