前端面试题
设置样式HTML中
html{
filter: grayscale(100%); /*滤镜 灰色全透*/
}
浏览器是可以支持小于12px字体,但谷歌浏览器认为小于12px字体难以辨别,便对浏览器进行了调整
transform: scale(0.5);
transform-origin:0 0;
/*设置样式*/
svg text{
font-size: 8px;
}
/**设置标签/
<svg>
<text x="0" y="8">缩放字体8pxtext>
svg>
.wrapper{
width: 600px;
height: 600px;
background-color: pink;
text-align: center; // 左右居中
line-height: 600px; // 上下居中
}
.box{
width: 300px;
height: 300px;
background-color: skyblue;
display: inline-block;
vertical-align: middle;
}
.wrapper{
width: 600px;
height: 600px;
background-color: pink;
position: relative;
}
.box{
width: 300px;
height: 300px;
background-color: skyblue;
position: absolute;
top: 50%;
left: 50%;
margin-left: -150px; /* 子元素一半*/
margin-top: -150px;
}
.wrapper{
width: 600px;
height: 600px;
background-color: pink;
position: relative;
}
.box{
width: 300px;
height: 300px;
background-color: skyblue;
position: absolute;
top: 0;
right: 0;
bottom: 0;
left: 0;
margin: auto;
}
.wrapper{
width: 600px;
height: 600px;
background-color: pink;
position: relative;
}
.box{
width: 300px;
height: 300px;
background-color: skyblue;
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%,-50%);
}
.wrapper{
width: 600px;
height: 600px;
background-color: pink;
display: flex;
justify-content: center;
align-items: center;
}
.box{
width: 300px;
height: 300px;
background-color: skyblue;
}
const test=undefined;
console.log(typeof test)
// undefined
const arr=[1,2,2,3,3]
console.log(arr instanceof Array)
// true arr是数组实例
const arr=[1,2,2,3,3]
console.log(arr.constructor)
// [Function: Array]
const arr=[1,2,2,3,3]
console.log(toString.call(arr))
// [object Array]
const arr=[1,2,2,3]
for (let i=0;i<arr.length;i++){
for (let j=i+1;j<arr.length;j++){
if(arr[i]=== arr[j]){ // 如果有相同,删除
arr.splice(j,1)
}
}
}
console.log(arr) // [1,2,3]
const arr=[1,2,2,3]
const newArr=[]
for (let i=0;i<arr.length;i++){
if(newArr.indexOf(arr[i])===-1){ // 判断新建数组中是否有这个元素,没有的话就添加
newArr.push(arr[i])
}
}
console.log(newArr) // [1,2,3]
const arr=[1,2,2,3,3]
const newArr=Array.from(new Set(arr)) // Set去除相同元素,然后转化为真正的数组
console.log(newArr)
function fun(){
let num1=0;
return function(){
let num2=0
console.log(++num1)
console.log(++num2)
}
}
const fun1=fun()
fun1() // 1 1
fun1() // 2 1 // 因为闭包num1被缓存了
var a=1
console.log(this)
console.log(this.a)
const btn=document.querySelector('.btn')
function test(){
console.log(this)
}
test() // window //注意这个在严格模式下为undefined
window.test() // window
btn.onclick=test // 指向节点
const obj={
name:'obj',
test:function(){
console.log(this.name)
}
}
obj.test() // 指向obj,显示obj
const fun=obj.test
fun() // 指向window,显示为空
指向实例对象
function Person(name){
this.name=name;
}
const p1=new Person('jack')
console.log(p1.name) // 指向实例对象,显示jack
会先在自己实例上找,没有的话,顺着原型链找
var oo = {
f: function() {
return this.a + this.b;
}
};
var p = Object.create(oo);
p.a = 1;
p.b = 2;
console.log(p.f()); // 3
箭头函数中this是静态的,不会随着调用而改变,总是指向上一作用域中的this
var name = 'window'
const obj = {
name:'obj',
fn:()=>{
console.log(this.name)
}
}
obj.fn() //window

