这里分享一份由字节前端面试官整理的「2021大厂前端面试手册」,内容囊括Html、CSS、Javascript、Vue、HTTP、浏览器面试题、数据结构与算法。全部整理在下方文档中,共计111道
HTML
-
HTML5有哪些新特性?
-
Doctype作⽤? 严格模式与混杂模式如何区分?它们有何意义?
-
如何实现浏览器内多个标签页之间的通信?
-
⾏内元素有哪些?块级元素有哪些? 空(void)元素有那些?⾏内元 素和块级元素有什么区别?
-
简述⼀下src与href的区别?
-
cookies,sessionStorage,localStorage 的区别?
-
HTML5 的离线储存的使用和原理?
-
怎样处理 移动端 1px 被 渲染成 2px 问题?
-
iframe 的优缺点?
-
Canvas 和 SVG 图形的区别是什么?
JavaScript
开源分享:【大厂前端面试题解析+核心总结学习笔记+真实项目实战+最新讲解视频】
-
问:0.1 + 0.2 === 0.3 嘛?为什么?
-
JS 数据类型
-
写代码:实现函数能够深度克隆基本类型
-
事件流
-
事件是如何实现的?
-
new 一个函数发生了什么
-
什么是作用域?
-
JS 隐式转换,显示转换
-
了解 this 嘛,bind,call,apply 具体指什么
-
手写 bind、apply、call
-
setTimeout(fn, 0)多久才执行,Event Loop
-
手写题:Promise 原理
-
说一下原型链和原型链的继承吧
-
数组能够调用的函数有那些?
-
PWA使用过吗?serviceWorker的使用原理是啥?
-
ES6 之前使用 prototype 实现继承
-
箭头函数和普通函数有啥区别?箭头函数能当构造函数吗?
-
事件循环机制 (Event Loop)
height: 40rpx;
line-height: 40rpx;
content: “新品”;
color: #fff;
font-size: 9pt;
text-align: center;
background: #8CC64A;
}
.goods-img.on-sale:before{ /立减标签样式/
position: absolute;
left: 0;
top: 0;
width: 100rpx;
height: 40rpx;
line-height: 40rpx;
content: “立减”;
font-size: 9pt;
color: #fff;
text-align: center;
background: #ec6055;
}
逻辑分析:
首页只是些商品,所以逻辑层只要根据每个商品的id
来跳到对应商品的详情页即可,很显然这个方法在多个页面都要使用的,所以使用模块化思想,创建一个modules
文件夹,把方法写在单独的js文件里,并向外输出
const showDetail=(e)=>{
const id=e.currentTarget.dataset.pid; //获取每个商品的id
wx.navigateTo({
url: /pages/goods/show?id=${id}
})
};
export default showDetail;
哪里要使用,就用import引入
import showDetail from “…/…/modules/showDetail”;
二、商品分类页
页面结构分析:
商品分类页分为左侧的商品分类菜单和右边的商品分类展示区,
用两个scroll-view
就可以了,左右两边都设置scroll-y
让它们垂直方向滚动,此外,scroll-view
还有一个scroll-into-view
属性能让我们实现类似a
标签的锚点功能,scroll-into-view
的值是某个子元素的id,但是此处有一个小坑,这个id不能以数字开头
当时查了一下文档就开做了,于是乎给左侧菜单取了些数字id,现在想起来当时我太自以为然了 ,此外如果内容太多,是会产生滚动条的,如图:
这样看起来也太丑了。。
**解决办法:给全局样式加入下面的样式
//隐藏滚动条
::-webkit-scrollbar{
height: 0;
width: 0;
color: transparent;
}
嗯,beautiful !!
商品分类功能
逻辑分析:给页面注册个curIndex(当前选中菜单的下标),如果当前下标和选中的菜单下标相同,则处于激活状态
部分代码:
wxml:
<view class=“cate-nav-list” wx:for=“{{cate_nav_list}}” wx:key=“{{item.id}}” data-id=“{{item.id}}” data-index=“{{index}}”
bindtap=“switchCategory”>
{{item.cate_name}}
js:
const app=getApp();
Page({
/**
- 页面的初始数据
*/
data: {
cate_nav_list:[
{name:“新品”,id:“new”},
{name:“手机”,id:“phone”},
{name:“电视”,id:“tv”},
{name:“电脑”,id:“laptop”},
{name:“家电”,id:“appliance”},
{name:“路由”,id:“router”},
{name:“智能”,id:“smart”},
{name:“儿童”,id:“kids”},
{name:“灯具”,id:“lignts”},
{name:“电源”,id:“adapter”},
{name:“耳机”,id:“headset”},
{name:“音箱”,id:“voicebox”},
{name:“生活”,id:“life”},
{name:“服务”,id:“service”},
{name:“米粉卡”,id:“card”}
],
curIndex:0, //初始化当前下标为0
toView:“new”, //默认显示“新品展示”
detail:[]
},
switchCategory(e){
const curIndex=e.currentTarget.dataset.index?e.currentTarget.dataset.index:0; //获取每个菜单的id
//更新数据
this.setData({
toView:e.currentTarget.dataset.id,
curIndex
});
},
onLoad: function (options) {
const detail=app.globalData.category; //获取分类展示数据
this.setData({
detail
});
}
})
三、发现页
页面结构分析:
里面展示了一些商品宣传视频(当时还是不太想切太多的页面😂)这里用弹性布局+video组件就搞定了。这里是文档
四、商品详情页
页面结构分析:
商品详情顶部由一个swiper组成,中间部分由一些cells组成(weui)点击这里快速查看
底部是商品的概述及参数配置两部分组成,这个还是比较简单的, 给两个元素分别绑定一个布尔值来控制谁显示谁隐藏。
当然,要使用weui必须引入它的样式文件,我们在app.wxss里引入,然后全局都可以使用了
@import “./lib/weui.wxss”;
嗯,weui的官网和github地址自然少不了。weui官网 、weui github官方文档,在github上阅读代码是一个非常有效的学习方式,但是文件夹嵌套太深找个文件也不容易,这里奉上github阅读神器
使用到的weui:
请容许我贴一段weui抄来的结构
{{goods.header}}
{{goods.description}}
{{goods.meta}}
商品详情页的显示
逻辑分析:
每个商品通过id来跳转到相应的详情页,但是这个id会在哪里呢因为先这个详情页是通过一个个商品来打开的,所以在商品详情页加载时,可以在 onLoad 中获取打开当前页面所调用的 query 参数(是条json数据),因为在showDetail里只用了id来跳转,所以options只有id属性
onLoad: function (options) {
console.log(options); //{id:“4”}
const id=options.id; //获取options里的id
const goods=app.globalData.goodsDetail.filter(item=>{
return item.id==id; //通过id来筛选显示对应的商品
});
this.setData({
goods:goods[0] //因为id是唯一的,所以上面筛选出来的数组只有一条数据,这条数据就是要显示的商品数据
});
}
商品图片预览实现
微信小程序为我们提供了wx.previewImage()方法来实现图片的预览,实现方法如下:
previewImage(e){
const index=e.currentTarget.dataset.index; //获取swiper里的图片的下标
const slide=this.data.goods.goods_slides; //获取商品轮播图
const imgList=[]; //定义一个数组来存放轮播图的url
slide.map(item=>{
imgList.push(item.slide_url); //用js的map方法把图片的url地址取出来放到数组里
});
wx.previewImage({
current: imgList[index], // 当前显示图片的链接,不填则默认为 urls 的第一张
urls: imgList
})
}
五、商品属性选择
页面结构:整个商品属性页的结构是由一些cells组成,所以用weui最合适(插一段:原生的radio真的是很丑,不过weui对它做了改进)
一开始想有weui就简单多了,直接用form表单的bindsubmit
处理就可以了,奈何在weui里使用bindsubmit,取不到radio的值,折腾了很久,还是取不到,但是换成原生的radio竟可以取到!!
这个不行,那就换一种吧,用weui radiogroup
里的bindchange
事件吧,虽然繁琐了些,但是还是能解决问题的。。
话不多说,看代码(感觉写了一些很累赘的代码。。)
selectVersion(e) {
const version = e.detail.value;
const memory = version.split(“,”)[0];
const price = version.split(“,”)[1];
wx.setStorageSync(‘memory’, memory);
wx.setStorageSync(‘price’, price);
this.setData({
memory,
price
});
},
selectColor(e) {
let color = e.detail.value;
let cover_img=this.data.goods_attrSelect[0].goods_slides[0].slide_url;
wx.setStorageSync(‘color’, color);
wx.setStorageSync(‘cover’, cover_img);
this.setData({
color,
cover_img
});
},
colorHasSelected(e) {
const curcIndex = e.currentTarget.dataset.index ? e.currentTarget.dataset.index : 0;
console.log(curcIndex);
this.setData({
curcIndex
});
},
versionHasSelected(e) {
const curvIndex = e.currentTarget.dataset.index ? e.currentTarget.dataset.index : 0;
console.log(curvIndex);
this.setData({
curvIndex
});
}
对应商品的属性页的跳转
逻辑分析:在商品详情页中,通过当前商品的id跳转
toSelect(e){
const id=e.currentTarget.dataset.id;
wx.navigateTo({
url:../selectGoods/selectGoods?id=${id}
});
}
在商品的属性选择页里,同样是通过id筛选显示不同商品的属性选择信息
onLoad: function (options) {
const id = options.id;
console.log(id);
const goods_attrSelect = app.globalData.goodsDetail.filter(item => {
return item.id == id;
});
}
商品属性的联动选择
既然是商品,没有属性选择怎么行呢?兴致一来二话不说就是写。但是这里有一个难点:用户的选择多样性,难道你会把所有用户可能做出的选择都列出来吗?天哪,商品这么多,这是要累死我吗?no,no,no,这种方法不可取。哪还有什么解决办法呢?当时一想到的就是用数据缓存,嗯,这种方法可行,但是用缓存存取用户选择的值的话,这里有一个很大的问题:微信小程序里的数据缓存将data存储在本地缓存中指定的key中,会覆盖掉原来该key对应的内容,无论是wx.setStorage(异步接口)还是wx.setStorageSync(同步接口),这样的话,无论用户选择了多少个商品,只要key值一样,缓存数据永远只有一条!!!我此时的内心是痛苦的。
有什么方法能让数据缓存不覆盖原来的值,而是加上去?说实话,这里卡了我好久。废话不多说,直接上代码
submit(e) {
const pre_item = wx.getStorageSync(‘attr_item’);
const temp = {
‘goods_name’: wx.getStorageSync(‘goods_name’),
‘memory’: wx.getStorageSync(‘memory’),
‘price’: wx.getStorageSync(‘price’),
‘color’: wx.getStorageSync(‘color’),
‘select_num’: wx.getStorageSync(‘select_num’),
‘cover’: wx.getStorageSync(‘cover’),
‘selected’: false,
‘isTouchMove’: false
}
wx.setStorageSync(‘attr_item’, [temp, …pre_item]); //把获取到的pre_item和temp的数据缓存存入attr_item
wx.showToast({
title: ‘已加入购物车’,
icon: ‘success’,
duration: 3000,
success() {
setTimeout(() => {
wx.navigateBack({
url: “…/goods/show”
});
}, 1000)
}
});
}
商品数量的加减操作
实现思路:分别绑定一个事件来操作商品数量的加减,最后将用户选择的数量设置到数据缓存里。
实现代码:
data: {
select_num: 1 //默认选择为数量为1
},
minusCount(e) { //用户减操作
let select_num = this.data.select_num;
select_num–;
if (select_num < 1) {
return;
}
this.setData({
select_num
});
wx.setStorageSync(‘select_num’, select_num);
},
addCount(e) { //用户加操作
let select_num = this.data.select_num;
select_num++;
if (select_num > 5) {
return;
}
this.setData({
select_num
});
wx.setStorageSync(‘select_num’, select_num);
}
六、购物车操作
页面结构分析:购物车列表用一个弹性布局就搞定。底部的操作栏用固定定位 + 弹性布局就搞定。
购物车还是空的
到小米商城逛逛
<icon type=“{{item.selected?‘success’:‘circle’}}” class=“” color=“#ff6700” size=“20” bindtap=“selectList” data-index=“{{index}}”
/>
<image src=“{{item.cover}}”
mode=“aspectFill” />
删除
合计:
结算
底部操作栏样式
.user-operation{
width: 100%;
height: 100rpx;
line-height: 100rpx;
display: flex;
flex-direction: row;
justify-content: space-between;
align-items: center;
position: fixed;
left: 0;
bottom: 0;
}
.select-all,.total-price,.btn-primary.pay{
flex: 1; //三个盒子等分所有设备宽度
font-size: 12pt;
text-align: center;
}
加入购物车操作
逻辑分析:之前解决数据缓存问题,是为加入购物车功能做铺垫。在商品属性的级联选择中,已经获得了用户的所有要加入购物车的数据,这时候,把数据取出来在绑定到购物车页面上就可以了。
实现代码:
data: {
cart_list: [], //初始化一个空数组用来存放购物车列表
},
goIndex() { //如果购物车为空,则让用户去首页
wx.switchTab({
url: “…/index/index”
})
},
onShow: function () {
const attr_item = wx.getStorageSync(‘attr_item’); //获取数据缓存里将要加入购物车的数据
let cart_list = this.data.cart_list;
cart_list = […attr_item]; //把缓存里的数据加到购物车列表里
const select_num = cart_list.map(item => { //获取用户每次选择的数量
return item.select_num;
})
let goods_sum=select_num.reduce(function(prev,cur){
return prev+cur; //用es6的reduce()方法把用户每次选择的数量相加
});
wx.setStorageSync(‘goods_sum’, goods_sum); //再次存入缓存
this.setData({ //更新购物车列表
cart_list
});
}
购物车全选、反选、计算总价功能
这是一个很经典的问题,凡事涉及到购物车的操作,这个功能都是少不了的。
data: {
cart_list: [],
totalPrice: 0,
},
selectList(e) {
let selectAllStatus = this.data.selectAllStatus;
const index = e.currentTarget.dataset.index;
let cart_list = this.data.cart_list;
// console.log(cart_list[index].selected);
const selected = cart_list[index].selected;
cart_list[index].selected = !selected;
console.log(selected);
//购物车列表里的条目只要有一个取消,全选就取消
const symbol = cart_list.some(cart => { //这里用es6的some()函数
return cart.selected === false;
});
if (symbol) { //如果找到false,全选就取消
this.data.selectAllStatus = false;
} else {
this.data.selectAllStatus = true;
}
this.setData({ //更新数据
cart_list,
selectAllStatus: this.data.selectAllStatus
});
this.getTotalPrice();
},
getTotalPrice() { //定义一个计算总价的方法
let cart_list = this.data.cart_list;
let totalPrice = 0;
for (let i = 0; i < cart_list.length; i++) {
if (cart_list[i].selected) {
totalPrice += parseInt(cart_list[i].select_num) * parseInt(cart_list[i].price); //注意这里要用parseInt()把数量和价格取出来
}
}
//更新总价
this.setData({
totalPrice
});
},
selectAll(e) {
let selectAllStatus = this.data.selectAllStatus;
selectAllStatus = !selectAllStatus;
let cart_list = this.data.cart_list;
for (let i = 0; i < cart_list.length; i++) {
cart_list[i].selected = selectAllStatus; //全选为true,则所有购物车列表为true,全选为false,则所有购物车列表为false
}
//更新数据
this.setData({
cart_list,
selectAllStatus
});
this.getTotalPrice();
}
删除购物车操作
data: {
startX: 0, //开始坐标
startY: 0,
},
//滑动事件处理
touchmove(e) {
let
index = e.currentTarget.dataset.index, //获取当前索引
文末
技术是没有终点的,也是学不完的,最重要的是活着、不秃。
零基础入门的时候看书还是看视频,我觉得成年人,何必做选择题呢,两个都要。喜欢看书就看书,喜欢看视频就看视频。
最重要的是在自学的过程中,一定不要眼高手低,要实战,把学到的技术投入到项目当中,解决问题,之后进一步锤炼自己的技术。
自学最怕的就是缺乏自驱力,一定要自律,杜绝“三天打鱼两天晒网”,到最后白忙活一场。
高度自律的同时,要保持耐心,不抛弃不放弃,切勿自怨自艾,每天给自己一点点鼓励,学习的劲头就会很足,不容易犯困。
技术学到手后,找工作的时候一定要好好准备一份简历,不要无头苍蝇一样去海投简历,容易“竹篮打水一场空”。好好的准备一下简历,毕竟是找工作的敲门砖。
拿到面试邀请后,在面试的过程中一定要大大方方,尽力把自己学到的知识舒适地表达出来,不要因为是自学就不够自信,给面试官一个好的印象,面试成功的几率就会大很多,加油吧,骚年!