前端面试题
进程和线程的区别?
2.同一个进程可以包括多个线程,并且线程共享整个进程的资源(寄存器、堆栈、上下文),一个进程至少包括一个线程)
死锁是什么?产生的原因?必要条件?怎么处理?
1.相互等待资源所产生的一种僵持状态,如果没有外力干预将一直保持这个状态。
2.系统资源不足、相互争抢资源、资源请求顺序不当
3.互斥、不可抢占、循环等待、请求与保持
4.互斥是不可改变的,所以只能破坏其他三个条件中的一个来解除死锁,方法:剥夺资源、杀死其中一个线程
什么是HTML语义化?
根据内容使代码结构化,选择合适的标签能使代码语义化,便于开发者阅读和写出更优雅的代码,同时也让浏览器的爬虫和机器更好地解析
为什么要语义化?
-
为了在没有CSS的情况下也能呈现出很好的内容结构
-
用户体验更好,如title解释名词信息,alt解释图片信息
-
有利于SEO(search engine optimization):和搜索引擎建立良好的沟通,有利于爬虫抓取更多信息:爬虫依赖标签来确定上下文和各个关键字的权重
-
方便其他设备解析(屏幕阅读器、盲人阅读器、移动设备)
-
便于团队开发维护
写代码时要注意什么?
-
尽可能减少无语义标签的使用,如div和span
-
语义不明显时,如果可以用div或p,尽量用p,它默认有上下间距,对兼容特殊终端有利
-
不要用纯样式标签,如b,font,u等
-
需要强调的文本,可以包含在strong(加粗)或者em(斜体)中
-
使用表格时,标题用caption,表头用thead,主体部分用tbody包围,尾部用tfoot包围。表头和一般单元格要区分开,表头用th,单元格用td
-
表单域要用fieldset标签包起来,并用legend标签说明表单的用途
-
每个input标签对应的文本都用label标签,并通过为input设置id属性,在label中设置for=someid让说明文本和对应的input关联起来
HTML5新增的语义标签
header
footer
hgroup
nav
aside
section
article
行内元素有哪些?
a b(粗体) span img input strong select label em button textarea
块级元素有哪些?
div ul li dl(定义列表) dt(列表中的项目) dd(描述列表中的项目) p h1-h6 blockquote
空元素有哪些?
br meta hr(水平线分隔) link input img
inline、block、inline-block的区别?
inline:和其他元素都在一行上,高、行高及顶和底边距不可变,宽度就是其文字或图片的宽度,不可改变
block:总在新行上开始,高度,行高,及顶和底边距都可变,宽度缺省是它容器的100%,除非设定一个宽度
inline-block:将对象呈现为内联对象,但是对象的内容作为块对象呈现,块对象在一行内呈现,允许空格,可以设置块元素宽度和高度
inline和inline-block之间产生空隙的原因
因为标签段之间的空格
移除空格的方法
-
去掉HTML中的空格
-
使用margin负值,这个负值的大小与上下文的字体和文字大小相关,大约在3-4像素之间
-
只在最后一个a标签后加上</a>(兼容IE),在HTML5直接不加结束标签
-
使用font-size:0
-
使用letter-spacing(字符间距)、word-spacing(单词间距)
position的取值
static:默认值,对象遵循常规流
relative:对象遵循常规流,并参照自身在常规流中通过top、right、bottom、left这4个定位偏移属性进行偏移时不会影响常规流中的任何元素
absolute:对象脱离常规流,此时偏移属性参照的是离自身最近的定位祖先元素,如果没有定位的祖先元素,则一直回溯到body元素。盒子的偏移位置不影响常规流中的任何元素,其中margin不与其他任何margin折叠
fixed:与absolute一致,但偏移定位是以窗口为参考。当出现滚动条时,对象不会随着滚动
center:与absolute一致,但偏移定位是以定位祖先元素的中心点为参考,盒子在其包含容器内水平居中(CSS3)
清除浮动的方法?
1.父级div定义height
-
原理:父级div手动定义height,解决了父级div无法自动取得高度的问题
-
优点:简单,代码少
-
缺点:只适合高度固定的布局,要给出精确的高度
2.在结尾加一个空div标签,style="clear:both"
-
原理:添加一个空div,利用css的clear:both清除浮动,让父级div能自动获取高度
-
优点:简单,代码少,浏览器支持好
-
缺点:如果页面浮动布局过多,就要增加很多空div,不建议使用
3.父级div定义,伪类:after和zoom(IE专有属性,解决IE6,7浮动问题)
father:after{display:block;clear:both;content:"";visibility:hidden;height:0}
father{zoom:1}
-
优点:浏览器支持好
-
缺点:代码多
4.父级div定义 overflow:hidden
-
原理:必须定义width,同时不能定义height,使用overflow:hidden时,浏览器会自动检查浮动区域的高度
-
优点:简单代码少,浏览器支持好
-
缺点:不能和position配合使用,因为超出的尺寸会被隐藏
5.父级定义 overflow:auto
-
原理同上
-
优点:简单,浏览器支持好
-
缺点:内部宽高超过父级div时,会出现滚动条
BFC
定义:(Block formatting context)块级格式化上下文,是一个独立的渲染区域,只有Block-level box参与,它规定了内部的Block-level Box如何布局,并且与这个区域外部毫不相干
BFC布局规则:
-
内部的Box会在垂直方向,一个接一个地放置
-
box垂直方向的距离有margin决定。属于同一个BFC的两个相邻的box的margin会发生重叠
-
每个元素的margin box的左边,与包含快border box的左边相接触,即使存在浮动也是如此
-
BFC的区域不会与float box重叠
-
BFC就是一个独立的隔离的容器,容器里面的子元素不会影响到外面的元素,反之也如此
-
计算BFC高度时,浮动元素也参与运算
哪些元素会产生BFC:
-
根元素
-
float属性不为none
-
position为absolute或者fixed
-
display属性为inline-block、table-cell、table-caption、flex、inline-flex
-
overflow不为visible
盒模型
盒模型分为IE盒模型和W3C标准盒模型
1.W3C盒模型:width,height只包含content,不包含border和padding
2.IE盒模型:width,height包含border和padding,指的是content+padding+border
在IE8+的浏览器中可以设置box-sizing控制,默认值为content-box,如果设为border-box则用的是IE盒模型
Transform/transition/animation
transform:一个静态属性,一旦写到style里面会直接显示作用,无任何变化过程
transform/animation:这两个属性实现的功能都是一样的,通过控制属性变化的过程,实现动画,都是立足于控制本身dom和css属性变化过程,来实现动画的视觉效果;区别在于,两者的控制力度不一样,transition更加粗糙一点,对过渡的速度进行了封装,可以控制是匀速改变还是贝塞尔曲线等,而animation指定的keyframe方法,可以手动去指定每个阶段的属性,此外还有循环次数、动画延迟等功能,更加自由强大
padding/margin使用百分比单位
这种情况下都是根据最近的父级容器的宽度width进行计算的
使用百分比单位的目的:弥补元素高度无法自适应地与元素宽度保持一致的缺陷
css元素权重
-
内联样式表的权值最高 1000。
-
ID
选择器的权值为 100。 -
Class
类选择器的权值为 10。 -
HTML
标签(类型)选择器的权值为 1。 -
属性选择器的权重为10
-
通配符权重为0
-
伪类选择器权重为10
-
伪元素选择器权重为1
JavaScript数据类型
一共7种:字符串、数字、布尔值、数组、对象、Null、Undefined
this的指向问题
this:在执行上下文创建时确定的一个在执行过程中不可更改的变量
this只在函数调用阶段确定,也就是执行上下文创建的阶段进行赋值,保存在变量对象中,所以也导致了this的多变性,函数在不同的调用方式下都可能导致this的值不同
但是this的指向在严格模式下和非严格模式下有所不同。当函数独立调用时,严格模式下this指向undefined,非严格模式下,当this指向undefined时,自动指向全局对象(浏览器中就是window)
当一个对象在全局声明时,对象内部属性中的this指向全局对象,当对象在一个函数中声明时,严格模式下this会指向undefined,非严格模式下转为指向全局对象
函数的调用方式:
-
在全局环境或是普通函数中直接调用
1 var a = 1; 2 var obj = { 3 a:2, 4 b:function (){ 5 function fun(){ 6 return this.a 7 } 8 console.log(fun()); 9 } 10 } 11 obj.b();//1
-
作为对象的方法
1 var a = 1; 2 var obj = { 3 a:2, 4 b:function() { 5 return this.a 6 } 7 } 8 console.log(obj.b())//2
b所引用的匿名函数作为obj的一个方法调用,这时候this指向调用它的对象,这里也就是obj
那么不作为对象的方法调用是什么意思呢?
1 var a = 1; 2 var obj = { 3 a:2, 4 b:function(){ 5 return this.a; 6 } 7 } 8 var t = obj.b; 9 console.log(t());//1
这里的t应该理解为指向b属性的指针
-
使用apply或者call
-
作为构造函数
构造函数:通过new关键字批量生产我们需要的对象的函数,如FUNCTION、object、Array、Date等全局定义的构造函数,注意,构造函数首字母要大写
1 function Category(){ 2 this.name = 'annika'; 3 this.school = 'college'; 4 this.height = '160'; 5 this.run = function(){ 6 return this.name + '正在跑步'; 7 } 8 } 9 Fun.prototype = { 10 constructor:Fun, 11 say: function(){ 12 return this.name + '正在说话'; 13 } 14 } 15 var f = new Fun(); 16 f.run(); //annika正在跑步 17 f.say(); //annika正在说话
如果函数作为构造函数用,那么其中的this就代表它即将new出来的对象
new做了如下的事:
-
创建一个临时对象
-
给临时对象绑定原型
-
给临时对象对应的属性复制
-
将临时对象返回
箭头函数:
箭头函数不可以用call和apply改变this
1 var a = 1; 2 var obj = { 3 a:2 4 }; 5 var fun = () => console.log(this.a); 6 fun(); //1 7 fun.call(obj); //1 8 var a = 1; 9 var obj={ 10 a:2 11 }; 12 function fun(){ 13 var a = 3; 14 let f = () => console.log(this.a); 15 fun(); 16 } 17 fun();//1 18 fun.call(obj);//2
注意,此时fun的上下文就是箭头函数所在的上下文,因此此时的f的this为fun的this,也就是window,当fun.call(obj)再次调用时,新的上下文被创建,fun此时的this为obj,也就是箭头函数的this值
1 function Fun(){ 2 this.name = 'annika'; 3 } 4 Fun.prototype.say = () => { 5 console.log(this); 6 } 7 var f = new Fun(); 8 f.say(); //window
此时箭头函数所在的上下文是__proto__所在的上下文也就是Object函数的上下文,而object的this值就是全局对象
1 function Fun(){ 2 this.name = 'annika' 3 this.say = () => { 4 console.log(this); 5 } 6 } 7 var f = new Fun(); 8 f.say(); //Fun的实例对象
此时箭头函数所在的上下文变成了Fun的上下文环境,因为当函数作为构造函数调用的时候,上下文环境的this指向实例对象
作用域和作用域链
变量的作用域分为两种:全局变量和局部变量
全局作用域:最外层定义的变量拥有全局作用域,对于其内部函数来说,都是可以访问的
局部作用域:一般只在固定的代码片段内可以访问到,对于函数外部是无法访问的,最常见的例如函数内部
作用域链:内部函数可以访问外部函数变量的机制,用链式查找决定哪些数据能被内部函数访问。
执行环境(execution context):每个函数运行时都会产生一个执行环境js为每一个执行环境关联了一个变量对象。环境中定义的所有变量和函数都保存在这个对象中。
全局执行环是最外围的执行环境,被认为是window对象,因此所有的全局变量和函数都是作为window的属性和方法创建的。
js的执行顺序是根据函数的调用来决定的,当一个函数被调用时,该函数环境的变量对象就被压入一个环境栈中,而在函数执行之后,栈将该函数的变量对象弹出,把控制权交给之前的执行环境变量对象
当某个函数第一次被调用时,就会创建一个执行环境,及相应的作用域链,并把作用域链赋值给一个特殊的内部属性[scope]。然后用this,arguments和其他的命名参数的值来初始化函数的活动对象。当前执行环境的变量对象始终在作用域链的第0位。
标识符解析是沿着作用域链一级一级地搜索标识符的过程。搜索过程始终从作用域链的前端开始,然后逐级向后回溯,直到找到标识符为止
闭包
定义:一种内部函数的作用域链依然保持着对父函数活动对象的引用的函数
作用:
-
可以读取自身函数外部的变量(沿着作用域链查找)
-
可以让这些外部变量始终保存在内存之中
缺点:
1.由于闭包会使得函数中的变量都保存在内存中,内存消耗很大,所以不能滥用闭包,否则会造成网页的性能问题,在IE中可能导致内存泄漏,解决方法是,在退出函数之前,将不使用的局部变脸全部删除
2.闭包会在父函数外部,改变父函数内部变量的值。
原型与原型链
函数的原型对象:在js中,我们在声明一个函数的时候,浏览器的内存中会创建一个对象B,而且而且每个函数都默认会有一个属性prototype指向了这个对象,这个B就是函数A的原型对象,简称函数的原型。这个原型对象B默认会有一个属性constructor指向这个函数A。原型对象默认只有属性:constructor。其他都是从Object继承而来
使用构造函数创建对象:把一个函数作为构造函数,使用new创建对象的时候,那么这个对象就会存在一个默认的不可见的属性,来指向了构造函数的原型对象。这个不可见的属性一般用[[prototype]]来表示,只是这个属性没有办法直接访问到
与原型有关的几个属性和方法:
1.constructor属性:存在于原型对象中,它指向构造函数。我们有时候可以根据prototype属性,来修改原来的原型对象,但是经过修改,原型对象会失去对原来的constructor的指针,此时可以手动再绑定回去
2.__proto__属性:个别浏览器提供了对[[prototype]]的访问方法,就是这个属性,这个是对象的属性,但是尽量不要用这种方法访问,他有可能改变这个对象的继承原型链
3.hasOwnProperty()方法
用来判断一个属性是来自对象本身,还是来源于其所在的原型链。注意只能判断是不是来自对象,不能判断原型中存不存在,因为不管存不存在都会返回false
4.in操作符
用来判断一个属性是否存在于这个对象中。但是在查找这个属性的时候,先在对象自身中找,如果对象找不到,再到原型中去找。只要对象和原型中有一个地方存在这个属性,就返回true
原型创建对象的缺陷:因为原型中的所有属性都是共享的,所以如果对一个对象的原型的属性做了修改,会反应到所有的对象上面。这个共享的形式对属性值是函数的属性很方便
构造函数模型创建对象的缺陷:构造函数中的所有属性和方法,每个对象独有,不会共享,但是对方法又不太合适,因为有时只需要一个方法
组合模式解决缺陷:基于以上两台产生了组合模式,原型模式适合封装方法,构造函数模式适合封装属性,综合两种模式的优点就有了组合模式
动态原型模式创建对象:组合模式有一点不完美,就是把构造方法和原型分开写,让人感觉不舒服,所以应该想办法把构造方法和原型封装在一起,所以有了动态原型模式。动态原型模式把所有的属性和方法都封装在构造方法中,仅在需要的时候才去构造方法中初始化原型,又保持了原型和构造函数的优点。
1 function(name,age){ 2 this.name = name; 3 this.age = age; 4 if(typeof this.eat !== "function"){ 5 Person.prototype.eat = function(){ 6 return(this.name + 'eating') 7 } 8 } 9 } 10 var p1 = new Person('annika',21); 11 p1.eat();
宏任务与微任务
-
宏任务按顺序执行,且浏览器在每个宏任务之间渲染页面
-
所有的微任务也按顺序执行,且在以下场景会立即执行所有微任务
-
每个回调之后,且js执行栈中为空
-
每个宏任务结束之后
-
跨域问题
-
为什么会出现跨域问题
-
处于浏览器的同源策略限制,浏览器会拒绝跨域请求,但不是所有的跨域都会被拒绝,有如下三种情况:
-
通常浏览器允许跨域写操作(cross-site writes),如链接、重定向
-
通常允许跨域资源嵌入(cross-site embedding),如img,script
-
通常浏览器不允许跨域读操作(cross-site reads)
-
-
-
什么情况算作跨域
-
非同源请求,同源——两个页面拥有相同的协议(protocol),端口(port)和主机(host),那么这两个页面就属于同一个源(origin)
-
-
为什么有跨域需求
-
工程服务化以后,不同职责额服务分散在不同的工程中,往往这些工程的域名是不同的,一个需求可能对应多个服务,这是就需要使用不同的服务接口,因此会出现跨域
-
-
如何实现跨域
-
最常用的跨域方式有三种:JSONP、CORS、postMessage
-
JSONP
-
实现原理:虽然不能通过XHR请求不同域上的数据,但是在页面上引入不同域的js脚本是可以的,在js文件载入完毕后,触发回调,可以将需要的data作为参数传入
-
实现方式(需前后端配合)
1 <script type="text/javascript"> 2 function dosomething(data){ 3 //处理获得的数据 4 } 5 </script> 6 <script src="http://example.com/data.php?callback=dosomething"></script> 7 <?php 8 $callback = $_GET['callback'];//得到回调函数名 9 $data = array('a','b','c');//要返回的数据 10 echo $callback.'('.json_encode($data).')';//输出 11 ?>
-
优缺点
优点:兼容性好
缺点:1.JSONP只支持GET请求;2.XMLHTTPRequest相对于JSONP有更好地错误处理机制
-
-
CORS(cross-origin resource sharing )跨域源资源共享
-
基本思想:使用自定义的HTTP头部让浏览器与服务器进行沟通,从而决定请求或响应是应该成功还是失败。
-
IE实现
1 var xdr = new XDomainRequest(); 2 xdr.onload = function(){ 3 alert(xdr.responseText); 4 } 5 xdr.open("get","http://www.somewhere-else.com/page/"); 6 xdr.send(null);
-
其他浏览器:原生支持CORS
1 var xhr = createXHR(); 2 xhr.onreadyStatechange = function(){ 3 if(xhr.readyState == 4){ 4 if ((xhr.status >= 200 && xhr.status < 300) || xhr.status == 304){ 5 alert(xhr.responseText); 6 } else{ 7 alert("Request was unsuccessful:" + xhr.status); 8 } 9 } 10 }; 11 xhr.open("get","http://www.somewhere-else.com/page/",true); 12 xhr.send(null);
-
-
postMessage
-
window.postMessage(message,targetOrigin)方法是html5新引进的特性,可以用它来向其他window对象发送信息,无论同源不同源
1 otherWindow.postMessage(message,targetOrigin,[transfer]);
-
-
前端攻击
-
XSS攻击
深拷贝和浅拷贝
-
主要区别:在内存中的储存类型不同
栈是自动分配的内存空间,它由系统自动释放,而堆是动态分配的内存,大小不定,也不会自动释放
-
基本数据类型(boolean/number/string/undefined/null)存放在栈中,是直接按值存放的,可以直接访问,且基本数据类型的值不可变,基本类型的比较是值得比较,只要值相等就认为其相等
-
引用类型(object)存放在堆中,变量实际上是一个存放在栈内存的指针,这个指针指向堆内存中的地址。每个空间大小不一样,要根据情况进行特定的分配。引用类型的值可变,引用类型的比较是引用的比较,看其引用是否指向同一个对象
-
传值与传址:基本数据类型的赋值是在内存中新开辟一段栈内存,然后再把值赋到新的栈中,所以两个变量相互独立不影响;引用类型的赋值是传址,只是改变指针的指向,也就是说引用类型的赋值是对象保存在栈中的地址的赋值,两个变量指向同一个对象,因此两者之间操作相互有影响
-
浅拷贝只复制一层对象的属性,并不包括对象里面的为引用类型的数据。
-
深拷贝是对对象及所有的子对象进行拷贝
ajax的过程
0:未初始化,尚未调用open方法
1:启动,已经调用open方法,但尚未调用send方法
2:发送,已经调用send方法,但是尚未收到响应
3:接收,已经收到部分响应数据
4:完成,已经接收到全部响应数据,可以在客户端使用了
1 //创建XMLHttpRequest对象 2 var xmlhttp = null; 3 if(window.XMLHttpRequest) 4 { 5 xmlhttp = new XMLHttpRequest(); 6 } 7 else if(window.ActiveXObject) 8 { 9 xmlhttp = new ActiveXobject("Microsoft.XMLHTTP"); 10 } 11 12 if(xmlhttp != null) 13 { 14 //指定响应函数 15 xmlhttp.onreadyStatechange=State_change; 16 //打开连接(指定请求) 17 xmlhttp.open("GET","/example/note.xml",true); 18 //发送请求 19 xmlhttp.send(null) 20 } 21 22 //创建响应函数 23 function State_change() 24 { 25 if(xmlhttp.readyState == 4) 26 { 27 if(xmlhttp.status == 200) 28 { 29 //具体逻辑 30 } 31 } 32 else 33 { 34 alert("error"); 35 } 36 }
es6新特性
-
let和const
-
变量的解构赋值
-
箭头函数
-
Set和Map结构
-
Iterator和for..of循环
-
Generator函数
-
promise对象
-
三个状态(resolve/reject/pending)
-
async函数
-
Genarator函数的语法糖
-
优点在于内置执行器,拥有更好地语义和更广的适用性
-
-
-
class
事件捕获和事件冒泡
事件冒泡:先触发子元素的处理器,再触发父元素的事件处理器,每向上走一层都会检查这一层有没有事件处理器,如果有就触发,如果没有就继续向上查找,直到顶层body
事件捕获:从body向下找,如果父级元素有事件处理器就先触发父级元素,再向下一层,直到这个点击位置的最底层,也就是target
DOM2级事件流总共分为三个阶段:事件捕获、在目标元素上、事件冒泡
两个方法:addEventListener()和removeEventListener(),接受三个参数:事件名称、事件处理器,和布尔值,布尔值默认为false,代表事件处理器在冒泡阶段触发,true代表在捕获阶段触发
Dom0级事件只能在冒泡阶段触发
如果要一次性注册多个事件:
-
对于非target节点先执行捕获再执行冒泡
-
对于target节点先执行先注册事件
处理事件代理时,event有两个特殊属性,event.target和event.currentTargret,前者是事件触发元素,后者是事件绑定元素,大部分情况下,在使用事件代理时,event.target是子元素,event.currentTarget是父元素