发布时间:2024-12-15 14:01
记录一下在面试过程中遇到的以及搜罗到的web前端开发岗位的面试题,以八股为目录,部分题目会记录公司出处,先记录题目,答案之后补充,欢迎各位小伙伴评论区补充。
以下是本篇文章正文内容
什么是深拷贝和浅拷贝?
基本数据类型的拷贝操作:基本数类型的名字和value值都存在栈内存中,当进行复制的时候,就开辟新的内存进行存储。
引用数据类型的拷贝操作:引用数据类型名字存在栈中,value值存在堆中,但是栈中会提供一个地址指向堆中的value值。
深浅拷贝就是针对引用数据类型而言的。
浅拷贝:浅拷贝指的是只复制栈内存中的地址,当你对数据进行改变的时候改变的是堆内存中的变量,所以改变其中的一个值,另一个值会跟着改变,例如b=a,当你改变b的值时,a的值也会变化。
深拷贝:深拷贝指的是复制之后会开辟新的堆内存进行value值的存储,所以复制之后的值的改变不会影响到被复制的变量的值。
为什么要讨论深浅拷贝?
在一个团队中,多人开发一个项目时,当无法得知某个数据的用处时,随意使用浅拷贝操作数据会带来无法估量的影响。
浅拷贝和深拷贝操作举例。
浅拷贝:“传址操作”
var a = [1,2,3,4]
var b = a
b.splice(1,1)
console.log(a)//[1,3,4]
console.log(b)//[1,3,4]
深拷贝的几种方法:“传值操作”
//方法1:只是深拷贝第一层,如果该数组存在下一层,则仍然是浅拷贝
var s = [1,2,3,4,5]
var s1 = s.slice(1,3)
s1[1] = 34
console.log(s)//[1,2,3,4,5]
console.log(s1)//[2,34]
//方法2:
var s = [1,2,3,4,5]
var s1 = [7,8,9]
console.log(s.concat(s1))//[1,2,3,4,5,7,8,9]
console.log(s)//[1,2,3,4,5]
//方法3:遍历数组的操作
var copy = function(obj){
let objclone = Array.isArray(obj)?[]:{};
for(key in obj){
if(typeof obj[key]==="object"){
objclone[key] = copy(obj[key])
}else{
objclone[key] = cobj[key]
}
}
return objclone;
}
var c = [1,2,3,[4,5,6]]
var d = c
d[0] = 11
d[3][0] = 50
console.log(c)//[1,2,3,[4,5,6]]
console.log(d)//[11,2,3,[50,5,6]]
function debounce(fn,delay) {
let timeout = null; // 创建一个标记用来存放定时器的返回值
return function() {
clearTimeout(timeout); // 每当用户输入的时候把前一个 setTimeout clear 掉
timeout = setTimeout(() => { // 然后又创建一个新的 setTimeout, 这样就能保证输入字符后的 interval 间隔内如果还有字符输入的话,就不会执行 fn 函数
fn();
}, delay);
};
}
function sayHi() {
console.log('防抖成功');
}
let box = document.getElementById('box')
box.addEventListener('click', debounce(sayHi,2000))
节流:
function throttle(fn,delay) {
let canRun = true; // 通过闭包保存一个标记,这个标记使得第一次是可以进入的
return function () {
if (!canRun) return; //若判断为false,则返回,不能继续执行
canRun = false; // 立即设置为false,防止在此期间再次触发函数,canrun为true就可以再次进入,阻断程序的再次执行,直到执行完毕canrun被再次赋值true
setTimeout(() => { // 将外部传入的函数的执行放在setTimeout中
fn();
canRun = true;//此时可以再次被执行
}, delay);
};
}
function sayHi(e) {
console.log('节流');
}
window.addEventListener('resize', throttle(sayHi,1000));
简单来说,“===”比“= =”更加严格。
console.log("1"==1)//true
console.log(null==undefined)//true
console.log(true==1)//true
console.log(Symbol(1)==1)//false
typeof(undefined)//undefined
typeof(null)//object
null == undefined //true
null === undefined // false
var a;
console.log(a)//undefined
var arr = [1,2,3,4,5]
var obj = {
name:"张三"
age:24
}
判断方法如下
//方法一
console.log(arr.constructor)//[function:Array]
console.log(obj.constructor)//[function:Object]
//方法二
console.log(Array.isArray(arr))//true
console.log(Array.isArray(obj))//false
//方法三
console.log(arr instanceof Array)//true
console.log(obj instanceof Array)//false
//方法四
console.log(Object.prototype.toString.call(arr))//[object Array]
console.log(Object.prototype.toString.call(obj))//[object Object]
//方法五
console.log(arr.__proto__)//[]
console.log(obj.__proto__)//{}
step1>>查找域名对应的IP地址。
浏览器向DNS服务器发起DNS请求,DNS服务器解析域名后返回域名对应的网站服务器IP地址。
step2>>浏览器向IP对应的Web服务器发送HTTP请求(在这里三次握手)
step3>>服务器响应,发回网页内容
服务器解析浏览器的请求之后从数据库获取资源,将生成的html文件封装至HTTP响应包中,返回值浏览器解析。
step4>>浏览器解析网页内容(之后四次挥手)
浏览器解析HTTP响应之后,下载html文件,继而根据文件内部包含的外部引用文件、图片或者多媒体文件等逐步下载,最终将获取到的全部文件渲染成完整的网站页面。
浏览器渲染页面:把html代码通过html解析器形成
father{
position: relative;
}
/* 定位法:必须知道宽高 */
#son{
height:100px;
width:100px;
background-color: blanchedalmond;
position: absolute;
top:50%;
left:50%;
margin-left:-50px;
margin-top:-50px;
}
father{
position: relative;
}
/* 无需知道宽高但是必须知道宽高 */
#son{
height:100px;
width:100px;
background-color: blanchedalmond;
position: absolute;
top:0;
left:0;
right:0;
bottom:0;
margin:auto;
}
father{
position: relative;
}
/* 无需知道宽高,用内容撑起来即可,但是兼容性不是很好 */
#son{
background-color: blanchedalmond;
position: absolute;
top:50%;
left:50%;
transform: translate(-50%,-50%); 在x轴上和y轴上移动
}
father{
display: table-cell;/* 只作用于文本,所以子元素设置为 inline-block;*/
vertical-align: middle;/* 垂直居中*/
text-align: center;/* 水平居中*/
background-color: aqua;
height:500px;
width:500px;
}
.son{
display: inline-block;
background-color: azure;
}
let box = document.getElementById('box')
boxw = box.offsetWidth;
boxh=box.offsetHeight;
let inner = document.getElementById('inner')
innerw=inner.offsetWidth
innerh=inner.offsetHeight
inner.style.position="absolute"
inner.style.left=(boxw-innerw)/2+'px'
inner.style.top=(boxh-innerh)/2+'px'
优先级比较
!Important > 内联样式(标签内部style1000)> id选择器(0100)>class 选择器【包括类,伪类,属性选择器】(0010)>标签(包括标签和伪元素)(0001)>通配符>继承>默认
权重计算方法(相加计算权重,后定义覆盖先定义)
不同等级的选择器权重定义如下
第一等:代表内联样式,如: style=””,权值为1000。
第二等:代表ID选择器,如:#content,权值为0100。
第三等:代表类,伪类和属性选择器,如.content,权值为0010。
第四等:代表标签选择器和伪元素选择器,如div p,权值为0001。
第五等:通用选择器(),子选择器(>),相邻同胞选择器(+),权值为0000
定义css的时候,把权重进行相加,示例:
#content#in_block.user_infoa{/权重:1002+101+11=211/
注意
1、但是可能出现同一个元素定义了两个css,且权重相同,此时,后定义的样式会覆盖先定义的样式(覆盖指的是只覆盖被定义过的属性,不是全部替换)
2、权重相同,一个定义在html文件里面的style标签里面,一个定义在外联的css文件中,也是哪一个在html中后引入用哪个
p:first-child p标签或者类的父元素的第一个子元素(且第一个子元素必须是p或者该类)
p:last-child
p:nth-child(n) 匹配第n个p标签 n可以是数字、公式、或者关键字(even 偶数 odd 奇数)
p:first-of-type 各个父元素(包括body)下的第一个p标签
p:last-of-type
p:nth-of-type(n)
<div class="wei">abcde</div>
.wei::after{
content:'我在伪元素的后面';
display: block;
}
.wei::before{
content: "我在伪元素的前面";
display: block;
}
.wei::first-letter{ 容器(包括新增的内容)内的第一个字母
color:hotpink
}
.wei::first-line{ 容器(包括新增的内容)内的第一行
color:indianred;
}
在这里
<input type="radio" name="sex1" class="icon-dan"> 男性
<input type="radio" name="sex2" class="icon-dan"> 女性
属性选择器
/*匹配name的值为sex1的标签*/
input[name="sex1"]{
width:100px;
height:100px;
}
通配符选择器
input[name^="sex"]匹配name的值以sex开头的
input[name$="sex"]匹配name的值以sex结尾的
input[name*="sex"]匹配name的值中含有sex的
//消失在视野中,并且依然占用空间
visibility:hidden;//隐藏
background-color:transparent;//设置透明度为0
transform:scale(0);//缩放为0
//消失在视野中,并且不占空间
display:none;
for/while/for…in/for…of/forEach/map/filter等遍历方法的区别
这里对文章进行总结:
失败不可怕,胆怯最可怕,失败之后无所进益最可怕