深拷贝和浅拷贝
深拷贝和浅拷贝是关于对象(包括数组)复制的两个概念。
浅拷贝在复制对象属性的时候,复制的是指针(引用),所以,修改目标对象的属性值会影响到原对象的对应属性值 obj。assign
深拷贝
这个函数使用递归的方式对对象进行深拷贝。对于每个属性,如果是基本类型或null,则直接复制;如果是数组,则创建一个新的数组,并递归深拷贝每个元素;如果是对象,则创建一个新的对象,并递归深拷贝每个属性。
function deepCopy(obj) {
if (typeof obj !== 'object' || obj === null) {
// 对于基本类型和null,直接返回
return obj;
}
let copy;
if (Array.isArray(obj)) {
// 处理数组
copy = [];
for (let i = 0; i < obj.length; i++) {
copy[i] = deepCopy(obj[i]);
}
} else {
// 处理对象
copy = {};
for (let key in obj) {
if (obj.hasOwnProperty(key)) {
copy[key] = deepCopy(obj[key]);
}
}
}
return copy;
}
如何实现一个new
1 创建一个新的对象obj
2 将对象与构建函数通过原型链连接起来
3 将构建函数中的this绑定到新建的对象obj上
4 根据构建函数返回类型作判断,如果是原始值则被忽略,如果是返回对象,需要正常处理
如果构造函数有返回值并且返回值是一个对象,则返回该对象;否则返回新对象实例
举个例子:
function Person(name, age){
this.name = name;
this.age = age;
}
const person1 = new Person('Tom', 20)
console.log(person1) // Person {name: "Tom", age: 20}
t.sayName() // 'Tom'
手动实现一下
function mynew(Func, ...args) {
// 1.创建一个新对象
const obj = {}
// 2.新对象原型指向构造函数原型对象
obj.__proto__ = Func.prototype
// 3.将构建函数的this指向新对象
let result = Func.apply(obj, args)
// 4.根据返回值判断
return result instanceof Object ? result : obj
}
跨域是什么?怎么解决?isonp方式是如何做的
以下是使用 JSONP 的具体实现步骤:
- 客户端发送 JSONP 请求:在客户端(例如浏览器中的 JavaScript)中创建一个
<script>
标签,其中的src
属性指向目标服务器的接口,并在 URL 中包含一个回调函数名作为参数。
function handleResponse(data) {
// 处理响应数据
}
const script = document.createElement('script');
script.src = 'https://api.example.com/data?callback=handleResponse';
document.head.appendChild(script);
- 服务器返回响应:服务器接收到请求后,将数据包装在回调函数中返回给客户端。回调函数名由客户端指定,它会作为参数出现在响应中。例如,服务器响应如下:
handleResponse({ message: 'Hello, JSONP!' });
- 客户端处理响应:由于服务器返回的是一个包裹在回调函数中的 JavaScript 代码,浏览器会将该代码当作脚本执行,从而触发客户端事先定义的回调函数。通过在客户端定义一个与服务器返回的回调函数名相同的函数,可以在回调函数中处理服务器返回的数据。
javascript
function handleResponse(data) {
// 处理响应数据
console.log(data.message); // 输出:Hello, JSONP!
}
需要注意的是,JSONP 的使用有一些限制和安全风险:
-
JSONP 只支持 GET 请求,不支持其他 HTTP 方法。
-
由于 JSONP 是通过动态创建
<script>
标签来实现的,因此无法像使用 XMLHttpRequest 或 Fetch API 那样获取完整的响应对象,例如状态码、响应头等。 -
JSONP 要求服务器能够返回一个包装在回调函数中的 JavaScript 代码,这可能会导致安全问题,因为服务器端的代码可以被注入到客户端页面中。
事件循环说说,宏任务和微任务有哪些
事件循环(Event Loop)是 JavaScript 中处理异步操作的机制。它负责管理任务队列(Task Queue)中的任务,并按照特定的执行顺序将它们执行。
宏任务:setTimeout setInterval Ajax DOM事件 微任务:Promise
事件循环的执行顺序如下:
**事件循环的执行顺序是先执行同步任务,然后处理微任务队列,再执行异步任务,接着处理宏任务队列,最后再次处理微任务队列,然后进入下一个循环。**微任务具有高优先级,会在下一个宏任务之前执行,这意味着微任务队列中的任务会在下一个宏任务执行之前完成。
事件循环的基本流程如下:
-
执行同步任务: 从调用栈中取出一个同步任务执行,直到调用栈为空或遇到异步任务。
-
执行微任务(Microtasks): 在同步任务执行完毕后,会检查微任务队列(也称为 Promise 队列)。如果存在微任务,事件循环会依次执行微任务直到微任务队列为空。微任务通常包括 Promise 的回调函数、
MutationObserver
和process.nextTick
等。 -
执行宏任务(Macrotasks): 如果微任务队列为空,事件循环会从宏任务队列中选择一个任务执行。宏任务包括定时器回调函数(
setTimeout
、setInterval
)、事件回调函数(如用户交互事件、网络请求、文件读写等)等。 -
更新渲染(Update rendering): 如果浏览器需要重绘或重新布局页面,会执行相应的渲染操作。
-
重复步骤: 重复以上步骤,不断处理任务直到所有任务完成。
需要注意的是,微任务优先级高于宏任务,即在执行微任务队列之前不会执行下一个宏任务。这意味着微任务可以在同一次事件循环中立即执行,而宏任务需要等待下一次事件循环。
this指向问题
// 全局上下文
console.log(this); // 输出全局对象 (window 或 global)
// 函数调用
function sayHello() {
console.log(this);
}
sayHello(); // 输出全局对象 (window 或 global)
const obj = {
name: 'John',
greet: function() {
console.log(this.name);
}
};
obj.greet(); // 输出 "John"
function Person(name) {
this.name = name;
this.greet = function() {
console.log('Hello, ' + this.name);
};
}
const person1 = new Person('Alice');
person1.greet(); // 输出 "Hello, Alice"
const greet = person1.greet;
greet(); // 输出 "Hello, undefined" (非严格模式下),或抛出错误 (严格模式下)
const arrowFunc = () => {
console.log(this);
};
arrowFunc(); // 输出全局对象 (window 或 global)
在 JavaScript 中,this
关键字用于引用当前执行上下文中的对象。它的值取决于函数被调用的方式和上下文。
-
全局上下文:
当在全局作用域中使用
this
时,它将指向全局对象(浏览器环境中为window
对象,Node.js 环境中为global
对象)。 -
函数调用:
在函数内部,
this
的指向取决于函数的调用方式:- 作为函数调用时,
this
指向全局对象(非严格模式下),或者是undefined
(严格模式下)。 - 作为对象方法调用时,
this
指向调用该方法的对象。 - 作为构造函数调用时,
this
指向新创建的对象实例。 - 使用
call()
、apply()
或bind()
方法显式地指定this
的值。
- 作为函数调用时,
-
箭头函数:
箭头函数的
this
在定义时就被绑定,它的指向始终是定义时所在的上下文,而不是运行时的调用方式。
箭头函数和普通函数的区别
-
this 的绑定:
- 普通函数:每个函数都有自己的
this
值,它在运行时根据调用方式进行绑定。在普通函数内部,this
的值取决于函数是如何被调用的。 - 箭头函数:箭头函数没有自己的
this
值,而是继承其父作用域的this
值。箭头函数内部的this
始终指向定义时所在的上下文的this
值。
- 普通函数:每个函数都有自己的
-
arguments 对象:
- 普通函数:普通函数内部可以访问到特殊的
arguments
对象,该对象包含了函数被调用时传递的参数列表。 - 箭头函数:箭头函数没有自己的
arguments
对象。如果需要访问参数,可以使用剩余参数语法...args
或者使用普通函数来获取arguments
对象。
- 普通函数:普通函数内部可以访问到特殊的
-
构造函数:
- 普通函数:普通函数可以用作构造函数,通过
new
关键字创建实例对象。 - 箭头函数:箭头函数不能用作构造函数,无法通过
new
关键字创建实例对象。
- 普通函数:普通函数可以用作构造函数,通过
异步任务分为哪些
宏任务和微任务
垂直居中的方式
1 使用Flexbox布局:。这种方法适用于父元素和子元素都是块级元素的情况,这将使容器内的所有子元素都垂直和水平居中。
.parent {
display: flex;
justify-content: center;
align-items: center;
}
2 使用绝对定位和transform属性, left: 50%; top: 50%会使子元素的左上角在父元素的中心位置;
.parent {
position: relative;
}
.child {
position: absolute;
left: 50%;
top: 50%;
transform: translate(-50%, -50%);
}
3 使用绝对定位和margin属性
.parent {
position: relative;
}
.child {
position: absolute;
left: 0;
right: 0;
top: 0;
bottom: 0;
margin: auto;
}
怎么理解回流和重绘
回流(Reflow)和重绘(Repaint)是浏览器渲染过程中的两个重要概念
回流是跟据元素的样式和结构,确定其在页面上位置的过程,当元素的大小和位置发生变化时,就回触发回流,
- 添加或删除可见的DOM元素
- 元素的位置发生变化
- 元素的尺寸发生变化(包括外边距、内边框、边框大小、高度和宽度等)
- 内容发生变化,比如文本变化或图片被另一个不同尺寸的图片所替代
- 页面一开始渲染的时候(这避免不了)
- 浏览器的窗口尺寸变化(因为回流是根据视口的大小来计算元素的位置和大小的)
重绘,确定位置之后,跟据其样式,将元素绘制到屏幕上的过程。
-
颜色的修改
-
文本方向的修改
-
阴影的修改
避免,
- 尽量减少对样式的频繁修改,最好一次性修改多个样式,或者使用 CSS 类进行批量修改。
- 避免频繁访问布局信息(如 offsetTop、offsetWidth 等),因为这会导致强制回流。
- 使用 CSS3 的 transform 属性来进行复杂的动画效果,它可以在不引起回流的情况下改变元素的视觉呈现。
- 使用分离布局和样式的原则,避免样式改变引起布局的变化。
- 合理使用 CSS 布局技术,如 Flexbox 和 Grid,它们可以更高效地处理元素布局
- 如果想设定元素的样式,通过改变元素的
class
类名 (尽可能在 DOM 树的最里层)
元素显示和隐藏的方法有哪些
-
CSS 的 display 属性:
- 显示元素:
element.style.display = 'block';
或element.style.display = '';
- 隐藏元素:
element.style.display = 'none';
- 显示元素:
-
CSS 的 visibility 属性:
- 显示元素:
element.style.visibility = 'visible';
- 隐藏元素:
element.style.visibility = 'hidden'
- 显示元素:
vue的双向数据绑定的原理
-
数据劫持(Data Observation):
Vue 使用了一种称为「数据劫持」的技术来追踪数据的变化。它通过使用 Object.defineProperty() 方法,将数据对象的属性转换为 getter 和 setter。当访问或修改属性时,Vue 能够捕获到这些操作,并触发相应的更新。
-
监听器(Watcher):
在 Vue 中,每个数据属性都有一个对应的监听器对象。监听器会订阅数据的变化,并根据变化触发相应的更新操作。当数据发生变化时,监听器会通知相关的视图进行更新。
-
模板编译(Template Compilation):
Vue 的模板编译过程会解析模板中的指令(如 v-model),并生成对应的更新函数。这些更新函数能够在数据发生变化时,更新视图的内容。
-
视图更新:
当数据发生变化时,监听器会收到通知,并触发视图的更新操作。这样,当用户在视图中修改数据时,数据会自动更新,反之亦然。
虚拟dom的理解
虚拟 DOM 的优势在于它能够提供一种高效的方式来管理和更新界面,减少直接操作真实 DOM 的成本。通过比较虚拟 DOM 树的差异,可以最小化对真实 DOM 的操作,提高性能和用户体验。
如何清除浮动
伪元素
可以使用伪元素在浮动元素的父元素中插入一个额外的元素,并设置其样式为 clear: both;
<div class="clearfix">
<div class="float-left">浮动元素1</div>
<div class="float-left">浮动元素2</div>
</div>
.clearfix::after {
content: "";
display: table;
clear: both;
}
overflow:hidden
BFC
BFC(块级格式化上下文)是一种页面渲染的 CSS 布局概念。它是一种独立的渲染区域,其中的元素按照一定规则进行布局和渲染
BFC 主要用于解决以下问题:
- 清除浮动(Clear Float):当父元素包含浮动元素时,父元素的高度会塌陷,导致无法正确包裹浮动元素。通过创建一个 BFC,可以使父元素包含浮动元素的布局正常显示,不再塌陷。
例子:
<div class="parent">
<div class="float-left">Float Left</div>
<div class="float-right">Float Right</div>
</div>
.parent {
overflow: hidden;
}
通过设置 overflow: hidden;,.parent 元素成为一个 BFC,它会包裹内部的浮动元素,防止元素的高度塌陷。
-
阻止边距重叠(Margin Collapse):在普通流中,相邻的两个元素的上下边距可能会发生重叠。但是,位于不同的 BFC 中的元素的边距不会重叠,这可以用于控制边距的表现。
-
自适应两栏布局:通过创建两个相邻的 BFC 容器,可以实现一种自适应的两栏布局,其中一个容器可以固定宽度,另一个容器会自动填充剩余空间。
-
避免浮动元素覆盖文字:当文字环绕在浮动元素周围时,文字可能会被浮动元素覆盖。通过创建一个 BFC 容器来包裹文字,可以避免文字被浮动元素覆盖。
如何设置一个bfc , 一般用overflow:hidden 和flex盒子
.bfc-container {
overflow: hidden;
}
.bfc-container {
display: inline-block;
}
.flex-container {
display: flex;
}
CSS如何画一个三角形
.triangle {
width: 0;
height: 0;
border-left: 5px solid transparent;
border-right: 5px solid transparent;
border-top: 10px solid #000;
}
响应式布局
响应式布局是一种设计和开发网页的方法,使网页能够适应不同的设备和屏幕尺寸,提供更好的用户体验。它通过使用媒体查询(Media Queries)和弹性布局(Flexbox)等技术,根据设备的特性和视口(Viewport)大小来自动调整和重新排列页面的元素和内容。
响应式布局的目标是使网页在各种设备上都能呈现良好的布局和可用性,无论是在桌面电脑、笔记本电脑、平板电脑还是手机等移动设备上。
以下是一个简单的响应式布局的示例,假设我们有一个包含导航栏和内容区域的网页布局:
html
<!DOCTYPE html>
<html>
<head>
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<style>
/* 基础样式 */
body {
margin: 0;
padding: 0;
font-family: Arial, sans-serif;
}
/* 导航栏样式 */
.navbar {
background-color: #333;
color: #fff;
padding: 10px;
}
/* 内容区域样式 */
.content {
padding: 20px;
}
/* 媒体查询 */
@media (max-width: 768px) {
/* 在小屏幕上调整布局 */
.navbar {
text-align: center;
}
}
</style>
</head>
<body>
<div class="navbar">导航栏</div>
<div class="content">
<h1>响应式布局示例</h1>
<p>这是页面的内容。</p>
</div>
</body>
</html>
在上述示例中,我们使用了媒体查询来针对小屏幕设备(最大宽度为768px)调整导航栏的样式。在小屏幕上,导航栏的文本居中显示,以适应较小的视口。
通过设置 <meta>
标签中的 viewport
属性,我们告诉浏览器对页面进行响应式布局。width=device-width
表示将视口宽度设置为设备的宽度,initial-scale=1.0
表示初始缩放级别为 1.0。
当在不同的设备上打开上述示例页面时,可以看到导航栏在桌面和小屏幕设备上有不同的布局效果,以适应不同的屏幕尺寸。
这只是一个简单的示例,实际的响应式布局可能涉及更复杂的调整和适配,包括使用媒体查询和弹性布局来改变元素的大小、位置和显示方式,以适应不同的设备和屏幕布局
git svn
webpack
内存泄露是什么,怎么导致的
promise介绍
图片懒加载和预加载
数组去重的方法?至少说出2种
前端性能优化问题
前端缓存
https和http的区别,http缓存有哪些,为什么要减少http的请求,https为什么比http更安全
## 了解axios的原理吗?怎么实现的
## Vue的导航守卫有哪一些? 你的登录拦截怎么实现的?
## keep-alive是什么?有哪几个生命周期阶段?
## 判断一个变量是否是数组,有哪些办法? 判断一个变量是否是对象,有哪些办法?
## 数组去重
## mixins有几个生命周期阶段?
## 弹性布局,一行两列,一列固定宽,如何实现?
## Flex:1 包含哪三种属性
## async async的作用是定义这个函数是异步:async 放在函数前的一个关键字,再函数内 return 1、return "1"、return new Promise(),
外部打印这个函数都是获取到的是一个promise对象,通过函数.then获取到return的值
await的作用是等待:如果它等到的不是一个 Promise 对象,那 await 表达式的运算结果就是它等到的东西。如果它等到的是一个 Promise 对象,await 就忙起来了,它会阻塞后面的代码,等着 Promise 对象 resolve,然后得到 resolve 的值,作为 await 表达式的运算结果。
和promise的区别,
现在把业务要求改一下,仍然是三个步骤,但每一个步骤都需要之前每个步骤的结果。
/*
* 传入参数n,表示这个函数执行的时间(毫秒)
* 执行的结果是 n+200,这个值将用于下一步骤
*/
function takeLongTime(n){
return new Promise((resolve) => {
setTimeout(() => resolve(n + 200),n);
})
}
function step1(n){
console.log(`step1 with ${n}`);
return takeLongTime(n);
}
function step2(m,n){
console.log(`step2 with ${m} + ${n}`);
return takeLongTime(m + n);
}
function step3(k,m,n){
console.log(`step3 with ${k} + ${m} + ${n}`);
return takeLongTime(k + m + n);
}
==========================================
async function doIt() {
console.time('doIt');
let time1 = 300;
let time2 = await step1(time1);//将Promise对象resolve(n+200)的值赋给time2
let time3 = await step2(time2,time1);
let result = await step3(time3,time2,time1);
console.log(`result is ${result}`);
console.timeEnd('doIt');
}
doIt();
//执行结果为:
//step1 with 300
//step2 with 500 + 300
//step3 with 1000 + 500 + 300
//result is 2000
//doIt: 2916.655029296875ms
有没有感觉有点复杂的样子?那一堆参数处理,就是 Promise 方案的死穴------ 参数传递太麻烦了,看着就晕!
注意点
就目前来说,已经理解 async/await 了吧?但其实还有一些事情没提及------Promise 有可能 reject 啊,怎么处理呢?
await 命令后面的 Promise 对象,运行结果可能是 rejected,所以最好把 await 命令放在 try...catch 代码块中。
async function myFunction() {
try {
await somethingThatReturnAPromise();
} catch (err){
console.log(err);
}
}
//另一种写法
async function myFunction() {
await somethingThatReturnAPromise().catch(function(err) {
console.log(err);
})
}
webpack
常见的plugin
1 html -webpack=plugin
在打包结束后,⾃动生成⼀个 html
⽂文件,并把打包生成的js
模块引⼊到该 html
中
2 clean-webpack-plugin
每次打包时,都会把上一次打的包删除
3 mini-css-extract-plugin
打包之后i的css文件在js中,该插件可以把css单独提取出来
如何借助webpack来优化前端性能?
关于webpack
对前端性能的优化,
可以通过文件体积大小入手,
对js,css,html代码进行压缩
使用插件
使用HtmlWebpackPlugin插件来生成HTML的模板时候,通过配置属性minify进行html优化
module.exports = {
...
plugin:[
new HtmlwebpackPlugin({
...
minify:{
minifyCSS:false, // 是否压缩css
collapseWhitespace:false, // 是否折叠空格
removeComments:true // 是否移除注释
}
})
]
}
其次还可通过分包的形式
这里通过splitChunksPlugin
来实现,将代码分离到不同的bundle
中,之后我们可以按需加载,或者并行加载这些文件
module.exports = {
...
optimization:{
splitChunks:{
chunks:"all"
}
}
}
splitChunks主要属性有如下:
Chunks,对同步代码还是异步代码进行处理
minSize: 拆分包的大小, 至少为minSize,如何包的大小不超过minSize,这个包不会拆分
maxSize: 将大于maxSize的包,拆分为不小于minSize的包
minChunks:被引入的次数,默认是1
提高webpack的构建速度?
随着我们的项目涉及到页面越来越多,功能和业务代码也会随着越多,相应的 webpack
的构建时间也会越来越久
构建时间与我们日常开发效率密切相关,当我们本地开发启动 devServer
或者 build
的时候,如果时间过长,会大大降低我们的工作效率
所以,优化webpack
构建速度是十分重要的环节
1· 缩小文件搜索范围
可以通过配置include
、exclude
、test
属性来匹配文件,接触include
、exclude
规定哪些匹配应用loader
2 使用dll进行分包
3 bable缓存,第二次构建时,会读取之前的缓存
使用 cache-loader
在一些性能开销较大的 loader
之前添加 cache-loader
,以将结果缓存到磁盘里,显著提升二次构建速度
保存和读取这些缓存文件会有一些时间开销,所以请只对性能开销较大的 loader
使用此loader
echart
1 创建一个div,给其宽和高
2 拿到这个div,初始化,获取echart实例
const echartaa = echart.init(this.$ref.social)
3
echartaa.setOptin({
})
难点之一
拿home。data来设置optins,但是这里的homedata为空
虽然 getHomeData是在create里
总
后台管理系统登录
退出
清除cooike 并且 跳转到登录页
登录
如果token不存在,并且当前页面不是login页面,就跳转到登录页
如果token存在,并且当前页面是login,就跳转到home页面