手写事件模型及事件代理/委托
- 捕获(低版本IE不支持捕获阶段)
- 目标
- 冒泡
绑定事件的方法:
target.addEventListener(eType, listener, useCapture);
useCapture一般为false。为true时在捕获阶段触发。
target.attachEvent(eType, listener);
(仅IE8及以下版本浏览器)
事件委托
可以大量节省内存占用,减少事件注册
可以实现当新增子对象时无需再次对其绑定事件,对于动态内容部分尤为合适
原生JS事件委托
关键代码
obj.onclick = function(e){
var ev = e || window.event;
var target = ev.target || ev.srcElement; //具体点到的目标,最内层
var cTarget = currentTarget || this; //对象this始终等于currentTarget
}
实现事件模型
即写一个类或是一个模块,有两个函数,一个bind一个trigger,分别实现绑定事件和触发事件,核心需求就是可以对某一个事件名称绑定多个事件响应函数,然后触发这个事件名称时,依次按绑定顺序触发相应的响应函数。
var Demo = (function(){
var dirc = {};
function bind(eType, func){
if(typeof eType == "string" && typeof func == "function"){
if(typeof dirc[eType] != "undefined"){
dirc[eType].push(func);
}else{
dirc[eType] = [func];
}
}else{
alert("bind传参有误");
}
console.log(dirc);
}
function trigger(eType){
if(typeof dirc[eType] != "undefined"){
for(var i=0; i < dirc[eType].length; i++){
dirc[eType][i]();
}
}
}
function unbind(eType){
if(typeof dirc[eType] != "undefined"){
delete dirc[eType];
}
}
return {
bind : bind,
trigger: trigger,
unbind : unbind
}
}());
Demo.bind("hello",function(){alert("hello")});
Demo.bind("hello",function(){alert("bbb")});
Demo.trigger("hello");
Demo.unbind("hello");
Demo.trigger("hello");
前端性能优化
- CDN
- 压缩静态文件
- CSS Sprite
- 图片压缩
- CSS靠前/JS靠后
- DOM操作放在一起
- lazyload
- 提高js的性能
闭包原理及应用
function fun(n,o) {
console.log(o)
return {
fun:function(m){
return fun(m,n);
}
};
}
var a = fun(0); a.fun(1); a.fun(2); a.fun(3); //undefined / ? / ? / ?
var b = fun(0).fun(1).fun(2).fun(3); //undefined / ? / ? / ?
var c = fun(0).fun(1); c.fun(2); c.fun(3); //undefined / ? / ? / ?
手写Function.bind函数
if (!Function.prototype.bind) {
Function.prototype.bind = function(oThis) {
if (typeof this !== "function") {
// closest thing possible to the ECMAScript 5
// internal IsCallable function
throw new TypeError("TypeError");
}
var aArgs = Array.prototype.slice.call(arguments, 1),
fToBind = this,
fNOP = function() {},
fBound = function() {
return fToBind.apply(this instanceof fNOP ? this : oThis || this,
aArgs.concat(Array.prototype.slice.call(arguments)));
};
fNOP.prototype = this.prototype;
fBound.prototype = new fNOP();
return fBound;
};
}
排序算法
快速排序(去重)
//第一种:
//找到一个基准值,左边找到大于基准值,右边找到小于基准值的同时,左右换位。继续遍历。
function sort(arr){
return quickSort2(arr,0,arr.length-1);
function quickSort2(arr,l,r){
if(l<r){
var mid=arr[parseInt((l+r)/2)],i=l-1,j=r+1;
while(true){
while(arr[++i]<mid);
while(arr[--j]>mid);
if(i>=j)break;
var temp=arr[i];
arr[i]=arr[j];
arr[j]=temp;
}
quickSort2(arr,l,i-1);
quickSort2(arr,j+1,r);
}
return arr;
}
}
//第二种:
//遍历数组, 小的放基准值左边,大的放右边,递归
var quickSort = function(arr) {
if (arr.length < 2) {
return arr;
}
var key = arr[0],
leftPart = [],
rightPart = [];
for (var i = 0; i < arr.length; i++) {
if (arr[i] < key) {
leftPart.push(arr[i]);
}
if (arr[i] > key){
rightPart.push(arr[i]);
}
//不去重的话可以在此增加判断,等于key的push到middle数组,最后拼接进去。
};
return quickSort(leftPart).concat([key],quickSort(rightPart));
}
冒泡排序
function bubbleSort(arr){
var result = arr,
len = result.length;
for(var i=0; i < len; i++){
for(var j=0; j < len-1-i; j++){
console.log(++sss);
if(result[j]>result[j+1]){
var temp = result[j];
result[j] = result[j+1];
result[j+1] = temp;
}
}
}
return result;
}
去重
function unique(arr){
var result=[],
record = {}; //记录每个元素出现的次数
for(var i=0; i< arr.length; i++){
if(record[arr[i]]){
record[arr[i]]++;
}else{
result.push(arr[i]);
record[arr[i]]=1;
}
}
return record;
}
JS的定义提升
function Foo() {
getName = function() {
alert(1);
};
return this;
}
Foo.getName = function() {
alert(2);
};
Foo.prototype.getName = function() {
alert(3);
};
var getName = function() {
alert(4);
};
function getName() {
alert(5);
}
//请写出以下输出结果:
Foo.getName();
getName();
Foo().getName();
getName();
new Foo.getName();
new Foo().getName();
new new Foo().getName();
跨域问题
1、document.domain+iframe的设置
对于主域相同而子域不同的例子,试用该方法。
xxx.a.com上的a.html
document.domain = 'a.com';
var ifr = document.createElement('iframe');
ifr.src = 'http://script.a.com/b.html';
ifr.style.display = 'none';
document.body.appendChild(ifr);
ifr.onload = function(){
var doc = ifr.contentDocument || ifr.contentWindow.document;
// 在这里操纵b.html
alert(doc.getElementsByTagName("h1")[0].childNodes[0].nodeValue);
};
script.a.com上的b.html
document.domain = 'a.com';
问题:
1、安全性,当一个站点(b.a.com)被攻击后,另一个站点(c.a.com)会引起安全漏洞。
2、如果一个页面中引入多个iframe,要想能够操作所有iframe,必须都得设置相同domain。
2、JSONP
思路
动态创建script标签,src带参数请求其它地址。
返回后进行操作。
最后要销毁。
3、利用iframe和location.hash
a.html和c.html同域
b.html不同域
实现a.html与b.html通信
a.html > b.html > c.html
HTML5 postMessage
//a.html
<iframe id="ifr" src="b.com/index.html"></iframe>
<script type="text/javascript">
window.onload = function() {
var ifr = document.getElementById('ifr');
var targetOrigin = 'http://b.com'; // 若写成'http://b.com/c/proxy.html'效果一样
// 若写成'http://c.com'就不会执行postMessage了
ifr.contentWindow.postMessage('I was there!', targetOrigin);
};
</script>
//b.html
<script type="text/javascript">
window.addEventListener('message', function(event){
// 通过origin属性判断消息来源地址
if (event.origin == 'http://a.com') {
alert(event.data); // 弹出"I was there!"
alert(event.source); // 对a.com、index.html中window对象的引用
// 但由于同源策略,这里event.source不可以访问window对象
}
}, false);
</script>
window.name跨域
a,b,c三个页面. a,c同域, b不同域
a里面添加iframe,设置src为b页面.b页面里面修改window.name,也就是iframe里面的window.name,
第一次onload时,将iframe里的src改成c的地址.此时c跟a同域,可以传递window.name.
document.domain+iframe的设置 (用于主域相同,子域不同的情况)
正则表达式
方法
var reg = /\w+/g;
reg.test(str);
str.match(reg);
规则
/d [0-9] 匹配数字
/D [^0-9] 匹配非数字字符
/s [ /n/r/t/f/x0B] 匹配一个空白字符
/S [^ /n/r/t/f/x0B] 匹配一个非空白字符
/w [a-zA-Z0-9_] 匹配字母数字和下划线
/W [^a-zA-Z0-9_] 匹配除字母数字下划线之外的字符
* 匹配前面的子表达式零次或多次。例如,zo* 能匹配 "z" 以及 "zoo"。 * 等价于{0,}。
+ 匹配前面的子表达式一次或多次。例如,'zo+' 能匹配 "zo" 以及 "zoo",但不能匹配 "z"。+ 等价于 {1,}。
? 匹配前面的子表达式零次或一次。例如,"do(es)?" 可以匹配 "do" 或 "does" 中的"do" 。? 等价于 {0,1}。
{n} n 是一个非负整数。匹配确定的 n 次。例如,'o{2}' 不能匹配 "Bob" 中的 'o',但是能匹配 "food" 中的两个 o。
{n,} n 是一个非负整数。至少匹配n 次。例如,'o{2,}' 不能匹配 "Bob" 中的 'o',但能匹配 "foooood" 中的所有 o。
'o{1,}' 等价于 'o+'。'o{0,}' 则等价于 'o*'。
{n,m} m 和 n 均为非负整数,其中n <= m。最少匹配 n 次且最多匹配 m 次。刘, "o{1,3}" 将匹配 "fooooood" 中的前三个 o。
'o{0,1}' 等价于 'o?'。请注意在逗号和两个数之间不能有空格。
字符串操作
concat() – 将两个或多个字符的文本组合起来,返回一个新的字符串。
indexOf() – 返回字符串中一个子串第一处出现的索引。如果没有匹配项,返回 -1 。
charAt() – 返回指定位置的字符。
lastIndexOf() – 返回字符串中一个子串最后一处出现的索引,如果没有匹配项,返回 -1 。
match() – 检查一个字符串是否匹配一个正则表达式。
substring() – 返回字符串的一个子串。传入参数是起始位置和结束位置。
substr() – 返回字符串的一个子串。传入参数是起始位置和字符串长度。(不建议使用)
replace() – 用来查找匹配一个正则表达式的字符串,然后使用新字符串代替匹配的字符串。
search() – 执行一个正则表达式匹配查找。如果查找成功,返回字符串中匹配的索引值。否则返回 -1 。
slice() – 提取字符串的一部分,并返回一个新字符串。
split() – 通过将字符串划分成子串,将一个字符串做成一个字符串数组。
length – 返回字符串的长度,所谓字符串的长度是指其包含的字符的个数。
toLowerCase() – 将整个字符串转成小写字母。
toUpperCase() – 将整个字符串转成大写字母。
数组操作
concat() – 连接两个或更多的数组,并返回结果。
join() – 把数组的所有元素放入一个字符串。元素通过指定的分隔符进行分隔。
pop() – 删除并返回数组的最后一个元素
push() – 向数组的末尾添加一个或更多元素,并返回新的长度。
reverse() – 颠倒数组中元素的顺序。
shift() – 删除并返回数组的第一个元素
unshift() – 向数组的开头添加一个或更多元素,并返回新的长度。
slice() – 从某个已有的数组返回选定的元素
sort() – 对数组的元素进行排序
splice() – 删除元素,并向数组添加新元素。
toSource() – 返回该对象的源代码。
toString() – 把数组转换为字符串,并返回结果。
toLocaleString() – 把数组转换为本地数组,并返回结果。
valueOf() – 返回数组对象的原始值
字符串和数组操作应用
//获取url参数
var getUrlPara = function(){
var arr = window.location.search.substring(1).split("&"),
result = {};
for(var i=0; i < arr.length; i++){
result[arr[i].split("=")[0]] = arr[i].split("=")[1];
}
return result;
}
设计模式
(这一块得回家看笔记...)
函数节流
通过setTimeout和clearTimeout对于频繁执行的函数进行延时处理。
例如: onresize, onscroll 还有drag之类的。(为避免由于频率过高导致一直都不执行,可以设置一个必然触发执行的时间间隔)
var throttle = function(fn, delay){
var timer = null;
return function(){
var context = this, args = arguments;
clearTimeout(timer);
timer = setTimeout(function(){
fn.apply(context, args);
}, delay);
};
};
var throttleV2 = function(fn, delay, mustRunDelay){
var timer = null;
var t_start;
return function(){
var context = this, args = arguments, t_curr = +new Date();
clearTimeout(timer);
if(!t_start){
t_start = t_curr;
}
if(t_curr - t_start >= mustRunDelay){
fn.apply(context, args);
t_start = t_curr;
}
else {
timer = setTimeout(function(){
fn.apply(context, args);
}, delay);
}
};
};