当对象本身或对象属性被读和写的时候,我们需要知道该数据被操作了,并在这过程中执行一些函数,例如:render函数,而这一过程我把它定义为数据响应式
const obj={}
Object.defineProperty(obj,'hello',{
get:function(){
console.log("调用get方法")
},
set:function(newVal){
console.log("调用set方法"+newVal)
}
})
obj.hello; // 调用get方法
obj.hello='hi'; // 调用set方法hi
watch: {
firstName: function (val) { this.fullName = val + this.lastName }
}
/*
* 只有当firstName触发后才执行,而firstName对应的函数就相当于监听到事件发生后执行的方法
*/
new Vue({
......
beforeCreate() {
Vue.prototype.$bus = this //安装全局事件总线,$bus就是当前应用的vm
}
})
<Test ref="test"/>
...
this.$refs.test.childMethod('val12343');
methods: {
childMethod(val) {
console.log(val); // val就是传过来的值 val12343
},
},
vue-router为非浏览器环境准备了一个abstract模式,其原理为用一个数组stack模拟出浏览器历史记录栈的功能
<router-link :to="{name:'home',params:{id:1}}" />
<router-link :to="{path:'home',params:{id:1}}" />
// 取参 this.$route.params.id
<router-link :to="{name:'home',query:{id:1}}" />
<router-link :to="{path:'home',query:{id:1}}" />
// 取参 this.$route.query.id
this.$router.push({name:'home',params:{id:1,age:2}})
this.$router.push({path:'/home',query:{id:1,age:2}})
this.$router.push(`/home?id=1`);
beforeRouterEnter(to,from,next){}
beforeRouterUpdate(to,from,next){}
beforeRouteLeave(to,from,next){}
click事件的延迟300ms是为了移动端缩放
<!-- 1.禁用缩放 user-scalable=no -->
<meta name="viewport" content="width=device-width, initial-scale=1.0,user-scalable=no">
touchstart后延迟350ms后再隐藏,先把透明度设为0,解决视觉层面效果,在设置定时器延迟,让元素消失
$('.mask').on('touchstart',function() {
console.log('mask-touchstart');
$(this).css('opacity', 0); // 设置视觉层面消失
setTimeout(function() {
$('.mask').css('dispaly', 'none'); // DOM节点消失
},350);
})
$('.mask').on('touchstart',function() {
console.log('mask-touchstart');
$(this).css('display', 'none');
$('.box').css('pointer-events', 'none'); // 让被覆盖元素无法响应click
setTimeout(function() {
$('.box').css('pointer-events', 'auto'); // 恢复被覆盖元素
},300);
})
使用fastclick库后,所有点击事件都使用click,没有350ms延时,也没有样式穿透问题
<script type='application/javascript' src='/path/to/fastclick.js'></script>
...
if ('addEventListener' in document) {
document.addEventListener('DOMContentLoaded', function() {
FastClick.attach(document.body);
}, false);
}
在上层的布局中增加android:clickable="true"的属性,这样下层的事件就不会被触发了
//获取meta节点
var metaNode = document.querySelector('meta[name=viewport]');
//定义设计稿宽度为375
var designWidth = 375;
//计算当前屏幕的宽度与设计稿比例
var scale = document.documentElement.clientWidth/designWidth;
//通过设置meta元素中content的initial-scale值达到移动端适配
meta.content="initial-scale="+scale+",minimum-scale="+scale+",maximum-scale="+scale+",user-scalable=no";
//对屏幕大小划分了html不同的font-size
@media screen and (min-width: 320px) {html{font-size:50px;}}
@media screen and (min-width: 360px) {html{font-size:56.25px;}}
@media screen and (min-width: 375px) {html{font-size:58.59375px;}}
@media screen and (min-width: 400px) {html{font-size:62.5px;}}
@media screen and (min-width: 414px) {html{font-size:64.6875px;}}
@media screen and (min-width: 440px) {html{font-size:68.75px;}}
@media screen and (min-width: 480px) {html{font-size:75px;}}
@media screen and (min-width: 520px) {html{font-size:81.25px;}}
@media screen and (min-width: 560px) {html{font-size:87.5px;}}
@media screen and (min-width: 600px) {html{font-size:93.75px;}}
@media screen and (min-width: 640px) {html{font-size:100px;}}
@media screen and (min-width: 680px) {html{font-size:106.25px;}}
@media screen and (min-width: 720px) {html{font-size:112.5px;}}
@media screen and (min-width: 760px) {html{font-size:118.75px;}}
@media screen and (min-width: 800px) {html{font-size:125px;}}
@media screen and (min-width: 960px) {html{font-size:150px;}}
var designWidth = 375; // 设计稿宽度
var remPx = 100; // 在屏幕宽度375px,的时候,设置根元素字体大小 100px
var scale = window.innerWidth / designWidth; //计算当前屏幕的宽度与设计稿比例
// 根据屏幕宽度 动态计算根元素的 字体大小
document.documentElement.style.fontSize = scale*remPx + 'px';
// 计算屏幕宽度
var screen = document.documentElement.clientWidth;
// 计算字体大小,取屏幕宽度的16分之一
var size = screen / 16;
// 设置根元素字体大小
document.documentElement.style.fontSize = size + 'px';
js获取当前屏幕的宽度,将屏幕宽度的16分之一设置给html的font-size
// 此时设计稿的宽度为375,定义一个less变量等于16分之一的设计稿宽度
@rem: 375/16rem;
// 计算屏幕宽度
var screen = document.documentElement.clientWidth;
// 设置根元素字体大小
document.documentElement.style.fontSize = screen + 'px';
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1, minimum-scale=1, user-scalable=no">
npm install postcss@8.2.6 --save
npm install postcss-import@14.0.0 --save
npm install postcss-loader@5.0.0 --save
npm install postcss-pxtorem@5.1.1 --save
npm install postcss-url@10.1.1 --save
module.exports = {
plugins: {
'postcss-pxtorem': {
rootValue: 32, //根目录的字体大小设为32px
propList: ['*'], //proplist:是那些属性需要转换成rem,全部
minPixelValue: 2 //最小转换单位.2px
}
}
};
<script>(function () {
function autoRootFontSize() {
document.documentElement.style.fontSize = Math.min(screen.width, document.documentElement
.getBoundingClientRect().width) / 470 * 32 + 'px';
// 取screen.width和document.documentElement.getBoundingClientRect().width的最小值;
// 除以470,乘以32;意思就是相对于470大小的32px;
}
window.addEventListener('resize', autoRootFontSize);
autoRootFontSize();
})();</script>
注:设计师制作的效果图常为750px,为了方便直接读取UI标好的像素大小,根目录的字体大小就设置为32px;
详情
Babel默认只转换新的JavaScript句法(syntax),而不转换新的API.(不转换新的API.的意思是:对极少部分浏览器无法支持的api,转换之后的也无法正常的使用,比如:import,export)
cnpm install babel-cli -g
cnpm install babel-preset-env --save-dev
{
"presets": ["env"]
}
文件到文件:babel js文件路径 -o 转译后生成的文件路径
cnpm install babel-cli babel-preset-env --save-dev
{
"presets": ["env"]
}
"scripts": {
"build": "babel src -d lib"
},
npm run build
// webpack.base.js
{
test: /\.js$/,
use: [
'thread-loader',
'babel-loader'
],
}
XSS 前端替换关键字,建议后端也替换,如<>的替换,避免脚本的执行
XSRF增加验证流程,比如输入密码,指纹,短信验证码,人脸识别等
| 生命周期 | 说明 |
|---|---|
| onLanch | 小程序初始化完成时触发,全局只触发一次 |
| onShow | 小程序启动,后者后台进入前台触发 |
| onHide | 小程序从前台进入后台触发 |
| onError | 小程序发生脚本错误或者API调用时报错时触发 |
| onPageNotFound | 小程序打开时页面不存在时触发 |
| onUnhandledRejection | 有未处理的promise时触发 |
| onThemeChange | 系统切换主题时触发 |
| 生命周期 | 说明 | 作用 |
|---|---|---|
| onLoad | 监听页面加载 | 发送请求获取数据 |
| onShow | 监听页面显示 | 发送请求获取数据 |
| onReady | 监听页面初次渲染完成 | 获取页面元素 |
| onHide | 监听页面隐藏 | 终止任务,如定时器或播放音乐 |
| onUnLoad | 页面卸载 | 终止任务 |
| 生命周期 | 说明 |
|---|---|
| created | 监听页面加载 |
| attached | 监听页面显示 |
| ready | 页面初次渲染完成 |
| moved | 监听页面隐藏 |
| datached | 监听页面卸载 |
| error | 组件方法抛出错误时执行 |
{
"pages":[
"pages/index",
"pages/logs"
],
"subpackages": [
{
"root": "packageA", // 分包根目录
"pages": [
"pages/cat",
"pages/dog"
]
}, {
"root": "packageB",
"name": "pack2", // 分包别名,分包预下载时可以使用
"pages": [ // 分包页面路径,相对于分包根目录
"pages/apple",
"pages/banana"
],
independent:false // 是否独立分包
}
]
}
wx.getUserProfile
<button
class="btn"
open-type="getPhoneNumber"
bindgetphonenumber="getPhoneNumber"
>
{{phoneNumber==null?'获取手机号':phoneNumber}}
</button>
...
getPhoneNumber: function (e) {
var that = this;
if (e.detail.errMsg == "getPhoneNumber:ok") {
wx.request({
url: app.serverUrl+'/api/login/decodePhone',
data: {
encryptedData: e.detail.encryptedData,
iv: e.detail.iv,
userToken: that.data.userToken,
},
header: {
'content-type': 'application/json'
},
success: function (res) {
that.setData({
phoneNumber:res.data.phoneNumber
})
}
})
}
},
// 调用方法准换为图片路径
window.URL.createObjectURL(res)
<template>
<div class='testDate'>
<div v-for="item in newList" :key="item.id">
<span>{{item.text}}</span>
</div>
</div>
</template>
<script>
export default {
name: 'testDate',
components: {},
props: {},
data() {
return {
list: [], // 用来模拟后端给的10W条数据
newList: [], // 用于渲染的数据
total: 100000, // 一共多少条数据
page: 0, // 第几页,默认0为第一页
limit: 200, // 一页200条数据
};
},
computed: {},
watch: {},
created() {
for (let i = 0; i < 100000; i += 1) {
this.list.push({
id: i,
text: `测试数据${i}`,
});
}
},
mounted() {
this.render();
},
methods: {
render() {
if (this.page >= Math.ceil(this.total / this.limit)) return; // 如果第几页的页数已经大于总共的页数,说明数据已经完了,直接return返回
setTimeout(() => {
for (let i = this.page * this.limit; i < this.page * this.limit + this.limit; i += 1) {
this.newList.push(this.list[i]); // 添加到newList用于渲染的数据列表中
}
this.page += 1;
this.render(); // 递归调用
}, 0);
},
},
};
</script>
<template>
<div class='testDate'>
<div v-for="item in newList" :key="item.id">
<span>{{item.text}}</span>
</div>
</div>
</template>
<script>
export default {
name: 'testDate',
components: {},
props: {},
data() {
return {
list: [], // 用来模拟后端给的10W条数据
newList: [], // 用于渲染的数据
total: 100000, // 一共多少条数据
page: 0, // 第几页,默认0为第一页
limit: 200, // 一页200条数据
};
},
computed: {},
watch: {},
created() {
for (let i = 0; i < 100000; i += 1) {
this.list.push({
id: i,
text: `测试数据${i}`,
});
}
},
mounted() {
this.render();
},
methods: {
render() {
if (this.page >= Math.ceil(this.total / this.limit)) return;
requestAnimationFrame(() => {
for (let i = this.page * this.limit; i < this.page * this.limit + this.limit; i += 1) {
this.newList.push(this.list[i]);
}
this.page += 1;
this.render();
});
},
},
};
</script>
<template>
<div class='testDate'>
<div v-for="item in newList" :key="item.id">
<span>{{item.text}}</span>
</div>
<div ref="blank"></div> <!--加个空白元素来判断是否需要加载 -->
</div>
</template>
<script>
export default {
name: 'testDate',
components: {},
props: {},
data() {
return {
list: [], // 用来模拟后端给的10W条数据
newList: [], // 用于渲染的数据
total: 100000, // 一共多少条数据
page: 0, // 第几页,默认0为第一页
limit: 200, // 一页200条数据
};
},
computed: {},
watch: {},
created() {
for (let i = 0; i < 100000; i += 1) {
this.list.push({
id: i,
text: `测试数据${i}`,
});
}
},
mounted() {
this.render();
window.addEventListener('mousewheel', this.render);
},
methods: {
render() {
if (this.page >= Math.ceil(this.total / this.limit)) return;
// 当最后一个空白元素高度小于等于屏幕高度,则说明已经来到底部了,需要加载元素来补充视图
if (this.$refs.blank.getBoundingClientRect().top <= document.documentElement.clientHeight) {
for (let i = this.page * this.limit; i < this.page * this.limit + this.limit; i += 1) {
this.newList.push(this.list[i]);
}
this.page += 1;
}
},
},
};
</script>
实例:a+b=1+2=3;a+#=1+0=1
function sumFun(value){
let arr=[] // 定义一个数组保存值
const valueArr=value.toUpperCase().split('') // 字符串转大写,转数组
const englishLetter=['A','B','C','D','E','F','G','H','I','J','K','L','M','N','O','P','Q','R','S','T','U','V','W','X','Y','Z']
const operationSymbol=['+','-','*','/']
valueArr.forEach(item=>{ // 循环遍历数组
if(englishLetter.includes(item)){ // 是否是数字
arr.push(englishLetter.findIndex(i=>i===item))
}else if(operationSymbol.includes(item)){ // 是否是运算符
arr.push(item)
}else { // 其他都为0
arr.push(0)
}
})
return eval(arr.join('')) // 通过eval计算字符串中的表达式,返回该表达式计算的结果
}
const value='a+b+c-b+#'
console.log(sumFun(value))