1.常用的几种居中方式盒子垂直居中
(1)margin:0 auto;text-align:center;line-height:子元素高度
(2)父元素设置display:flex+justify-content:center子元素自动水平居中,如果要垂直也居中则还要设置align-items:center或者align-content:center此时子元素都水平垂直居中
(3)父元素给个定位属性和高宽,子元素设置position:absolute+left:50%+top:50%+transform:translate(-50%,-50%),transform属性可以换成margin-left:-(子元素宽度一半)和margin-top:-(子元素高度一半)这样就能水平垂直居中了
(4)父元素有定位和高宽,子元素设置position:absolute+margin:auto+top:0+left:0+right:0+bottom:0,这样也能实现水平垂直居中了,过有些繁琐就是了
2.vue响应式原理:https://juejin.cn/post/7074422512318152718#heading-5
Vue采用了响应式的设计模式,它基于JavaScript的特性,利用了ES5的Object.defineProperty()函数,监听数据变化并作出响应。
在Vue中,数据是被封装到对象中的,而这个对象原型上的每个数据属性都被定义成getter和setter,并且所有的操作都是通过这些getter和setter进行的。在数据发生变化时,Vue会自动地更新DOM节点。
Vue的响应式原理可以被概括为以下几个步骤:
1. 定义数据:在Vue实例的data属性中定义应用所需的数据。
2. 在Vue实例中创建观察者:Vue实例会在创建时扫描data对象中的属性,为每一个属性创建一个观察者对象。
3. 监听数据变化:Vue使用Object.defineProperty()函数监听data属性的变化,并在属性发生变化时通知相应的观察者。
4. 更新视图:当Vue检测到数据发生变化时,会通知所有相关的观察者对象,并调用每个观察者的更新方法来更新视图。
总的来说,Vue通过定义数据的方式,使其能够自动地监控数据的变化并作出响应,这是Vue的核心特性之一,也是Vue在开发者社区中广受欢迎的原因之一。
3.如何用var实现块级作用域 https://juejin.cn/post/7071903737425428516
在ES5之前,JavaScript只有函数作用域,没有块级作用域。这就意味着,使用var定义的变量会被提升到函数的顶部,而不是当前块级作用域的顶部,这可能会导致一些意外的行为。
在实际开发中,如果想使用块级作用域,可以使用自执行函数包裹代码块来实现,例如:
```
(function(){
var foo = 'bar';
console.log(foo); // 'bar'
})();
console.log(foo); // ReferenceError: foo is not defined
```
上述代码中,我们使用了一个自执行函数来包裹代码块,将其转换为函数作用域。在函数内部使用var定义的变量foo,在函数之外是无法访问的。
另外,从ES6开始,JavaScript引入了let和const关键字,它们支持块级作用域,因此更推荐使用它们来实现块级作用域。例如:
```
{
let foo = 'bar';
console.log(foo); // 'bar'
}
console.log(foo); // ReferenceError: foo is not defined
```
在使用let定义变量时,变量只在当前块级作用域内有效,并且不会被提升到块级作用域的顶部。
什么是闭包
闭包:
闭包是指函数可以访问定义在函数外部的变量,即使函数已经执行完毕,这些变量仍然可以被访问和修改。通常情况下,在函数执行完毕后,函数内部定义的变量和函数的作用域都会被销毁,但是如果在函数内部定义了一个函数,并且返回了该函数,那么该函数就会形成一个闭包,可以访问定义在外部函数中的变量和作用域。闭包的常见用途包括封装变量和数据,实现私有成员等。
用过哪些ES6新特性
ES6(也称为ES2015)是ECMAScript6标准,是JavaScript的一项重大更新,提供了许多新的语言特性和API库。以下是ES6的一些新特性:
1. 块级作用域:引入了let和const关键字来支持块级作用域,可以有效解决变量声明的提升和难以维护的问题。
2. 箭头函数:可以更方便地定义函数,并且作用域绑定于上下文执行环境,避免了this指向问题。
3. 类和继承:引入了类和继承的概念,使得面向对象编程更加简单、直观。
4. 模板字符串:使用反引号`和${}来支持多行文本和表达式插值。
5. 解构赋值:一种方便的方式,可以从数组和对象中提取数据,并将其分配到变量中,更加简洁。
6. 默认参数:可以为函数参数设置默认值,简化了函数的使用。
7. 简化的对象字面量语法:支持计算属性名和简写的方法定义,可以更方便地创建对象。
8. for...of循环:支持迭代器和生成器,更加灵活。
9. Promise:一种异步编程模式,让异步代码更容易编写和维护。
10. async/await:ES7引入了async/await语法,使得异步编程更加简单易懂。
11. 模块化:引入了模块化编程,并定义了一套标准的模块化语法,可以更轻松地使用和维护代码。
这里只列举了ES6的一些新特性,实际上ES6还包含了很多其他功能和改进,如二进制和八进制的支持、Map和Set等新的数据结构、Symbol、Proxy等元编程特性、Array.from等方法和函数、new.target元属性等等。
了解过webpack吗
webpack是一个开源的前端模块化打包工具,它将整个前端项目按模块进行划分,将每个模块都视为一个模块来处理,并最终将它们打包成一个或多个JavaScript文件,用于在浏览器中运行。
webpack的主要功能包括:
1. 模块打包:将整个前端项目划分为多个模块,并将每个模块打包成单独的文件,以便在浏览器中加载和运行。
2. 静态资源处理:webpack可以将其他文件(如CSS、图片、字体等)转换为JavaScript模块,并对它们进行处理和优化。
3. 代码拆分:webpack支持将打包成单个文件的应用程序拆分为多个包,以优化加载速度和减少带宽消耗。
4. 插件系统:webpack提供了一套丰富的插件系统,可以通过插件来扩展和定制webpack的功能。
5. 开发服务器:webpack提供了一个开发服务器,可以实时刷新浏览器并提供其他开发便利。
总的来说,webpack是一个功能强大的前端构建工具,可以帮助开发人员管理和打包前端应用程序中的模块和静态资源,提高开发效率,减少打包体积,优化性能。
说说对盒子模型的理解
盒子模型是CSS中一个重要的概念,其本质是把HTML标签看做一个三维盒子,包含了内容、内边距(padding)、边框(border)和外边距(margin)四个部分。HTML元素的可视面积就是它的内容区加上内边距和边框之和。
盒子模型由内到外分别为:
1. 内容区(content):HTML元素中放置实际内容的区域。
2. 内边距(padding):内容区与元素边框之间的间隔区域,用于增加内容与边框的间距。
3. 边框(border):包围内容区和内边距的可见或隐藏的形状边框,可以通过CSS样式来改变边框的颜色、宽度和样式等。
4. 外边距(margin):边框与相邻元素之间的空白区域,用于控制元素与其他元素之间的间距。
盒子模型的理解在页面布局和样式上均非常重要,在盒子模型中优化边框、内边距等的样式和值,可以帮助实现更好的页面布局效果和更好的用户体验。例如,当需要元素之间的垂直距离时,可以使用上下的外边距进行控制,而当需要元素之间的左右距离时,可以使用左右的内边距进行控制,同时还可以使用CSS中盒子模型调整边框的颜色、宽度、圆角等样式来实现个性化需求。
宏任务和微任务
在JavaScript中,任务(Task)分为宏任务(Macrotask)和微任务(Microtask)。当JavaScript代码执行时,会产生一连串的任务,而这些任务通常是按照一定的顺序被执行的。了解宏任务和微任务的概念可以帮助我们更好地理解JavaScript的运行机制。
宏任务(Macrotask):
宏任务既可以是由浏览器发起的任务(如页面渲染、用户交互、网络请求等),也可以是由JavaScript代码发起的任务(如定时器、事件监听等)。
宏任务包括:
* 从浏览器事件队列中取出事件(如click等),交给JavaScript引擎执行。
* 从异步队列中取出回调函数(如setTimeout、setInterval、setImmediate等),交给JavaScript引擎执行。
* 从Node.js的I/O事件队列中取出事件,交给JavaScript引擎执行。
执行完一个宏任务后,会清空所有微任务。
微任务(Microtask):
微任务是指在JavaScript执行时,由Promise.then()、Promise.catch()、Promise.finally()、queueMicrotask和MutationObserver等 API 产生的的任务。
微任务包括:
* Promise.then()
* Promise.catch()
* Promise.finally()
* queueMicrotask
* MutationObserver
当一个微任务产生后,会被推到微任务的执行队列中,等待JavaScript引擎执行它们。当JavaScript引擎执行完当前宏任务之后,会优先执行微任务队列中的所有微任务,直到微任务队列为空。只有当微任务队列为空时,才会去执行下一个宏任务。总结:
JavaScript引擎执行一个宏任务,就会先执行这个宏任务所产生的所有微任务。而在这个宏任务中,又可能产生微任务,需要先执行这些微任务,再执行宏任务队列中的下一个宏任务。
原型链的理解
原型链:
JavaScript中的对象是通过原型链进行继承的。每个对象都有一个原型对象,原型对象又有自己的原型对象,形成一个链式结构。当对象访问一个不存在的属性时,JavaScript引擎会沿着这条原型链向上查找,直到找到该属性或者查找到原型链的顶端。如果最终都没有找到该属性,那么返回undefined。
原型链的作用是实现继承。子对象可以通过原型链继承父对象的属性和方法,从而减少代码的重复。在JavaScript中,每个函数都有一个原型对象,该对象包含了函数的属性和方法。当使用new运算符创建一个对象时,JavaScript引擎会将该对象的原型设置为构造函数的原型对象,从而实现继承。
Function和Object的理解(从原型链的方面说)
在JavaScript中,所有对象都具有原型(prototype)属性,它们都是通过原型链(prototype chain)相互关联的。Function和Object都是JavaScript中的内置函数,它们也具有原型属性,并且在原型链中具有特殊的作用。
Object原型链:
所有JavaScript对象都是Object的后代,即所有对象通过原型链都能够访问Object的原型(Object.prototype)。Object有若干属性和方法,例如toString()、valueOf()等等,它们都包含在Object.prototype属性中。因此,所有的JavaScript对象都可以调用这些属性和方法,例如:
```
const obj = { name: 'John' };
console.log(obj.hasOwnProperty('name')); // true
console.log(obj.toString()); // [object Object]
```
通过原型链,obj对象可以访问Object.prototype,从而可以调用其上的方法和属性。
Function原型链:
所有函数(Function)也都是对象,也都是通过Function的原型链相互关联的。Function的原型链中除了包含Object的原型链之外,还具有Function.prototype,即Function.prototype也是一个对象,它包含了几个常用的属性和方法,例如call()、apply()、bind()等。所有的函数对象都可以调用Function.prototype上的这些方法,例如:
```
function add(x, y) {
return x + y;
}
console.log(add.call(null, 1, 2)); // 3
```
通过原型链,add函数可以访问Function.prototype,从而可以调用其上的方法和属性。
总的来说,Object和Function都是JavaScript中的内置函数,它们通过原型链相互关联。对象都可以通过原型链访问其特定的原型上的属性和方法,函数也一样,包括其本身以及Function.prototype上的方法和属性。理解原型链的概念可以更好地理解对象和函数在JavaScript中间的关系。
new 实现了什么
在JavaScript中,`new`是一个用于创建对象实例的关键字。 `new` 的作用是创建一个新对象,并将这个新对象的构造函数和参数传递给它。这个对象继承了构造函数原型对象的所有属性和方法。
当一个函数作为构造函数被调用时,它的`this`关键字引用了新创建的对象,因此构造函数可以在对象上设置属性和方法。使用`new`关键字调用构造函数时,会发生以下操作:
1. 新建一个空对象
2. 将空对象的构造函数指向构造函数
3. 执行构造函数,同时将this指针指向新建立的对象
4. 如果构造函数返回非空对象,则返回该对象,否则返回新建立的对象
这样,使用`new`关键字就能够实现在JavaScript中创建对象实例的功能。
数组去重(原生js)
可以通过循环遍历数组,将数组元素与一个新数组中的元素依次比较,如果新数组中已经存在相同的元素,则不将该元素加入新数组中。以下是一种常见的用原生JavaScript实现数组去重的方式:
```
function unique(arr){
var result = [];
for(var i=0; i
result.push(arr[i]);
}
}
return result;
}
```
该函数接受一个数组参数arr,使用循环遍历数组中的每个元素。每次循环将该元素与result数组中的元素依次比较,如果result数组中不存在该元素,则将该元素添加到result数组中。
注意,这种方式虽然简单易懂,但其时间复杂度是O(n^2),在数组长度较大的情况下性能较低。更高效的方式可以使用对象字面量或Set等数据结构来实现去重。
for of 和for in 的区别
`for...of` 和 `for...in` 都是 JavaScript 中用于遍历对象的循环语句,但它们用途略有不同。
`for...of` 是 ES6 新增的特性,主要用于遍历可迭代对象(iterables),如数组、字符串、Set、Map 等。该循环语句将依次取出可迭代对象中的每个元素,并且可以直接访问每个元素的值。例如:
```
const arr = [1, 2, 3];
for (const val of arr) {
console.log(val);
}
// 输出:1 2 3
```
`for...in` 是用于遍历对象属性的循环语句,它枚举对象的可枚举属性,包括从原型链继承的属性。由于该语句遍历的是对象的属性名,而不是属性值,因此可以在循环体内使用属性名访问相应的属性值。例如:
```
const obj = { a: 1, b: 2, c: 3 };
for (const prop in obj) {
console.log(prop + ': ' + obj[prop]);
}
// 输出:a: 1 b: 2 c: 3
```
需要注意的是,`for...in` 循环不保证按照属性添加的顺序遍历对象的属性,因此不适用于需要按照指定顺序遍历属性的情况。而 `for...of` 循环则可以保证元素的遍历顺序与它们在可迭代对象中的顺序一致。
介绍一下css选择器
在 CSS 中,选择器(selector)用于指定要样式化的 HTML 元素。CSS 选择器可以根据不同的唯一标识符(id)、类名(class)、标签名、 属性、伪类等,选取指定的元素或元素组。下面介绍一些常见的 CSS 选择器:
1. 标签选择器(tag selector):用于选择 HTML 中的标签元素。
```
p { color: red; }
```
2. 类选择器(class selector):以句点(.)开头,用于选择带有特定 class 的元素。
```
.my-class { color: blue; }
```
3. ID 选择器(id selector):以井号(#)开头,用于选择具有特定 ID 的元素。
```
#header { background-color: gray; }
```
4. 子元素选择器(child selector):以大于号(>)连接父元素和子元素,只会匹配作为该父元素直接子元素的元素。
```
ul > li { font-size: larger; }
```
5. 后代元素选择器(descendant selector):以空格分隔父元素和子孙元素,会匹配该父元素的所有后代元素。
```
div .my-class { background-color: yellow; }
```
6. 通配符选择器(wildcard selector):用星号(*)表示,匹配 HTML 中所有元素。
```
* { margin: 0; padding: 0; }
```
7. 属性选择器(attribute selector):用来选择具有特定属性(无论属性值如何)的元素。可以使用等号(=)表示精确匹配,用波浪线(~),表示属性值与指定值相当,则选择该元素。还可以使用竖线(|)选择某个属性值以指定值开头的元素。
```
input[type="text"] { border: 1px solid #ddd; }
a[href~="example.com"] { color: red; }
a[href|="https"] { font-weight: bold; }
```
8. 伪类选择器(pseudo-class selector):选择具有特定状态的元素,如hover、active等。
```
a:hover { text-decoration: underline; }
input:focus { outline: none; }
```
这些CSS选择器的使用可以通过组合来更精确地匹配元素,可以提高 CSS 的灵活性和可重用性。
什么是事件委托
事件委托是一种利用事件冒泡机制,避免在DOM树中的每个子元素上都注册事件处理程序的方式。 事件委托的特点是将事件处理程序绑定于父元素,而不是子元素,当事件触发时,再通过事件对象的目标元素和事件类型,判断是否需要调用处理程序。 相比在每个子元素上都绑定事件,利用事件委托可以节省内存,提高性能,避免因动态添加元素而需要重复绑定事件的问题,也可以使代码更简洁,更易于维护。
例如,我们可以在`ul`元素上绑定一个`click`事件,而不是在`li`元素上绑定`click`事件。这样,当用户点击`li`元素时,该事件将冒泡到父元素`ul`上,并被`ul`的事件处理程序捕获处理。然后根据事件对象的目标元素判断是否需要调用处理程序。
以下是使用事件委托的示例代码:
```
```
在上述代码中,我们通过使用 `addEventListener` 方法将 `click` 事件绑定到 `ul` 元素上。当用户单击 `li` 元素时,事件被触发并冒泡到 `ul` 元素上。通过判断 `target` 属性和 `nodeName` 属性,我们可以确定单击的是哪个子元素,并且仅在该子元素是 `li` 元素(而不是其他元素)时才调用事件处理程序。
需要注意的是,使用事件委托时应当注意父元素的选择和判断目标元素是否为需要处理的子元素,以免事件处理程序被误触发或漏掉需要处理的子元素。
隐藏元素的方法有哪些?有什么区别
在Web开发中,隐藏元素有多种方法,常用的方法包括以下几种:
1. display: none;:将元素隐藏并从文档流中移除。元素在页面中不可见,同时其占据的空间也不会被保留。对应的CSS属性是`display`,可以通过javascript设置或更改。
2. visibility: hidden;:将元素隐藏但不从文档流中移除。元素在页面上不可见,但其占据的空间仍被保留。对应的CSS属性是`visibility`,可以通过javascript设置或更改。
3. opacity: 0;:将元素透明度设置为0。元素在页面上依然占有空间,但是内容不可见。对应的CSS属性是`opacity`,可以通过javascript设置或更改。
4. width: 0; height: 0;:将元素的宽度和高度都设置为0。元素在页面上占据位置但不可见。对应的CSS属性是`width`和`height`,可以通过javascript设置或更改。
这些隐藏元素的方法之间有一些区别:
1. `display: none;`:隐藏后,元素不占据空间,不会影响页面布局;如果需要再次显示,需要重新设置为`display: block;`或其他恰当的值。
2. `visibility: hidden;`:隐藏后,元素占据空间,但不可见,影响页面布局;如果需要再次显示,需要重新设置为`visibility: visible;`。
3. `opacity: 0;`:隐藏后,元素占据空间,但内容不可见,影响页面布局;如果需要再次显示,需要重新设置为`opacity: 1;`。
4. `width: 0; height: 0;`:隐藏后,元素占据空间,但不可见,影响页面布局;如果需要再次显示,需要重新设置其宽度和高度。
需要根据实际需求和应用场景选择适合的隐藏元素的方法,以达到最佳的 UI 和性能效果。
对虚拟DOM的理解
虚拟DOM(Virtual DOM)是React.js提出的一种基于内存中的树形结构的DOM解决方案。它是一个抽象的概念,用一个轻量级的JavaScript对象来描述真实的DOM节点。在React中,当状态发生改变时,React会重新计算整个虚拟DOM树,并将新的虚拟DOM与旧的虚拟DOM进行比较,找出差异(Diff算法),然后计算出如何高效地更新真实的DOM。相比于直接操作DOM,虚拟DOM的优点主要体现在以下几个方面:
1. 避免直接操作真实的DOM,降低DOM操作的次数,提高性能。
2. 在计算差异时,React会使用高效的Diff算法来尽量减少DOM操作,从而更快地更新DOM。
3. 提高了开发效率,因为通过一个虚拟DOM树,可以集中处理DOM的更新,减少代码的耦合度和难度。
需要注意的是,虚拟DOM并不是解决性能问题的唯一方法,某些情况下会带来不必要的性能开销。但是,在大多数情况下,使用虚拟DOM可以带来良好的性能和开发体验。虚拟DOM也是React的核心特性之一,它封装了React对DOM控制的方式,让开发者专注于组件的构建、数据的维护,可以更好地规划整个应用程序的逻辑。
深拷贝和浅拷贝的理解。用转换josn完成深拷贝有什么弊端
深拷贝和浅拷贝都是指将一个对象复制到另一个变量中,以便独立存放和修改,但它们的机制不同:
1. 浅拷贝:只复制对象的第一层属性,而不是递归地复制整个对象。这意味着新的对象和原始对象是独立的,但是对于对象的嵌套属性,会与原始对象共享同一个内存地址,对嵌套属性的修改可能会影响原始对象中的相应属性。常见的浅拷贝方式包括对象解构、Object.assign等。
2. 深拷贝:递归地复制整个对象,而不是只复制第一层。这意味着新的对象和原始对象彻底独立,不会相互影响。通常实现深拷贝的方式有很多,包括JSON.parse(JSON.stringify(obj))、手动实现深拷贝函数等。
使用JSON.parse(JSON.stringify(obj))实现深拷贝有以下几个弊端:
1. 不能复制函数和特定的对象属性。比如日期对象,在序列化和反序列化后,可能会变成字符串,失去特殊的日期时间属性。
2. 不能很好地处理循环引用的情况。如果需要复制的对象中包含循环引用的对象,会导致序列化失败。
3. 序列化和反序列化会导致类型信息丢失。不同于JS中的基本类型和引用类型,JSON数据只能表示一种对象类型,所有的对象在经过序列化和反序列化后,会变成Object类型,失去特定的子类型信息。
由于这些原因,使用JSON.parse(JSON.stringify(obj))实现深拷贝不适用于所有情况,如果需要完整、准确地复制对象,需要使用一些其他的方法,比如手动实现深拷贝函数。
vue 父子组件传递的方法
在 Vue 中,可以使用 props 和 $emit 两个方法来实现父组件和子组件之间的数据传递。
1. 父组件向子组件传递数据:使用 props
在父组件中使用子组件时,可以通过 props 将数据传递给子组件。具体做法是在子组件元素中添加属性并将数据传递给子组件,例如:
```
```
在子组件中,可以使用 props 接收来自父组件的数据,例如:
``` {{ prop1 }}
```
2. 子组件向父组件传递数据:使用 $emit
在子组件中,可以使用 $emit 发送一个自定义事件,该事件会被父组件监听并触发对应的处理函数。具体做法是在子组件 methods 中使用 $emit 方法触发一个事件,例如:
```
```
在父组件中,可以使用 v-on 指令监听子组件触发的事件,并在对应的处理函数中接收来自子组件的数据,例如:
```
```
如何实现图片懒加载
图片懒加载是一种提高页面性能的技术,可以减少网页的加载时间和流量消耗。其核心思想是将图片的加载延迟到图片进入视口时再进行加载,而不是在页面加载时就加载所有图片。
下面是一个简单的实现图片懒加载的方法:
1. 在页面中添加占位符图片
首先,在页面中添加一个占位符图片,用于在图片未加载时占据图片所需的空间。该占位符图片应与需要懒加载的图片大小相同,并且可以根据页面设计自定义样式(如背景色等)。
```
```
2. 使用 Intersection Observer API 监测图片是否进入视口
使用 Intersection Observer API 可以轻松地检测元素是否进入视口。在监听到元素进入视口时,就可以触发图片的加载。
```
function handleIntersection(entries, observer) {
entries.forEach(entry => {
if (entry.isIntersecting) {
const lazyImage = entry.target;
lazyImage.src = lazyImage.dataset.src;
lazyImage.classList.remove('lazy');
observer.unobserve(lazyImage);
}
});
}
const observer = new IntersectionObserver(handleIntersection);
const lazyImages = document.querySelectorAll('.lazy');
lazyImages.forEach(lazyImage => observer.observe(lazyImage));
```
在上面的示例中,监听到每个有 'lazy' 类的 img 元素,当该元素进入视口时,修改其 src 属性为 data-src(即真正需要加载的图片),并移除 'lazy' 类以便区分已经被加载的图片。
3. 样式处理
为了实现懒加载的效果,我们需要使用一个 'lazy' 类来标记需要懒加载的图片。同时,占位符图片应该默认显示,需要设置其样式,让其撑满它的容器并避免图片尺寸变化造成页面的跳动。
```
.lazy {
opacity: 0;
transition: opacity 0.3s ease-in;
}
.lazy-loaded {
opacity: 1;
}
img {
display: block;
max-width: 100%;
height: auto;
}
.lazy {
height: 200px;
background: #f5f5f5;
position: relative;
}
.lazy:after {
content: "";
position: absolute;
top: 0;
right: 0;
bottom: 0;
left: 0;
}
```
通过上述步骤,就可以实现图片懒加载。需要注意的一点是,由于 Intersection Observer API 不支持老版本浏览器,如果需要支持老版本浏览器,我们需要使用一些 polyfill 来填充完善这一 API。