又到了一年一度的金三银四,这是菜狗子前端为自己记录下的面试题目,不对的或者有什么补充的可以留言哦~~~
2022.03.02 面试
1、A页面分享到B页面,不同手机型号保留分享页面位置
1. 看场景记录操作过程或者操作结果
2. 记录滚动位置onPageScroll,设备宽度getSystemInfo(记录单位为px)
3. 分享url带入记录参数onShareAppMessage
4. 用户打开页面Promise.all所有异步调用结束后 操作一记录结果
5. pageScrollTo(记录单位px) 不同机型rpx相同px不同 根据1px/1rpx=750/屏幕宽度计算
例如 a页面(屏宽414 1px=750/414rpx)onPageScroll记录top距离为500(px)=>905.8(rpx)
a页面(屏宽360 1px=750/360rpx)pageScrollTo 905.8rpx/2.083=434.85px
a页面(屏宽320 1px=320/320rpx)pageScrollTo 905.8rpx/2.344=386.47px
2、setDate数据操作限制
**注意:**
1. 直接修改 this.data 而不调用 this.setData 是无法改变页面的状态的,还会造成数据不一致**。
2. 仅支持设置可 JSON 化的数据。
3. 单次设置的数据不能超过1024kB,请尽量避免一次设置过多的数据。
4. 请不要把 data 中任何一项的 value 设为 `undefined` ,否则这一项将不被设置并可能遗留一些潜在问题。
3、长列表性能优化
js回调里,将页面的page做为数组的下标来实现增量渲染,而不是将数据所有从新渲染一次
wxml文件里作一个双重循环,达到增量渲染的效果,实测数万级列表也不会卡顿
that.setData({["List[" + page + "]"] :resList})
<block wx:for="{{List}}" wx:for-item="list-item">
<block wx:for="{{list-item}}" wx:for-item="list">
<view class="tab-content">
{{list.content}}
</view>
</block>
</block>
4、边框阴影 加伪类
<style type="text/css">
.qp {
width: 500px;
margin: 20px auto;
}
/* 方法一 */
/* .sjx {
position: relative;
width: 40px;
height: 40px;
background-color: green;
margin-left: 20px;
transform: rotate(45deg);
top: 20px;
z-index: 1;
box-shadow: -4px -5px 7px cyan
}
.sjx::after {
position: absolute;
content: '';
display: block;
width: 40px;
height: 40px;
background-color: white;
top: 5px;
left: 5px;
} */
/* 方法二 */
.sjx {
width: 0;
border: 30px solid;
border-left-color: transparent;
border-right-color: transparent;
border-top-color: transparent;
border-bottom-color: green;
margin-left: 10px;
position: relative;
}
.sjx::before {
position: absolute;
content: '';
border: 28px solid;
border-left-color: transparent;
border-right-color: transparent;
border-top-color: transparent;
border-bottom-color: white;
top: -19px;
z-index: 1;
left: -28px;
}
.sjx::after {
position: absolute;
content: '';
width: 60px;
height: 60px;
left: -30px;
top: 12px;
box-shadow: 0px 0px 10px aqua;
transform: rotate(45deg);
}
.qpk {
position: relative;
z-index: 0;
width: 500px;
height: 200px;
border-radius: 10px;
border: 5px solid green;
background-color: white;
position: relative;
box-shadow: 0px 0px 10px aqua
/* 这里也可以用伪类 */
}
</style>
<div class="qp">
<div class="sjx">
</div>
<div class="qpk">
</div>
</div>
5、前端图片性能优化
1. 选择合适的图片格式和压缩大图,可从根源上解决大图加载过慢的问题。
2. 使用雪碧图,iconfont,base64,css 代替图片等可减少图片 http 请求,提高页面加载速度。
3. 使用 CDN 图片可达到分流的效果,减少服务器压力。
4. 图片懒加载,预加载,渐进式图片等可不同程度减少白屏时间,提高产品体验。
6、开发小程序需要哪些技能
7、小程序支付
8、如何阻止频繁请求,网络抖动怎么办
1.防抖节流
2.btn发出请求后禁用,同时设置loading或其他提示信息,返回结果或者返回失败解除禁用
// 节流:一定的时间内多次触发只执行一次
function throttle(fn, delay) {
// 记录上一次函数触发的时间
let lastTime = Date.now();
return function () {
// 记录当前函数触发的时间
var nowTime = Date.now()
// 当前时间减去上一次执行时间大于这个时间间隔才让他触发这个函数
if (nowTime - lastTime >= delay) {
// 绑定this 指向
fn.apply(this, arguments);
// 同步时间
lastTime = Date.now();
}
}
}
function fn() {
console.log('节流');
}
addEventListener('scroll', throttle(fn, 1000))
//防抖:触发后delay秒后再执行,再次触发重新计算时间
function debounce(fn, delay) {
// 利用闭包保存定时器
let timer = null;
return function () {
// 在规定时间内再次触发会先清除定时器后再重设定时器
clearTimeout(timer)
// 重新设置一个新的延时器
timer = setTimeout(() => {
fn.apply(this, arguments)
}, delay);
}
}
function fn() {
console.log('防抖');
}
addEventListener('scroll', debounce(fn, 1000))
9、使用uni-app开发小程序,比直接原生开发小程序好在哪里?
| uni-app | 微信 | |
|---|---|---|
| 功能 | 相同 | 相同 |
| 性能 | 常规场景更优 | 需要自己编写复杂代码才能提高性能 |
| 社区生态 | 丰富,更多高性能组件 | 丰富 |
| 开发体验 | 纯vue体验,高效、统一;工程化能力强 | 语法私有化;工程化能力弱 |
| 多端能力 | 同时支持H5、多家小程序、跨平台App | 只能用于微信小程序 |
9、for in for of
1. for in遍历的是数组的索引(即键名),而for of遍历的是数组元素值
2. for-in得到的是对象的key或数组、字符串的下标
3. for-of得到的是对象的value或数组、字符串的值,另外还可以用于遍历Map和Set
***伪数组
含有length属性和索引的对象,不能调用数组方法,用for in循环
转数组
console.log(Array.prototype.slice.call(arr));
console.log(Array.prototype.splice.call(arr,0,arr.length));
console.log(Array.from(arr));
function Demo(list){
this.list = list;
this[Symbol.iterator] = function(){
let index = 0;
return {
next: function(){
return index < list.length ? {value: list[index++], done: false} : {value: undefined, done: true};
}
}
}
}
let arr = new Demo({0:1,1:2,2:3,length:3})
for (let i of arr) {
console.log(i)//1 2 3
}
2022.03.04 面试
1、箭头函数
1. 箭头函数是匿名函数,不能作为构造函数,不能使用new
2. 箭头函数不绑定arguments,取而代之用rest参数...解决
3. this的作用域不同,箭头函数不绑定this,会捕获其所在的上下文的this值,作为自己的this值
4. 箭头函数没有原型属性
5. 箭头函数不能当做Generator函数,不能使用yield关键字
2、get post区别
### Get
1. GET请求的数据会附加在URL之后,用问号分割,多个参数用&进行连接。
2. GET请求的数据会暴露在地址栏中。
3. GET请求URL的编码格式采用的是ASCII编码,而不是Unicode编码。
4. GET请求传输大小有限制,大小在2KB。
5. GET相对安全性较差,会被浏览器主动缓存。
6. GET产生一个TCP数据包,head和data一起发送。
7. GET浏览器回退无害。
### POST
1. POST请求会把数据放置在HTTP请求包的包体中,不会直接暴露给用户。
2. POST请求,理论上大小是不会限制的,但是实际上各个服务器会规定POST提交数据大小。
3. POST相对Get更安全,因为参数不会保存浏览器立式或者是web服务器日志中。
4. POST产生两个TCP数据包,header先发送,服务器响应100ms然后继续,发送data,服务器200然后返回数据。
5. POST浏览器回退重新请求。
3、js事件绑定的三种方式
1. 在标签中直接绑定
<button onclick="handleClick()" >自定义函数</button>
<button onclick="alert('原生函数')" >原生函数</button>
2. 在js事件中获取dom事件绑定
<button id="btn" onclick="handleClick()" >dom事件绑定</button>
document.getElementById('btn').onclick=handleClick();
3. 事件监听addEventListener和attachEvent
elementDOM.addEventListener(eventName,handle,useCapture);
elementDOM.attachEvent(eventName,handle);
4. 对this对象的理解
this 是执行上下文中的一个属性,它指向最后一次调用这个方法的对象。在实际开发中,this 的指向可以通过四种调用模式来判断。
- 第一种是**函数调用模式**,当一个函数不是一个对象的属性时,直接作为函数来调用时,this 指向全局对象。
- 第二种是**方法调用模式**,如果一个函数作为一个对象的方法来调用时,this 指向这个对象。
- 第三种是**构造器调用模式**,如果一个函数用 new 调用时,函数执行前会新创建一个对象,this 指向这个新创建的对象。
- 第四种是 **apply 、 call 和 bind 调用模式**,这三个方法都可以显示的指定调用函数的 this 指向。其中 apply 方法接收两个参数:一个是 this 绑定的对象,一个是参数数组。call 方法接收的参数,第一个是 this 绑定的对象,后面的其余参数是传入函数执行的参数。也就是说,在使用 call() 方法时,传递给函数的参数必须逐个列举出来。bind 方法通过传入一个对象,返回一个 this 绑定了传入对象的新函数。这个函数的 this 指向除了使用 new 时会被改变,其他情况下都不会改变。
这四种方式,使用构造器调用模式的优先级最高,然后是 apply、call 和 bind 调用模式,然后是方法调用模式,然后是函数调用模式。
5、分别介绍一下原型、原型链、作用域和作用域链的含义和使用场景
作用域
可访问变量的集合就叫作用域,作用域分为全局作用域和局部作用域(函数作用域),局部变量只能在函数内部访问
作用域链
一个函数在执行前,会创建一个执行期上下文对象,而作用域所存储的执行期上下文的集合,这种集合呈链式链接,我们称之为作用域链
原型
函数都有一个propotype属性,它指向一个对象,该对象称之为原型对象,即原型
原型链
每个对象都可以有一个原型__proto__,这个原型还可以有它自己的原型,以此类推,形成一个原型链
2022.03.10 面试
1、渲染十万条数据解决方案
2、setTimeout setInterval执行10次区别
3、js性能优化 另一个
4、js造成内存泄漏的几种情况
5、css选择器权重问题
选择器的种类
- !important
- 内联样式
- ID选择器
- class选择器
- 元素选择器
- 通配符选择器
- 伪类选择器
- 属性选择器
**伪类选择器**和**属性选择器**的权重相当于类选择器的权重,权级为2级。
- 伪元素选择器
**伪元素选择器**的权重相当于元素选择器的权重,权级为1级。
- 子代选择器
- 后代选择器
子代和后代有可能是元素选择器、类选择器、id选择器,因此要根据具体情况来具体分析。
6、slice splice区别 分别返回什么
slice(start,end)substring(),substr()
splice(index,howmany,item1,...itemX)
var str = "Hello,world!";
var sliceStr = str.slice(1,5);//ello (不包含end)
var subStr = str.substring(1,5);//ello
var subStr = str.substr(1,5);//ello,
var str = "1000000";
var sliceStr = str.slice(-3);//000 从序列号为-3的位置到最后
splice(index,howmany,item1,...itemX)
var arr = [1,2,3,4,5];
console.log(arr.splice(2,1,"hello"));//[3] 返回的新数组
console.log(arr);//[1, 2, "hello", 4, 5] 改变了原数组
7、keep-alive情况下,首次进入和再次进入,created,mounted,actived的执行
页面第一次进入,钩子的触发顺序created-> mounted-> activated,退出时触发deactivated。
当再次进入(前进或者后退)时,只触发activated。
8、flex :1是哪三个属性的简写
flex-grow、flex-shrink、flex-basis三个属性的缩写。
flex-grow: <number>; /* default 0 */
定义项目的放大比例,默认为`0`,即如果存在剩余空间,也不放大。
如果所有项目的`flex-grow`属性都为1,则它们将等分剩余空间(如果有的话)
如果一个项目的`flex-grow`属性为2,其他项目都为1,则前者占据的剩余空间将比其他项多一倍。
flex-shrink: <number>; /* default 1 */
定义了项目的缩小比例,默认为1,即如果空间不足,该项目将缩小。
如果所有项目的`flex-shrink`属性都为1,当空间不足时,都将等比例缩小。
如果一个项目的`flex-shrink`属性为0,其他项目都为1,则空间不足时,前者不缩小。
flex-basis: <length> | auto; /* default auto */
定义了在分配多余空间之前,项目占据的主轴空间(main size)。
浏览器根据这个属性,计算主轴是否有多余空间。它的默认值为`auto`,即项目的本来大小。
它可以设为跟`width`或`height`属性一样的值(比如350px),则项目将占据固定空间。
9、深拷贝如何解决循环引用问题
function isObject(obj) {
return (typeof obj === "object" || typeof obj === "function") && obj !== null;
}
function cloneDeep(source, hash = new WeakMap()) {
if (!isObject(source)) return source;
if (hash.has(source)) return hash.get(source); // 新增代码,查哈希表
var target = Array.isArray(source) ? [] : {};
hash.set(source, target); // 新增代码,哈希表设值
for (var key in source) {
if (Object.prototype.hasOwnProperty.call(source, key)) {
if (isObject(source[key])) {
target[key] = cloneDeep(source[key], hash); // 新增代码,传入哈希表
} else {
target[key] = source[key];
}
}
}
return target;
}
10、v-model
代码题
1、递归
let tree = [{
id: "1",
name: "北京",
children: [{
id: "11",
name: "北京市",
children: [{
id: "111",
name: "东城区",
children: null,
},
{
id: "112",
name: "西城区",
children: null,
},
],
}, ],
},
{
id: "2",
name: "上海",
children: [{
id: "21",
name: "上海市",
children: [{
id: "211",
name: "黄埔区",
children: null,
},
{
id: "212",
name: "静安区",
children: null,
},
],
}, ],
},
]
function getName(data, id) {
let name
data.forEach(item => {
if (item.id == id) {
name = item.name
} else if (item.children) {
name = getName(item.children, id)
}
})
return name
}
console.log(getName(tree, 211)) //黄埔区
2、闭包
let add = (function() {
let num = 0;
return function() {
return num++;
}
})()
console.log(add()) //0
console.log(add()) //1
console.log(add()) //2
3、打印对称数
function isSymmetryNum(start, end) {
for (var i = start; i < end + 1; i++) {
var iInversionNumber = i.toString().split("").reverse().join("")
if (iInversionNumber === i && i > 10) {
console.log(i);
}
}
}
isSymmetryNum(1, 10000);
3、parseInt
var a = ["1", "2", "3", "4", "5", 6, 7, 8, 9, 10, 11, 12, 13, 14, 15];
console.log(a.map(parseInt))
//[1, NaN, NaN, NaN, NaN, NaN, NaN, NaN, NaN, 9, 11, 13, 15, 17, 19]
4、深拷贝
function checkType(obj) {
let type = Object.prototype.toString.call(obj)
return type.split(' ')[1].slice(0, -1)
}
function clone(obj) {
let result;
let type = checkType(obj)
if (type === 'Object') {
result = {}
} else if (type === 'Array') {
result = []
} else {
result = obj
}
for (const key in obj) {
if (obj.hasOwnProperty(key)) {
let value = obj[key]
if (checkType(value) === 'Object' || checkType(value) === 'Array') {
result[i] = clone(value)
} else {
result[i] = value
}
}
}
return result
}
5、二分快排
var arr = [12, 7, 19, 55, 10, 21, 45, 13, 25, 8];
function quickSort(arr) {
if (arr.length <= 1) {
return arr
};
var mNumIndex = Math.floor(arr.length / 2);
var mNum = arr.splice(mNumIndex, 1)[0];
var left = [];
var right = [];
for (var i = 0; i < arr.length; i++) {
if (arr[i] < mNum) {
left.push(arr[i])
} else {
right.push(arr[i])
};
};
return quickSort(left).concat([mNum], quickSort(right));
}
console.log(quickSort(arr));
6、substr,substring,slice
let str = "Hello world"
1、str.substr(起始下标start,子串长度length); 其实下标为负数(-2),表示从后算下标(倒数第二个数); length可不写,不写代表到字符串结束;
console.log(str.substr(3, 5)); //从下标3处开始截取,截取5位数,输出lo wo;
console.log(str.substr(-3, 1)); //从倒数第3位开始截取,截取1位数, 输出r;
console.log(str.substr(-5)); //没有写length代表截取到最后,从倒数第5位开始,去到最后,输出world;
2、substring(start,end)用于截取“介于两个指定下标之间”的字符
console.log(str.substring(2, 7)); //截取下标2-下标6“之间”的字符,即下标2-5的字符; 输出llo w
console.log(str.substring(3)); //截取下标3至最后的字符; 输出lo world;
3、slice()和substring(start,end)类似,只不过slice的参数可以为负数;
console.log(str.slice(3, 7)); //截取下标3-下标6的字符; 输出lo w;
console.log(str.slice(-5, -2)); //截取倒数第5位-倒数第2位"之间"的字符,输出wor;
console.log(str.slice(-3)); //截取倒数第3位到最后的字符串,输出rld;
7、事件捕获、冒泡
第一次结果为: 先弹出“ child 事件触发, child”, 再弹出“ parent 事件触发, parent”
第二次结果为: 先弹出“ child 事件触发, child”, 再弹出“ parent 事件触发, child”
第三次结果为: 先弹出“ parent 事件触发, child”, 再弹出“ child 事件触发, child”
document.getElementById("parent").addEventListener("click", function() {
alert(`parent 事件触发,` + this.id);
});
document.getElementById("child").addEventListener("click", function() {
alert(`child 事件触发,` + this.id);
});
------------------------------------------------------------------------------
document.getElementById("parent").addEventListener("click", function(e) {
console.log(e)
alert(`parent 事件触发,` + e.target.id);
});
document.getElementById("child").addEventListener("click", function(e) {
alert(`child 事件触发,` + e.target.id);
});
------------------------------------------------------------------------------
document.getElementById("parent").addEventListener("click", function(e) {
alert(`parent 事件触发,` + e.target.id);
}, true);
document.getElementById("child").addEventListener("click", function(e) {
alert(`child 事件触发,` + e.target.id);
}, true);
8、数反转数组本身的顺序,不用reverse
function reverse(arr) {
for (let i = 0; i < arr.length / 2; i++) {
let temp = arr[i]
arr[i] = arr[arr.length - i - 1]
arr[arr.length - i - 1] = temp
}
}
9、链表反转
var reverseList = function(head) {
var list = head;
var p = list;
var q= null;
while(p.next !== null) {
q = p.next;
p.next = q.next;
q.next = list;
list = q;
}
return list;
};