css部分
1、简单说明一下盒模型
CSS盒模型定义了盒的每个部分包含: margin, border, padding, content 。根据盒子大小的计算方式不同盒模型分成了两种,标准盒模型和怪异盒模型。 标准模型,给盒设置 `width` 和 `height`,实际设置的是 content box。`padding` 和 `border `再加上设置的宽高一起决定整个盒子的大小。 怪异盒模型,给盒设置 `width` 和 `height`,包含了`padding`和`border `,设置的 `width` 和 `height`就是盒子实际的大小 默认情况下,盒模型都是标准盒模型 设置标准盒模型:`box-sizing:content-box` 设置怪异盒模型:`box-sizing:border-box`
2、什么是浮动
浮动的作用,设置浮动的图片,可以实现文字环绕图片,设置了浮动的块级元素可以排列在同一行,设置了浮动的行内元素可以设置宽高,同时可以按照浮动设置的方向对齐排列盒子。 设置浮动元素的特点: -设置了浮动,该元素脱标。元素不占位置 -浮动可以进行模式转换(行内块元素) 浮动造成的影响,使盒子脱离文档流,如果父级盒子没有设置高度,需要被子盒子撑开,那么这时候父级盒子的高度就塌陷了,同时也会造成父级盒子后面的兄弟盒子布局受到影响。如果浮动元素后面还有其他兄弟元素,其他兄弟元素的布局也会受到影响。 清除浮动的方法: -伪元素清除浮动:给浮动元素父级增加 .clearfix::after { content: ''; display: table; clear: both; } /*兼容IE低版本 */ .clearfix { *zoom: 1; } overflow:hidden`:给浮动元素父级增加`overflow:hidden`属性 额外标签法:给浮动元素父级增加标签
三种清除浮动的特点和影响: -伪元素清除浮动:不会新增标签,不会有其他影响,是当下清除浮动最流行的方法 ;`overflow:hidden`:不会新增标签,但是如果父级元素有定位元素超出父级,超出部分会隐藏,在不涉及父级元素有超出内容的情况,overflow:hidden比较常用,毕竟写法方便简洁;标签插入法:清除浮动的语法加在新增标签上,由于新增标签会造成不必要的渲染,所以这种方法目前不建议使用
3、说一说样式优先级的规则是什么?
简单说明就是: `!important`、行内样式、嵌入样式、外链样式、id选择器、类选择器、标签选择器、复合选择器、通配符、继承样式
CSS样式的优先级应该分成四大类 -第一类`!important`,无论引入方式是什么,选择器是什么,它的优先级都是最高的。 -第二类引入方式,行内样式的优先级要高于嵌入和外链,嵌入和外链如果使用的选择器相同就看他们在页面中插入的顺序,在后面插入的会覆盖前面的。 -第三类选择器,选择器优先级:id选择器>(类选择器 | 伪类选择器 | 属性选择器 )> (后代选择器 | 伪元素选择器 )> (子选择器 | 相邻选择器) > 通配符选择器 。 -第四类继承样式,是所有样式中优先级比较低的。 -第五类浏览器默认样式优先级最低。
其中在使用!important要谨慎 - 一定要优先考虑使用样式规则的优先级来解决问题而不是 `!important` - 只有在需要覆盖全站或外部 CSS 的特定页面中使用 `!important` - 永远不要在你的插件中使用 `!important` - 永远不要在全站范围的 CSS 代码中使用 `!important` 优先级的比较指的是相同的样式属性,不同样式属性优先级比较失效,比如:在设置`max-width`时注意,已经给元素的`max-width`设置了`!important`但是还不生效,很有可能就是被width覆盖了 举例:`div`最终的宽度还是`200px` div { max-width: 400px !important; height: 200px;background-color: tomato; width: 200px; }
4、说一说CSS尺寸设置的单位的区别与用法
主要有这几类:px、rem、em、vw、vh 、rpx
- px:pixel像素的缩写,绝对长度单位,它的大小取决于屏幕的分辨率,是开发网页中常常使用的单位,常用于固定宽度的元素。使用方法:直接添加数值,如font-size: 12px;
- em:相对长度单位,在 `font-size` 中使用是相对于父元素的字体大小,在其他属性中使用是相对于自身的字体大小,如 width。如当前元素的字体尺寸未设置,由于字体大小可继承的原因,可逐级向上查找,最终找不到则相对于浏览器默认字体大小。
- rem:相对长度单位,相对于根元素的字体大小,根元素字体大小未设置,使用浏览器默认字体大小。
- vw:相对长度单位,相对于视窗宽度的1%。
- vh:相对长度单位,相对于视窗高度的1%。
- rpx:rpx是微信小程序中特有的单位,用于解决屏幕自适应问题。它可以根据屏幕宽度进行自适应,将屏幕宽度分为750份,每份为1rpx。这意味着在不同尺寸的屏幕上,rpx单位可以自动适配,从而实现响应式设计。
rem应用:在移动端网页开发中,页面要做成响应式的,可使用rem配合媒体查询或者flexible.js实现。原理是通过媒体查询或者flexible.js,能够在屏幕尺寸发生改变时,重置html根元素的字体大小,页面中的元素都是使用rem为单位设置的尺寸,因此只要改变根元素字体大小,页面中的其他元素的尺寸就自动跟着修改 vw应用:由于vw被更多浏览器兼容之后,在做移动端响应式页面时,通常使用vw配合rem。原理是使用vw设置根元素html字体的大小,当窗口大小发生改变,vw代表的尺寸随着修改,无需加入媒体查询和flexible.js,页面中的其他元素仍使用rem为单位,就可实现响应式。
5、BFC是什么及作用与用法
BFC(Block Formatting Context)块级格式化上下文,是Web页面一块独立的渲染区域,内部元素的渲染不会影响边界以外的元素。 BFC布局规则 -内部盒子会在垂直方向,一个接一个地放置。
Box垂直方向的距离由margin决定。属于同一个BFC的两个相邻Box的margin会发生重叠。 -每个盒子(块盒与行盒)的margin box的左边,与包含块border box的左边相接触(对于从左往右的格式化,否则相反)。即使存在浮动也是如此。
BFC的区域不会与float box重叠。 -BFC就是页面上的一个隔离的独立容器,容器里面的子元素不会影响到外面的元素。反之也如此。
计算BFC的高度时,浮动元素也参与计算。 BFC形成的条件 -`float `设置成 `left `或 `right` -`position `是`absolute`或者`fixed` -`overflow `不是`visible`,为 `auto`、`scroll`、`hidden` -`display`是`flex`或者`inline-block` 等 BFC解决能的问题:清除浮动 加分回答 BFC的方式都能清除浮动,但是常使用的清除浮动的BFC方式只有`overflow:hidden`,原因是使用float或者position方式清除浮动,虽然父级盒子内部浮动被清除了,但是父级本身又脱离文档流了,会对父级后面的兄弟盒子的布局造成影响。如果设置父级为`display:flex`,内部的浮动就会失效。所以通常只是用`overflow: hidden`清除浮动。 IFC(Inline formatting contexts):内联格式上下文。IFC的高度由其包含行内元素中最高的实际高度计算而来(不受到竖直方向的padding/margin影响),IFC中的line box一般左右都贴紧整个IFC,但是会因为float元素而扰乱。
GFC(GrideLayout formatting contexts):网格布局格式化上下文。当为一个元素设置display值为grid的时候,此元素将会获得一个独立的渲染区域。 FFC(Flex formatting contexts):自适应格式上下文。display值为flex或者inline-flex的元素将会生成自适应容器。
6、说几个未知宽高元素水平垂直居中方法
- 设置元素相对父级定位`position:absolute;left:50%;right:50%`,让自身平移自身高度50% `transform: translate(-50%,-50%);`,这种方式兼容性好,被广泛使用的一种方式
- 设置元素的父级为弹性盒子`display:flex`,设置父级和盒子内部子元素水平垂直都居中`justify-content:center; align-items:center` ,这种方式代码简洁,但是兼容性ie 11以上支持,由于目前ie版本都已经很高,很多网站现在也使用这种方式实现水平垂直居中
- 设置元素的父级为网格元素`display: grid`,设置父级和盒子内部子元素水平垂直都居中`justify-content:center; align-items:center` ,这种方式代码简介,但是兼容性ie 10以上支持
- 设置元素的父级为表格元素`display: table-cell`,其内部元素水平垂直都居中`text-align: center;vertical-align: middle;` ,设置子元素为行内块`display: inline-block; `,这种方式兼容性较好
7、说一说三栏布局的实现方案
三栏布局,要求左右两边盒子宽度固定,中间盒子宽度自适应,盒子的高度都是随内容撑高的,一般都是中间盒子内容较多,为了保证页面渲染快,在写结构的时候,需要把中间盒子放在左右盒子的前面。
实现三栏布局的方法通常是圣杯布局和双飞翼布局:
- 圣杯布局的实现方案:三个元素放在同一个父级元素中,代表中间盒子的元素放在最前面,父级盒子设置左右`padding`,三个盒子全部浮动,设置中间盒子宽度100%,左右盒子设置固定宽度,设置左边盒子左边距-100%同时相对自身定位,右边平移自身宽度,右边盒子设置右边距-自身宽度,最后设置父级盒子清除浮动,否则父级盒子的高度无法被撑开
- 双飞翼布局的实现方案:三个盒子对应三个元素,其中中间盒子套了两层,中间盒子内部盒子设置`margin`,三个盒子全部浮动,设置中间盒子宽度100%,左右盒子设置固定宽度,设置左边盒子左边距-100%,右边盒子设置右边距-自身宽度,最后设置父级盒子清除浮动,否则父级盒子的高度无法被撑开
圣杯布局: 优点:不需要添加dom节点; 缺点:正常情况下是没有问题的,但是特殊情况下就会暴露此方案的弊端,当middle部分的宽小于left部分时就会发生布局混乱。
双飞翼布局: 优点:不会像圣杯布局那样变形,CSS样式代码更简洁; 缺点:多加了一层dom节点
JS部分
1、说一说JS数据类型有哪些,区别是什么?
JS数据类型分为两类:一类是基本数据类型,也叫简单数据类型,包含7种类型,分别是Number 、String、Boolean、BigInt、Symbol、Null、Undefined。另一类是引用数据类型也叫复杂数据类型,通常用Object代表,普通对象,数组,正则,日期,Math数学函数都属于Object。
Symbol是ES6新出的一种数据类型,这种数据类型的特点就是没有重复的数据,可以作为object的key。
BigInt也是ES6新出的一种数据类型,这种数据类型的特点就是数据涵盖的范围大,能够解决超出普通数据类型范围报错的问题。
数据分成两大类的本质区别:基本数据类型和引用数据类型它们在内存中的存储方式不同。
- 基本数据类型是直接存储在栈中的简单数据段,占据空间小,属于被频繁使用的数据。
- 引用数据类型是存储在堆内存中,占据空间大。引用数据类型在栈中存储了指针,该指针指向堆中该实体的起始地址,当解释器寻找引用值时,会检索其在栈中的地址,取得地址后从堆中获得实体。
2、 说一说null 和 undefined 的区别,如何让一个属性变为null
undefind 是全局对象的一个属性,当一个变量没有被赋值或者一个函数没有返回值或者某个对象不存在某个属性却去访问或者函数定义了形参但没有传递实参,这时候都是undefined。undefined通过typeof判断类型是'undefined'。undefined == undefined undefined === undefined 。 null代表对象的值未设置,相当于一个对象没有设置指针地址就是null。null通过typeof判断类型是'object'。null === null null == null null == undefined null !== undefined undefined 表示一个变量初始状态值,而 null 则表示一个变量被人为的设置为空对象,而不是原始状态。在实际使用过程中,不需要对一个变量显式的赋值 undefined,当需要释放一个对象时,直接赋值为 null 即可。 让一个变量为null,直接给该变量赋值为null即可。
null 其实属于自己的类型 Null,而不属于Object类型,typeof 之所以会判定为 Object 类型,是因为JavaScript 数据类型在底层都是以二进制的形式表示的,二进制的前三位为 0 会被 typeof 判断为对象类型,而 null 的二进制位恰好都是 0 ,因此,null 被误判断为 Object 类型。 对象被赋值了null 以后,对象对应的堆内存中的值就是游离状态了,GC 会择机回收该值并释放内存。因此,需要释放某个对象,就将变量设置为 null,即表示该对象已经被清空,目前无效状态。
3、请讲下var、let和const的差异?
- var声明变量存在变量提升,let和const不存在变量提升
- let、const都是块级局部变量
- 同一作用域下let和const不能声明同名变量,而var可以
var | let | const | |
作用域 | var 声明的作用域是全局的或函数/局部的。当 var 变量在函数外部声明时,作用域是全局的。这意味着在函数体外用 var 声明的任何变量都可以在整个窗口中使用。var 在函数中声明时,它的作用域是在函数体内。这意味着它只能在该函数中被访问。 | let 是块作用域。块是由 {} 界定的代码块。一个块存在于花括号中。花括号内的任何内容都是一个块。因此,在带有 let 的块中声明的变量只能在该块中使用。 | const 声明是块作用域。与 let 声明一样,const 声明只能在它们声明的块内访问。 |
提升(hoisting) | 提升到其作用域的顶部,并使用 undefined 值进行初始化。 | 就像 var 一样,let 声明被提升到顶部。但与初始化为 undefined 的 var 不同,let 关键字未初始化。所以如果你在声明之前尝试使用 let 变量,你会得到一个 Reference Error。 | 就像 let 一样,const 声明被提升到顶部但没有被初始化。 |
可变性 | 可更新、可在同作用域重复声明。 | 可更新、不可在同作用域重复声明。 | 不可更新、不可在同作用域重复声明。 |
4、== 和 === 操作符的差别?
==(松散相等操作符):此操作符执行类型强制转换,这意味着它在进行比较之前将操作数转换为相同的类型。它检查值是否相等,而不考虑它们的数据类型。例如,1 == '1' 将返回 true,因为 JavaScript 在比较之前将字符串 '1' 转换为数字。
===(严格相等操作符):此操作符执行严格比较而不进行类型强制转换。它检查值及其数据类型是否相等。例如,1 === '1' 将返回 false,因为数据类型不同(数字和字符串)。
总之,== 在类型强制转换后检查相等性,而 === 检查严格相等性,同时考虑值及其数据类型。
5、介绍下...操作符?
剩余操作符(rest operator),由三个点(...)表示,用于函数参数中以将可变数量的参数收集到数组中。它允许你将任意数量的参数传递给函数,而无需显式地将它们定义为命名参数。
展开运算符,也称为三个点(...),用于将数组或对象的元素展开到另一个数组或对象中。它使你能够轻松克隆数组、连接数组和合并对象。
6、防抖和节流的区别
防抖(Debounce)是指在事件被连续频繁触发时,只有最后一次触发的事件会被执行,如果在指定的时间间隔内事件再次被触发,则重新计算时间间隔。如搜索框自动完成、窗口大小调整(resize)、射击游戏中的mousedown或keydown事件。
节流(Throttle)是指在一定时间间隔内,无论事件触发多少次,都只执行一次处理函数。如页面滚动加载、ue + Axios全局接口防抖、节流封装。
前者只执行性最后一次,后者只(至多)执行一次。要理解,背多少遍都没有用。
7、什么是跨域?
当一个请求url的协议、域名、端口三者之间任意一个与当前页面url不同即为跨域
跨域解决方法:
- jsonp方式
- 代理服务器的方式
- 服务端允许跨域访问(CORS)
取消浏览器的跨域限制()
- 本地开发,可通过设置谷歌浏览器插件
8、深拷贝和浅拷贝
深拷贝(Deep Copy
)和浅拷贝(Shallow Copy
)都是在编程中处理对象或数据
的复制操作。
浅拷贝创建一个新对象或数据结构,其中包含原始对象的引用。换句话说,新对象与原始对象共享相同的内存地址,因此对其中一个对象进行更改会影响到另一个对象。浅拷贝仅复制对象的第一层结构,而不会递归复制嵌套的对象或数据
。
深拷贝创建一个全新的对象或数据结构,其中包含原始对象完全独立的副本。新对象与原始对象具有不同的内存地址,因此彼此之间的更改是相互独立的
。深拷贝会递归复制所有嵌套的对象或数据,确保整个对象及其子对象都被复制
。
区别如下:
- 内存引用:浅拷贝复制的是对象的引用,深拷贝复制的是对象的值。
- 对象的变化:对浅拷贝的修改会影响原始对象,因为它们共享相同的引用。而对深拷贝的修改不会影响原始对象。
- 嵌套对象或数据的复制:浅拷贝仅复制第一层对象或数据,嵌套的对象或数据仍然是共享的。深拷贝通过递归复制嵌套的对象或数据,确保每个对象都有独立的副本。
在实际编程中,选择使用深拷贝还是浅拷贝取决于具体的需求。如果需要对对象进行修改而不影响原始对象,或者处理嵌套的对象结构,那么深拷贝是更合适的选择。而对于简单的数据结构或者只需要引用原始对象的情况,浅拷贝可能更加高效和节省内存。
9、如何进行浅拷贝?请列举一些浅拷贝的方法或技巧。
- 扩展运算符(Spread Operator):使用扩展运算符可以创建一个对象或数组的浅拷贝副本。
const originalObj = { name: "John", age: 30 };
const shallowCopyObj = { ...originalObj };
- Object.assign() 方法:使用 Object.assign() 可以将一个或多个源对象的属性复制到目标对象,并返回目标对象的浅拷贝。
const originalObj = { name: "John", age: 30 };
const shallowCopyObj = Object.assign({}, originalObj);
- Array.slice() 方法:对于数组,可以使用 Array.slice() 方法来创建一个浅拷贝的副本。
const originalArr = [1, 2, 3, 4, 5];
const shallowCopyArr = originalArr.slice();
- Array.concat() 方法:使用 Array.concat() 方法也可以在数组中创建一个浅拷贝。
const originalArr = [1, 2, 3, 4, 5];
const shallowCopyArr = originalArr.concat();
需要注意的是,这些方法只复制了对象或数组的第一层结构,如果存在嵌套对象或数组,则仍然是浅拷贝,即嵌套的对象或数组将被共享。如果需要进行深层复制,需要使用深拷贝方法,如 JSON.parse(JSON.stringify())
或第三方库(如 lodash
的 cloneDeep()
方法)。
浅拷贝的应用场景:
- 复制简单的数据结构
- 传递引用而不是副本
- 缓存数据
10、如何进行深拷贝?请列举一些深拷贝的方法或技巧。
- JSON.parse(JSON.stringify()):使用 JSON.stringify() 将对象转换为 JSON 字符串,再使用 JSON.parse() 将字符串解析回对象。这种方法能够实现一层深拷贝,适用于没有循环引用的简单对象和数组。但是,它无法处理包含函数、RegExp、Date 等特殊类型的对象。
const originalObj = { name: 'John', age: 30 };
const deepCopyObj = JSON.parse(JSON.stringify(originalObj));
- 第三方库(如 lodash 的 cloneDeep()):许多 JavaScript 的第三方库提供了深拷贝的方法,其中最常见的是 lodash 库的 cloneDeep() 方法。该方法能够处理循环引用和特殊类型的对象。
const originalObj = { name: 'John', age: 30 };
需要注意的是,深拷贝可能会带来性能上的损耗,特别是对于大型和复杂的对象。因此,在进行深拷贝时,应根据实际需求和性能考虑来选择合适的方法。
如果需要进行自定义的深拷贝操作,可以编写递归函数来遍历对象或数组的属性,并对每个属性进行深层复制。这需要考虑到复杂的情况,如嵌套对象、循环引用等,并进行适当的处理。