地情网站建设,申请一个域名可以建设一个网站吗,如何做商业网站,销售管理app软件HTMLCSS部分
一.说一说HTML的语义化
在我看来#xff0c;它的语义化其实是为了便于机器来看的#xff0c;当然#xff0c;程序员在使用语义化标签时也可以使得代码更加易读#xff0c;对于用户来说#xff0c;这样有利于构建良好的网页结构#xff0c;可以在优化用户体…HTMLCSS部分
一.说一说HTML的语义化
在我看来它的语义化其实是为了便于机器来看的当然程序员在使用语义化标签时也可以使得代码更加易读对于用户来说这样有利于构建良好的网页结构可以在优化用户体验对于机器而言语义化的使用方便了搜索引擎的一些优化方便机器识别便于爬取利用。
一般常用的标签有 header,footernav,main,article等 二.说一说盒模型
我对于盒子模型的认知里
盒模型的几个部分由外到内外边距margin边框border内边距padding内容content包括长和宽
所以根据盒子大小计算的不同方式把盒子模型分为了两种一种是标准盒一种是怪异盒。
标准盒在设置width和height时只是修改内容content的大小盒子的大小还要加上边框border和内边距padding
怪异盒在设置width和height时设置的是整个盒子的大小它包含了边框border内边距padding内容content区域所以显示的时候内容区域看起来会被压缩
一般我们使用的是W3C标准盒模型content-box
也可以通过设置box-sizing属性决定盒模型 box-sizing:border-box代表怪异盒模型 box-sizing:content-box代表标准盒模型 三.说一下浮动
浮动就是给块级元素添加一个属性 floatleft/right 使用浮动可以实现文字的环绕图片
浮动的特点使得元素脱离文档流容易造成塌陷影响其他元素的排列
所以在使用浮动时我们还要解决可能出现的塌陷问题
塌陷问题就是指浮动的元素超出了父元素的宽高使得父元素塌陷
所以解决方法如下 1.给父元素设置 overflowhidden超出部分隐藏 2.给父元素添加高度。使其能包裹住浮动元素 3.在浮动元素的最后添加新的div标签使用clearleft/right/both属性清除浮动 4.使用伪元素::after { content: ; display: block; clear: both; } 还可以说使用flex布局来解决浮动带来的问题然后话题就跳转到flex
四.说一说样式优先级的规则是什么
css的样式优先级 !important 内联样式 ID 选择器#id{} 类选择器.class{} 属性选择器a[hrefsegmentfault.com]{} 伪类选择器 :hover{} 标签选择器span{} 伪元素选择器 ::before{} 后代选择器.father .child{} 子选择器.father .child{} 相邻选择器 .bro1 .bro2{} 通配符选择器*{} 五.说一说CSS尺寸设置的单位
分为以下几类 px:绝对大小取决于屏幕的分辨率 %相对父元素的大小所占据的百分比 rem相对于根元素的大小即 html 元素的字体大小。 em相对长度单位在 font-size 中使用是相对于父元素的字体大小在其他属性中使用是相对于自身的字体大小如 width。如当前元素的字体尺寸未设置由于字体大小可继承的原因可逐级向上查找最终找不到则相对于浏览器默认字体大小 vh,vw相对于屏幕视口大小 六.说一说BFC
定义
块级的格式化上下文独立的渲染区域不会影响边界以外的元素布局
产生BFC 1.使用 float属性不为none 2.position为absolute或fixed 3、display为inline-block、table-cell、table-caption、flex、inline-flex 4、overflow不为visible 一些情况下使用border也可以产生BFC 例如: 把父元素的border和overflow都去除后产生了外边距塌陷即浮动元素与另一个元素的上外边距产生了合并都使用了大的那个上外边距此时父元素添加任意一个属性都可以产生一个BFC解决外边距的塌陷。 !DOCTYPE html
html langen
head
meta charsetUTF-8
meta nameviewport contentwidthdevice-width, initial-scale1.0
titleBFC Example/title
style.container {border: 1px solid black;overflow: auto; /* 触发 BFC */}.float-left {float: left;width: 100px;height: 100px;background-color: red;margin-right: 20px;}.child {background-color: blue;width: 100px;height: 100px;margin-top: 20px;}
/style
/head
body
div classcontainerdiv classfloat-left/divdiv classchild/div
/div
/body
/htmlBFC的一些特性 1、在BFC中盒子从顶部开始垂直地一个接一个排列 2、盒子垂直方向的距离由margin决定。同一个BFC的两个相邻盒子margin会重叠 3、BFC中margin-left会触碰到border-left对于从左至右的方式反之 4、BFC区域不会与浮动的盒子产生交集而是紧贴边缘浮动 5、计算BFC高度时自然会检测浮动的盒子高度 目前来说第五点算是比较直观的可以体现的其他几点的话简单场景是否使用BFC样式体现是几乎没有区别的 使用 BFC 的好处体现在更复杂的布局和样式设计中其中一些情况下会更明显 阻止外边距折叠BFC 可以防止相邻块级元素之间外边距的折叠确保布局更加可控和可预测。 清除浮动BFC 可以包含浮动元素使得父元素可以自适应浮动元素的高度防止父元素坍塌。 自适应布局BFC 可以使得元素在布局过程中自适应父元素的大小并防止元素溢出父元素的边界。 避免文字环绕使用 BFC 可以防止文本环绕浮动元素使得文本不会被浮动元素覆盖。 BFC解决问题 1、清除内部浮动父元素设置为BFC可以清除子元素的浮动最常用overflow:hiddenIE6需加上*zoom:1计算BFC高度时会检测浮动子盒子高度 2、解决外边距合并问题 3、右侧盒子自适应BFC区域不会与浮动盒子产生交集而是紧贴浮动边缘 七.说几个未知宽高元素水平垂直居中方法 1.使用displayflex布局 justify-content: center;
align-items: center; 2.设置元素相对父级定位 position:absolute;
left:50%;
right:50% 3.让自身平移自身高度50% 这种方式兼容性好被广泛使用的一种方式 transform: translate(-50%,-50%); 4.使用displaygrid布局 justify-content:center;
align-items:center 5.使用display: table-cell, 设置元素的父级为表格元素
display: table-cell;
text-align: center;
vertical-align: middle;设置子元素为行内块
display: inline-block; 八.说一说三栏布局的实现方案
粗略方案 使用浮动Float 左右栏使用float: left;和float: right;中间内容区域使用margin来调整位置。优点兼容性好适用于旧版浏览器。缺点需要清除浮动以避免父容器高度塌陷可能需要额外的清除浮动的样式。 使用定位Positioning 左右栏使用position: absolute;中间内容区域使用margin来调整位置。优点灵活性高可以轻松实现各种复杂布局。缺点对父容器定位可能造成影响需要谨慎使用。 使用Flexbox布局 将父容器设置为display: flex;并且使用flex-grow来调整左右栏和中间内容的比例。优点简单易用支持响应式布局适应性强。缺点对于一些旧版浏览器的兼容性不好。 使用Grid布局 使用CSS Grid布局将父容器设置为网格布局然后通过设置网格列来实现三栏布局。优点灵活性强对于复杂的布局可以更容易实现。缺点对于一些旧版浏览器的兼容性不好。 使用表格布局 使用HTML表格标签table来实现三栏布局左右栏放在表格的两侧中间内容放在表格的中间。优点兼容性好简单易懂。缺点不推荐使用表格来布局不利于语义化不够灵活。 在选择布局方案时可以根据项目需求、兼容性要求和开发者的技术栈选择合适的方案。Flexbox和Grid布局是现代Web开发中推荐的布局方式它们提供了更多的布局控制和灵活性。 1.使用浮动 一般情况的等比三栏布局都设置 floatleft注意最后清除浮动 双飞翼 它的主要思想是将左右两个侧边栏用负外边距进行定位使它们能够脱离文档流而主要内容区域则通过左右内边距来避开侧边栏的位置。这样做的好处是使得主要内容区域可以在文档流中优先渲染而侧边栏则在视觉上紧跟在主要内容后面。双飞翼布局的关键在于使用额外的空元素作为浮动容器通过负外边距来实现定位。 圣杯 与双飞翼布局类似圣杯布局也使用了负外边距和浮动来实现。不同之处在于圣杯布局采用了更多的 CSS 技巧来实现侧边栏的自适应高度避免了双飞翼布局中使用空元素的方式。这种布局模型通常会使用相对定位和负边距来为侧边栏留出空间并使用相对定位将主要内容区域拉回来。 总结 圣杯流程中间元素放最前宽度100%左右元素固定宽度三个元素都用floatleft 中间元素使用padding空出左右的位置左右通过margin和相对定位进行移动 双飞翼中间元素放最前需要单独在把内容部分包裹然后设置padding之后只使用margin进行左右位置的移动 双飞翼布局比圣杯布局多了一层DOM节点 双飞翼布局源码 !DOCTYPE html
html langenheadmeta charsetUTF-8meta http-equivX-UA-Compatible contentIEedgemeta nameviewport contentwidthdevice-width, initial-scale1.0title双飞翼布局/title
/head!-- 双飞翼布局实现效果 --
!-- 1、目的两侧内容宽度固定中间内容宽度自适应 --
!-- 2、三栏布局中间一栏最先加载、渲染出来 --
!-- 实现方法floatmargin --!-- 靠在中间这层外面套一层div加padding将内容挤出来中间 --bodydiv classheaderheader/divdiv classmain middlediv idmain-wrappermiddle/div/divdiv classleftleft/divdiv classrightright/divdiv classfooterfooter/div
/body/html
style* {margin: 0;padding: 0;}body {/* 设置最小宽度防止挤压使中间内容消失 */min-width: 600px;}.header {text-align: center;height: 70px;background-color: coral;}.main #main-wrapper {margin-left: 100px;margin-right: 100px;}.left,.middle,.right {float: left;}.left {height: 100px;width: 100px;background-color: darkmagenta;margin-left: -100%;}.right {height: 100px;width: 100px;background-color: darkslategray;margin-left: -100px;}.middle {height: 100px;width: 100%;min-width: 200px;background-color: forestgreen;/* 不能在外层容器里面加padding否则会使布局乱套 *//* padding-left: 100px;padding-right: 100px; */}.footer {text-align: center;height: 50px;clear: both;background-color: darkgrey;}
/style 圣杯布局源码
!DOCTYPE html
html langenheadmeta charsetUTF-8meta http-equivX-UA-Compatible contentIEedgemeta nameviewport contentwidthdevice-width, initial-scale1.0title圣杯布局/title
/head!-- 圣杯布局实现效果 --
!-- 1、目的两侧内容宽度固定中间内容宽度自适应 --
!-- 2、三栏布局中间一栏最先加载、渲染出来 --
!-- 实现方法float搭建布局margin使三列布局到一行上relative相对定位调整位置 --!-- 不同之处怎么处理两列的位置 --
!-- 给外部容器加padding通过相对定位把两边定位出来 --!-- 相同之处 --
!-- 让左中右三列浮动通过父外边距形成三列布局 --bodydiv classheaderheader/divdiv classcontent wrapperdiv classmiddlemiddle/divdiv classleftleft/divdiv classrightright/div/divdiv classfooterfooter/div
/body/html
style* {margin: 0;padding: 0;}body {/* 设置最小宽度防止挤压使中间内容消失 */min-width: 600px;}.header,.footer {background-color: #bbe0e3;height: 100px;border: dimgray 1px solid;}/* 通过BFC解决高度塌陷 *//* .content {overflow: hidden; } */.footer {/* 通过清除底部浮动解决高度塌陷 */clear: both;}.wrapper {padding-left: 100px;padding-right: 100px;}.content .middle,.left,.right {background-color: #d9d9d9;}.middle {height: 100px;background-color: dimgray;/* 中间列自适应所以宽度100%继承父元素宽度 */width: 100%;float: left;}.left {height: 100px;background-color: #d5d50f;width: 100px;float: left;position: relative;margin-left: -100%;right: 100px;}.right {height: 100px;background-color: #8cca4d;width: 100px;float: left;margin-right: -100px;}
/style 2.使用 displayflex 通过flex-grow分配比例 拓展 flex: 1; 是 flex-grow, flex-shrink, 和 flex-basis 属性的缩写形式。这三个属性通常一起使用来定义 Flexbox 容器中每个项目的伸缩性、收缩性和初始大小。 具体地说 flex-grow定义了项目的增长系数决定了项目在可用空间中的分配比例。flex-shrink定义了项目的收缩系数决定了项目在空间不足时的缩小比例。flex-basis定义了项目的初始大小。它可以是一个长度值如像素、百分比等也可以是 auto表示由项目的内容决定初始大小。 当使用 flex: 1; 缩写时这三个属性的值被设置为默认值 flex-grow: 1;即项目可以根据可用空间扩张。flex-shrink: 1;即项目可以缩小。flex-basis: 0%;即项目的初始大小为0%允许项目根据内容和空间自动调整大小。 因此flex: 1; 的效果是使得所有具有该属性的项目平均地占据剩余空间而不考虑它们的初始大小。这在创建灵活的布局时非常有用例如使所有项目在父容器中均匀分布并填充剩余空间。 JS部分
九.说一说JS数据类型有哪些,区别是什么
JS数据类型分为两类 基本数据类型 也叫简单数据类型包含7种类型分别是Number 、String、Boolean、BigInt、Symbol、Null、Undefined。 引用数据类型(复杂数据类型) 通常用Object代表普通对象数组正则日期Math数学函数都属于Object。 数据分成两大类的本质区别
基本数据类型和引用数据类型它们在内存中的存储方式不同。 基本数据类型 是直接存储在栈中的简单数据段占据空间小属于被频繁使用的数据。 引用数据类型 是存储在堆内存中占据空间大。引用数据类型在栈中存储了指针该指针指向堆中该实体的起始地址当解释器寻找引用值时会检索其在栈中的地址取得地址后从堆中获得实体。 拓展到数据类型判断方法 拓展 Symbol 是ES6新出的一种数据类型这种数据类型的特点就是没有重复的数据可以作为object的key。 数据的创建方法Symbol()因为它的构造函数不够完整所以不能使用new Symbol()创建数据。由于Symbol()创建数据具有唯一性所以 Symbol() ! Symbol(), 同时使用Symbol数据作为key不能使用for获取到这个key需要使用Object.getOwnPropertySymbols(obj)获得这个obj对象中key类型是Symbol的key值。 let key Symbol(key);
let obj { [key]: symbol};
let keyArray Object.getOwnPropertySymbols(obj); // 返回一个数组[Symbol(key)]
obj[keyArray[0]] // symbol BigInt 也是ES6新出的一种数据类型这种数据类型的特点就是数据涵盖的范围大能够解决超出普通数据类型范围报错的问题。 使用方法 -整数末尾直接n647326483767797n -调用BigInt()构造函数BigInt(647326483767797) 注意BigInt和Number之间不能进行混合操作 十.说一说null 和 undefined 的区别如何让一个属性变为null 1.类型不同 null和undefined其实是两种数据类型 当使用 typeof 进行判断时 typeof null object 而 typeof undefined undefined 但是实际上的null 的类型是NULL typeof 判断为 object是因为在JS的底层的二进制判断中二进制的前三位为0都会被判断为对象类型而null的值都是0所以使用 typeof 判断是object 2.含义不同 null表示的是 一个数据被定义并且赋值是 null空 undefined 表示的是一个数据被定义了但是没有被赋值或是函数的返回为空 让一个属性变为null 其实很简单只需要使这个变量等于 null即可 注意当基本类型被设置为null时由于基本类型是储存在栈上按值传递的所以设置为空null时并不会影响内存的变化。 当引用类型被设置为null时会引起内存的变化因为引用类型的属性方法是储存在堆内存上在创建引用类型时会在栈中存储一个地址来指向对应的堆内存当为null时即把栈内存的地址指向为null,所以此时的堆内存没有了对应的引用当JS的垃圾回收机制就会清除掉这种没有被引用的内存空间。 这么回答可以引导面试官 到 JS的垃圾回收机制再细思一下还可以引导到 深拷贝和浅拷贝 十一.说一说JavaScript有几种方法判断变量的类型 1.typeof 用于基本数据类型判断对于引用类型一致返回 object对于function返回 function 2.instanceof 用于具体判断区分引用类型 [] insranceof Array - true {} insranceof Object - true注意如果使用 引用类型/函数 insranceof Object - 最后结果都是true涉及到原型链知识 对于 undefined, null, symbol 基本类型无法判断 instanceof的实现原理验证当前类的原型prototype是否会出现在实例的原型链__proto__上只要在它的原型链上则结果都为true。因此instanceof 在查找的过程中会遍历左边变量的原型链直到找到右边变量的 prototype找到返回true未找到返回false。 期间可能会拓展到 原型链 上 3.Array.isArray(obj) 这个方法就是来判断一个数据是否为数组 4.constructor (用于引用数据类型) 检测方法是获取实例的构造函数判断和某个类是否相同如果相同就说明该数据是符合那个数据类型的这种方法不会把原型链上的其他类也加入进来避免了原型链的干扰。 [].constructor Array - true {}..constructor Object - 报错 在 JavaScript 中由于对象字面量 {} 是一个独立的语法结构不是一个对象实例因此无法直接通过 .constructor 来访问其构造函数 5.Object.prototype.toString.call() (对象原型链判断方法 Object.prototype.toString.call()原理Object.prototype.toString 表示一个返回对象类型的字符串call()方法可以改变this的指向那么把Object.prototype.toString()方法指向不同的数据类型上面返回不同的结果 讲到这里可能会牵引到原型链对象的 call bind 等方法或者手写一个 call方法 十二.说一说数组去重都有哪些方法
具体实现代码参考JS数组去重方法-CSDN博客 1.双层循环 splice(当前索引1) 时间复杂度O(n^2), 空间复杂度O(1) for(let i0;iarr.length;i){for(let ji1;jarr.length;j){if(arr[i] arr[j]){arr.splice(j,1)}}
} 这种方法虽然可以去除数组中的重复元素但它的时间复杂度相对较高为 O(n^2)因为每次 splice 操作都会导致数组的重新排列。在大型数组中使用时可能性能较差尤其是当数组长度较大时。 2.循环 indexOf 方法 时间复杂度O(n^2), 空间复杂度O(1) for(let i0;iarr.length;i){let index arr.indexOf(arr[i],i1)if(index ! -1){arr.splice(index,1)i--}
} 这种方法的缺点也是相对较高的时间复杂度因为每次调用 indexOf() 都需要遍历数组来查找元素的位置。对于大型数组性能可能会受到影响。 3.循环 arr.sort() 方法 时间复杂度O(n log n), 空间复杂度O(1) arr.sort()
for (let i 1; i arr.length; i) {if (arr[i] arr[i - 1]) {arr.splice(i, 1);i--; // 减少 i 的值以便下一次继续比较同位置的元素}} 这种方法的时间复杂度取决于 arr.sort() 的实现通常为 O(n log n)再加上一次循环总体效率较高。但请注意这种方法改变了原数组的顺序如果要保持原数组的顺序可以在排序前创建数组的副本。 4.循环 includes() 时间复杂度O(n *m), 空间复杂度O(n) let newArr []
for(let i0;iarr.length;i){if(!newArr.includes(arr[i])){newArr.push(arr[i])}
} 这种方法通过 includes() 来判断当前元素是否已经存在于结果数组中如果不存在则添加到结果数组中。这样可以保证结果数组中的元素是唯一的。 然而需要注意的是 includes() 方法的时间复杂度为 O(n)因此总体的时间复杂度为 O(n*m)在大型数组上性能可能较差。 5.reduce includes 时间复杂度O(n^2), 空间复杂度O(n) arr.reduce((result, current) {if (!result.includes(current)) {result.push(current);}return result;
}, []);6. filterindexOf 时间复杂度O(n^2), 空间复杂度O(n) arr.filter((item, index) arr.indexOf(item) index); 7.利用对象属性 key 排除重复项 时间复杂度O(n^2), 空间复杂度O(n) const obj {};
const result [];for (let i 0; i arr.length; i) {const item arr[i];if (!obj.hasOwnProperty(item)) {obj[item] true;result.push(item);}
}8.Set方法 new Set(oldArr) 时间复杂度O(n), 空间复杂度O(n) const newArr [...new Set(arr)]; 9.循环 Map 时间复杂度O(n), 空间复杂度O(n) let map new Map()
let newArr []for(let i0;iarr.length;i){if(map.has(arr[i]){map.set(arr[i],true)} else {newArr.push(arr[i])map.set(arr[i],false)}
} 最后可能会问一下哪一个比较好就是比较时间复杂度空间复杂度 十三.数组与伪数组的区别 区别 数组类型是 Array 可以使用数组的方法 伪数组 是 Object 不能使用数组方法获取但是可以使用 .[item]获取值可以使用.length 转化方法 // 方法1: 使用 Array.prototype.slice.call()
const pseudoArray document.querySelectorAll(.pseudo); // 伪数组
const trueArray1 Array.prototype.slice.call(pseudoArray);// 方法2: 使用 [].slice.call()
const trueArray2 [].slice.call(pseudoArray);// 方法3: 使用 Array.from()
const trueArray3 Array.from(pseudoArray);可能转到对象原型链方法上去 十四.说一说map 和 forEach 的区别 map方法是创建一个新数组返回处理之后的值 forEach方法返回值是undefined 二者都不会主动修改原数组 map的处理速度比forEach快而且返回一个新的数组方便链式调用其他数组新方法 十五.说一说es6中箭头函数 简答 没有this、this是从外部获取、不能使用new、没有arguments、没有原型和super 展开 箭头函数相当于匿名函数简化了函数定义。箭头函数有两种写法当函数体是单条语句的时候可以省略{}和return。另一种是包含多条语句不可以省略{}和return。 箭头函数最大的特点就是没有this所以this是从外部获取就是继承外部的执行上下文中的this由于没有this关键字所以箭头函数也不能作为构造函数 同时通过 call() 或 apply() 方法调用一个函数时只能传递参数不能绑定this第一个参数会被忽略。 箭头函数也没有原型和super。不能使用yield关键字因此箭头函数不能用作 Generator 函数。不能返回直接对象字面量。 箭头函数的不适用场景 var dog { lives: 20, jumps: () { this.lives--; } } -定义对象上的方法 当调用 dog.jumps 时lives 并没有递减。因为 this 没有绑定值而继承父级作用域。 var button document.querySelector(button);
button.addEventListener(click,() { this.classList.toggle(on); }); -不适合做事件处理程序 此时触发点击事件this不是button无法进行class切换 箭头函数函数适用场景 -简单的函数表达式内部没有this引用没有递归、事件绑定、解绑定适用于map、filter等方法中写法简洁 var arr [1,2,3];
var newArr arr.map((num)num*num) -内层函数表达式需要调用this且this应与外层函数一致时 let group {title: Our Group,students: [John, Pete, Alice],showList() {this.students.forEach(student alert(this.title : student));}
};group.showList();十六.说一说this指向普通函数、箭头函数 普通函数中的 this 指向 当一个函数被作为普通函数调用时this 默认指向全局对象在浏览器中通常是 window 对象。在严格模式下如果函数被作为普通函数调用this 将会是 undefined。如果函数被绑定在某个对象上并且通过该对象进行调用this 将会指向该对象。使用 call()、apply() 或 bind() 可以显式地指定函数内部的 this。 箭头函数中的 this 指向 箭头函数没有自己的 this它继承自包含它的最近一层非箭头函数的 this 值。换句话说箭头函数的 this 指向了它外部的作用域中的 this。因为箭头函数没有自己的 this所以在箭头函数内部无法通过 call()、apply() 或 bind() 来改变 this 的指向。箭头函数在定义时绑定了 this 的值而不是在运行时。 十七.事件扩展符用过吗(...)什么场景下 展开语法Spread syntax在 JavaScript 中的应用非常广泛它可以在函数调用、数组构造、对象字面量等场景中展开数组、字符串和对象。 1. 在函数调用中使用 这里的 ...args 将数组 args 中的元素展开作为函数的参数传递给 myFunction。 function myFunction(x, y, z) {console.log(x, y, z);
}const args [1, 2, 3];
myFunction(...args); // 等同于 myFunction(1, 2, 3)2. 在数组构造中使用 展开语法可以将一个数组中的元素展开并将它们合并到另一个数组中。 const arr1 [1, 2, 3];
const arr2 [4, 5, 6];
const mergedArray [...arr1, ...arr2];
console.log(mergedArray); // 输出[1, 2, 3, 4, 5, 6]3. 在对象字面量中使用 对象的展开语法可以将一个对象中的属性展开并将它们合并到另一个对象中。 const obj1 { x: 1, y: 2 };
const obj2 { z: 3 };
const mergedObject { ...obj1, ...obj2 };
console.log(mergedObject); // 输出{ x: 1, y: 2, z: 3 }4. 在字面量数组或字符串连接中使用 这里 ...parts 将数组 parts 中的元素展开并将它们插入到新数组中。 const parts [apple, banana];
const fruits [orange, ...parts, kiwi];
console.log(fruits); // 输出[orange, apple, banana, kiwi]5. 构造字面量对象时进行浅克隆或属性拷贝 使用展开语法可以方便地进行对象的浅克隆或属性拷贝将原对象的属性展开到新对象中。 const obj { x: 1, y: 2 };
const clone { ...obj };
console.log(clone); // 输出{ x: 1, y: 2 }拓展 展开语法Spread syntax 展开语法用于展开数组或对象中的元素并将它们作为独立的参数传递给函数、数组字面量或对象字面量。在数组或对象中使用展开语法时被展开的对象必须是可迭代的。如果对象不是可迭代的则会抛出 TypeError 错误。 const obj { key1: value1 };
const array [...obj]; // TypeError: obj is not iterable剩余语法Rest syntax 剩余语法用于将多个参数收集起来并“凝聚”为单个参数。它通常用于函数参数中以允许函数接受任意数量的参数并将它们放入一个数组中。在函数参数中使用剩余语法时它将未被显式命名的参数收集到一个数组中。 function f(...rest) {return rest;
}
console.log(f(1)); // [1] b 和 c 未定义
console.log(f(1, 2, 3)); // [1, 2, 3]
console.log(f(1, 2, 3, 4)); // [1, 2, 3, 4] 第四个参数未被解构十八.说一说JS变量提升 简答 Var声明的变量声明提升、函数声明提升、let和const变量不提升 展开 变量提升是指JS的变量和函数声明会在代码编译期提升到代码的最前面。 变量提升成立的前提是使用Var关键字进行声明的变量并且变量提升的时候只有声明被提升赋值并不会被提升同时函数的声明提升会比变量的提升优先。 变量提升的结果可以在变量初始化之前访问该变量返回的是undefined。在函数声明前可以调用该函数。 使用let和const声明的变量是创建提升形成暂时性死区在初始化之前访问let和const创建的变量会报错。 十九.说一说js继承的方法和优缺点 1.原型链继承 优点 写法方便简洁容易理解。 缺点 引用类型值的实例属性会在子类型原型上变成原型属性被所有子类型实例所共享。创建子类型实例时不能向父类型构造函数中传递参数。 function Parent() {this.name Parent;
}
Parent.prototype.sayHello function() {console.log(Hello, I am this.name);
};function Child() {// 继承了 Parent
}
Child.prototype new Parent();var child1 new Child();
var child2 new Child();
child1.sayHello(); // 输出 Hello, I am Parent
child2.sayHello(); // 输出 Hello, I am Parent2.借用构造函数继承 优点 解决了原型链继承的共享属性和无法传参的问题。 缺点 方法都在构造函数中定义无法实现函数复用。父类型原型中定义的方法对子类型不可见。 function Parent(name) {this.name name;
}
Parent.prototype.sayHello function() {console.log(Hello, I am this.name);
};function Child(name) {Parent.call(this, name); // 借用父类构造函数
}
var child1 new Child(Child1);
var child2 new Child(Child2);
child1.sayHello(); // 输出 Hello, I am Child1
child2.sayHello(); // 输出 Hello, I am Child23.组合继承 优点 解决了原型链继承和借用构造函数继承的影响。函数复用。每个实例都有自己的属性。 缺点 无论在什么情况下都会调用两次超类型构造函数。 function Parent(name) {this.name name;
}
Parent.prototype.sayHello function() {console.log(Hello, I am this.name);
};function Child(name, age) {Parent.call(this, name); // 借用父类构造函数this.age age;
}
Child.prototype new Parent(); // 继承父类原型
Child.prototype.constructor Child; // 修复构造函数指向var child1 new Child(Child1, 20);
var child2 new Child(Child2, 25);
child1.sayHello(); // 输出 Hello, I am Child1
child2.sayHello(); // 输出 Hello, I am Child24.原型式继承 优点 不需要单独创建构造函数。 缺点 属性中包含的引用值始终会在相关对象间共享。 var person {name: Alice,age: 25
};
var anotherPerson Object.create(person);
console.log(anotherPerson.name); // 输出 Alice5.寄生式继承 优点 写法简单不需要单独创建构造函数。 缺点 函数难以重用。 function createAnother(original) {var clone Object.create(original);clone.sayHello function() {console.log(Hello, I am this.name);};return clone;
}
var person {name: Alice,age: 25
};
var anotherPerson createAnother(person);
anotherPerson.sayHello(); // 输出 Hello, I am Alice6.寄生组合式继承 优点 高效率只调用一次父构造函数。避免了在子原型上创建不必要多余的属性。原型链保持不变。 缺点 代码复杂。 function Parent(name) {this.name name;
}
Parent.prototype.sayHello function() {console.log(Hello, I am this.name);
};function Child(name, age) {Parent.call(this, name); // 借用父类构造函数this.age age;
}
Child.prototype Object.create(Parent.prototype); // 继承父类原型
Child.prototype.constructor Child; // 修复构造函数指向var child1 new Child(Child1, 20二十.说一说defer和async区别 它们都可以让 script 标签异步加载 区别有以下几点 执行时机 async脚本的加载和执行是异步的当脚本加载完成后会立即执行不会阻塞 HTML 解析和其他资源的加载。脚本加载完成和执行的顺序与它们在 HTML 中的顺序不一定相同。defer脚本的加载是异步的但是脚本会在 HTML 解析完毕之后DOMContentLoaded 事件触发之前执行。多个 defer 脚本按照它们在 HTML 中出现的顺序依次执行。 执行时机受限制 async脚本一旦加载完成会立即执行无论 HTML 文档的解析是否完成。因此如果有多个 async 脚本它们的执行顺序是不确定的取决于加载完成的顺序。defer脚本会在 HTML 解析完毕之后执行因此它们的执行顺序与它们在 HTML 中出现的顺序一致。 对文档的影响 async由于脚本加载完成后会立即执行可能会影响页面的内容和交互特别是对于需要立即执行的脚本。适合不需要依赖页面内容的脚本。defer脚本会在 HTML 解析完毕后执行因此不会影响页面内容和交互。适合需要等待页面解析完毕后执行的脚本例如操作 DOM 元素的脚本。 拓展 渲染阻塞的原因由于js的执行与页面的渲染它是由两个不同的线程操纵的并且js是可以操作DOM的如果二者同时进行假如在渲染过程js操作了某个DOM那么就会造成渲染的元素可能出现前后不一致从而产生一些列的潜在问题所以为了避免出现这种情况浏览器就将JS引擎与GUI渲染引擎设置为互斥的关系这样当JS引擎执行的时候GUI渲染引擎就会进入一个队列等待JS引擎运行完成它才运行。所以假如JS运行时间过长就会造成页面的阻塞。 二十一.说一说JS实现异步的方法 1.定时器 回调函数 早期的异步编程方法原理就是将一个函数作为参数传递当异步操作完成后调用该函数 例如在 AJAX 请求完成时调用一个回调函数来处理响应数据。 优点 简单、容易理解和实现 缺点 不利于代码的阅读和维护各个部分之间高度耦合使得程序结构混乱、流程难以追踪尤其是多个回调函数嵌套的情况而且每个任务只能指定一个回调函数。此外它不能使用 try catch 捕获错误不能直接 return function fetchData(callback) {// 模拟异步操作setTimeout(function() {var data 异步数据;callback(data); // 异步操作完成后调用回调函数}, 1000);
}// 调用 fetchData 函数并传递一个回调函数处理数据
fetchData(function(data) {console.log(收到数据, data);
});2.promise的使用 优点 它能够解决回调地狱的问题 原理就是使用时它会创建一个Promise实例然后根据异步调用的结果分别调用实例中的resolve 和 reject方法然后通过 .then/.catch分别接收成功/失败的对应信息数据做出相应的处理。 缺点 它不能取消。 function fetchData() {return new Promise(function(resolve, reject) {// 模拟异步操作setTimeout(function() {// 模拟错误var error true;if (error) {reject(发生错误);} else {var data 异步数据;resolve(data); // 异步操作成功调用 resolve 方法}}, 1000);});
}// 使用 Promise 的 catch() 方法捕获错误
fetchData().then(function(data) {console.log(收到数据, data);}).catch(function(error) {console.error(发生错误, error);});3.生成器Generators/yield Generator 函数是 ES6 提供的一种异步编程解决方案Generator 函数是一个状态机封装了多个内部状态可暂停函数, yield可暂停next方法可启动每次返回的是yield后的表达式结果。优点是异步语义清晰缺点是手动迭代Generator 函数很麻烦实现逻辑有点绕 生成器Generators是 ES6 中引入的一种特殊类型的函数它与普通函数不同可以在需要时暂停和恢复执行。生成器函数通过 function* 关键字定义而不是普通函数的 function 关键字。 在生成器函数内部使用 yield 关键字来暂停函数的执行并返回一个值给调用方。每次调用生成器函数时它都会返回一个迭代器对象Iterator可以通过该对象的 next() 方法来继续执行生成器函数并在遇到下一个 yield 语句时再次暂停。 function* counter() {let count 0;while (true) {yield count;count;}
}const iterator counter();console.log(iterator.next().value); // 输出0
console.log(iterator.next().value); // 输出1
console.log(iterator.next().value); // 输出2
// 依次类推...4.async/await async/awt是基于Promise实现的async/awt使得异步代码看起来像同步代码所以优点是使用方法清晰明了缺点是awt 将异步代码改造成了同步代码如果多个异步代码没有依赖性却使用了 awt 会导致性能上的降低代码没有依赖性的话完全可以使用 Promise.all 的方式。 async function fetchData() {return new Promise(function(resolve, reject) {// 模拟异步操作setTimeout(function() {var data 异步数据;resolve(data); // 异步操作成功调用 resolve 方法}, 1000);});
}// 使用 async 函数调用 fetchData并使用 await 等待异步操作结果
async function getData() {var data await fetchData();console.log(收到数据, data);
}getData();拓展 JS 异步编程进化史callback - promise - generator/yield - async/awt。 async/awt函数对 Generator 函数的改进体现在以下三点 - 内置执行器。 Generator 函数的执行必须靠执行器而 async 函数自带执行器。也就是说async 函数的执行与普通函数一模一样只要一行。 - 更广的适用性。 yield 命令后面只能是 Thunk 函数或 Promise 对象而 async 函数的 awt 命令后面可以跟 Promise 对象和原始类型的值数值、字符串和布尔值但这时等同于同步操作。 - 更好的语义。 async 和 awt比起星号和 yield语义更清楚了。async 表示函数里有异步操作awt 表示紧跟在后面的表达式需要等待结果。 目前使用很广泛的就是promise和async/awt 二十二.说一说cookie sessionStorage localStorage 区别 这些都是浏览器的本地存储都是储存在本地浏览器的 cookie 存储大小一般是4K,经常用来存储一些信息凭证之类的例如登录后的token信息他一般是由服务器端设置过期时间前端也可以通过setCookies时添加一个时间戳参数用来设置cookie的过期时间。 sessionStorage 大小在5M左右它的存在时间是浏览器窗口的存在时间。即关闭当前窗口时它就会自动清除。它的限制就是必须是在同一页面的使用。 localStorage 大小也是5M左右但是它的存在时间是永久在浏览器本地要想清除必须手动设置一般用来存储一些不易变动的数据减少服务器的性能压力 二十三.说一说如何实现可过期的localstorage数据 1.惰性删除 惰性删除是指某个键值过期后该键值不会被马上删除而是等到下次被使用的时候才会被检查到过期此时才能得到删除。 在存储信息时保存一对键值value用来存储数据key用来存储一个当前时间下次访问请求数据时用存储的时间和获取此次数据时的时间进行对比超过设置的时间限制后进行删除 2.定时删除 每隔一段时间执行一次删除操作并通过限制删除操作执行的次数和频率来减少删除操作对CPU的长期占用。另一方面定时删除也有效的减少了因惰性删除带来的对localStorage空间的浪费。 获取所有设置过期时间的key判断是否过期过期就存储到数组中遍历数组每隔1S固定时间删除5个固定个数直到把数组中的key从localstorage中全部删除。 LocalStorage清空应用场景token存储在LocalStorage中要清空 二十四.说一下token 能放在cookie中吗 单说这个问题它是可以的一般自己做的一些小项目当用户输入用户名与密码后服务器端会返回一个token此时我们就可以把它设置在cookie中用来当作登陆的一个凭证。 但是这么做会有一个弊端无法防止 CSRF的攻击 拓展token认证流程 1. 客户端使用用户名跟密码请求登录 2. 服务端收到请求去验证用户名与密码 3. 验证成功后服务端签发一个 token 并把它发送给客户端 4. 客户端接收 token 以后会把它存储起来比如放在 cookie 里或者 localStorage 里 5. 客户端每次发送请求时都需要带着服务端签发的 token把 token 放到 HTTP 的 Header 里 6. 服务端收到请求后需要验证请求里带有的 token 如验证成功则返回对应的数据