一、$HTML, HTTP,web综合问题
1、前端需要注意哪些SEO(搜索引擎优化)
- 合理的title、description、keywords:搜索对着三项的权重逐个减小,title值强调重点即可,重要关键词出现不要超过2次,而且要靠前,不同页面title要有所不同;description把页面内容高度概括,长度合适,不可过分堆砌关键词,不同页面description有所不同;keywords列举出重要关键词即可
- 语义化的HTML代码,符合W3C规范:语义化代码让搜索引擎容易理解网页
- 重要内容HTML代码放在最前:搜索引擎抓取HTML顺序是从上到下,有的搜索引擎对抓取长度有限制,保证重要内容一定会被抓取
- 重要内容不要用js输出:爬虫不会执行js获取内容
- 少用iframe:搜索引擎不会抓取iframe中的内容
- 非装饰性图片必须加alt
- 提高网站速度:网站速度是搜索引擎排序的一个重要指标
2、img的title和alt有什么区别
- title通常当鼠标滑动到元素上的时候显示
- alt是img的特有属性,是图片内容的等价描述,用于图片无法加载时显示、读屏器阅读图片。可提图片高可访问性,除了纯装饰图片外都必须设置有意义的值,搜索引擎会重点分析。
3、HTTP的几种请求方法用途
GET方法:
发送一个请求来取得服务器上的某一资源
POST方法:
向URL指定的资源提交数据或附加新的数据
PUT方法:
跟POST方法很像,也是想服务器提交数据。但是,它们之间有不同。PUT指定了资源在服务器上的位置,而POST没有
HEAD方法:
只请求页面的首部
4、从浏览器地址栏输入url到显示页面的步骤
- 浏览器将请求的url地址交给DNS域名解析,找到真实的IP,向服务器发起请求
- 服务器交给后台处理完成后返回数据,浏览器接收文件(HTML,JS,CSS);
- 浏览器对加载到的资源(HTML,JS,CSS)进行语法解析,建立相应的内部数据结构(如
HTML
的DOM
)- 载入解析到的资源,渲染页面,完成
5、如何进行网站性能优化
content
方面
- 减少
HTTP
请求:合并文件、CSS
精灵、inline Image
- 减少
DNS
查询:DNS
缓存、将资源分布到恰当数量的主机名- 减少
DOM
元素数量
Server
方面
- 使用
CDN
- 配置
ETag
- 对组件使用
Gzip
压缩
Cookie
方面
- 减小
cookie
大小
css
方面
- 将样式表放到页面顶部
- 不使用
CSS
表达式- 使用
<link>
不使用@import
Javascript
方面
- 将脚本放到页面底部
- 将
javascript
和css
从外部引入- 压缩
javascript
和css
- 删除不需要的脚本
- 减少
DOM
访问
- 图片方面
- 优化图片:根据实际颜色需要选择色深、压缩
- 优化
css
精灵- 不要在
HTML
中拉伸图片
6、语义化的理解
- 用正确的标签做正确的事情!
html
语义化就是让页面的内容结构化,便于对浏览器、搜索引擎解析;- 在没有样式
CSS
情况下也以一种文档格式显示,并且是容易阅读的。- 搜索引擎的爬虫依赖于标记来确定上下文和各个关键字的权重,利于
SEO
。- 使阅读源代码的人对网站更容易将网站分块,便于阅读维护理解
7、对浏览器内核的理解
- 主要分成两部分:渲染引擎(
layout engineer
或Rendering Engine
)和JS
引擎- 渲染引擎:负责取得网页的内容(
HTML
、XML
、图像等等)、整理讯息(例如加入CSS
等),以及计算网页的显示方式,然后会输出至显示器或打印机。浏览器的内核的不同对于网页的语法解释会有不同,所以渲染的效果也不相同。所有网页浏览器、电子邮件客户端以及其它需要编辑、显示网络内容的应用程序都需要内核JS
引擎则:解析和执行javascript
来实现网页的动态效果- 最开始渲染引擎和
JS
引擎并没有区分的很明确,后来JS引擎越来越独立,内核就倾向于只指渲染引擎
8、埋点
- 埋点的作用就是用来做动作行为分析的
- 在产品流程关键部位植相关统计代码,用来追踪每次用户的行为,统计关键流程的使用程度。
9、本地存储 cookies
,sessionStorage
和 localStorage
的区别?
cookie
是网站为了标示用户身份而储存在用户本地终端(Client Side)上的数据(通常经过加密)- cookie数据始终在同源的http请求中携带(即使不需要),记会在浏览器和服务器间来回传递
sessionStorage
和localStorage
不会自动把数据发给服务器,仅在本地保存
有效期
cookie
设置的cookie
过期时间之前一直有效,即使窗口或浏览器关闭localStorage
存储持久数据,浏览器关闭后数据不丢失除非主动删除数据sessionStorage
数据在当前浏览器窗口关闭后自动删除
10、document.write和innerHTML的区别:
- document.write是直接写入到页面的内容流,如果在写之前没有调用document.open, 浏览器会自动调用open。每次写完关闭之后重新调用该函数,会导致页面被重写。
- innerHTML则是DOM页面元素的一个属性,代表该元素的html内容。你可以精确到某一个具体的元素来进行更改。如果想修改document的内容,则需要修改document.documentElement.innerElement。innerHTML将内容写入某个DOM节点,不会导致页面全部重绘
11、XHTML和HTML的区别
XHTML 是更严格更纯净的 HTML 代码。
- XHTML 元素必须被正确地嵌套。
- XHTML 元素必须被关闭。
- 标签名必须用小写字母。
- XHTML 文档必须拥有根元素。
12、Node 和 Element 是什么关系?
- Node的一些方法,返回值为Node,比如说文本节点,注释节点之类的
- Element的一些方法,返回值则一定是Element
- Element 继承于 Node,具有Node的方法,同时又拓展了很多自己的特有方法
二、CSS部分
1、display: none与visibility: hidden的区别
联系:
它们都能让元素不可见
区别:
- display:none;会让元素完全从渲染树中消失,渲染的时候不占据任何空间;visibility: hidden;不会让元素从渲染树消失,渲染时元素继续占据空间,只是内容不可见
- display: none;是非继承属性,子孙节点消失由于元素从渲染树消失造成,通过修改子孙节点属性无法显示;visibility: hidden;是继承属性,子孙节点消失由于继承了hidden,通过设置visibility: visible;可以让子孙节点显式
- 修改常规流中元素的display通常会造成文档重排。修改visibility属性只会造成元素的重绘。
- 读屏器不会读取display: none;元素内容;会读取visibility: hidden;元素内容
2、link与@import的区别
- link是HTML方式, @import是CSS方式
- link最大限度支持并行下载,@import过多嵌套导致串行下载,出现FOUC
- link可以通过rel="alternate stylesheet"指定候选样式
- 浏览器对link支持早于@import,可以使用@import对老浏览器隐藏样式
- @import必须在样式规则之前,可以在css文件中引用其他文件
- 总体来说:link优于@import
3、position的值, relative和absolute定位原点是
absolute
:生成绝对定位的元素,相对于static
定位以外的第一个父元素进行定位fixed
:生成绝对定位的元素,相对于浏览器窗口进行定位relative
:生成相对定位的元素,相对于其正常位置进行定位static
默认值。没有定位,元素出现在正常的流中inherit
规定从父元素继承position
属性的值
4、行内元素float:left后是否变为块级元素?
- 浮动后,行内元素不会成为块状元素,但是可以设置宽高。行内元素要想变成块状元素,占一行,直接设置
display:block
;。但如果元素设置了浮动后再设置display:block
;那就不会占一行。
5、双飞翼布局 中间宽度高根据内容自适应
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>双飞翼布局</title>
<style>
* {
margin: 0;
padding: 0;
}
.app {
overflow: hidden;
}
.app div {
float: left;
/* height: 100px; */
}
.left {
width: 100px;
background-color: aqua;
margin-left: -100%;
}
.content {
width: 100%;
height: 100%;
}
.content .middle {
margin-left: 110px;
margin-right: 160px;
background-color: blue;
}
.right {
width: 150px;
background-color: brown;
margin-left: -150px;
}
</style>
</head>
<body>
<div class="app">
<div class="content">
<div class="middle">
sss sss
</div>
</div>
<div class="left">
左边
</div>
<div class="right">
右边
</div>
</div>
</body>
</html>
6、三栏等高
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>三栏等高</title>
<style>
.lh {
overflow: hidden;
height: 100%;
width: 100%;
margin-top: 100px;
}
.l {
float: left;
background-color: chartreuse;
width: 100px;
}
.r {
float: right;
background-color: coral;
width: 100px;
}
.main {
margin: 0 110px;
background-color: cornflowerblue;
}
.l,
.r,
.main {
margin-bottom: -9999px;
padding-bottom: 9999px;
}
</style>
</head>
<body>
<!-- 三栏等高 -->
<ul class="lh">
<li class="l">左边内容1111多少度撒多撒多撒多撒房东房东的</li>
<li class="r">右边内容多右边内容多右边内容多右边内容多右边内容多右边内容多右边内容多右边内容多</li>
<li class="main">sss sss sss sss sss sss sss sss sss sss ssssss sss sss
</li>
</ul>
</body>
</html>
7、BFC--块级格式化上下文
页面上的一个隔离的渲染区域,容器里面的子元素不会在布局上影响到外面的元素
如何产生BFC?
float的值不为none、overflow的值不为visible、position的值不为relative和static、display的值为table-cell, table-caption, inline-block中的任何一个。
比如常见的多栏布局,结合块级别元素浮动,里面的元素则是在一个相对隔离的环境里运行。
8、div 水平垂直居中
<div class="app1">
<div class="box1">
水平垂直居中
</div>
</div>
绝对定位方法:
不确定当前div的宽度和高度,采用 transform: translate(-50%,-50%);
当前div的父级添加相对定位(position: relative;)
.app1 {
position: relative;
height: 1000px;
background-color: #ccc;
}
.app1 .box1 {
position: absolute;
left: 50%;
top: 50%;
transform: translate(-50%, -50%);
background-color: cyan;
}
绝对定位方法:
确定了当前div的宽度,margin值为当前div宽度一半的负值
.box1{
position: absolute;
width: 600px;
height: 600px;
background-color: darkblue;
left: 50%;
top: 50%;
margin-left: -300px;
margin-top: -300px;
}
绝对定位方法:
绝对定位下top left right bottom 都设置0
.box1 {
position: absolute;
width: 600px;
height: 600px;
background-color: rgb(196, 156, 24);
top: 0;
left: 0;
right: 0;
bottom: 0;
margin: auto;
}
flex布局方法:
当前div的父级添加flex css样式
.app1 {
height: 1000px;
background-color: #ccc;
display: flex;
justify-content: center;
align-items: center;
}
.app1 .box1 {
width: 600px;
height: 600px;
background-color: rgb(12, 206, 22);
}
9、单行文字居中,多行文字居左显示
div {
display: flex;
justify-content: center;
width: 100px;
height: 100px;
background-color: aqua;
}
<div>
单行文字居中,多行文字居左显示
</div>
10、介绍一下标准的CSS的盒子模型?低版本IE的盒子模型有什么不同的?
- 有两种,
IE
盒子模型、W3C
盒子模型;- 盒模型: 内容(content)、填充(
padding
)、边界(margin
)、 边框(border
);- 区 别:
IE
的content
部分把border
和padding
计算了进去;
11.子元素选择器 > 、相邻兄弟选择器 + 、后续兄弟选择器 ~ 有什么区别?
与后代选择器相比,子元素选择器(Child selectors)只能选择作为某元素直接/一级子元素的元素
<div>
<h2>My name is Donald</h2>
<p>I live in Duckburg.</p>
</div>
<style>
div>p{
color:blue
}
</style>
相邻兄弟选择器(Adjacent sibling selector)可选择紧接在另一元素后的元素,且二者有相同父元素。
如果需要选择紧接在另一个元素后的元素,而且二者有相同的父元素,可以使用相邻兄弟选择器
<style>
div+p
{
background-color:yellow;
}
</style>
<div>
<p>DIV 内部段落。</p>
</div>
<p>DIV 之后的第一个 P 元素。</p> //这一行的样式生效
后续兄弟选择器选取所有指定元素之后的相邻兄弟元素。
<div>
<p>段落 1。 在 div 中。</p>
<p>段落 2。 在 div 中。</p>
</div>
<p>段落 3。不在 div 中。</p> //样式生效
<p>段落 4。不在 div 中。</p>//样式生效
<style>
div~p{
color:yellow
}
</style>
12.css中阻止事件冒泡
pointer-events: none;
三、JS部分
1、['1', '2', '3'].map(parseInt)
parseInt(string,radix)函数解析一个字符串参数,并返回一个指定基数的整数 (数学系统的基础)。
radix 一个介于2和36之间的整数(即进制数 )默认是10进制。
例如如果为3的话,则表示该字符串是以3进制来表示的
map()方法创建一个新数组,其结果是该数组中的每个元素都调用一个提供的函数后返回的结果。
const arr = [1, 2, 3];
arr.map((num) => num + 1); // [2, 3, 4]
['1', '2', '3'].map((item, index) => {
return parseInt(item, index)
})
//即返回的值分别为:
parseInt('1', 0) // 1 默认是十进制
parseInt('2', 1) // NaN
parseInt('3', 2) // NaN, 3 不是二进制 二进制只包含0或 1
['10','10','10','10','10'].map(parseInt);
//返回的结果为 [10,NaN,2,3,4]
2、函数节流与函数防抖
函数节流:throttle
指定时间间隔内只会执行一次任务;
函数防抖:debounce
任务频繁触发的情况下,只有任务触发的间隔超过指定间隔的时候,任务才会执行。
vue写法:
//vue写法
//toolFun.js公共方法
export default function debounceFn(fn,delay=200){ //这里需要定义成函数
// e.stopPropagation()
console.log("外层",fn);
var timer = 0;
return function(){
console.log("timer",timer);
if(timer){
clearTimeout(timer)
}
timer = setTimeout(()=>{
fn.apply(this,arguments) //透传this 和参数
timer = 0
},delay)
// timer = setTimeout(fn,delay)
}
}
<template>
<Button class="btn-mrLine" type="primary" shape="circle" @click="debounceClick">防抖 </Button>
</template>
<script>
import debounceFn from '@/utils/toolFun.js'
method:{
debounceClick:debounceFn((e) =>{
console.log("防抖数据");
},1000),
}
</script>
react写法:
//react 写法
state = {
name:'hahaha'
}
render() {
return (
<div >
<h2>Home</h2>
{/*这里不能用箭头函数 */}
<button onClick={this.debounceFn(this.fn,1000)} >按钮</button>
</div>
);
}
debounceFn = (fn,delay=200) =>{
// e.stopPropagation()
console.log("外层",fn);
var timer = 0;
return function(){
console.log("timer",timer);
if(timer){
clearTimeout(timer)
}
timer = setTimeout(()=>{
fn.apply(this,arguments) //透传this 和参数
timer = 0
},delay)
// timer = setTimeout(fn,delay)
}
}
fn = ()=>{
console.log("打印数据",this.state.name)
}
3、export、export default、import
- export与export default均可用于导出常量、函数、文件、模块等,
- 你可以在其它文件或模块中通过
import+(常量 | 函数 | 文件 | 模块)名
的方式,将其导入,以便能够对其进行使用- 但在一个文件或模块中,export、import可以有多个,export default仅有一个。
- export 导出后,在import导入时可以加{xx1,xx2 } 导出多个
- export default 导出后,在使用import导入时,不能加{ } ,,,而且只能只有一个
4、闭包
闭包是指有权访问另一个函数作用域中的变量的函数。(闭包 = 函数 + 函数能够访问的自由变量)
缺点:闭包使用过多,会占用较多的内存,这也是一个副作用,内存泄漏。
优点:变量长期驻扎在内存中;避免全局变量的污染;能够实现封装和缓存等;
有一个函数,参数是一个函数,返回值也是一个函数,返回的函数功能和入参的函数相似,但这个函数只能执行3次,再次执行无效,如何实现:
function sayHi() {
console.log('hi')
}
function threeTimes(fn) {
let times = 0
return () => {
if (times++ < 3) {
fn()
}
}
}
const newFn = threeTimes(sayHi)
newFn()//hi
newFn()//hi
newFn()//hi
newFn()//不执行
newFn() // 后面两次执行都无任何反应
实现add函数,让add(a)(b)和add(a,b)两种调用结果相同(柯里化)
let sum = add(1)(2);
console.log(sum);
let sum2 = add(1);
console.log(sum2(2))
function add(a, b) {
if (b === undefined) {
return function (x) {
return a + x
}
}
return a + b
}
let sum = add(1)(2);//执行的是return a+x
console.log(sum)
let sum2 = add(1, 2);//执行的是return a+b
console.log(sum2)
5、异步加载js的方式有哪些
https://blog.csdn.net/weixin_44486539/article/details/102793904
- script 标签的 async 属性;该属性是HTML5中新增的异步支持,这种加载方式执行完之前会阻止onload事件的触发,会阻塞部分页面的初始化处理
- script 的 defer 属性;defer 属性规定是否对脚本执行进行延迟,直到页面加载为止;只支持IE。
- onload时的异步加载;这种方法只是把插入script的方法放在一个函数里面,然后放在window的onload方法里面执行,这样就解决了阻塞onload事件触发的问题
6、js 浅拷贝与深拷贝的实现
浅拷贝
只复制指向某个对象的指针,而不复制对象本身,新旧对象共享一块内存;
用 = 号赋值引用地址:
let obj = {
name: '李四',
age: 20,
sex: '男',
school: {
colleges: "野鸡烧烤工程系",
grade: 2020,
name: '野鸡大学',
stuNo: 2020123456,
},
};
let obj2 = obj;
obj2.name = 'Rose';
obj2.sex = '女';
obj2.school.name = '野鸡变凤凰';
console.log('obj', obj);
console.log('obj2', obj2);
//{age: 20,name: "Rose",school:{colleges: "野鸡烧烤工程系",grade: 2020,name: "野鸡变凤凰",stuNo: 2020123456},sex: "女"}
for…in 被循环的对象存在嵌套对象时为浅拷贝(如代码中的school):
let obj = {
name: '李四',
age: 20,
sex: '男',
school: {
name: '野鸡大学',
grade: 2020,
stuNo: 2020123456,
colleges: '野鸡烧烤工程系',
},
};
let obj2 = {};
for (let key in obj) {
obj2[key] = obj[key];
}
obj2.name = '小六';
obj2.sex = '女';
obj2.school.name = '野鸡变凤凰';
obj2.school.colleges = '凤凰系';
console.log('obj', obj);
//{age: 20,name: "李四",school:{colleges: "凤凰系",grade: 2020,name: "野鸡变凤凰",stuNo: 2020123456},sex: "男"}
console.log('obj2', obj2);
//{age: 20,name: "小六",school:{colleges: "凤凰系",grade: 2020,name: "野鸡变凤凰",stuNo: 2020123456},sex: "女"}
深拷贝
复制并创建一个一摸一样的对象,不共享内存,修改新对象,旧对象保持不变。
JSON.parse(JSON.stringify())
这种方式无法拷贝 正则表达式,undefine,function
let obj2 = JSON.parse(JSON.stringify(obj));
**for.. in ** 被循环的对象不存在嵌套对象时为深拷贝
Object.assign():
只复制源对象中可枚举的属性和对象自身的属性。如果目标对象中的属性具有相同的键,则属性将被源中的属性覆盖:
let obj = {
name: '李四',
age: 20,
sex: null,
tel: /^1[345789]\d{9}$/,
address: undefined,
flag: true,
say: () => {
console.log('哇塞')
}
};
let obj2 = {
name: '小二',
};
Object.assign(obj2, obj);
obj2.name = '小六';
console.log('obj', obj);//还是原来的值,不受影响
console.log('obj2', obj2);//name 为小六,其他的和obj 一样
递归方法:
function deepClone(targetObj = {}, map = new Map()) {
if (typeof targetObj !== 'object') {
return targetObj
}
if (map.get(targetObj)) {
return map.get(targetObj)
}
let result = {}
if (targetObj instanceof Array || Object.prototype.toString(targetObj) === "[object Array]") {
result = []
}
map.set(targetObj, result)
for (const key in targetObj) {
if (targetObj.hasOwnProperty(key)) {
//递归使用
result[key] = deepClone(targetObj[key], map)
}
}
return result
}
let obj = {
a: 1,
b: undefined,
c: null,
d: {
d1: 'ddddddddddd'
},
e: [111111111111, 333333333333]
};
let newObj = deepClone(obj)
newObj.e = [9999, 333333]
console.log(obj, newObj)
7、数组去重
利用 ES6的set 方法:
function unique10(arr) {
//Set数据结构,它类似于数组,其成员的值都是唯一的
return Array.from(new Set(arr)); // 利用Array.from将Set结构转换成数组
}
console.log(unique10([1, 1, 2, 3, 5, 3, 1, 5, 6, 7, 4]));
// 结果是[1, 2, 3, 5, 6, 7, 4]
利用for嵌套for,然后splice去重(ES5中最常用):
function unique(arr) {
for (var i = 0; i < arr.length; i++) {
for (var j = i + 1; j < arr.length; j++) {
if (arr[i] === arr[j]) {
//第一个等同于第二个,splice方法删除第二个
arr.splice(j, 1);
j--;
}
}
}
return arr;
}
var arr = [1, 1, 'true', 'true', {}, {},true, true, 15, 15, false, false, undefined, undefined, null, null, NaN, NaN]
console.log(unique(arr)) //{}和NaN没有被去重
//[1, 'true', {}, {}, true, 15, false, undefined, null, NaN, NaN]
indexOf 去重:
新建一个空的结果数组,for循环原数组,判断结果数组是否存在当前元素,如果有则跳过,没有则push进数组。indexOf() 方法可返回某个指定的字符串值在字符串中首次出现的位置。
function unique(arr) {
if (!Array.isArray(arr)) {
console.log(' type error!')
return
}
var array = [];
for (var i = 0; i < arr.length; i++) {
if (array.indexOf(arr[i]) === -1) {
array.push(arr[i])
}
}
return array;
}
var arr = [1, 1, 'true', 'true', {}, {}, true, true, 15, 15, false, false, undefined, undefined, null, null, NaN, NaN]
console.log(unique(arr)) //{}和NaN没有被去重
// [1, 'true', {}, {}, true, 15, false, undefined, null, NaN, NaN]
利用includes:
function unique(arr) {
if (!Array.isArray(arr)) {
console.log(' type error!')
return
}
var array=[];
for(var i=0;i< arr.length;i++){
if(!array.includes(arr[i])){ //includes 检查数组中是否包含某个值
array.push(arr[i]);
}
}
return array;
}
var arr = [1, 1, ' true', 'true', {}, {}, true, true, 15, 15, false, false, undefined, undefined, null, null, NaN, NaN]
console.log(unique(arr)) //{}没有被去重
//[1, 'true', {}, {}, true, 15, false, undefined, null, NaN]
利用filter:
function unique(arr) {
return arr.filter(function (item, index, arr) {
//当前元素,在原始数组中的第一个索引==当前索引值,否则返回当前元素
return arr.indexOf(item, 0) === index;
});
}
var arr = [1, 1, 'true', 'true', {}, {}, true, true, 15, 15, false, false, undefined, undefined, null, null, NaN, NaN];
console.log(unique(arr)) //{}没有去重,NaN被忽略
//[1, 'true', true, 15, false, undefined, null]
8、一个 ul 中有一千个li 每一个li都需要触发js操作,如何降低重复的事件绑定,从而降低dom操作的消耗性能?
事件委托
把原本需要绑定的事件委托给父元素,让父元素担当事件监听的职务。
<body>
<ul id="ulist">
<li>1</li>
<li>2</li>
<li>3</li>
<li>4</li>
<li>5</li>
</ul>
<script>
var ulist = document.getElementById('ulist');
ulist.addEventListener("click", function(e) {
let target = e.target || window.event.srcElement;
// IE下:window.event.srcElement
// 标准下:event.target
if (target && target.nodeName.toLowerCase() == 'li') {
console.log(target.innerHTML);
}
})
</script>
</body>
9、JavaScript页面跳转并传参的常用方法
页面A跳转到页面B及URL携带参数
传递参数:window.location.href = "../list.html?id="+id;
接收参数:url = location.search
通过localStorage 和 sessionStorage 先存本地存储数据
setItem来存数据:
window.localStorage.setItem("data", "kevin");
window.sessionStorage.setItem("data", "kevin");
用getItem来取数据:
//取数据
window.localStorage.getItem("data");
window.sessionStorage.getItem("data");
10、怎样阻止浏览器默认行为,怎样防止事件冒泡,怎么防止移动端穿透
DOM中提供preventDefault()方法来取消事件默认行为
DOM中提供stopPropagation()方法来阻止事件冒泡
防止移动端穿透 :body滚动 + 弹层内部滚动[js-检测touchmove的target]
11、const
const {
a1,
b1
} = {
a1: '这是第一个值',
b1: '这是第二个值'
}
console.log(a1);//'这是第一个值'
var a1 = '这是第三个值';
console.log(a1);//报错'a1' has already been declared
12、Ajax的优缺点分别是什么
Ajax的原理简单来说是在用户和服务器之间加了—个中间层(AJAX引擎),通过XMLHttpRequest对象来向服务器发异步请求,从服务器获得数据,然后用javascript来操作DOM而更新页面。使用户操作与服务器响应异步化。这其中最关键的一步就是从服务器获得请求数据
优点:
- 交互性更好。来自服务器的新内容可以动态更改,无需重新加载整个页面
- 减少与服务器的连接,因为脚本和样式只需要被请求一次。
- 状态可以维护在一个页面上
缺点:
- 动态网页很难收藏。
- 如果 JavaScript 已在浏览器中被禁用,则不起作用
- 有些网络爬虫不执行 JavaScript,也不会看到 JavaScript 加载的内容。
13、ajax promise
function f(url) {
let promise = new Promise(function (resolve, reject) {
$.ajax({
url: url,
dataType: 'json',
success: function (data) {
console.log(data);
if (data.erros === 0) {
resolve(data.msg);
} else {
reject("失败")
}
}
})
})
return promise
}
f("http://39.102.61.13:66/insucate.php?act=list&page=1&size=2").then(function (data) {
console.log(data)
}, function (fail) {
console.log(fail);
})
14、作用域链
- 作用域:规定了如何查找变量,也就是确定当前执行代码对变量的访问权限。换句话说,作用域决定了代码区块中变量和其他资源的可见性。(全局作用域、函数作用域、块级作用域)
- 作用域链的作用是保证执行环境里有权访问的变量和函数是有序的,作用域链的变量只能向上访问,变量访问到window对象即被终止,作用域链向下访问变量是不被允许的
- 简单的说,作用域就是变量与函数的可访问范围,即作用域控制着变量与函数的可见性和生命周期
- js 采用的是静态作用域,所以函数的作用域在函数定义时就确定了。
var value = 1;
function foo() {
console.log(value);
}
function bar() {
var value = 2;
foo();
}
bar(); //1
var scope = "gloab scope"
function a() {
var scope = "local scope"
function fn() {
return scope
}
return fn()
}
a() //'local scope'
15、JavaScript原型,原型链 ? 有什么特点?
- 每个对象都会在其内部初始化一个属性,就是prototype(原型),当我们访问一个对象的属性时
- 如果这个对象内部不存在这个属性,那么他就会去prototype里找这个属性,这个prototype又会有自己的prototype,于是就这样一直找下去,一直检索到 Object 内建对象。也就是我们平时所说的原型链的概念
16、Javascript如何实现继承?
-
构造继承
-
原型继承
-
实例继承
-
拷贝继承
原型prototype机制或apply和call方法去实现较简单,建议使用构造函数与原型混合方式
function Parent() {
this.name = 'wang';
}
function Child() {
this.age = 28;
}
Child.prototype = new Parent();//继承了Parent,通过原型
var demo = new Child();
alert(demo.age);
alert(demo.name);//得到被继承的属性
17、new操作符具体干了什么呢?
- 创建一个空对象,并且
this
变量引用该对象,同时还继承了该函数的原型 - 属性和方法被加入到
this
引用的对象中 - 新创建的对象由
this
所引用,并且最后隐式的返回this
18、offsetWidth,clientWidth与scrollWidth的区别
offsetWidth/offsetHeight返回值包含content + padding + border,效果与e.getBoundingClientRect()相同
clientWidth/clientHeight返回值只包含content + padding,如果有滚动条,也不包含滚动条
scrollWidth/scrollHeight返回值包含content + padding + 溢出内容的尺寸
19、JSON 的了解
JSON(JavaScript Object Notation) 是一种轻量级的数据交换格式
它是基于JavaScript的一个子集。数据格式简单, 易于读写, 占用带宽小
JSON字符串转换为JSON对象:
var obj = str.parseJSON()
var obj =eval('('+ str +')');
JSON对象转换为JSON字符串:
var last=obj.toJSONString();
20、什么是面向对象编程及面向过程编程,它们的异同和优缺点
- 面向过程就是分析出解决问题所需要的步骤,然后用函数把这些步骤一步一步实现,使用的时候一个一个依次调用就可以了
- 面向对象是把构成问题事务分解成各个对象,建立对象的目的不是为了完成一个步骤,而是为了描叙某个事物在整个解决问题的步骤中的行为
- 面向对象是以功能来划分问题,而不是步骤
21、如何通过JS判断一个数组?
instanceof方法
instanceof
运算符是用来测试一个对象是否在其原型链原型构造函数的属性
ES5新增方法isArray()
constructor方法
constructor
属性返回对创建此对象的数组函数的引用,就是返回对象相对应的构造函数
22、JS哪些操作会造成内存泄露
意外的全局变量引起的内存泄露
function leak(){ leak="xxx";//leak成为一个全局变量,不会被回收 }
闭包引起的内存泄露
没有清理的DOM元素引用
24、[]空数组 和 {} 空对象
// 空数组初始化后,即使数组arr中没有元素,也是一个object
//用于判断条件时就会被转化为true。
console.log([] ? true : false) //true
//但是与布尔值相比较的话,会被先转换为Number。[]=>0,true=>1
console.log(([] == true) ? true : false) //false
console.log(([] == false) ? true : false) //true
console.log({} ? true : false) //true
//与布尔值比较时,同样被转为Number ,{}=>NaN,所以与false 、true 都不相等
console.log(({} == false) ? true : false) //false
25、请解释JSONP的工作原理,以及它为什么不是真正的AJAX(必能达)
- JSONP(JSON with Padding)是一种非官方跨域数据交互协议,它允许在服务器端集成< script >标签返回至客户端,通过javascript回调的形式实现跨域访问。
- ajax的核心是通过xmlHttpRequest获取非本页内容 jsonp的核心是动态添加script标签调用服务器提供的js脚本 jsonp只支持get请求,ajax支持get和post请求
26、在制作一个Web应用或Web站点的过程中,你是如何考虑他的UI、安全性、高性能、SEO、可维护性以及技术因素的?
UI:界面美观,要有个性,考虑用户使用的逻辑要简单,用起来舒适自由。使用习惯要符合大部分用户的习惯,比如少让用户输入,采用选择的方式,提供搜索和提示功能。
安全性:
- 对输入进行有效性验证
- 对交互操作进行身份验证和授权
- 异常错误处理
高性能:
- DNS(域名系统)负载均衡
- HTTP重定向
- 数据库扩展:读写分离,垂直分区,水平分区
- SEO:选好关键字,描述语言,修饰性图片换成文本,合理使用h1-h6,对图片添加alt属性,链接添加target属性。
- 可维护性:代码是否容易被理解,是否容易被修改和增加新的功能,当出现问题时是否能快速定位到问题代码。
27、同步和异步的区别?
- 同步:浏览器访问服务器请求,用户看得到页面刷新,重新发请求,等请求完,页面刷新,新内容出现,用户看到新内容,进行下一步操作
- 异步:浏览器访问服务器请求,用户正常操作,浏览器后端进行请求。等请求完,页面不刷新,新内容也会出现,用户看到新内容
28、请尽可能详尽的解释AJAX的工作原理
第一步:创建ajax对象(XMLHttpRequest/ActiveXObject(Microsoft.XMLHttp))
第二步:判断数据传输方式(GET/POST)
第三步:打开链接 open()
第四步:发送 send()
数据接收完成,判断http响应状态(status)200-300之间或者304(缓存)执行回调函数
29、请描述一下cookies,sessionStorage和localStorage的区别?
- localStorage长期存储数据,浏览器关闭数据后不丢失;
- sessionStorage数据在浏览器关闭后自动删除;
- cookie是网站为了标识用户身份而存储在用户本地终端(Client Side)上的数据(通常经过加密)。cookie始终在同源的http请求中携带(即使不需要)都会在浏览器和服务器端间来回传递
- 存储大小:cookie数据大小不会超过4K,session storage和local storage虽然也有存储大小的限制,但比cookie大得多,可以达到5M或者更多;
30、Cookie和Session的区别
- 存放位置不同: Cookie保存在客户端,Session保存在服务端。
- 存取方式的不同: Cookie中保存的是字符串,Session保存的是对象
- 安全性(隐私策略)的不同 : Cookie存储在浏览器中,对客户端是可见的;而Session存储在服务器上,对客户端是透明的,不存在敏感信息泄露的风险。
31、说说你对promise的了解
依照 Promise/A+ 的定义,Promise 有四种状态:
pending:初始状态
fulfilled:成功的操作
rejected: 失败的操作.
settled:Promise已被fulfilled或rejected
Promise:对象用来进行延迟(deferred) 和异步(asynchronous) 计算
Promise:实例拥有 then 方法
接收两个函数作为参数
面试题
setTimeout(() => {
console.log(1); //最后输出1
}, 0)
new Promise(function (resolve) {
console.log(2); //第一个输出2
for (var i = 0; i < 1000; i++) {
i = 9999 && resolve();
}
console.log(3) //第二个输出3
}).then(function (data) {
console.log(4); //第四个输出4
})
console.log(5) //第三个输出5
//当前主线程的任务完成后,会读取任务队列(task queue)中的是事件。
//setTimeout会放到任务队列中,代码继续往下走
//promise中的then操作是放在执行栈,也就是主线程的最后
32、对象(属性值不一定是字符串)
- 堆(heap)存放引用类型;保存的不是变量本身,而是指向该对象的指针(引用类型:Function,Array,Object)
- 栈(stack):栈会自动分配内存空间,会自动释放,存放基本类型,(基本类型:String,Number,Boolean,Null,Undefined)
let a = {};
let b = '0',
c = 0;
a[b] = 'bbb';//a['0'] = 'bbb'
a[c] = 'ccc';//a[0] = 'ccc'
// 因为 '0' == 0 所以值会被覆盖
console.log(a[b]); //ccc
console.log(a); //{0:'ccc'}
//Symbol 创建唯一值
let i = {},
j = Symbol(1),
k = Symbol(1);
i[j] = 'jjj';
i[k] = 'kkk';
console.log(i[j]); //jjj
console.log(i);//{Symbol(1): "jjj",Symbol(1): "kkk"}
// 对象属性
let d = {},
e = {
n: 1
},
f = {
n: 2
};
d[e] = 'eee';
d[f] = 'fff';
console.log(d[e]); //fff
console.log(d); //{[object Object]: "fff"}
var a1 = 0,
b1 = 0;
function A(a1) {
A = function (b1) {
alert(a1 + b1++);
}
alert(a1++);
}
A(1); //'1' 第一次执行 alert(a1++);a1 = 2 此时A 的堆内存指针已经被替换
A(2) //'4' 第二次执行的是 alert(a1 + b1++);此时b1 = 2;a1的值往上找,a1=2
33、alert()输出的是字符串
34、call 、apply 、bind
- call 方法第一个参数是要绑定给this的值,后面传入的是一个参数列表。当第一个参数为null、undefined的时候,默认指向window。
- apply接受两个参数,第一个参数是要绑定给this的值,第二个参数是一个参数数组。当第一个参数为null、undefined的时候,默认指向window。
- bind 和call很相似,第一个参数是this的指向,从第二个参数开始是接收的参数列表。区别在于bind方法返回值是函数以及bind接收的参数列表的使用。
- 区别就在于传参时参数是一个一个传或者是以一个数组的方式来传。
- fn.call(obj,attr1,attr2,attr3,..);
- fn.apply(obj,[attr1,attr2,attr3,..])
- 硬绑定 bind 方法 使用call,apply 也改变不了函数的bar的this绑定
function foo() {
console.log(this);//{a3: 23 }
console.log(this.a);//23
}
var obj = {
a: 23
}
var a = 33;
var bar = foo.bind(obj)
bar.call(window);
35、手写ajax请求
<button id="btn">点击发送请求</button>
btn.onclick = function () {
//创建ajax对象
var xml = new XMLHttpRequest();
// 2.创建一个新的http请求,(打开浏览器,输入请求地址)
// xhr.open(请求的方式(get/post),请求的地址[,异步同步请求])
xml.open('get', 'http://localhost/index.php', true);
// 3.发送请求
// xhr.send(传递post请求的数据/ get请求设置 null)
xml.send(null);
//监听状态变化事件,获取后台返回数据
xml.onreadystatechange = function () {
//readyState 表示ajax的状态
//ajax一共有5种状态
//0 ==> 创建ajax对象完毕
//1 ==> 调用了open()方法
//2 ==> 调用了send()方法
//3 ==> 服务器端只返回了一部分数据
//4 ==> 服务器端数据全部返回,ajax请求完成
// 304 not modified ,文件没有改变
if (xml.readyState == 4) {
if (xml.status >= 200 && xml.status < 300 || xml.status == 304) {
alert(xml.responseText)
}
}
}
}
36、什么是跨域
出于浏览器的同源策略限制
当一个请求url的协议、域名、端口三者之间任意一个与当前页面url不同即为跨域
37、递归
var m = +prompt('请输入斐波那契列数');
document.write(FiboNaQie(m));
function FiboNaQie(n) {
if (n === 1) return 1;
if (n === 2) return 1;
else {
return FiboNaQie(n - 1) + FiboNaQie(n - 2);
}
}
38、+、-运算符
+遇到字符串时为拼接,- 无论时什么情况下都是减
console.log("10"+2-"1")
//输出数字 101
39、reflow(回流)和repaint(重绘)
- reflow:例如某个子元素样式发生改变,直接影响到了其父元素以及往上追溯很多祖先元素(包括兄弟元素),这个时候浏览器要重新去渲染这个子元素相关联的所有元素的过程称为回流。
- repaint:如果只是改变某个元素的背景色、文 字颜色、边框颜色等等不影响它周围或内部布局的属性,将只会引起浏览器 repaint(重绘)
40、标准的事件模型执行顺序
事件捕获=》事件处理=》事件冒泡
41、前端请求接口验证(使用token)
https://www.jianshu.com/p/24825a2683e6 (什么是token)
https://blog.csdn.net/qq_18549249/article/details/81329654?utm_medium=distribute.pc_relevant.none-task-blog-BlogCommendFromBaidu-1.control&depth_1-utm_source=distribute.pc_relevant.none-task-blog-BlogCommendFromBaidu-1.control 前后端分离-通过header传递token实现认证
-
Token的定义:Token是服务端生成的一串字符串,以作客户端进行请求的一个令牌,当第一次登录后,服务器生成一个Token便将此Token返回给客户端,以后客户端只需带上这个Token前来请求数据即可,无需再次带上用户名和密码。
-
使用Token的目的:Token的目的是为了减轻服务器的压力,减少频繁的查询数据库,使服务器更加健壮。
主要使用token的基本流程如下:
- 客户端使用用户名密码进行登录。
- 服务端收到请求,去验证用户名与密码。验证成功后,服务端会签发一个 Token,再把这个 Token 发送给客户端。
- 客户端将收到的Token存储起来,可以放在cookie或者localStorage。
- 客户端每次需要登录验证的请求都需要带着这个Token发送给服务器端。
- 服务器端收到请求后先验证Token,如果验证成功,就向客户端返回数据。
42、this指向
var name = 'zzj';
var objects = {
name: 'xiaoming',
getName: function () {
return function () {
return this.name
}
}
}
console.log(objects.getName())
//输出ƒ () {return this.name }
console.log(objects.getName()()) //此时this指向的是window输出zzj
43、 从10-100中随机抽取10个数,存到数组中去,并排序(代码题)
var arr = [];
function Round(start, end) {
var temp = end - start + 1 //加1是为能取到100
// Math.random() 随机抽取[0,1)的值
//floor 是向下取整
var res = Math.floor(Math.random() * temp + start)
return res;
}
for (var i = 0; i < 10; i++) {
arr.push(Round(10, 100))
}
//排序若 a 大于 b,则返回一个大于 0 的值。
arr.sort(function (a, b) {
return a - b;
})
44、给数字字符串增加千分符(代码题)
// 方法一
function toThousand(str) {
var len = str.length;
var num = parseInt(len / 3);
console.log(num)
var head = str.slice(0, len - num * 3);
head += ',';
var body = str.slice(len - num * 3, len)
console.log(body)
var array = []
for (var i = 0; i < num; i++) {
array.push(body.slice(i * 3, i * 3 + 3))
}
console.log(array)
body = array.join(','); //数组转字符串
if (len % 3 == 0) {
//刚好位数是3的倍数
str = body
} else {
str = head + body;
}
return str
}
console.log(toThousand("123456789"))
// 方法二
function format(n) {
n = Math.floor(n);
const s = n.toString()
const arr = s.split('').reverse() //字符串转成数组变翻转
return arr.reduce((pre, val, index) => {
console.log(pre, val, index)
if (index % 3 === 0) {
if (pre) {
return val + ',' + pre
} else {
return val
}
} else {
return val + pre
}
}, '')
}
const n = 12345678;
console.log(format(n))
// 方法三
function format2(n) {
n = Math.floor(n);
const s = n.toString()
let res = ''
for (let i = s.length - 1; i >= 0; i--) {
let j = s.length - i;
console.log(j)
if (j % 3 === 0) {
if (i === 0) {
res = s[i] + res
} else {
res = ',' + s[i] + res
}
} else {
res = s[i] + res
}
}
return res
}
const n = 12345678;
console.log(format2(n))
45、画一个三角形
<style>
/* 一定要将宽高设为0 */
.triangle {
width: 0px;
height: 0px;
border-top: 100px solid red;
border-right: 100px solid transparent;
boder-left: 100px solid transparent; //透明
}
</style>
<div class="triangle"></div>
46、typeof的返回值(类型为字符串)
对未声明的变量执行 typeof 不会报错,会返回 undefined
放在运算符中的函数声明在执行阶段时找不到的 。
- 'number' var a = 11; console.log(typeof a); console.log(typeof NaN)
- 'boolean'
- 'string'
- 'undefined'
- 'object' console.log(typeof null) 对于一些创建的对象,它们都会返回'object'
- 'function' 函数类型
var test = 1;
if (function f() { }) {
test += typeof f;
}
console.log(test);//1undefined
47、基础数据类型是不能有属性和方法。属性和方法只有对象有,是对象特有的,像Function,Object,Array 都是对象
- 字符串,布尔值,数字有两种,原始值是没有属性和方法的,另外一种对象,是有属性和方法的
48、浏览器的垃圾回收机制
- 标记清除:标记阶段即为所有活动对象做上标记,清除阶段则把没有标记(也就是非活动对象)销毁。
- 引用计数:它把对象是否不再需要简化定义为对象有没有其他对象引用到它。如果没有引用指向该对象(引用计数为 0),对象将被垃圾回收机制回收。
49、数组扁平化
将一个嵌套多层的数组转换为只有一层的数组
function flat(arr, depth = 1) {
if (depth > 0) {
return arr.reduce((pre, cur) => {
//cur 当前值
return pre.concat(Array.isArray(cur) ? flat(cur, depth - 1) : cur)
}, []);
}
return arr.slice()
}
let arr1 = [11, 22, [55, 999], [{ id: 1, number: 8888 }, { id: 2, number: 6666666666 }]];
flat(arr1, 2) // [11,22,55,999,{id:1,number:8888},{id:2,number:6666666666}]
50、await async 代码题
setTimeout(function () {
console.log(1);
}, 0);
console.log(2);
async function s1() {
console.log(7)
await s2(); //后面的代码被挂起
console.log(8);
};
async function s2() {
console.log(9);
}
s1();
new Promise((resolve, reject) => {
console.log(3);
resolve();
console.log(6);
}).then(() => console.log(4))
console.log(5);
// 输出 2 7 9 3 6 5 8 4 1
//await表示遇到await关键字后先将这块代码的asyncContext挂起并执行上级上下文
51、不能使用箭头函数的场景
- 对象方法
- 对象原型
- 构造函数
- vue method 和生命周期函数
- 动态上下文的回调函数
52、JS严格模式特点
- 全局变量必须先声明
- 禁止使用with
- 创建eval作用域(自己独有的作用域)
- 禁止this指向window
- 函数参数不能重名
53、promise-then 执行顺序
交替执行,多个fulfilled promise 实例,同时执行then
//交替执行,多个fulfilled promise 实例,同时执行then
Promise.resolve().then(res => {
console.log("1")
}).then(res => {
console.log("2")
}).then(res => {
console.log("3")
})
Promise.resolve().then(res => {
console.log("10")
}).then(res => {
console.log("20")
}).then(res => {
console.log("30")
})
//打印输出 1 10 2 20 3 30
then 中执行promise 实例
- 会出现“慢两拍”的效果,然后再交替执行
- 第一拍,promise需要由pending变为fulfilled
- 第二拍,then 函数挂载到 MicroTaskQueue
Promise.resolve().then(res => {
console.log("1")
return Promise.resolve(5)
}).then(res => {
console.log("res", res)
}).then(res => {
console.log("2")
}).then(res => {
console.log("3")
}).then(res => {
console.log("4")
})
Promise.resolve().then(res => {
console.log("10")
}).then(res => {
console.log("20")
}).then(res => {
console.log("30")
}).then(res => {
console.log("40")
})
//输出 1 10 20 30 5 40 2 3 4
54、setInterval 方法的返回值是什么?
setInterval
返回一个唯一的 id。此 id 可被用于 clearInterval
函数来取消定时。
55、Object.freeze 对一个对象进行冻结
- 不能对属性进行添加,修改,删除
- 仅对对象进行浅冻结,只有 对象中的 直接 属性被冻结。如果属性是另一个 object。则可以修改
56、generator 和promise使用
function* bar() {
//生成器
console.log("1111");
const result = yield new Promise((resolve, reject) => {
setTimeout(() => {
resolve('Hello')
}, 2000)
})
console.log(result)
}
//迭代器 ,使用.next()方法
const it = bar()
it.next().value.then(res => {
it.next(res)
})
//先打印 1111
//间隔两秒后打印 Hello
57、代码题
1:
const add = () => {
const cache = {};
return num => {
if (num in cache) {
return `From cache! ${cache[num]}`;
} else {
const result = num + 10;
cache[num] = result;
return `Calculated! ${result}`;
}
};
};
const addFunction = add();
console.log(addFunction(10));
console.log(addFunction(10));
console.log(addFunction(5 * 2));
//add函数是一个记忆函数。 通过记忆化,我们可以缓存函数的结果,以加快其执行速度。上述情况,我们创建一个cache对象,用于存储先前返回过的值。
//输出Calculated! 20 From cache! 20 From cache! 20
2:
const handler = {
set: () => console.log("Added a new property!"),
get: () => console.log("Accessed a property!")
};
const person = new Proxy({}, handler);
person.name = "Lydia";
person.name;
//输出 Added a new property! Accessed a property!
使用 Proxy 对象,我们可以给一个对象添加自定义行为 设置属性值时 set 被调用,每当我们获取属性值时 get 被调用 3:
const name = "Lydia Hallie";
const age = 21;
console.log(Number.isNaN(name)); //false
console.log(Number.isNaN(age)); //false
//方法 Number.isNaN,你可以检测你传递的值是否为 数字值 并且是否等价于 NaN
console.log(isNaN(name)); //true
console.log(isNaN(age)); //false
4:
const add = x => y => z => {
console.log(x, y, z);
return x + y + z;
};
add(4)(5)(6); //4 5 6
5:
const name = "Lydia Hallie";
console.log(!typeof name === "object"); //false -----!typeof name 返回false
console.log(!typeof name === "string"); //false
6:
const config = {
languages: [],
set language(lang) {
return this.languages.push(lang);
}
};
console.log(config.language); //undefined
//方法 language 是一个 setter。Setters 并不保存一个实际值,它们的使命在于 修改 属性。当调用方法 setter, 返回 undefined
7:
console.log(`${(x => x)('I love')} to program`)
//I love to program
8:
const colorConfig = {
red: true,
blue: false,
green: true,
black: true,
yellow: false,
}
const colors = ["pink", "red", "blue"]
console.log(colorConfig.colors[1]) //TypeError
9:
// index.js
console.log('running index.js');
import { sum } from './sum.js';
console.log(sum(1, 2));
// sum.js
console.log('running sum.js');
export const sum = (a, b) => a + b;
//running sum.js, running index.js, 3
/**
*import命令是编译阶段执行的,在代码运行之前。因此这意味着被导入的模块会先运行,而导入模块的文件会
*后执行。 这是CommonJS中require()和import之间的区别。使用require(),您可以在运行代码时根据需要
*加载依赖项。 如果我们使用require而不是import,running index.js,running sum.js,3会被依次打印
*/
10:
const person = { name: "Lydia" };
Object.defineProperty(person, "age", { value: 21 });
console.log(person);
console.log(Object.keys(person));
//{ name: "Lydia", age: 21 }, ["name"]
/**
* 通过defineProperty方法,我们可以给对象添加一个新属性,或者修改已经存在的属性。
* 而我们使用defineProperty方法给对象添加了一个属性之后,属性默认为 不可枚举(not enumerable).
* Object.keys方法仅返回对象中 可枚举(enumerable) 的属性,因此只剩下了"name".
* 用defineProperty方法添加的属性默认不可变。你可以通过writable, configurable 和 enumerable属性来改变这一行为。
* 这样的话, 相比于自己添加的属性,defineProperty方法添加的属性有了更多的控制权。
*
*/
11:
const set = new Set([1, 1, 2, 3, 4]);
console.log(set);
//{1,2,3,4} Set对象是独一无二的值的集合:
12:
function greeting() {
throw "Hello world!Hello world!"; //抛出错误信息
}
function sayHi() {
try {
const data = greeting();
console.log("It worked!", data);
} catch (e) {
console.log("Oh no an error:", e);
}
}
sayHi(); //Oh no an error: Hello world!
//通过throw语句,我么可以创建自定义错误。 而通过它,我们可以抛出异常
13:
function getInfo(member, year) {
member.name = "Lydia";
year = "1998";
}
const person = { name: "Sarah" };
const birthYear = "1997";
getInfo(person, birthYear);
console.log(person, birthYear);
//{ name: "Lydia" }, "1997"
//对象作为参数传参时,相应的属性被修改时,原始数据也会改变
14:
[1, 2, 3].map(num => {
if (typeof num === "number") return;
return num * 2;
});
//[undefined,undefined,undefined]
15.遍历一个任意长度的list中的元素并依次创建异步任务,如何获取所有任务的执行结果?
const p1 = new Promise((resolve, reject) => {
resolve("成功了")
})
const p2 = Promise.resolve('success')
const p3 = Promise.reject('错误')
Promise.all([p1, p2]).then((result) => {
console.log(result)['成功了', 'success']
}).catch((err) => {
})
Promise.all([p1, p2, p3]).then((result) => {
console.log(result)
}).catch((err) => {
console.log(err) //错误 只会抛出错误的那个任务
})
//可用于并行执行独立的异步操作,并收集这些操作的结果
Promise.allSettled([p1, p2, p3]).then((result) => {
console.log(result)
//[{status: 'fulfilled', value: '成功了'},{status: 'fulfilled', value: 'success'}{status: 'rejected', value: '错误'}]
}).catch((err) => {
console.log(err) //错误 只会抛出错误的那个任务
})
四、前端安全性能
1、XSS
XSS是一种经常出现在web应用中的计算机安全漏洞,它允许恶意web用户将代码植入到提供给其它用户使用的页面中。
防范:
- 将重要的cookie标记为http only, 这样的话Javascript 中的document.cookie语句就不能获取到cookie了.
- 表单数据规定值的类型,例如:年龄应为只能为int、name只能为字母数字组合。。。。
- 对数据进行Html Encode 处理
- 过滤或移除特殊的Html标签, 例如: <script>, <iframe> , < for <, > for >, " for
- 过滤JavaScript 事件的标签。例如 "οnclick=", "onfocus" 等等。
2、CSRF
CSRF(Cross-site request forgery)跨站请求伪造,也被称为“One Click Attack”或者Session Riding,通常缩写为CSRF或者XSRF,是一种对网站的恶意利用。
防御:
- 通过referer、token或者验证码来检测用户提交。
- 尽量不要在页面的链接中暴露用户隐私信息。
- 对于用户修改删除等操作最好都使用post操作 。
- 避免全站通用的cookie,严格设置cookie的域。
3、跨域问题
同源策略可防止 JavaScript 发起跨域请求。源被定义为 URI、主机名和端口号的组合。此策略可防止页面上的恶意脚本通过该页面的文档对象模型,访问另一个网页上的敏感数据。
- 原因 浏览器的同源策略导致了跨域
- 作用 用于隔离潜在恶意文件的重要安全机制
解决:
- jsonp ,允许 script 加载第三方资源
- 反向代理(nginx 服务内部配置 Access-Control-Allow-Origin *)
- cors 前后端协作设置请求头部,Access-Control-Allow-Origin 等头部信息
- iframe 嵌套通讯,postmessage
4、web常用的常用缓存技术
-
Opcode缓存
-
内存式缓存
-
php APC缓存扩展
-
全页面静态化缓存
-
页面部分缓存
-
数据缓存
-
查询缓存
五、vue
1、vue优点
- 轻量级框架:只关注视图层,是一个构建数据的视图集合,大小只有几十kb
- 双向数据绑定:保留了angular的特点,在数据操作方面更为简单;
- 组件化:保留了react的优点,实现了html的封装和重用,在构建单页面应用方面有着独特的优势;视图,数据,结构分离:使数据的更改更为简单,不需要进行逻辑代码的修改,只需要操作数据就能完成相关操作;
- 运行速度更快:相比较与react而言,同样是操作虚拟dom,就性能而言,vue存在很大的优势。
2、vue父组件向子组件传递数据?子组件向父组件
通过props:
<!-- 父组件 -->
<template>
<div id="example">
<HelloWorld :message="message" @Msg="message = $event"></HelloWorld>
</div>
</template>
<script>
import HelloWorld from "../../components/HelloWorld.vue";
export default {
name: "",
data() {
return {
message: "哈哈"
};
},
methods: {
},
components: {
HelloWorld
}
};
</script>
<!-- 子组件 -->
<template>
<div class="hello">
<h1>{{ message }}</h1>
<button @click="changeMsg"> 点击</button>
</div>
</template>
<script>
export default {
name: "HelloWorld",
props: ['message'],
data(){
return{
}
},
methods:{
changeMsg(){
//子组件自定义事件,向父组件传值,改变父组件中的值
this.$emit('Msg','bye')
},
}
};
</script>
ref方法:
<template>
<div id="example">
<HelloWorld ref="children"></HelloWorld>
<button @click="chageAge">点击改变age</button>
</div>
</template>
<script>
import HelloWorld from "../../components/HelloWorld.vue";
export default {
methods: {
chageAge() {
console.log('点击前', this.$refs.children.age);
this.$refs.children.chanceAge();
console.log('点击后', this.$refs.children.age);
}
},
components: {
HelloWorld
}
};
</script>
<!-- 子组件 -->
<template>
<div class="hello">
<h2>{{ age }}</h2>
</div>
</template>
<script>
export default {
name: "HelloWorld",
data(){
return{
age:10,
}
},
methods:{
chanceAge(){
this.age = 18
},
}
};
</script>
3、Vue hash 路由和 history 路由的区别
- 默认模式,通过路径中的hash值来控制路由跳转,不存在兼容问题
- H5新增的 history API,相对hash而言,不会显示#号,但是需要服务器端配置。在使用history模式的时候,由于浏览器路径与后端路径类似,所以当刷新页面的时候,浏览器还是会向服务器发送请求,但是由于服务器并没有对应的路由页面,所以会导致404空白
4、active-class
-
active-class属于vue-router的样式方法 当routerlink标签被点击时将会应用这个样式 使用有两种方法routerLink标签内使用
-
<router-link to='/'active-class="active">首页</router-link>
5、computed和 watch的区别和运用的场景?
-
computed:是计算属性,依赖其它属性值,并且computed的值有缓存,只有它依赖的属性值发生改变,下一次获取computed的值时才会重新计算computed的值
-
·当我们需要进行数值计算,并且依赖于其它数据时,应该使用computed,因为可以利用computed的缓存特性,避免每次获取值时,都要重新计算;
-
watch:更多的是[观察]的作用,类似于某些数据的监听回调,每当监听的数据变化时都会执行回调进行后续操作;
- 对数组列表项的内容是否发生改变做监听,需要加上一个deep:true
watch: {
bufferInfoList: {
handler(newValue, oldValue) {
// console.log("newValue", newValue);
},
immediate: true,
deep: true
}
},
- 当我们需要在数据变化时执行异步或开销较大的操作时,应该使用watch,使用watch选项允许我们执行异步操作(访问一个API),限制我们执行该操作的频率,并在我们得到最终结果前,设置中间状态。这些都是计算属性无法做到的
6、vue $nextTick方法
- Vue异步更新DOM的原理:Vue在观察到数据变化时,并不是直接更新DOM,而是开启一个队列
- 并且缓存
同一轮事件循环中的所有数据改变
。在缓冲时会除去重复的操作
,等到下一轮事件循环时,才开始更新。 - $nextTick就是用来告知DOM什么时候更新完,当DOM更新完毕后,nextTick方法里面的回调就会执行。
<div class="app">
<div ref="msgDiv">{{msg}}</div>
<!-- //ref 被用来给元素或子组件注册引用信息 -->
<div v-if="msg1">Message got outside $nextTick: {{msg1}}</div>
<div v-if="msg2">Message got inside $nextTick: {{msg2}}</div>
<div v-if="msg3">Message got outside $nextTick: {{msg3}}</div>
<button @click="changeMsg">
Change the Message
</button>
</div>
<script>
new Vue({
el: '.app',
data: {
msg: 'Hello Vue.',
msg1: '',
msg2: '',
msg3: ''
},
methods: {
changeMsg() {
this.msg = "Hello world."
this.msg1 = this.$refs.msgDiv.innerHTML
this.$nextTick(() => {
this.msg2 = this.$refs.msgDiv.innerHTML
})
this.msg3 = this.$refs.msgDiv.innerHTML
}
}
})
</script>
<!-- 点击按钮之后msg1和msg3显示的内容还是变换之前的,而msg2显示的内容是变换之后的
是因为Vue中DOM更新是异步的 -->
主要应用的场景:
- 在Vue生命周期的
created()
钩子函数进行的DOM操作一定要放在Vue.nextTick()
的回调函数中
7、vue路由跳转的方式
router-link
<router-link :to="{name:'home', query: {id:1}}">
// query传参数 (类似get,url后面会显示参数)
// 路由可不配置 path: "/home/:id"
// html 取参 $route.query.id
// script 取参 this.$route.query.id
this.$router.push() (函数里面调用)
-
//query传参 this.$router.push({name:'home',query: {id:'1'}}) // html 取参 $route.query.id // script 取参 this.$route.query.id
-
//params传参 this.$router.push({name:'home',params: {id:'1'}})// 只能用 name // 路由配置 path: "/home/:id" 或者 path: "/home:id" , // 不配置path ,第一次可请求,刷新页面id会消失 // 配置path,刷新页面id会保留 // html 取参 $route.params.id // script 取参 this.$route.params.id
-
query和params区别
- query类似 get, 跳转之后页面 url后面会拼接参数,类似?id=1, 非重要性的可以这样传, 密码之类还是用params刷新页面id还在
- params类似 post, 跳转之后页面 url后面不会拼接参数 , 但是刷新页面id 会消失
-
$route 和 $
router的区别- $route是路由信息对象,主要包括param,query、path、name等路由参数
- $router 是路由实例,主要包括路由跳转方法和钩子函数
8、第一次页面加载会触发哪几个钩子
beforeCreate, created, beforeMount, mounted
9、vue生命周期
总共分为8个阶段创建前/后,载入前/后,更新前/后,销毁前/后。
创建前/后:
在beforeCreate阶段,vue实例的挂载元素$el和数据对象data都为undefined,还未初始化。在**created**阶段,vue实例的数据对象data有了,$
el还没有,如果要调用 methods 中的方法,或者操作 data 中的数据,最早可以在这个阶段中操作
载入前/后:
在beforeMount阶段,vue实例的$el和data都初始化了,但还是挂载之前为虚拟的dom节点,data.message还未替换。在mounted阶段,vue实例挂载完成,data.message成功渲染, 如果我们想要通过插件操作页面上的DOM节点,最早可以在和这个阶段中进行
beforeUpdate:
当执行这个钩子时,页面中的显示的数据还是旧的,data中的数据是更新后的, 页面还没有和最新的数据保持同步 updated:页面显示的数据和data中的数据已经保持同步了,都是最新的。
销毁前/后:
在执行destroy方法后,对data的改变不会再触发周期函数,说明此时vue实例已经解除了事件监听以及和dom的绑定,但是dom结构依然存在。
10、父子组件的挂载顺序
父组件先初始化 ->父组件渲染完成 ->子组件开始初始化 -> 子组件挂载完成(mount) -> 父组件挂载完毕(mount)
11、路由懒加载
运用懒加载则可以将页面进行划分,需要的时候加载页面,可以有效的分担首页所承担的加载压力,减少首页加载用时
{
// 我的
path: 'my',
name: 'my',
component: () => import( /* webpackChunkName: 'my' */ "../views/my/my.vue")
},
//使用这种方法,每个组件打包成一个js文件,当进入组件时才会加载
12、Vue2中检测数组变化的限制和解决方法
-
索引、数组长度,,数组的这两个发生变化,vue 检测不到变化
-
使用$set方法:三个参数分别为:操作的数组,索引,改变后的值
vm.$set(vm.names, 0, "luluba");
-
使用数组的原型方法splice
vm.names.splice(0, 1, "luluba");
13、导航守卫
全局前置守卫
router.beforeEach((to, from, next) => {
// ...
})
//to: Route: 即将要进入的目标 路由对象
//from: Route: 当前导航正要离开的路由
//next: Function: 一定要调用该方法来 resolve 这个钩子。执行效果依赖 next 方法的调用参数
路由独享守卫
routes: [
{
path: '/foo',
component: Foo,
beforeEnter: (to, from, next) => {
// ...
}
}
]
组件内的守卫
beforeRouteEnter(to, from, next) {
// 在渲染该组件的对应路由被 confirm 前调用
// 不!能!获取组件实例 `this`
// 因为当守卫执行前,组件实例还没被创建
},
beforeRouteUpdate(to, from, next) {
// 在当前路由改变,但是该组件被复用时调用
// 举例来说,对于一个带有动态参数的路径 /foo/:id,在 /foo/1 和 /foo/2 之间跳转的时候,
// 由于会渲染同样的 Foo 组件,因此组件实例会被复用。而这个钩子就会在这个情况下被调用。
// 可以访问组件实例 `this`
},
beforeRouteLeave(to, from, next) {
// 导航离开该组件的对应路由时调用
// 可以访问组件实例 `this`
}
14、为什么使用key?**
答:需要使用key来给每个节点做一个唯一标识,Diff算法就可以正确的识别此节点。 作用主要是为了高效的更新虚拟DOM。
15、.vue组件中data为什么必须是一个函数
组件中的data写成一个函数,数据以函数返回值{ }的形式定义,这样每次复用组件的时候,都会返回一份新的data,相当于每个组件实例都有自己私有的数据空间,它们只负责各自维护的数据,不会造成混乱。而单纯的写成对象形式,就是所有的组件实例共用了一个data,这样改一个全都改了。
16、vuex
http://caibaojian.com/vuex-interview.html
- vuex是一个专门为vue.js应用程序开发的状态管理模式
- vuex可以帮助我们管理共享状态,也就是管理全局变量
vuex有哪几种属性?
有五种,分别是 State、 Getter、Mutation 、Action、 Module
- state => 基本数据(数据源存放地)
- getters => 从基本数据派生出来的数据
- mutations => 提交更改数据的方法,同步!
- actions => 像一个装饰器,包裹mutations,使之可以异步。
- modules => 模块化Vuex
我们可以在组件中触发 Action,Action 则会提交 Mutation,Mutaion 会对 State 进行修改,组件再根据 State 、Getter 渲染页面
17、兄弟组件间的通信
-
通过父组件接收某一个子组件的事件,对数据进行修改,从而会影响到全部的子组件
-
通过vuex
// store/index.js
state: {
name: '嗨喽'
},
mutations: {
changeName(state) {
state.name = "hello"
}
},
//子组件2
<template>
<div class="hello">
<p>vuex 方法</p>
<p>nsme:{{ $store.state.name }}</p>
<button @click="changeName"> name</button>
</div>
</template>
<script>
export default {
name: "children2",
data() {
return {
}
},
methods: {
changeName() {
this.$store.commit('changeName')
}
}
};
//子组件1
<template>
<div class="hello">
<p>vuex 方法</p>
<p>name:{{ $store.state.name }}</p>
</div>
</template>
18 、keep-alive 的作用是什么?
答:keep-alive 是 Vue 内置的一个组件,可以使被包含的组件保留状态,或避免重新渲染。
属性:
- include(包含的组件缓存)
- exclude(排除的组件不缓存,优先级大于include)
多出两个钩子函数:
activated
被激活, 表示被缓存的组件激活使用, 当前处于活跃状态deactivated
被活化,表示当前组件虽然被缓存,但不是处于活跃状态
19、axios的特点有哪些
-
从浏览器中创建XMLHttpRequests;
-
支持Promise API;
- 拦截请求和响应;
//在created 生命周期内
axios.interceptors.request.use(function (config) {
//请求拦截
})
axios.interceptors.response.use(function (config) {
//响应拦截
})
- 转换请求数据和响应数据
-
取消请求;自动换成json。
-
axios中的发送字段的参数是data跟params两个,两者的区别在于params是跟请求地址一起发送的,data的作为一个请求体进行发送
-
params一般适用于get请求,data一般适用于post put 请求。
20、导航链接同时被激活?精确匹配模式
在router-link上加exact ,判断当前路径与to是否相等
21、MVVM模型
- ViewModel将视图 UI 和业务逻辑分开,它可以取出 Model 的数据同时帮忙处理 View 中由于需要展示内容而涉及的业务逻辑。
- m- model数据结构
- v - View 用户界面
- vm - ViewModel 双向绑定
六、Vue3框架
1、vite和wabpack的区别
【必答】 webpack会先打包,然后启动开发服务器,请求服务器时直接给予打包结果,当项目文件大的时候会出现打包时间长的问题启动服务器缓慢的问题;vite是直接启动开发服务器,请求哪个模块再对该模块进行实时编译,所以启动速度快,感受好,启动服务器时的优势相对明显
【必答】在热更新方面,当改动了一个模块后,仅需让浏览器重新请求该模块即可,不像webpack那样需要把该模块的相关依赖模块全部编译一次,效率更高。
【选答】当需要打包到生产环境时,vite使用传统的rollup进行打包,因此,vite的主要优势在开发阶段。另外,由于vite利用的是ES Module,因此在代码中不可以使用CommonJS
2、Pinia和vuex的区别,vuex的不足
Vuex和Pinia都是vue.js的状态管理工具,Vuex是vue2使用,而在vue3推荐了Pinia,主要有以下几点区别:
- Pinia没有mutation,他只有state,getters,action【同步、异步】使用它来修改state数据
- Pinia语法上比vuex更容易理解和使用,灵活。
- Pinia没有modules配置,每一个独立的仓库都是definStore生成出来的、
- Pinia的state是一个在函数中返回的对象,和vue组件中的data编写方式差不多
vuex的不足 :
Pinia和Vuex都是非常好用的数据管理工具,在某些情况下,使用Pinia的web应用程序会比使用Vuex更快,这种性能的提升可以归因于Pinia的极轻的重量,Pinia体积约1KB。
3、vue3双向绑定怎么实现
vue3 是通过Proxy实现的数据双向绑定,采用的proxy劫持的是整个对象,相比vue2.0defineProperty,能够监听动态新增的属性,可以监听数组的索引和length属性。
参考:Vue3.0 实现数据双向绑定的方法 ?_vue3.0双向绑定实现-CSDN博客
4、Vue3中 setup的作用是什么?为什么Vue3比Vue2在script中多了一个setup这个语法糖?
setup的设计是为了在Vue3中使用组合式api。
在Vue2中data、computed、methods、watch 组织逻辑在大多数情况下都有效。然而,当我们的组件变得更大时,我们同一个功能的代码会分散在data、computed、methods、watch,这会导致组件难以阅读和理解而通过setup可以将该部分抽离成函数,让其他开发者就不用关心该部分逻辑了。
script中多了一个setup,主要是为了让我们更加方便的编写代码,在setup函数中编写的属性和方法都需要return,如果在<script setup>中编写可以直接使用而无需return
在 script setup 语法糖中,引入的组件可以自动注册,不需要再通过 components 进行注册,而且无法指定当前组件的名字,会自动以文件名为主,省去了 name 属性。
5、vue3和vue2区别
答题技巧:以下4部分内容,挑选出你能够hold得住的去回答即可
vscode插件和调试工具方面:
1、代码高亮,语法提示方面:vue2项目主要用Vetur插件,vue3中主要用Volar
2、语法片段方面:在vue2中我们一直使用Vue 2 Snippets,在vue3我们推荐使用Vue 3 Snippets,因为它支持vue3的同时完全向前兼容vue2
3、在浏览器调试工具方面:vue2版本的chrome devtools不再支持vue3,vue3我们需要单独下载Vue.js devtools beta
兼容性方面:
1、vue2 不支持 IE8 及以下版本,因为 Vue2 使用了 IE8 无法模拟的 ECMAScript 5 特性,例如:Object.defineProperty()
2、vue3 不支持 IE11 及以下版本。
语法层面:
1、在vue2中,我们只要定义在data()方法中的数据就是响应式数据,在vue3中我们可以使用ref和reactive定义的响应式数据
2、组合式api:为了让相关代码更紧凑vue3提出了组合式api,组合式api能将同一个逻辑关注点相关代码收集在一起。 组合式api的入口就是setup方法。
3、在 vue2 中template不支持多根节点组件,vue3支持了多根节点的组件
底层实现方面:
vue2的响应式使用的是Object.defineProperty()实现的,Vue3使用的Proxy实现的
参考:https://juejin.cn/post/7098575243240800286
七、React
1、HOC 和 hook 的区别
- hoc 能复用逻辑和视图,hook 只能复用逻辑
2、useEffect 依赖为空数组与 componentDidMount 区别
- 在 render 执行之后,componentDidMount 会执行,如果在这个生命周期中再一次 setState ,会导致再次 render ,返回了新的值,浏览器只会渲染第二次 render 返回的值,这样可以避免闪屏。
- useEffect 是在真实的 DOM 渲染之后才会去执行,这会造成两次 render ,有可能会闪屏。
3、React.memo() 和 React.useMemo() 的区别
https://cloud.tencent.com/developer/article/2033830
memo 是一个高阶组件,默认情况下会对 props 进行浅比较,如果相等不会重新渲染。多数情况下我们比较的都是引用类型,浅比较就会失效,所以我们可以传入第二个参数手动控制。
useMemo 返回的是一个缓存值,只有依赖发生变化时才会去重新执行作为第一个参数的函数,需要记住的是,useMemo 是在 render 阶段执行的,所以不要在这个函数内部执行与渲染无关的操作,诸如副作用这类的操作属于 useEffect 的适用范畴。
React.memo() 是一个高阶组件,我们可以使用它来包装我们不想重新渲染的组件,除非其中的 props 发生变化。
useMemo() 是一个 React Hook,我们可以使用它在组件中包装函数。我们可以使用它来确保该函数中的值仅在其依赖项之一发生变化时才重新计算。
4、setState本质是同步
- setState本质是同步,只不过让React做成了异步的样子
- 因为要考虑性能
5、react事件机制
- 事件注册、事件的合成、事件冒泡、事件派发
- react 的所有事件并没有绑定到具体的dom节点上而是绑定在了document 上,然后由统一的事件处理程序来处理,同时也是基于浏览器的事件机制(冒泡),所有节点的事件都会在 document 上触发。
- React 所有事件都挂载在
document
对象上 - 当真实 DOM 元素触发事件,会冒泡到
document
对象后,再处理 React 事件; - 所以会先执行原生事件,然后处理 React 事件;
- 最后真正执行
document
上挂载的事件。 - React 为什么使用合成事件
1、进行浏览器兼容,实现更好的跨平台
2、避免垃圾回收
3、方便事件统一管理和事务机制
6、react 事件 与 原生事件的区别
- 事件名称命名方式不同:原生事件采用全部小写方式(onclick);react事件采用小驼峰(onClick)
- 事件处理函数语法:原生事件使用字符串(οnclick="btnClick");react事件采用函数(onClick={btnClick})
- 阻止默认行为:原生事件通过返回false来阻止默认行为;react事件需要使用preventDefault()方法来阻止
7、受控组件与非受控组件的区别
- 受控组件 :即通过setState的形式控制输入的值及更新
1、及时验证
2、执行输入格式
3、有条件的禁用提交按钮
- 非受控组件:通过dom的形式更新值,要获取其值可以通过ref的形式去获取。
八、TypeScript
1、TS的作用、TS代码可以在浏览器解析吗
【必答】ts相对于js而言主要增加了一个类型检测系统,我们大多数也是用它来帮助我们更好的开发项目,它多了如下作用:
- 可以轻松避免在编写代码过程中的类型错误,例如: let num = 10; num ="20" ts会给你实施抛出类型错误,这样能够让我们的代码变得更加规范,减少系统在运行时的错误
- 使大型、复杂的应用程序源码更易阅读。
【必答】TS代码本身不能在浏览器中解析,它需要基于node的一个typescript编译器来将ts代码编译成js代码,才能在浏览器上运行
九、小程序
1、做uniapp商城小程序遇到了什么坑?
参考:
2、之前做uniapp开发的时候有做过多端吗?最多做过几端?
uni-app 是一个使用 Vue.js 开发所有前端应用的开源框架,开发者编写一套代码,可发布到的端有:
- iOS
- Android
- Web(响应式)
- 以及各种小程序(微信/支付宝/百度/头条/飞书/QQ/快手/钉钉/淘宝)等多个平台。
根据你了解的情况去回答,我们微信小程序是学过的,如何发布成H5页讲过,这两个端至少可以回答
参考:手摸手教你快速上手uniapp开发跨端应用实战及踩坑总结_uniapp打包安卓apk踩坑-CSDN博客
3、扫码进来,有一条链接,但是我要获取链接里面带的参数,怎么获取?
答:直接在对应的页面中的onLoad生命周期方法中通过options即可获取到,二维码中url传入的参数
4、uniapp 项目要在不同多端发布那么在开发的时候要考虑哪方面的问题呢?
- 多端兼容性问题,
- 微信开发文档中的配置问题
5、uniApp开发一般适合什么项目呢?开发中的项目要怎么能达到uniapp的专业规范呢?
为了实现多端兼容,综合考虑编译速度,运行性能等因素,uni-app约定了如下开发规范:
- 页面文件遵循Vue单文件组件(SFC)规范
- 组件标签靠近小程序规范,详见uni-app组件规范
- 接口能力(JS API)靠近微信小程序规范,但需将前缀wx替换为uni,详见uni-app接口规范
- 数据绑定及事件处理同Vue.js规范,同时补充了App及页面的生命周期
- 为兼容多端运行,建议使用flex布局进行开发
6、uniapp 项目要在不同多端发布那么在开发的时候要考虑哪方面的问题呢?
- 多端兼容性问题,
- 微信开发文档中的配置问题
7、uniApp开发一般适合什么项目呢?开发中的项目要怎么能达到uniapp的专业规范呢?
为了实现多端兼容,综合考虑编译速度,运行性能等因素,uni-app约定了如下开发规范:
- 页面文件遵循Vue单文件组件(SFC)规范
- 组件标签靠近小程序规范,详见uni-app组件规范
- 接口能力(JS API)靠近微信小程序规范,但需将前缀wx替换为uni,详见uni-app接口规范
- 数据绑定及事件处理同Vue.js规范,同时补充了App及页面的生命周期
- 为兼容多端运行,建议使用flex布局进行开发
8、uniapp组件中父组件,子组件,兄弟组件,彼此之前的数据交换有什么?
- 父传子:props
- 子传父:
-
- 子传:$emit("自定义事件名", 参数)
- 父收:on+自定义事件名=“父组件的处理函数”
- 兄弟组件:
-
- eventBus:$emit传、$on接收
- 借鉴React的状态提升
- vuex
9、小程序的生命周期
- 应用级别:Page()中触发
1、onLaunch:小程序启启动时
2、onShow:小程序前台运行时
3、onHide:小程序后台运行时
4、onError:执行错误时
5、onPageNotFount:冷启动(如扫码)打开小程序的页面不存在时
- 页面级别:
1、onLoad:页面加载时触发。一个页面只会调用一次,可以在onLoad的参数中获取打开当 前页面路径的参数
2、onShow:页面显示/切入前台时触发(返回、tabBar切换、前台运行)
3、onReady:页面初次渲染完毕,相当于vue的mounted。一个页面只会调用一次,代表页面已经准备妥当,可以可视图层进行交互
4、onHide:页面隐藏/切入后台时触发(跳转、tabBar切换、后台运行)
5、onUnload:页面卸载时触发。如redirectTo 或 navigateBack 到其他页面时
10、小程序的登陆流程
【阐述具体流程-合格】
- 调用 wx.login() 获取 临时登录凭证 code
- 将临时 code 传到我们的后端,后端调用换取用户唯一标识 OpenID 和 会话密钥 session_key
- 后端自定义新的密钥并关联返回的 session_key 和 openid,将新的密钥返给前端,前端将其存储在 storage 中。
【登录特点-优秀】小程序登录流程主要是要与微信服务器进行通信验证
参考:
11、小程序的跳转方式
[回答第1,2,3条-合格,因为是常用的]
- wx.navigateTo() : 保留当前页面,跳转到应用内的某个页面。但是不能跳到 tabbar 页面
- wx.switchTab() : 跳转到 TabBar 页面,并关闭其他所有非 tabBar 页面
- wx.navigateBack() : 关闭当前页面,返回上一页面或多级页面。可通过getCurrentPages() 获取当前的页面栈,决定需要返回几层
[回答第4条-良好]
- wx.redirectTo() : 关闭当前页面,跳转到应用内的某个页面。但是不允许跳转到 tabbar 页面
[回答第5条-优秀]
- wx.reLaunch() : 关闭所有页面,打开到应用的某个页面
12、小程序的数据绑定和vue有什么区别
参考:微信小程序之数据的绑定(与vue做对比)_小程序获得data中的值与vue的差距-CSDN博客