关于事件
1、事件的三个阶段
捕获 目标 冒泡
低版本IE(IE8及以下版本)不支持捕获阶段
捕获事件流:Netscape提出的事件流,即事件由页面元素接收,主机箱下,传播到最具体的元素
冒泡事件流:IE提出的事件流,即事件由最具体的元素接收,逐级向上,传播到页面
2、IE和W3C不同绑定事件解绑事件的方法有什么区别,参数是什么,以及事件对象ev有什么区别
W3C:target.addEventListener(event,listener,useCapture);
event——事件类型;
listener——事件触发时执行的函数;
useCapture——指定时间是否再补货或冒泡阶段执行,为true时事件句柄在捕获阶段执行,为false(默认false)时,事件句柄在冒泡阶段执行。
例:
btn.addEventListener('click',function(){
//do something...
},false);
对应的事件移除:
removeEventListener(event,function,capture/bubble);
例:
removeEventListener(event,function,capture/bubble);
IE:target.attacEvent(type,listener);
type——字符串,事件名称,含"on",比如"onclick","onmouseover"等。
listener——实现了EventListener接口或者是JavrScript中的函数。
例:
btn.attachEvent('onclick',function(){
//do something...
})
对应的事件移除:
detachEvent(event,function);
目前支持以addEventListener绑定事件的浏览器:FF、Chrome、Safari、Opera、IE9-11
目前支持以attachEvent绑定事件的浏览器:IE6-10
阻止:
通过stopPropagation()或cancelBubble来阻止事件向下一级元素传递。
cancelBubble是IE标准下阻止事件传递的属性,设置cancelBubble=true表示阻止冒泡
3、【事件的代理/委托】的原理以及优缺点
原理:靠事件的冒泡机制来实现的
优点:
1.可以大量节省内存占用,减少事件注册,比如在table上代理所有td的click事件就非常棒
2.可以实现当新增子对象时无需再次对其绑定事件,对于动态内容部分尤为合适
缺点:
事件代理的应用常用应该仅限于上述需求下,如果把所有事件都代理就可能会出现事件误判,即本不应用触发事件的被绑上了事件(有人把页面里所有的时间都绑定到document用委托的,只是及其不智的做法)
4、实现事件代理,要求兼容浏览器(考核对事件对象ev的了解程度,以及在IE下对应的属性名)
用target,currentTarget,以及IE下的srcElement和this(说完这些可略过了)
5、实现事件模型
即写一个类或是一个模块,有两个函数,一个bind一个trigger,分别实现绑定事件和触发事件,核心需求就是可以对末一个事件名称绑定多个事件响应函数,然后触发这个事件名称时,依次按绑定顺序触发相应的响应函数。
(这个需求如果对于做过C#的人来讲就再熟悉不过,他根本就是C#中的【委托】(delegate)。而委托与事件几乎是一家子。回到前面说的题目,大致实现思路就是创建一个类或是匿名函数,在bind和trigger函数外层作用域创建一个字典对象,用于存储注册的事件及响应函数列表,bind时,如果字典没有则创建一个,key是事件名称,value是数组,里面放着当前注册的响应函数,如果字段中有,那么就直接push到数组即可。trigger时调出来依次触发事件响应函数即可)
6、事件广播(dispatchEvent)
一般在元素上绑定事件后,是靠用户在这些元素上的鼠标行为来捕获或者触发事件的,或者自带的浏览器行为事件,比如click,mouseover,load等等,有些时候我们需要自定义事件或者在特定的情况下需要触发这些事件。这时可以使用IE下fireEvent方法,高级浏览器(chrome,firefox等)有dispatchEvent方法。
性能优化
简单来说,常用的优化有两部分:
第一:面向内容的优化
- 减少 HTTP 请求
从设计实现层面简化页面
合理设置HTTP缓存(即能缓存越多越好,能存越久越好)
- 减少 DNS 查找
- 避免重定向
- 使用 Ajax 缓存
- 延迟载入组件
- 预先载入组件
- 减少 DOM 元素数量
- CDN加速的原理
通过缓存技术,将网站文字,图片,视频等内容缓存到分布与各地的节点服务器上,使终端访问用户就近访问,从而提高访问速度。
- 最小化 iframe 的数量
- 不要出现http 404 错误
第二:面向 Server
1.cookie优化
减少Cookie大小
页面内容使用无Cookie域名
- 针对 Web 组件使用域名无关性的
怎样实现原生JS异步加载
默认情况javascript是同步加载的,也就是javascript的加载时阻塞的,后面的元素要等待javascript加载完毕后才能进行再加载,对于一些意义不是很大的javascript,如果放在页头会导致加载很慢的话,是会严重影响用户体验的。
有以下三种方法:
(1) defer,只支持IE
defer属性的定义和用法(我摘自w3school网站)
defer 属性规定是否对脚本执行进行延迟,直到页面加载为止。
有的 javascript 脚本 document.write 方法来创建当前的文档内容,其他脚本就不一定是了。
如果您的脚本不会改变文档的内容,可将 defer 属性加入到 <script> 标签中,以便加快处理文档的速度。
因为浏览器知道它将能够安全地读取文档的剩余部分而不用执行脚本,它将推迟对脚本的解释,直到文档已经显示给用户为止。
示例:
代码如下:
<script type="text/javascript" defer="defer">
alert(document.getElementById("p1").firstChild.nodeValue);
</script>
(2) async:
async的定义和用法(是HTML5的属性)
async 属性规定一旦脚本可用,则会异步执行。
示例:
代码如下:
<script type="text/javascript" src="demo_async.js" async="async"></script>
注释:async 属性仅适用于外部脚本(只有在使用 src 属性时)。
注释:有多种执行外部脚本的方法:
•如果 async="async":脚本相对于页面的其余部分异步地执行(当页面继续进行解析时,脚本将被执行)
•如果不使用 async 且 defer="defer":脚本将在页面完成解析时执行
•如果既不使用 async 也不使用 defer:在浏览器继续解析页面之前,立即读取并执行脚本
(3) 创建script,插入到DOM中,加载完毕后callBack,见代码:
代码如下:
function loadScript(url, callback){
var script = document.createElement_x("script")
script.type = "text/javascript";
if (script.readyState){ //IE
script.onreadystatechange = function(){
if (script.readyState == "loaded" || script.readyState == "complete"){
script.onreadystatechange = null;
callback();
}
};
} else { //Others: Firefox, Safari, Chrome, and Opera script.onload = function(){
callback();
};
}
script.src = url;
document.body.appendChild(script);
JSONP原理
只要说到跨域,就必须聊到JSONP,就必须讲一下JSONP的实现原理,以及在项目中哪个需求使用了JSONP,简单讲就是HTML里面所有带src属性的标签都可以跨域,如iframe,img,script等。
所以可以把需要跨域的请求改用成script脚本加载即可,服务器返回执行字符串,但是这和字符串实在window全局作用下执行的,你需要把他返回到你的代码的作用域内,这里就需要临时创建一个全局的回掉函数,并传到后台,最后再整合实际要求的数组,返回给前端,让浏览器直接调用,用回调的形式回到你的源代码流程中
将url的查询参数解析成字典对象
这个题目不约而同的出现在了多家公司的面试题中,当然也是因为太过于典型,解决方案无非就是拆字符或者用正则匹配来解决,我个人强烈建议用正则匹配,因为url允许用户随意输入,如果用拆字符的方式,有任何一处没有考虑到容错,就会导致整个js都报错。而正则就没有这个问题,他只匹配出正确的配对,非法的全部过滤掉,简单,方便。
实现代码:
function getQueryObject(url){
url = url == null? window.location.href : url;
var search = url.substring(url.lastIndexOf("?") + 1);
var obj = {};
var reg = /([^?&=]+)=([^?&=]*)/g;
search.replace(reg,function(rs,$1,$2){
var name = decodeURIComponent($1);
var val = decodeURIComponent($2);
var = String(val);
obj[name] = val;
return rs;
});
return obj;
}
设计模式
面试时被问到较多的有观察者模式,职责链模式,工厂模式
主要是应用于js开发组件中经常设计,纯粹的页面业务逻辑可能涉及不多
比如如何去设计一个前端UI组件,应该公开出哪些方法,应该提供哪些接口,应该提供哪些事件。哪部分逻辑流程应该开放出去让用户自行编写,如何实现组件与组件之间的通信,如何实现高内聚低耦合,如何实现组件的高复用等等