1. 实现不同窗体间互相通信
(1) window.postMessage()
支持跨文档消息传输(Cross Document Messaging),并且可跨域传输信息。
兼容性:IE>=8 支持
消息监听:onmessage
,可接受任意发送过来的消息,应对消息来源(e.origin
)进行安全验证
window.addEventListener('message', fn, false);
消息发送:postMessage
,获取到要传送消息的窗体对象,向其发送消息。
targetWindow.postMessage(message, targetOrigin);
(2) 监听 localStorage
的 storage
事件,当localStorage
的键值改变时,就会触发此事件。
window.addEventListener("storage", fn, false);
window.attachEvent("onstorage", fn);
2. CORS
CORS(跨域资源共享)需要浏览器和服务器同时支持,除IE<10以外,其他浏览器都已支持,而且服务器实现了CORS接口,就可以跨源通信了。通过在服务器端设置 HTTP header 字段,服务器可以声明哪些网站有权限访问哪些资源。
浏览器将CORS请求分两类,简单请求和非简单请求。
(1) 简单请求:(不会触发CORS预检请求)
条件:1. 请求方法是HEAD
GET
或 POST
; 2. HTTP header是对CORS安全的首部字段。
浏览器和服务器之间使用 CORS 首部字段来处理跨域权限。浏览器在请求头信息中增加一个Origin
字段,用来说明本次请求来自哪个源,服务器根据这个字段值来决定是否同意这次请求。
如果Origin
指定的源不在许可范围,服务器会返回一个正常的 HTTP 回应,这个回应的头信息不包含Access-Control-Allow-Origin
字段,浏览器会抛出一个错误,这个错误会被 XMLHttpRequest
的 onerror
回调函数捕获。这个错误不能通过状态码识别,因为服务器返回的是一个正常的回应,状态码可能为200。
如果Origin
指定的源在许可范围内,服务器返回的响应会包含几个头信息字段:Access-Control-Allow-Origin
(Access-Control-Allow-Credentials
、Access-Control-Expose-Headers
、Content-Type
可选)
(2)非简单请求:(会进行预检请求)
条件:对服务器有特殊要求的请求,请求方法是PUT
或DELETE
,或者Content-Type
字段类型是application/json
。
非简单请求会在正式通信前,增加一次HTTP预检请求。
浏览器会先询问服务器,当前域名是否在服务器许可名单中,以及可以使用哪些HTTP请求方法和头信息字段,当得到服务器肯定回应,会返回Access-Control-Allow-Origin
字段,表示可以请求数据。如果服务器返回否定回应,会触发一个错误,被XMLHttpRequest
的 onerror
回调函数捕获。
3. flex-box
弹性盒子(flexible box)是CSS3的一种新的布局模式,尤其是在屏幕宽度自适应时,弹性布局使得父元素中子元素的排列、对齐、空间分配变得更便捷灵活。对于可伸缩的父元素,子元素随其扩大或缩小,并能够合适地填满父元素空间。弹性布局包括多个CSS3属性,有设置在父元素上的,有设置在子元素上的。
父元素上的属性:
display:flex;
让父元素变成Flex容器。
flex-direction
:子元素在Flex容器中放置的方向 (row
|row-reverse
|column
|column-reverse
)
flex-wrap
:默认子元素都显示在一行(nowrap
),改变此属性(wrap
/wrap-reverse
),可以让子元素多行显示。
justify-content
:在主轴上对齐子元素,在父元素有多余的空间时才会进行空间分配 (flex-start
|flex-end
|center
|space-between
|space-around
)
align-items
:在侧轴上对齐子元素(flex-start|flex-end|center|baseline|stretch)
align-content
:当多行的Flex容器的侧轴还有多余空间时,可用来调节多行子元素在容器内的对齐方式(flex-start
|flex-end
|center
|space-between
|space-around
|stretch
)
子元素上的属性:
order
:默认子元素是按文档中的顺序显示排列的,在Flex容器中可通过order
属性改变子元素的显示顺序
flex-grow
:可以定义子元素的扩大比例。所有的子元素值相同时,占据相同的空间。
flex-shrink
:可以定义子元素的缩小比例。
flex-basis
:子元素的分配Flex容器剩余空间之前的默认尺寸。0表示不考虑子元素周围额外空间,auto
表示额外空间根据flex-grow
值做分配。
flex
:flex-grow
、flex-shrink
和flex-basis
三个属性的缩写。默认值:0 1 auto
。
align-self
:单个子元素侧轴的对齐方式。
4. 前端优化
(1) 加载页面和静态资源方面:
静态资源的合并压缩;使用缓存;使用CDN;后端渲染,数据直接输出到html模版
(2)页面渲染和页面操作方面:
减少DOM操作;懒加载;css放在html文档的前面加载,js在后面加载;事件节流;尽早执行操作(DOMContentLoaded
: DOM渲染完即可操作,此时图片视频可能还没加载完;window.onload
:页面全部资源加载完才会执行,包括图片视频)
5. BFC
块级格式化上下文(block formatting context),创建了新BFC的盒子是独立布局的,是一个隔离的独立容器盒子,里面子元素的样式不会影响到外面的元素,盒子里的子元素的布局和定位和外部元素互不影响。
作用:
(1) 避免margin重叠。块级标签之间竖直方向的margin会重叠。可以用overflow:hidden;
来解决。
(2) 清除浮动。子元素浮动,父元素的高度会塌陷,父元素高度不会被子元素撑开的问题。
触发BFC的条件:
(1) 浮动元素:float
的值不为none
。
(2) position
的值为absolute
或fixed
。
(3) overflow
为除了visible
以外的值(auto
、scroll
或hidden
)。
(4) display
的值为inline-block
、table-cell
、table-caption
。
6. web安全-攻击及防范
(1) XSS 跨站脚本攻击(Cross Site Scripting)
攻击者向web页面插入恶意html标签或者js代码。比如一个看似安全的链接,用户点击后,攻击者窃取用户的cookie;或者攻击者在网页中加一个恶意表单,用户提交表单的时候,把消息传送到攻击者的服务器,而不是原本的站点。
防范:对用户输入做字符过滤或者转义;避免在cookie中泄露用户隐私;最好使用POST提交表单。
(2) CSRF 跨站请求伪造(Cross-site request forgery)
要完成一次CSRF攻击,关键点:登录受信任的网站A,并在本地生成cookie;在不登出A的情况下,访问危险网站B。
防范:通过验证码或者token检测用户;避免全站通用cookie,设置cookie的域;用户操作最好都使用POST。
7. getElementsByClassName
获取页面元素的方法:
getElementById()
返回一个对象
getElementsByTagName()
返回集合
getElementsByClassName()
返回集合
querySelector()
返回一个对象
querySelectorAll()
返回集合
8. 前端路由实现的方式
(1)h5: window.history
兼容性:IE<=9 不支持
window.history.pushState(data, title, url);
window.history.replaceState(data, title, url);
url: 绝对路径/相对路径, 同域。
这两个API可操作浏览器的历史栈,而不会引起页面刷新。
区别:pushState
会在历史记录中增加一条,replaceState
会替换当前的历史记录。
(2)hash: 当页面的 hash (window.location.hash
) 发生变化时,会触发hashchange
事件。
兼容性:IE<=7 不支持
window.onhashchange = fn;
window.addEventListener("hashchange", fn, false);
9. 一个DOM元素绑定既绑定了冒泡事件,又绑定了捕获事件,执行顺序是?
绑定在同一个DOM元素上的事件,是按照事件声明的顺序执行的,无论是捕获还是冒泡;
这个DOM元素能够“感知”到的绑定在其他元素上的事件(比如绑定在其父元素上的事件),会按照先捕获事件,后冒泡事件的顺序执行。
var box = document.getElementById('box'); // 容器
var btn = document.getElementById('button'); // 容器内的按钮
// btn
btn.addEventListener('click', function(){ //1
console.log('bubble','btn');
}, false);
btn.addEventListener('click', function(){ //2
console.log('capture','btn');
}, true);
// box
box.addEventListener('click', function(){ //3
console.log('bubble','box');
}, false);
box.addEventListener('click', function(){ //4
console.log('capture','box');
}, true);
//点击按钮,console.log:
"capture" "box" //4
"bubble" "btn" //1
"capture" "btn" //2
"bubble" "box" //3
//调换1、2顺序,打印结果顺序也会调换
//调换3、4顺序,打印结果不调换
10. HTTP及HTTP的缓存机制
11.apply、call、bind的区别
call、apply和bind都可以改变函数内部this
的指向。第一个参数都是this要指向的对象。
call和apply都可以改变某个函数运行的上下文,函数内部的this
指向call/apply的第一个参数。创建的时候会立即调用函数。
区别:函数的参数传入call/apply时,call第二个及其以后的参数位置依次传入函数的参数。apply的第二个参数是一个数组,是函数参数的数组。
bind会创建一个新的函数(绑定函数),创建的时候不会立即调用。当调用这个函数时,绑定函数会以创建时传入bind()方法的第一个参数作为this
,第二个及其以后的参数依次传入原函数的参数。
call/apply:
// 获取数组中的最大或最小值
var numbers = [1, 2, 3, 100, -100];
Math.max(...numbers);
Math.max.call(null, ...numbers); // 100
Math.max.apply(null, numbers); // 100
// 判断是否是数组
function isArray(obj) {
return Object.prototype.toString.call(obj) === '[object Array]';
}
bind:
var x = 1;
var module = {
x: 2,
getX: function(){
return this.x;
}
}
var fn = module.getX.bind(module);
fn(); // 2
12. 一个DOM元素注册多个事件,怎样做到只执行第一个事件,后面事件不执行?
(1)设置一个全局变量,同一个事件的所有回调函数都会判断这个条件,条件满足就执行,执行的时候就将这个条件置为false,这样就能保证只有第一个回调函数会执行。
var btn = document.getElementById('button');
var flag = true;
btn.addEventListener('click', function(){
console.log(1);
if(flag) console.log('1-inner');
flag = false;
}, false);
btn.addEventListener('click', function(){
console.log(2);
if(flag) console.log('2-inner');
}, false);
btn.addEventListener('click', function(){
console.log(3);
if(flag) console.log('3-inner');
}, false);
// console.log:
1
1-inner
2
3
我觉得,这不是个好方法。因为对于注册在元素上的多个同名事件,其实每个事件都执行了。在执行到回调函数内部再进行条件判断,并不算真正意义上的只执行了第一个事件吧。
(2)执行某个回调函数时取消事件监听
// 同一元素注册多个事件,同一个回调函数
var btn = document.getElementById('button');
var handler = function(){
console.log('1')
btn.removeEventListener('click', handler, false);
}
btn.addEventListener('click', handler, false);
btn.addEventListener('click', handler, false);
btn.addEventListener('click', handler, false);
// console.log:
1
这种方法,需要保证多个绑定事件的回调函数是同一个。如果多个事件绑定多个回调函数,则无效。
(3)利用Promise.race()
Promise.race()
方法简介:
Promise.race
函数返回一个Promise
,这个Promise
的完成方式,取决于传递进去的多个Promise
哪个先完成,可以是成功resolve
,也可以是失败reject
。
var p1 = new Promise(function(resolve, reject){
setTimeout(resolve, 1000, 'one');
});
var p2 = new Promise(function(resolve, reject){
setTimeout(resolve, 2000, 'two');
});
var p3 = new Promise(function(resolve, reject){
setTimeout(reject, 500, 'three'); // 最快执行
});
var p4 = new Promise(function(resolve, reject){
setTimeout(reject, 1000, 'four');
})
Promise.race([p1, p2, p3, p4]).then(function(value){
console.log(value);
}, function(reason){
console.log(reason);
});
// console.log:
'three'
一个DOM元素注册多个事件,只执行第一个事件。利用Promise.race()
实现:
var btn = document.getElementById('btn');
var handler = function(){
console.log('done');
};
var handler2 = function(){
console.log('done2');
};
var handler3 = function(){
console.log('done3');
};
var p1 = new Promise(function(resolve, reject){
console.log(1);
btn.addEventListener('click', handler, false);
resolve();
});
var p2 = new Promise(function(resolve, reject){
console.log(2);
btn.addEventListener('click', handler2, false);
resolve();
});
var p3 = new Promise(function(resolve, reject){
console.log(3);
btn.addEventListener('click', handler3, false);
resolve();
});
Promise.race([p1, p2, p3]).then(function(){
console.log('race');
btn.removeEventListener('click', handler2, false);
btn.removeEventListener('click', handler3, false);
})
// 点击按钮,依次打印:
1
2
3
race
done
网友评论