解析面试题二
1.问垂直居中的方案
以下几种:
方法一:flex布局实现
<!-- html部分 -->
<div class="center">
<div>垂直居中</div>
</div>
/* css部分 */
.center{
width: 300px;
height: 200px;
display: flex;
align-items: center;
border: 2px solid blue;
}
方法二:单行文本垂直居中
<!-- html部分方案一 -->
<div class="center">
123
</div>
/* css部分方案一 */
.center{
padding-top:50px;
padding-bottom:50px;
background: indianred;
}
<!-- html部分方案二 -->
<div class="center">
123
</div>
/* css部分方案二 */
.center{
width: 300px;
height: 200px;
line-height: 200px;
background: indianred;
}
方案三:多行文本垂直居中
通过设置父元素table,子元素table-cell和vertical-align
vertical-align:middle的意思是把元素放在父元素的中部
<!-- html部分 -->
<div class="center">
<div class="table-div">多行文本垂直居中</div>
</div>
/* css部分 */
.center{
width: 300px;
height: 200px;
display: table;
border: 2px solid blue;
}
.table-div{
display: table-cell;
vertical-align: middle;
border: 1px solid red;
}
方案四:利用position和top/bottom和margin:auto(注意不是margin:0 auto)
1.position:absolute/relative/fixed
2.top/bottom:0
3.margin:auto
<!-- html部分 -->
<div class="parent">
<div class="child">已知高度垂直居中</div>
</div>
/* css部分 */
.parent{
position: relative;
width: 200px;
height: 200px;
border: 1px solid blue;
}
.child{
position: absolute;
height: 100px;
width: 150px;
top: 0;
bottom: 0;
margin: auto;
line-height: 100px;
background-color: red;
}
方案五:利用position和top和transform
transform中的translate偏移的百分比就是相对元素自身的尺寸而言的。
transform方法,可用于未知元素大小的居中
<!-- html部分 -->
<div class="parent">
<div class="child">已知高度垂直居中</div>
</div>
/* css部分 */
.parent{
position: relative;
width: 200px;
height: 200px;
border: 1px solid blue;
}
/* 不知道被居中元素的尺寸 */
.child{
position: absolute;
top: 50%;
transform: translate(0,-50%);
line-height: 100px;
background-color: red;
}
顺便把垂直水平居中的再复习一下
方案一:绝对定位+margin:auto
<!-- html部分 -->
<div></div>
/* css部分 */
div{
width: 200px;
height: 200px;
background: green;
position:absolute;
left:0;
top: 0;
bottom: 0;
right: 0;
margin: auto;
}
方案二:绝对定位+负margin
<!-- html部分 -->
<div></div>
/* css部分 */
div{
width:200px;
height: 200px;
background:green;
position: absolute;
left:50%;
top:50%;
margin-left:-100px;
margin-top:-100px;
}
方案三:绝对定位+transform
<!-- html部分 -->
<div></div>
/* css部分 */
div {
width: 200px;
height: 200px;
background: green;
position: absolute;
left: 50%;
/* 定位父级的50%*/
top: 50%;
transform: translate(-50%,-50%);/*自己的50%*/
}
方案四:flex布局
<!-- html部分 -->
<div class="box">
<div></div>
</div>
/* css部分 */
.box{
height: 600px;
display: flex;
justify-content: center;
align-items: center;
}
.box>div{
background: indianred;
width: 200px;
height: 200px;
}
方案五:设置table-cell来实现居中
设置 display:table-cell;
text-align:center;
vertical-align: middle;
<!-- html部分 -->
<div class="parent">
<div>123</div>
</div>
/* css部分 */
.parent{
height: 200px;
width: 200px;
display: table-cell;
text-align: center;
border: 1px solid #ccc;
vertical-align: middle;
}
2.rgba()和opacity的透明效果有什么不同?
rgba()和opcity都能实现透明效果,但是最大的不同是opacity作用于元素,以及元素内的所有内容的透明度,
而rgba()只作用于元素的颜色或其背景色。(设置rgba透明的元素的子元素不会继承透明效果)
3.闭包原理 具体请看以下链接(www.ruanyifeng.com/blog/2009/0…)
闭包就是能够读取其他函数内部变量的函数。
由于在Javascript语言中,只有函数内部的子函数才能读取局部变量,因此可以把闭包简单理解成"定义在一个函数内部的函数"。
所以,在本质上,闭包就是将函数内部和函数外部连接起来的一座桥梁。
4.面向对象编程的思想
基本思想是使用对象,类,继承,封装等基本概念来进行程序设计
优点:易维护,采用面向对象思想设计的结构,可读性更高,由于继承的存在,即使改变需求,那么维护也只是在局部模块,所以维护起来是非常方便和较低成本的。易扩展:开发工作的重用性、继承性高,降低重复工作量。缩短开发周期。
5.let var const的区别
var声明变量的作用域限制在其声明位置的上下文中,而非声明变量总是全局的
由于变量声明(以及其他声明)总是在任意代码执行之前处理的,所以在代码中的任意位置声明变量总是等效于在代码开头声明;
let是更完美的var,不是全局变量,具有块级函数作用域,大多数情况不会发生变量提升。
- let声明的变量具有块级作用域
- let声明的变量不能通过window.变量名访问
- 形如for(let x...)的循环是每次迭代都为x创建新的绑定
- let约束了变量提升
- let不允许在相同作用域内重复声明同一个变量名,var是允许的
const定义的常量值,不能够重新赋值,如果值是一个对象,可以改变对象里边的属性值。const变量声明的时候必须初始化。
6.判断对象数组的方法
1.instanceof
//检测数组
let arr = []
console.log(arr instanceof Array) // true
//检测对象
console.log(arr instanceof Object) // true
2.constructor
let arr = [];
console.log(arr.constructor === Array) // true
console.log(arr.constructor === Object) // false
7.jsonp原理
jsonp的原理就是利用script标签没有跨域限制,通过script标签src属性,发送带有callback参数的GET请求,服务端将接口返回数据拼凑到callback函数中,返回给浏览器,浏览器解析执行,从而前端拿到callback函数返回的数据。
<script>
var script = document.createElement('script');
script.type = 'text/javascript';
// 传参一个回调函数名给后端,方便后端返回时执行这个在前端定义的回调函数
script.src = 'http://www.localhost.com:8080/login?user=admin&callback=handleCallback';
document.head.appendChild(script);
// 回调执行函数
function handleCallback(res) {
alert(JSON.stringify(res));
}
</script>
服务端返回数据
handleCallback({"success": true, "user": "admin"})
8.跨域解决方案:
1.解决方案jsonp实现如7所示。
2.document.domain+iframe跨域
1.父窗口:(http://www.domain.com/a.html)
<iframe id="iframe" src="http://child.domain.com/b.html"></iframe>
<script>
document.domain = 'domain.com';
var user = 'admin';
</script>
2.子窗口(http://child.domain.com/b.html)
<script>
document.domain = 'domain.com';
// 获取父窗口中变量
alert('get js data from parent ---> ' + window.parent.user);
</script>
弊端:查看页面渲染优化
3.nginx代理跨域
4.nodejs中间件代理跨域
5.后端在头部信息里面设置安全域名
更多的方案请参考:(juejin.cn/post/684490…)
9.rem、px、em区别
px在缩放页面时无法调整那些使用它作为单位的字体、按钮等的大小;
em的值并不是固定的,会继承父级元素的字体大小,代表倍数;
rem的值并不是固定的,始终是基于根元素 html的也代表倍数。
10.深拷贝、浅拷贝实现
1.a和b指向了同一块内存,所以修改其中任意一个值,另外一个值也会随之变化,这是浅拷贝。
2.a和b指向同一块内存,但是修改其中任意一个值,另外一个调用的变量,不会受到影响,这是深拷贝
深拷贝的实现
let a = {
age: 1,
jobs: {
first: 'FE'
}
}
let b = JSON.parse(JSON.stringify(a))
a.jobs.first = 'native'
console.log(b.jobs.first) // FE
该方法也是有局限性的
会忽略undefined
不能序列化函数
不能解决循环引用的对象
浅拷贝的实现方式
1.Object.assign()
Object.assign() 方法可以把任意多个的源对象自身的可枚举属性拷贝给目标对象,然后返回目标对象。但是 Object.assign()进行的是浅拷贝,拷贝的是对象的属性的引用,而不是对象本身。
var obj = {a:{a:"iwen",b:39}};
var initalObj = Object.assign({},obj);
initalObj.a.a = "kievn";
console.log(obj.a.a);//kievn
2.Array.prototype.concat()
let arr = [1,3,{
username:'iwen'
}];
let arr2 = arr.concat();
arr2[2].username = 'kievn';
console.log(arr);
//修改新对象会修改到原对象。
3.Array.prototype.slice()
let arr = [1,3,{
username:'iwen'
}];
let arr2 = arr.slice();
arr2[2].username = 'kievn';
console.log(arr);
//同上修改新对象会修改到原对象。
11.工作中有没有用到防抖和节流
防抖
防抖(debounce) 所谓防抖,就是指触发事件后在n秒内函数只能执行一次,如果在n秒内又触发了事件,则会重新计算函数执行时间。 防抖函数分为非立即执行版和立即执行版。
非立即执行:触发事件后函数不会立即执行,而是在n秒后执行,如果在n秒内又触发了事件,则会重新计算函数执行时间。
单独版:
function debounce(func, wait) {
let timeout
return function () {
let context = this
let args = arguments
if (timeout) clearTimeout(timeout)
timeout = setTimeout(() => {
func.apply(context, args)
}, wait)
}
}
完整版:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
<style>
* {
margin: 0;
padding: 0;
}
#con {
height: 100%;
width: 100%;
position: absolute;
line-height: 600px;
text-align: center;
color: #ff0000;
background-color: #ffff00;
font-size: 100px;
}
</style>
</head>
<body>
<div id="con">
</div>
<script>
let num = 1
let con = document.getElementById('con')
function count() {
con.innerHTML = num++
}
function debounce(func, wait) {
let timeout
return function () {
let context = this
let args = arguments
if (timeout) clearTimeout(timeout)
timeout = setTimeout(() => {
func.apply(context, args)
}, wait)
}
}
con.onmousemove = debounce(count, 1000)
</script>
</body>
</html>
立即执行:触发事件后函数会立即执行,然后n秒后不触发事件才能继续执行函数。
单独版:
function debounce(func, wait) {
let timeout
return function () {
let context = this
let args = arguments
if (timeout) clearTimeout(timeout)
console.log('timeout',timeout)
let callNow = !timeout
timeout = setTimeout(() => {
timeout = null
}, wait)
if (callNow) func.apply(context, args)
}
}
完整版:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
<style>
* {
margin: 0;
padding: 0;
}
#con {
height: 100%;
width: 100%;
position: absolute;
line-height: 600px;
text-align: center;
color: #ff0000;
background-color: #ffff00;
font-size: 100px;
}
</style>
</head>
<body>
<div id="con">
</div>
<script>
let num = 1
let con = document.getElementById('con')
function count() {
con.innerHTML = num++
}
function debounce(func, wait) {
let timeout
return function () {
let context = this
let args = arguments
if (timeout) clearTimeout(timeout)
let callNow = !timeout
timeout = setTimeout(() => {
timeout = null
}, wait)
if (callNow) func.apply(context, args)
}
}
con.onmousemove = debounce(count, 1000)
</script>
</body>
</html>
节流
连续触发事件,但是在n秒中只执行一次。节流会稀释函数的执行频率。
对于节流,一般有两种方式可以实现,分别是时间戳版和定时器版。 时间戳版:
function throttle(func, wait) {
var previous = 0
return function () {
let now = Date.now()
let context = this
let args = arguments
if (now - previous > wait) {
func.apply(context, args)
previous = now
}
}
}
在持续触发事件过程中,函数会立即执行,并且每1秒执行一次。
当时间每过去n秒后,执行加一事件。 完整版:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
<style>
* {
margin: 0;
padding: 0;
}
#con {
height: 100%;
width: 100%;
position: absolute;
line-height: 600px;
text-align: center;
color: #ff0000;
background-color: #ffff00;
font-size: 100px;
}
</style>
</head>
<body>
<div id="con">
</div>
<script>
let num = 1
let con = document.getElementById('con')
function count() {
con.innerHTML = num++
}
function throttle(func, wait) {
var previous = 0
return function () {
let now = Date.now()
let context = this
let args = arguments
if (now - previous > wait) {
func.apply(context, args)
previous = now
}
}
}
con.onmousemove = throttle(count, 1000)
</script>
</body>
</html>
定时器版:
function throttle(func, wait) {
let timeout
return function () {
let context = this
let args = arguments
if (!timeout) {
timeout = setTimeout(() => {
timeout = null
func.apply(context, args)
}, wait)
}
}
}
持续触发事件时,每当n秒后执行一次,timeout设为空。当为空又开始执行。
在持续触发事件的过程中,函数不会立即执行,并且每 1s 执行一次,在停止触发事件后,函数还会再执行一次。 我们应该可以很容易的发现,其实时间戳版和定时器版的节流函数的区别就是,时间戳版的函数触发是在时间段内开始的时候,而定时器版的函数触发是在时间段内结束的时候。
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
<style>
* {
margin: 0;
padding: 0;
}
#con {
height: 100%;
width: 100%;
position: absolute;
line-height: 600px;
text-align: center;
color: #ff0000;
background-color: #ffff00;
font-size: 100px;
}
</style>
</head>
<body>
<div id="con">
</div>
<script>
let num = 1
let con = document.getElementById('con')
function count() {
con.innerHTML = num++
}
function throttle(func, wait) {
let timeout
return function () {
let context = this
let args = arguments
if (!timeout) {
timeout = setTimeout(() => {
timeout = null
func.apply(context, args)
}, wait)
}
}
}
con.onmousemove = throttle(count, 1000)
</script>
</body>
</html>
每日一句
时间能治愈一切,请给时间一点时间。