HTML&CSS
Doctype的作用?严格模式和混杂模式有什么区别?它们有何意义
Doctype文档声明,放在文档中的最前面,处于html标签之前。它用于告知浏览器的解析器,用什么文档类型规范来解析这个文档;
严格模式下的排版和js运作模式是以该浏览器支持的最高标准运行的,而混杂模式中,页面以宽松的向后兼容的方式显示,模拟老式浏览器的行为以防止站点无法工作;
Doctype不存在或格式不正确将会导致文档以混杂模式呈现。HTML5有哪些新特性?如何处理HTML5新标签的浏览器兼容问题?如何区分HTML和HTML5?
HTML5不基于SGML,主要是关于图像、位置、存储、多任务等功能的添加。- 绘画canvas;
- 用于媒体回放的video和audio元素;
- 本地离线存储localStorage和sessionStorage,localStorage长期存储数据,浏览器关闭后数据不会丢失;sessionStorage的数据在浏览器关闭后会自动删除;
- 语义化更好的内容元素,如header,footer,nav,section,article,menu,aside等;
- 更丰富的表单控件,如calender,date,time,range,email,url,search,color,tel,number等;
- 新的技术,webworker,websocket,Geolocation
IE8/IE7/IE6支持通过document.createElement方法创建新标签,可以利用这一特性让这些浏览器支持HTML5标签,浏览器支持新标签后,还需要添加默认标签样式。当然也可以直接使用成熟的框架,比如html5shim。
- 你所知道的浏览器内核都有哪些?
- trident内核:IE浏览器
- gecko内核:Firefox浏览器
- webkit内核:Chrome浏览器、Safari浏览器
- presto内核:Opera浏览器
- cookie,sessionStorage,LocalStorage的区别?
sessionStorage和localStorage都是HTML5 Web Storage API提供的,可以方便地在web请求之间保存数据。有了本地数据,就可以避免数据在浏览器和服务器间不必要地来回传递。
它们三者都是在浏览器段存储数据。sessionStorage是在同源的窗口中始终存在的数据,也就是只要这个浏览器窗口没有关闭,及时刷新页面或进入另一个同源页面,数据仍然存在。关闭窗口后,sessionStorage即被销毁。同时“独立”打开的不同窗口,即使是在同一页面,sessionStorage对象也是不同的。
- cookie
每个域名存储量比较小(各浏览器不同,大致4K);
所有域名的存储量有限制(各浏览器不同,大致4K);
有个数限制(各浏览器不同);
会随请求发送到服务器。 - sessionStorage
只在session内有效;
存储量大(推荐没有限制,但是实际上各浏览器也不同)。 - localStorage
永久存储;
单个域名存储量比较大(推荐5MB,各浏览器不同);
总体数量无限制。
- cookie
cookie和session分别用来做什么?有什么关系?
- cookie用来在客户端存储用户信息,session用来在服务器端存储用户数据,均可用于跟踪用户状态。
- 由于存放在服务器端,session较存放在客户端的cookie来说更安全,用户验证等重要信息一般存放在session,但cookie也可以加密来提高安全性。
- cookie在使用脚本或用户手动删除前永久保存,最大一般为4KB,session会在会话期间保存,当访问增多会家中服务器负担。
- session的实现需要cookie的支持:session依赖session ID实现,而session ID存放与cookie,因此,禁用cookie之后session也会失效。但可使用其他方法实现,比如在URL中传递session ID。
- session在服务器端可存放于文件(默认)、数据库或内存。
- session支持各种数据类型对象,cookie只保存字符串。
CSS选择器有哪些?它们的优先级是怎样的?
CSS选择器有ID选择器,class选择器,伪类选择器,标签选择器,通配符选择器,属性选择器。它们的优先级为:
!important > 行内样式(1000)> ID选择器(100)> class选择器/伪类选择器(10)> 标签选择器/伪元素选择器/属性选择器(1)> 通配符选择器(0)。
更多CSS权重的相关内容见这里。px、em、rem、vh、vw之间的区别?
link和@import的区别?
两者都是永不外部引用CSS的方式,它们的区别是:- link是XHTML标签,除了加载CSS外,还可以定义RSS等其他事物,@import属于CSS范畴,只能加载CSS;
- link引用CSS时,在页面载入时同时加载,@import需要页面网页完全嵌入以后加载;
- link是XHTML标签,无兼容性问题,@import是在CSS2.1提出的,低版本的浏览器不支持;
- link支持使用JavaScript控制DOM去改变样式,而@import不支持。
HTML中的列表有哪些?它们的结构是怎样的。
HTML中的列表包括有序列表ol(order list)、无序列表ul(unorder list)和定义列表dl(definite list)。它们的结构分别如下:
<ol><li></li></ol>
<ul><li></li></ul>
<dl>
<dt></dt> <!-- 定义列表中的项目 -->
<dd></dd> <!-- 描述列表中的项目 -->
</dl>
li里面放标题的话用什么标签?
li里面只能放<h4>
~<h6>
标签img和input标签是属于块级还是行内元素?是行内的话为什么可以设置宽高?
img和input都是属于行内替换元素(inline replaced elements),属于inline element类。height/width/padding/margin均可用,效果等同于块级元素。
行内非替换元素(inline non-replaced elements),height/width/padding-top、bottom/margin-top、bottom均无效果,只能用padding-left/right和margin-left/right。
(X)HTML中的<img>
、<input>
、<textarea>
、<select>
、<object>
都是替换元素,这些元素往往没有实际的内容,即是一个空元素。替换元素一般有内在尺寸,所有具有width和height,可以设定。例如不指定img的width和height时,就按其内在尺寸显示,也就是图片被保存时的宽度和高度。什么是浮动?会产生哪些问题?如何解决?
浮动:通过设置元素的float属性不为none,使元素按照特定的方向脱离文档流,浮动元素碰到包含它的边框或者浮动元素的边框停留。
产生的问题:最常见的问题是父元素高度塌陷;同时浮动元素后面的非浮动元素会被提高,可能会被互动元素遮挡;对于浮动元素前面 的同级行内非浮动元素会紧随其后,导致布局混乱。
解决办法:对于同级的元素,不需要浮动的直接将其clear:both即可。对于父元素高度塌陷的解决方法大概可以分为一下三种:- 给父元素直接设置高度
这种方法比较死板,必须要精确计算出父元素的高度,否则布局还是会很混乱。 加一个没有脱离文档流的具有高度的元素,触发父元素的高度
父元素高度之所以会塌陷,是由于子元素浮动脱离了文档流,没有东西来把它给撑起来。那么在父元素所有子元素的最后加一个没有脱离文档流的元素,同时不允许它左右出现浮动,那么该元素必须通过增加外边距(css 2 以下)/增加空白(css 2.1 以上)来向下移动,直到换行。为了能让该元素换行,父元素必须包含浮动元素来提供足够的空间。从而父元素的高度也就撑起来了。为了不显示额外的东西,因此我们添加的是一个空标签。通常是通过添加<div>
、<br>
标签,并为其设置clear:both;
属性来清除浮动。添加空标签的方法会导致在html结构复杂,因此建议在简单页面中使用。
另外通过:after
伪元素清除浮动也是同样的原理,它是在父元素中添加一个不可见的标签。这种方法比较常用,但是代码有点偏多,而且会有浏览器的兼容问题。.clearfix:after { content: ""; display:block; width: 0; heigth: 0; clear: both; } .clearfix { /*兼容IE*/ zoom: 1; }
触发父元素BFC,根据BFC的“计算BFC的高度时,浮动元素也要参与计算”的规则强制父元素具有高度
BFC就是一个隔离的独立容器,容器里面的元素不会影响到外面的元素。在计算BFC的高度时,浮动元素也要参与计算,因此让父元素成为一个BFC,就可以解决它的高度塌陷问题。常用方法是设置父元素的overflow
属性为hidden
或者auto
。这种方法代码简单,但是可能会隐藏掉溢出的内容或者产生滚动条。
- 给父元素直接设置高度
如何区分伪类和伪元素?
伪类,类,就是class,表示一些既有元素的状态,如a链接的各个状态(link,hover,visited,active);
伪元素,就是一个元素,逻辑上存在,但实际上并不存在在文档树中,如after和before。
在CSS3中为避免这两个混淆,特意用冒号来加以区分,伪元素前面是两个冒号(::after),伪类前面是一个冒号(:hover)。
常用的伪类有:
:hover
,:link
,:active
,:visited
,:first-child
,:nth-child
,:focus
常用的伪元素:
::after
,::before
,::first-line
,::first-letter
position有哪些值?分别在什么情况下使用?
position的值包括:
static:默认值,没有定位,元素出现在正常流中;
relative:生成相对定位的元素,相对于其在普通文档流中的位置进行定位,不会脱离文档流;
absolute:生成绝对定位的元素,相对于最近一级的定位不是static的父元素进行定位;
fixed:生成绝对定位的元素,相对于浏览器窗口进行定位。如何实现多行文字的垂直居中?
单行文字垂直居中使用的是line-heigt: height;
对于多行文字的垂直居中,可以将文字包括在一个容器内,将该容器进行垂直居中即可。
JavaScript
在父元素的第一个子元素之前插入一个节点,原生JS如何实现?
原生JS中插入元素的方法是insertBefore()
,语法为node.insertBefore(newnode[,existingnode])
,如果没有传入第二个参数,则默认在结尾处插入,与appendChild()
相同。<body> <ul id="myList"><li>Coffee</li><li>Tea</li></ul> <script> var newNode = document.createElement('li'); var nodeText = document.createTextNode('Water'); newNode.append(nodeText); var node = document.getElementById('myList'); var existingNode = node.childNodes[0]; node.insertBefore(newNode, existingNode); </script> </body>
插入之前:
插入之后:
也可以使用insertBefore()
插入/移除以后元素或移动已有元素的位置。
把一个列表中的元素移动到另一个列表:<body> <ul id="myList1"><li>Coffee</li><li>Tea</li><li>Milk</li><li>Water</li></ul> <ul id="myList2"><li>Coffee</li><li>Tea</li><li>Milk</li><li>Water</li></ul> <script> var list1=document.getElementById("myList1"); var list2=document.getElementById("myList2"); list1.insertBefore(list2.lastChild,list1.childNodes[0]); </script> </body>
插入之前:
插入之后:
移动列表中的某一项:var node=document.getElementById("myList1").lastChild; var list=document.getElementById("myList") list.insertBefore(node, list1.childNodes[0]);
插入之前:
插入之后:
用原生JS实现jQuery中的append和prepend方法?
append: 在被选元素的最后面插入指定内容//jQuery: $('#p').append('<b>hello world!</b>'); //JavaScript: var p = document.getElmentById("p"); p.appendChild('<b>hello world!</b>');
prepend: 在被选元素的开头插入指定内容
//jQuery: $('#p').prepend('<b>hello world!</b>'); //JavaScript: var p = document.getElmentById("p"); p.insertBefore('<b>hello world!</b>', p.childNodes[0]);
用原生JS实现一个方法,将传入的所有数组进行合并并返回,传入数组的个数不限,可以不考虑去重
方法一:直接对arguments进行遍历function concat() { var res = []; for(var i=0;i<arguments.length;i++) { res = res.concat(arguments[i]); } return res; }
方法二:使用arguments+apply
function concat() { return Array.prototype.concat.apply([], arguments); }
从一段字符串中截取其中的一小段,怎么实现?
用字符串的slice()
、substr()
和substring()
方法。但是它们三个都是需要传入两个参数,但是是有区别的。slice(start,end)
和substring(start,end)
中传入的参数都是指截取字符串的开始位置和结束位置(不包括),substr(start,len)
中的第一个参数是开始位置,第二个参数是指截取的子字符串长度。如果不传第二个参数,则都是默认到字符串的结尾。var str = "hello world!"; str.slice(4,7); //'o w' str.substring(4,7); //'o w' str.substr(4,7); //'o world'
需要注意的是:
substring()
始终以两个参数中的最小值为开始位置,大值为结束位置。str.slice(7,4); //'' str.substring(7,4); //'o w' str.substr(7,4); //'orld'
当传入的参数是负数时,
slice()
会将字符串的长度与相应的负数相加,结果作为参数,substring()
则会将所有的负数都转化为0;substr()
会将第一个参数与字符串长度相加后的结果作为参数传入。str.slice(-4); //'rld!' str.substring(-4); //'hello world!' str.substr(-4); //'hello world!' str.slice(3, -4); //'lo wo' str.substring(3, -4); //'hel' str.substr(3, -4); //''
你对JSONP的理解?JSONP用原生JS怎么来写?它是怎样来实现跨域的?
JSONP(JSON with padding),填充式JSON。JSONP主要是利用<script>
标签的src属性没有同源限制,通过动态创建<script>
标签来实现跨域请求数据。
JSONP的原生JS写法://http://a.com/index.html function callbackHandler(data) { //处理接收到的数据 } var script = document.createElement('script'); script.src = 'http://b.com/index.html?callback=callbackHandler'; document.getElementByTagName('head')[0].appendChild(script');
在后台中通过获取要查询的数据请求,在数据库中查找相应的数据,并将查找到的json格式的数据作为callback带过来的回调函数的参数传入(这个过程是在后台按照JS的语法进行拼接),返回拼接后的结果(callbackHandler(json)),动态插入脚本,即相当于一个函数调用,通过执行该函数对接收到的数据进行处理。JSONP方法只能发起GET请求,而没有POST方法。同时JSONP无法对错误进行调试,如果正确接收到了数据,就进行正常处理,如果没有接收到的话,就静默失败,不会有任何的报错。
在jQuery中调用JSONP:$.ajax({ type: 'GET', url: 'http://b.com/index.html', dataType: 'jsonp', jsonp: callbackHandler, success: function(data) {}, erro: function() {} })
img标签可以实现跨域吗?
<img>
标签也可以用来发起跨域请求,它的原理和JSONP类似,都是利用src属性没有同源限制的特点。同时可以利用onload和onerror事件来进行监听。var img = new Image(); img.onload = function() { //请求成功 } img.onerror = function() { //请求失败 } img.src = 'http://a.b.com/a.gif';
它只能发起get请求,并且不能访问响应文本,只能用来监听是否响应而已。
更多关于<img>
标签的特性请看这里。跨域的方法有哪些?分别有什么样的使用条件?
跨域方法除了上面说的JSONP,还有以下几种:
document.domain + iframe : 这种方法只能在主域相同的时候才能使用,也就是用a.example.com与example.com之间是可以使用的,但是a.example.com与a.b.com之间是不能够实用该方法的。
window.name : 它是利用iframe跳转到其他地址时,window.name值是不变的,同时window.name可以支持非常长的name值(2MB)。但是iframe的window.name值是不能够跨域读取的,因此通常需要一个代理页面,该页面与我们的主页面是同源的,使要跨域获取数据的页面跳转到该页面,再在主页面中读取代理页面的window.name的值。页面结构通常如下:
http://a.com/index.html 应用页面
http://a.com/other.html 代理页面
http://b.com/index.html 数据页面
cors策略:通过在服务器端设置Access-Control-Allow-Origin,就可以在客户端像平常使用ajax一样使用来跨域获取数据。该方法需要客户端和服务器端同时支持,目前主流浏览器基本都支持,但是IE要在IE10以上才支持。
window.postMessage方法:它是HTML5中提供的一个API。通过window.postMessage(msg, targetOrigin)
向目标窗口发送数据,目标窗口则通过message事件来进行监听是否有数据发送过来。//目标窗口 window.addEventListener('message',function(event){ var data = event.data; //发送过来的消息 var origin = event.origin; //消息的来源 },false);
JS继承有哪些方法?
构造函数继承
通过apply或call方法,将父对象的构造函数绑定在子对象上。function Animal() { this.species = '猫科动物'; } function Cat(name,color) { Animal.apply(this, arguments); this.name = name; this.color = color; } var cat = new Cat('huahua', 'white'); alert(cat.species); //'猫科动物'
原型继承
利用prototype属性function Animal() { this.species = "猫科"; } function Cat(name,color) { this.name = name; this.color = color; } Cat.prototype = new Animal(); //重写了Cat的原型对象,并将Cat.prototype.constructor指向了Animal Cat.prototype.constructor = Cat; //重新将Cat的constructor指向它自己原来的构造函数 var cat = new Cat('huahua','white'); alert(cat.species); //猫科
直接继承prototype
就是对第二种方法的改造。由于Animal对象中,不变的属性可以直接写入Animal.prototype中,因此我们可以对Animal对象进行一个改造,让Cat直接继承Animal的prototype。该方法的优点是效率高,比较省内存。缺点也很明显,Cat.prototype和Animal.prototype指向了同一个对象,任何对Cat.prototype的修改也会直接显示到Animal.prototype上,比如此时Animal.prototype.constructor也是Cat。function Animal() {} Animal.prototype.species = '猫科'; function Cat(name,color) { this.name = name; this.color = color; } Cat.prototype = Animal.prototype; Cat.prototype.constructor = Cat; var cat = new Cat('huahua', 'white'); alert(cat.species); //'猫科'
利用一个空对象作为中介
采用“直接继承prototype”方法具有上述的缺点,因此可以采用一个空对象作为中介。F是一个空对象,几乎不占用内存。function Animal() {} Animal.prototype.species = '猫科'; function Cat(name, color) { this.name = name; this.color = color; } var F = function () {}; F.prototype = Animal.prototype; Cat.prototype = new F(); Cat.prototype.constructor = Cat; var cat = new Cat('huahua','white'); alert(cat.species);
拷贝(浅拷贝)继承
直接把父对象的所有属性和方法复制给子对象。function Animal() {} Animal.prototype.species = '猫科'; function Cat(name, color) { this.name = name; this.color = color; } //把父对象prototype上的属性和方法一一拷贝给子对象 function extend(child, parent) { var c = child.prototype; var p = parent.prototype; for(var i in p) { c[i] = p[i]; } } extend(Cat, Animal); var cat = new Cat('huahua','white'); alert(cat.species);
深拷贝
上面的是一种浅拷贝方法,它存在着一些缺点,例如当父对象的属性为一个数组或另一个对象时,实际上拷贝给子对象的是一个引用地址,而不是真正的拷贝,当改变子元素上的属性值时,父元素上的也会被篡改。因此又有了一种深拷贝方法。function deepCopy(p, c) { var c = c || {}; for(var i in p) { if(typeof p[i]==='object') { //如果父元素的属性是数组或对象,则在进行一次浅拷贝 c[i] = (p[i].constructor === Array ) ? [] : {}; deepCopy(p[i],c[i]); } else { c[i] = p[i]; } } return c; } var Chinese = { nation: '中国', birthPlace: ['上海', '北京', '杭州'] } var Doctor = deepCopy(Chinese); Doctor.birthPlace.push('成都'); alert(Doctor.birthPlace); //'上海', '北京', '杭州', '成都' alert(Chinese.birthPlace); //'上海', '北京', '杭州'
object()方法
上面的两种方法都是没有通过构造函数实现的继承。还有一种方法也可以不通过构造函数实现继承,那就是Douglas Crockford提出的object()方法。function object(o) { function F() {} F.prototype = o; return new F(); } var Chinese = { nation: '中国' } var Doctor = object(Chinese); Doctor.career = '医生'; alert(Doctor.nation); //'中国'
script标签的defer和async属性有啥区别?
defer是在HTML解析完之后才会执行,如果是多个,按照加载顺序依次执行;
async是在加载完成后立即执行,如果是多个,执行顺序和加载顺序无关。setTimeout中第一个参数除了使用匿名函数包裹,还可以怎么调用一个含参函数?
setTimeout
函数用来指定某个函数或某段代码在多少毫秒后执行,它返回一个参数,表示定时器的编号,可以用这个编号来取消定时器。var timer = setTimeout(func | code, delay);
setTimeout
函数的第一个参数我们通常是传入一个函数名或者一个匿名函数。setTimeout(func, 1000); function func() { //code } setTimeout(function() { //code }, 1000);
但是如果我们要在一定时间后执行一个带有参数的函数时,该怎么办呢?起始最容易想到的就是在匿名函数里面直接执行这个函数,并把参数传进去就行了。
function add(a,b) { console.log(a+b); } setTimeout(function() { add(2, 3); },1000); //5
其实
setTimeout
里面不止可以传入两个参数,它还可以传入更多的参数,第三个以及后面的参数会作为第一个函数参数的参数传入,也就是说我们如果要调用一个含参函数,不仅可以使用匿名函数包括,还可以直接把要传入的参数写在setTimeout
函数的第二个参数后面就可以了。setTimout(add, 1000, 2, 3); //5
setTimeout函数还有更多的功能,详见 这里。
DOM的onload和domContentLoaded事件有什么区别?
onload事件是在页面中所有的元素,包括图片、样式和脚本等全部都加载完毕后才开始执行;而domContentLoaded事件是在页面中的HTML文档完全被加载和解析(即所有的DOM完全解析)时触发,而不需要等待图片和脚本等加载。写一个方法,从cookie里面读取数据。
function getCookie(name) { var cookies = document.cookie; var pos = cookies.indexOf(name); if(pos !== -1) { var start = pos + name.length + 1; var end = cookies.indexOf(';', start); if(end == -1){ end = cookies.length; } var value = decondeURIComponent(cookies.substring(start,end); } return value; }
扩展:
cookie有哪些属性?
cookie中除了name和value之外,还包括expires(过期时间)、domain(域名)、path(路径)、secure(安全性)、size(大小)。既然有了cookie为什么还需要localStorage?
localStorage和cookie相比,它的存储量更大,可以用在浏览器段缓存更多数据,同时它仅保存在客户端中,不参与服务器端通信,但cookie会每次都随着请求发送到服务器端。同时,localStorage存储和读取数据的操作更简单:window.localStorage.setItem(key, value); //存储数据 window.localStorage.getItem(key); //获取数据 window.localStorage.removeItem(key); //删除某一项数据 window.localStorage.clear(); //删除所有数据
但是,localStorage是没法取代cookie的,为了防止XSS攻击,浏览器支持通过设置httpOnly来阻止js读取cookie,但是localStorage中并没有针对XSS的防御措施。
js是单线程还是多线程?为什么?
JavaScript是单线程的,作为浏览器脚本语言,JavaScript的作用主要是与用户互动,以及操作DOM。这决定了它只能是单线程的,否则会造成混乱。比如,假定JavaScript同时有两个线程,一个线程在某个节点上添加内容,而另一个线程删除了这个节点,这时浏览器就会陷入矛盾了。
单线程就意味着所有任务都需要排队,前一个任务结束了,才会执行后一个任务。如果前一个任务耗时很长的话,后一个任务就不得不一直等待。为了解决这个问题,又提出了“任务队列”的概念。将所有的任务分成了两种,同步任务和异步任务。同步任务是在主线程上排队执行的任务,只有前一个任务执行完毕,才能执行后一个任务;异步任务是不进入主线程,而进入任务队列的任务,所有的同步任务执行完成之后,再来依次判断当前这个异步任务是否可以执行了,如果可以,则该任务进入主线程执行。
在HTML5中提出了Web Worker标准,它允许JavaScript脚本创建多个线程,但是子线程完全受主线程控制,并且不得操作DOM。web Worker是运行在后台的JavaScript,独立于其他脚本,不会影响页面的性能,一般用它来处理一些耗时和消耗CPU的任务。
更多详细内容请看 这里不使用临时变量,交换a、b两个数
主要是利用+ -去进行运算,例如a = a+(b-a);最终得到的是a = b;function swap(a,b) { b = b - a; a = a + b; b = a - b; return [a,b]; }
js垃圾回收机制是怎样的?
JavaScript中的垃圾回收机制主要有两种,标记清除和引用计数。
标记清除:这是JavaScript中最常见的垃圾回收方式,当变量进入执行环境的时候,比如函数中声明一个变量,垃圾回收器将其标记为“进入环境”,当变量离开环境的时候(函数执行结束)将其标记为“离开环境”;
引用计数:它的策略是跟踪记录每个值被使用的次数,当声明了一个变量并将一个引用类型赋值给该变量的时候这个值的引用次数就加1,如果该变量的值变成了另外一个,则这个值的引用次数就减1,当这个值的引用次数变为0时,说明没有变量在使用,这个值没法被访问了,因此可以将其占用的空间回收,这样垃圾回收器会在运行的时候清理掉引用次数为0的值的占用空间。用原生js实现ES6 中的Map方法。
(暂时没有想出来。)let和var的区别?
let
和var
都是用来声明变量的,对于声明后未赋值的变量都会输出undefined
,它们的区别包括:对于先使用后声明的变量,var定义的会输出
undefined
,而let定义的会报错;console.log(a); //undefined console.log(b); //Uncaught ReferenceError: b is not defined var a = 4; let b = 5;
重复定义同一个变量,
var
定义的后一个变量会覆盖前一个,而用let会报错,它只能定义一次;var a = 4; let b = 5; var a = 6; let b = 7; //Uncaught SyntaxError: Identifier 'b' has already been declared console.log(a); //6
let
的作用域是块(也是就是大括号包裹的范围),而var
的作用域是函数var a = 5; var b = 10; if(a === 5) { let a = 4; var b = 1; console.log(a); //4 console.log(b); //1 } console.log(a); //5 console.log(b); //1
你所知道的前端性能优化方法有哪些?
对于CSS、JS文件进行压缩的原理是什么?
压缩主要是通过删除注释、换行符和无用的空白符等,在js中还可能会替换掉变量名,将长的变量名替换成如'a'
,'b'
,'c'
这种尽可能短的变量名,从而达到将文件压缩变小的目的。XHR.readyState的状态有哪些?
XHR的状态总共有5个,分别为:
0 —UNSENT
请求未初始化,XHR
对象已经成功构造,但是open()
方法还未被调用;
1 —OPENED
请求已建立但未发送。open()
方法已成功调用,但send()
方法还未被调用,注意:只有xhr
处于OPENED
状态,才可以调用xhr.setRequestHeader()
和xhr.send()
,否则会报错;
2 —HEADERS_RECEIVED
已获取响应头。send()
方法已被调用,响应头和响应状态已经返回;
3 —LOADING
正在下载响应体。响应体正在下载中,此状态下通过xhr.response
可能已经有了响应数据;
4 —DONE
整个数据传输过程结束,不管本次请求是成功还是失败。var xhr = new XMLHttpRequest(); xhr.open('GET/POST', 'http://a.com/index.html'); xhr.onreadyStateChange = function(){ if(xhr.readyState === 4) { if(xhr.status === 200) { //doSomething } } } xhr.send();
事件有哪几个阶段?
js事件分为三个阶段,分别是事件捕获、目标和事件冒泡阶段。什么是闭包?
闭包(closure)是指有权访问另一个函数作用域中的变量的函数。创建闭包常见的形式是在函数内部创建一个函数,通过返回这个函数可以访问到包括它的函数的局部变量。
闭包有三个特性:- 函数嵌套函数;
- 函数内部可以引用函数外部的参数和变量;
- 参数和变量不会被垃圾回收机制回收。
使用闭包的好处:
- 希望一个变量长期保存在内存中;
- 避免全局变量的污染;
- 私有成员的存在。
闭包带来的问题:
闭包的缺点是常驻内存,会增大内存使用量,使用不当可能会造成内存泄露。例如:function closure() { var oDiv = document.getElementById('oDiv'); //oDiv用完之后一直保存在内存中 oDiv.onclick = function() { alert(oDiv.innerHTML); //造成内存泄露 } } //正确的做法 function closure(){ var oDiv = document.getElementById('oDiv'); var html = oDiv.innerHTML; oDiv.onclick = function() { alert(html); } oDiv = null; //解除引用 }
AMD、CMD、UMD和Commonjs的区别?
Commonjs
是服务器端模块的规范,Nodejs
采用的就是这个规范,它的加载是同步的,只有在加载完成后才能执行后面的操作;
AMD(Asynchronous Module Definition)
是Requirejs
在推广过程中对模块定义的规范化产出,它是浏览器第一原则发展,异步加载模块,使用define
方法来定义模块;
CMD(Common Module Definition)
是Seajs
在推广过程中对模块定义的规范化产出。对于依赖的模块,AMD
是提前执行,而CMD
是延迟执行;CMD
是依赖就近,按需加载,而AMD
是依赖前置;
UMD
是AMD
和Commonjs
的糅合,UMD
会先判断是否支持Nodejs
的模块(exports
是否存在),如果存在则使用Nodejs
模块模式,否则,再判断是否支持AMD
(define
是否存在),如果存在,则使用AMD
模式加载模块。
详见 这里 和 这里从输入URL到页面完全呈现,经历了哪些过程?
处理异步的方法有哪些?
数组方法中的
map
、every
、some
、forEach
、filter
、reduce
的区别?
every
和some
返回的是布尔值,其中every
是数组中的每一项都符合函数中的条件时返回true
,some
是只要数组中有一项满足函数中的条件就返回true;
map
返回一个新数组,新数组每一项为调用函数后的结果;
filter
返回数组中符合函数条件的数组,一般用于对数组进行过滤操作;
forEach
没有返回值,对数组中的每一项运行传入的函数;
reduce
迭代数组的每一项,然后构建一个返回的值。接收一个callBack
函数,该函数接收四个参数:prev
: 上一次调用回调返回的值,或者是提供的初始值(initialValue
,作为reduce
的第二个参数传入,如果不传,第一次的prev
为数组的第一项,即迭代从第二项开始);cur
: 数组中当前被处理的数组项;index
: 当前被处理项在数组中的索引;array
: 调用reduce()
方法的数组。
//数组中所有项的求和 var values = [1, 2, 3, 4, 5]; var sum1 = values.reduce(function(prev, cur, index, array) { return prev + cur; }); console.log(sum1); //15 var sum2 = values.reduce(function(prev, cur, index, array) { return prev + cur; }, 10); console.log(sum2); //25
与
reduce
相对应的还有一个reduceRight
方法,顾名思义,它是从数组的最后一项开始往前迭代数组的每一项。call、apply、bind的区别?
三者都是用来改变函数的this对象的指向的,第一个参数都是this
要指向的对象,即想要指定的上下文。区别在于:
call
传入参数数量不固定,第二部分参数要一个一个传,用,
隔开;
apply
接收两个参数,第二个参数为一个带下标的集合,可以为数组,也可以为类数组;
bind
只能传一个参数,它返回一个改变了上下文的函数副本,便于稍后调用,而call和apply都是立即调用。js设计模式有哪些?
HTTP
谈一谈对前端安全的理解,有什么?怎么防范?
前端安全问题主要有XSS、CSRF。
XSS(Cross Site Scripting):跨站脚本攻击
它可以简单地理解为一种JavaScript脚本注入。通过在目标页面恶意注入JavaScript脚本,盗取用户的cookie信息等。
例如,在前端get一个请求:www.a.com/xss.php?name=userA
后台处理:
<?php echo 'hello'.$_GET['name'];
代码本意是通过queryString的name值来动态展示用户名,但是由于未对name值做编码校验,当链接为:
www.a.com/xss.php?name=<script>alert(document.cookie);</script>
XSS的防护措施:
- 过滤转义输入输出
- 尽量避免
eval
、new Function
等执行字符串的方法,除非确定字符串与用户输入无关 - 使用cookie的httpOnly属性,让js无法读取cookie
- 使用innerHTML、document.write的时候,如果数据是用户输入的,那么需要对关键字符进行过滤与转义
CSRF(Cross-site Request Forgery):跨站请求伪造
网站的一些提交行为,被黑客利用,在你访问黑客的网站的时候进行操作,会被操作到其他网站上。
CSRF防御措施:- 检测http referer是否是同域名;
- 避免登录的session长时间存储在客户端
- 关键请求使用验证码或token机制。
延伸: 什么是token机制?它保存在哪儿?
token机制的执行逻辑如下图所示:HTTP和HTTPS 的区别?HTTPS是如何进行加密的?
HTTPS协议是有SSL+HTTP协议构建的可进行加密传输、身份认证的网络协议,要比HTTP协议安全。它们的主要区别包括:- https协议需要到ca申请认证,一般免费证书较少,因而需要一定的费用;
- http是超文本传输协议,信息是明文传输,https是具有安全性的SSL加密传输协议;
- http和https使用的是完全不同的连接方式,用的端口号也不同,前者是80,后者是443;
- http的连接很简单,是无状态的;https协议是SSL+HTTP协议构建的可进行加密传输、身份认证的网络协议,比https协议安全。
https加密、解密及验证过程,如下图所示:
- 客户端发起https请求;
- 服务器端接收到请求后产生一对公匙和私匙;
- 服务器端将公匙(证书)传送给客户端;
- 客户端解析证书,如果证书无效,则弹出警告框,如果证书没有问题,则产生一个随机值,并将这个随机值用证书(公匙)进行加密;
- 客户端将机密后的随机值发送给服务器端;
- 服务器端用私匙进行解密,得到客户端传过来的随机值,并用这个随机值对要传输的内容进行加密处理;
- 服务器端将机密后的信息传输给客户端;
- 客户端用之前生成的随机值解密传过来的信息,至此完成了整个的加密传输过程。
扩展
http与https的区别
【上】安全HTTPS-全面详解对称加密,非对称加密,数字签名,数字证书和HTTPS
【下】安全HTTPS-全面详解对称加密,非对称加密,数字签名,数字证书和HTTPSTCP和UDP的区别?
TCP和UDP都是OSI模型中的运输层协议。它们的主要区别包括:- TCP面向连接(如打电话要先拨号建立连接);UDP是无连接的,即发送数据之前不需要建立连接
- TCP提供可靠的服务,也就是说通过TCP连接传输的数据,无差错,不丢失,不重复,且按序到达;UDP尽最大努力交付,即不保证可靠交付,有可能会丢包
- TCP面向字节流,实际上是TCP把数据看成一连串无结构的字节流;UDP是面向报文的,UDP没有阻塞控制,因此网络出现拥塞不会使源主机的发送效率降低(对实时应用很有用,如IP电话,实时视频会议等)
- 每一条TCP连接只能是点到点的;UDP支持一对一,一对多,多对一和多对多的交互通信
- TCP首部开销20字节;UDP首部开销小,只有8个字节
- TCP的逻辑通信信道是全双工的可靠信道;UDP则是不可靠信道
http状态码知道哪些?301和302的区别?200和304的区别?
如何强制使用浏览器缓存?
你对CDN有哪些了解?