文章目录
笔试题
1.数组扁平化/数组降维
目的:把多维数组
(尤其是二维数组
)转化为一维数组
方法一:使用遍历
function flat(arr) {
// 验证 arr 中,还有没有深层数组 [1, 2, [3, 4]]
const isDeep = arr.some(item => item instanceof Array)
if (!isDeep) {
return arr // 已经是 flatern [1, 2, 3, 4]
}
const res = Array.prototype.concat.apply([], arr)
return flat(res) // 递归
}
const res = flat([1, 2, [3, 4, [10, 20, [100, 200]]], 5])
// instanceof 运算符用于检测构造函数的 prototype 属性是否出现在某个实例对象的原型链上。
// some() 方法测试数组中是不是至少有1个元素通过了被提供的函数测试。它返回的是一个Boolean类型的值。
// concat() 方法用于合并两个或多个数组。此方法不会更改现有数组,而是返回一个新数组。
// apply() 方法调用一个具有给定this值的函数。apply方法的第一个参数会作为被调用函数的this值,apply方法的第二个参数(一个数组,或类数组的对象)会作为被调用对象的arguments值,也就是说该数组的各个元素将会依次成为被调用函数的各个参数;
// Array.prototype.concat.apply([], [1, 2, 3, [4, 5, 6], 7, 8, [9, 10]]) 效果等同于 [].concat(1, 2, 3, [4, 5, 6], 7, 8, [9, 10])
方法二:使用 ES6 API flat()
var arr1 = [1, 2, [3, 4]];
arr1.flat();
// [1, 2, 3, 4]
var arr2 = [1, 2, [3, 4, [5, 6]]];
arr2.flat();
// [1, 2, 3, 4, [5, 6]]
var arr3 = [1, 2, [3, 4, [5, 6]]];
arr3.flat(2);
// [1, 2, 3, 4, 5, 6]
//使用 Infinity,可展开任意深度的嵌套数组
var arr4 = [1, 2, [3, 4, [5, 6, [7, 8, [9, 10]]]]];
arr4.flat(Infinity);
// [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
2.数组去重
方法1.传统方式,遍历元素挨个比较、去重
// 传统方式
function unique(arr) {
const res = []
arr.forEach(item => {
if (res.indexOf(item) < 0) {
res.push(item)
}
})
return res
}
const res = unique([30, 10, 20, 30, 40, 10])
console.log(res)
方法2.使用Set (效率更高)
// 使用 Set (无序,不能重复)
function unique(arr) {
const set = new Set(arr) // Set(4) {30, 10, 20, 40}
return [...set] // [30, 10, 20, 40]
}
const res = unique([30, 10, 20, 30, 40, 10])
console.log(res)
3.场景题:async/await的顺序问题
async function async1() {
console.log('async1 start') // 2
await async2()
// await后面的都作为回调内容 —— 微任务
console.log('async1 end') // 6
}
async function async2() {
console.log('async2') // 3
}
console.log('script start') // 1
setTimeout(function () { // 宏任务 setTimeout
console.log('setTimeout') // 8
}, 0)
async1()
// 初始化 Promise 时,传入的函数会立刻被执行
new Promise(function (resolve) {
console.log('promise1') // 4
resolve()
}).then(function () { // 微任务
console.log('promise2') // 7
})
console.log('script end') // 5
// 同步代码执行完毕(event loop - call stack 被清空)
// 执行微任务
// 尝试触发 DOM 渲染
// 触发 Event Loop,执行宏任务
打印结果
面试题
1.描述vue双向数据绑定原理(包括底层代码如何实现)
object这个对象有个方法,叫做defineProperty
,当数据发生访问或者修改,它都能够监测到,就是做到了一个数据劫持
。
一旦数据变化,他就会立即通知相关DOM更新页面,这个就用到了发布订阅者模式
<!-- Object.defineProperty -->
<!-- 数据劫持 -->
<!-- 发布订阅者模式 -->
<script>
var book = {};
var name = "";
// 参1: 要给哪个对象设置属性
// 参2: 属性名称是啥
// 参3: 对象, 封装set和get方法
Object.defineProperty(book, "name", {
// 一旦修改了属性, 就会走到set方法中, value表示修改的内容
// book.name = "xxx"
set: function (value) {
name = value;
// 检测到数据变化的时候, 更新一下dom
$("h1").text(value);
$("input").val(value)
},
// 一旦读取属性, 就会走到get方法中
// book.name
get: function (value) {
return name;
}
});
//input事件: 只要文本框内容发生了变化, 就会执行回调函数
$("input").on("input", function () {
book.name = $(this).val(); //返回值, 决定了将来调用book.name返回的值
});
var num = 0;
$("button").click(function () {
book.name = num++;
console.log(book.name);
});
</script>
2.跨域解决方案
方案一:JSONP
1.原理
由于浏览器同源策略的限制
,网页中无法通过 Ajax 请求非同源的接口数据
。但是 <script> 标签不受浏览器同源策略的影响,可以通过 src 属性,请求非同源的 js 脚本。
因此,JSONP 的实现原理,就是通过 <script> 标签的 src 属性,请求跨域的数据接口,并通过函数调用
的形式,接收跨域接口响应回来的数据。
2.缺点
由于 JSONP 是通过 <script> 标签的 src 属性,来实现跨域数据获取的,所以,JSONP 只支持 GET 数据请求,不支持 POST 请求。
注意:JSONP 和 Ajax 之间没有任何关系
,不能把 JSONP 请求数据的方式叫做 Ajax,因为 JSONP 没有用到 XMLHttpRequest 这个对象。
方案二:跨域资源共享(CORS)
-
CORS是一个W3C标准,支持 GET 和 POST 请求。缺点是不兼容某些低版本的浏览器。
-
它允许浏览器向跨源服务器,发出XMLHttpRequest请求,从而克服了AJAX只能同源使用的限制。
-
实现CORS通信的关键是服务器。只要服务器实现了CORS接口,就可以跨源通信。
3.介绍http协议
HTTP协议
即超文本传输协议
(HyperText Transfer Protocol) ,它规定了客户端与服务器之间进行网页内容传输时,所必须遵守的传输格式。- HTTP 协议采用了
请求/响应
的交互模型。
- 客户端发起的请求叫做 HTTP 请求,客户端发送到服务器的消息,叫做 HTTP 请求报文。
- 服务器响应给客户端的消息内容,叫作响应报文。
4.介绍promise
-
概念
Promise 是异步编程的一种解决方案。从语法上讲,promise是一个对象,用它可以获取异步操作的消息 -
作用:
用同步的方式写异步的代码,可以解决回调地狱问题。
promise对象提供统一的接口,使得控制异步操作更加容易。 -
三种状态
pending (待定)状态,不会触发then和catch
resolved(成功,又称 Fulfilled)状态,会触发后续的then回调函数
rejected(失败)状态,会触发后续的catch回调函数
5.响应式布局有哪几种
- 媒体查询(media-query),根据不同的屏幕宽度设置根元素font-size
- flex 布局
- 百分比布局
6.rem+媒体查询实现响应式布局的原理
使用媒体查询,根据不同的屏幕宽度设置根元素font-size
@media (min-width: 320px) {
html {
/*font-size: 100/750 * 320px*/
font-size: 42.67px;
}
}
@media (min-width: 375px) {
html {
font-size: 50px;;
}
}
@media (min-width: 414px) {
html {
font-size: 55.2px;;
}
}
@media (min-width: 480px) {
html {
font-size: 64px;;
}
}
@media (min-width: 640px) {
html {
font-size: 85.33px;
}
}
@media (min-width: 750px) {
html {
font-size: 100px;;
}
}
7.flex弹性布局的特点
优点:容易上手,根据flex规则很容易达到某个布局效果。
缺点:浏览器兼容性比较差,只能兼容到ie9及以上。
8.css3动画效果如何实现
step1. 用keyframes 定义动画(类似定义类选择器)
@keyframes 动画名称 {
0%{
width:100px;
}
100%{
width:200px;
}
}
- 动画序列
step2. 元素使用动画
div {
width: 200px;
height: 200px;
background-color: aqua;
margin: 100px auto;
/* 调用动画 */
animation-name: 动画名称;
/* 持续时间 */
animation-duration: 持续时间;
}
-
动画常用属性
-
动画简写属性
9.inline元素加margin和padding值有效吗
10.场景题:隔行变色效果的实现
关键点:要用到css3新特性中的结构伪类选择器
- E:nth-child(even) 匹配父元素中的偶数个子元素
- E:nth-child(odd) 匹配父元素中的奇数个子元素
<style>
/* 1. 把所有的偶数 even 孩子选出来 */
ul li:nth-child(even) {
background-color: #ccc;
}
/* 2. 把所有的奇数 odd 的孩子选出来 */
ul li:nth-child(odd) {
background-color: #ddd;
}
</style>
11.场景题:一行上 有两个盒子,如何让左边的 固定宽度,右边盒子自适应
12.伪元素和伪类元素
结构伪类选择器
伪元素选择器
13.清除浮动的几种方式
清除浮动主要是为了解决,父元素因为子级元素浮动引起的内部高度为0的问题。
清除浮动之后,父级就会根据浮动的子盒子自动检测高度。父级有了高度,就不会影响下面的标准流了
方法1 — 额外标签法
也称为隔墙法,是 W3C 推荐的做法。
会在浮动元素末尾添加一个空的标签
。例如<div style=“clear:both”>,或者其他标签(如<br />等)
方法2 — 父级
添加 overflow 属性
可以给父级添加 overflow
属性,将其属性值设置为 hidden
、 auto
或 scroll
方法3 — 父级
添加after伪元素√
.clearfix:after {
content: '';
display: table;
clear: both;
}
14.溢出文字省略号显示
1. 单行文本
/*1. 先强制一行内显示文本*/
white-space: nowrap; ( 默认 normal 自动换行)
/*2. 超出的部分隐藏*/
overflow: hidden;
/*3. 文字用省略号替代超出的部分*/
text-overflow: ellipsis;
注意:
- 设置文本溢出显示省略号时必须有宽度 width;
- 必须要设置display属性为line-block/block,设置为其他值不生效。
2. 多行文本
overflow: hidden;
text-overflow: ellipsis;
/* 弹性伸缩盒子模型显示 */
display: -webkit-box;
/* 限制在一个块元素显示的文本的行数 */
-webkit-line-clamp: 2;
/* 设置或检索伸缩盒对象的子元素的排列方式 */
-webkit-box-orient: vertical;
多行文本溢出显示省略号,有较大兼容性问题, 适合于webKit浏览器或移动端(移动端大部分是webkit内核)
15.vuex的作用
-
概述
Vuex是实现组件全局状态(数据)管理的一种机制,可以方便的实现组件之间的数据共享 -
特性
State
提供唯一的公共数据源,所有共享的数据都要统一放到Store中的State中存储Mutation
用于修改变更$store中的数据- 使用
Action
来执行异步操作 Getter
用于对Store中的数据进行加工处理形成新的数据- Getter只会包装Store中保存的数据,并不会修改Store中保存的数据,当Store中的数据发生变化时,Getter生成的内容也会随之变化
16.vue组件如何通信
父传子
通过props传递
父组件: <child value = ‘传递的数据’ />
子组件: props[‘value’],接收数据,接受之后使用和data中定义数据使用方式一样
子传父
在父组件中给子组件绑定一个自定义的事件,子组件通过$emit()触发该事件并传值。
父组件: <child @receive = ‘receive’ />
子组件: this.$emit(‘receive’,‘传递的数据’)
兄弟组件传值
通过中央通信 let bus = new Vue()
A:methods :{ 函数{bus.$emit(‘自定义事件名’,数据)} 发送
B:created (){bus.$on(‘A发送过来的自定义事件名’,函数)} 进行数据接收