js相关笔记(十三)
1.定义Date日期对象的四种方式
【
//默认当前时间的定义 方式
var date1 = new Date();
console.log(date1);
//指定某个时间的定义 方式
var date2 = new Date("2019/1/31 15:48:20");//兼容最强
console.log(date2);
//直接指定字面量 但是兼容性不是很好 因为每个浏览器渲染的Date对象的字面量可能不一样
var date3 = new Date("Thu Jan 31 2019 15:48:20 GMT+0800 (中国标准时间)");
console.log(date3);
//指定年月日的方式 来定义
var date4 = new Date(2019, 1, 31);
console.log(date4);
】
2.操作日期对象
【
console.log(date1.getDate()); // 获取日 1-31
console.log(date1.getDay()); // 获取星期 0-6(0代表周日)
console.log(date1.getMonth()); // 获取月 0-11(1月从0开始)
console.log(date1.getFullYear()); // 获取完整年份(浏览器都支持)
console.log(date1.getHours()); //获取小时 0-23
console.log(date1.getMinutes()); //获取分钟 0-59
console.log(date1.getSeconds()); //获取秒 0-59
console.log(date1.getMilliseconds()); // 获取毫秒 (1s = 1000ms)
console.log(date1.getTime()); //返回累计毫秒数(从1970/1/1午夜)
】
3.为什么时间都是从1970/1/1午夜开始的,因为那是一个纪念的日子,有一个实验室,c语言和unix系统都是从那个实验室出来的,unix是收费的,linux是开源的,诺贝尔的获得者有七八个都是这个实验室的,后台他们把这个1970/1/1午夜被制定成了一个计算机时间的基本点,后面的人一直沿用至今。
4.返回当前距离时间基本点的毫秒数的四种方式
【
var date = Date.now();
console.log(date);
var date = +new Date();
console.log(date);
var date = new Date().getTime();
console.log(date);
var date = new Date().valueOf();
console.log(date);
】
5.获取页面代码执行的毫秒值
【
//获取代码开始执行之前的时间的毫秒数
var start=Date.now();
//执行的代码
// //开始进行1加到100000之间的运算
// var sum=0;
// for(var i=1;i<=100000;i++){
// sum+=i;
// }
//获取代码结束执行之后的时间的毫秒数
var end=Date.now();
console.log(end-start);//定义代码执行的毫秒数
】
5.简单数据类型无法绑定属性和方法,字符串之所以能够使用length属性和indexOf方法,完全是因为,当你使用字符串.属性或者方法的时候,底层会进行隐式转换复杂类型,将简单的字符串转换成了String对象了,所以才能够使用length属性和indexOf方法,用完之后会再转换回来。
6.真正的String对象的实例内部其实是这样的【
//创建String对象实例
var strobj=new String("abcdefg");
//打印该对象实例
console.log(strobj);//{0: "a",1: "b",2: "c",3: "d",4: "e",5: "f",6: "g", length: 7,[[PrimitiveValue]]: "abcdefg"}
每一个字符串都有对应的数字作为key,所以能够使用[]的方式来访问字符串中的每个字符。
】
7.for..in 遍历对象的时候只会遍历,允许让你访问的属性的名称,也就是key,你可以通过对象名[key]来获取value,也就是属性值。。
8.字符串获取单个字符的方式除了[]这种方式之外(IE678不支持这种方式),还能够使用charAt()方法,方法中的参数与[]中的参数一样,也是通过索引(key)来获取值(value)的,如果你想获取字符串中某个值得unicode码(十进制纯数字)的话,可以使用charCodeAt(key)方法,也是传索引,内部会先调用charAt(key)获取值,然后再进行unicode码的转换,数组的sort方法底层默认就是使用unicode码来进行比较的。【
var str="abcdefg";
console.log(str);//abcdefg
//使用[]方式来遍历
for(var i=0;i<str.length;i++){
console.log(str[i]);
}
//使用charAt方法来遍历 根据索引找到字符串 也是根据String对象中数字的key来找到字符
for(var i=0;i<str.length;i++){
console.log(str.charAt(i));
}
//使用charCodeA方法来遍历 也是根据索引找到字符串
// 然后再进行unicode码转换,最终变成了变成了数字 48代表1 65代表A 97代表a
for(var i=0;i<str.length;i++){
console.log(str.charCodeAt(i));
}
】
9.获取一条字符串的字符占位的长度(不是字符串的长度),可以使用charCodeAt()方法,unicode码在0-127中的话,所占的字符占位长度为1,除此之外的字符占位长度为2。【
function getCharlength(str) {
var count = 0;
for (var i = 0; i < str.length; i++) {
//占一个字符位的字符 unicode码在0-127
// 其它的占两个及两个以上的字符位的字符 unicode码不在0-127之间
if (str.charCodeAt(i) < 128 && str.charCodeAt(i) >= 0) {
count++;
} else {
count += 2;
}
}
return count;
}
】
10.基本包装类型 String/Number/Boolean对应了string/number/boolean,所以当这些简单数据类型调用方法或者属性时,底层都会去转换为复杂数据类型的对象实例来操作,操作完毕之后会再转换回去,相当于底层做了包装一样。
11.操作字符串的方法
◆charAt:根据字符串位置的索引返回指定字符
◆charCodeAt:根据字符串位置的索引返回指定字符的十进制的unicode编码的数值。
◆indexOf:根据字符串的字符返回索引值, 从前往后搜索。
◆lastIndexOf:根据字符串的字符返回索引值,从后往前搜索
◆encodeURIComponent:对url地址进行uri编码,他是window对象的方法,方法参数是字符串。
◆decodeURICompoent:对uri编码后的url进行解码,他是window对象的方法,方法参数是字符串。
◆concat:连接两个字符串,+的底层用的就是这个方法
◆slice:截取划分,和数组的silce方法类似,也是指定起始索引和结束索引,来截取原字符串返回新字符串,也是截取时包括左边的索引不包括右边的索引,索引为负数则为字符串的长度加上这个负数索引,如果起始索引大于结束索引则会返回空字符串,如果只有一个起始索引则会截取到最后。
◆substr:截取,和数组的splice方法类似,只不过这个方法只有截取没有替换,指定起始索引和要截取的长度来截取字符串,长度过大就直接等于字符串的长度,长度为负数则为0,索引为负数则字符串的长度加上这个负数索引。
◆substring:截取划分,和字符串的slice方法类似,只不过不同的地方是,索引为负数,那么直接返回全部字符串,如果起始索引大于结束索引,就会智能替换,把小的索引作为起始索引,大的索引作为结束索引。
◆trim:去掉字符串开头部分与结束部分的空格(IE678不支持,但是可以自己封装)。
◆replace:替换原字符串返回新字符串,第一个参数是原字符串中的某一段字符串,第二个参数是替换掉原字符串中的某一段字符串的字符串,使用str.replace(/\s/gi,"")可以替换掉所有的空格,这种方式叫做正则表达式。
12.字符串与数组之间的相互转换
◆join与split方法,数组通过join方法变成字符串,字符串通过split方法变成数组
【
//数组
var arr=["吕布","赵云","关羽","张飞","刘备"];
console.log(arr);//["吕布", "赵云", "关羽", "张飞", "刘备"]
//数组通过分隔符号连接成了字符串
console.log(arr.join("|"));//吕布|赵云|关羽|张飞|刘备
//字符串
var str="吕布|赵云|关羽|张飞|刘备";
console.log(str);//吕布|赵云|关羽|张飞|刘备
//如果split方法中的参数为空字符串,那么就会对字符串中每一个字符进行分割然后放入数组中,否做就会以分割符号来分割每一段字符串(不包括分隔符)然后放入数组中。
console.log(str.split("|"));//["吕布", "赵云", "关羽", "张飞", "刘备"]
】
13.字符串中的字母大小写相互转换
◆str.toLowerCase();//将str变量中的字母无论大小写都变成小写的字母,这个方法会返回新的字符串。
◆str.toUpperCase();//将str变量中的字母无论大小写都变成大写的字母,这个方法会返回新的字符串。
14.字符串中的转换为html标签字符串的方法
◆anchor()方法: "我是一个锚点".anchor("mao");等于<a name="mao">我是一个锚点</a>
◆link()方法: "我是一个超链接".link("http://www.baidu.com");等<a href="http://www.baidu.com">我是一个超链接</a>(记住这个)
◆其它不常用的标签【
var str="你好";
console.log(str.anchor());//<a name="undefined">你好</a>
console.log(str.big());// <big>你好</big>
console.log(str.sub());// <sub>你好</sub>
console.log(str.sup());// <sup>你好</sup>
console.log(str.link());//<a href="undefined">你好</a>
console.log(str.bold());//<b>你好</b>
】
15.数学对象的方法
◆Math.abs(num);num取绝对值
◆Math.pow(num1,num2);num1的num2次方
◆Math.power(num1,num2);num1的num2次幂
◆Math.sqrt(num);num开平方,相当于Math.power(num,2);
◆Math.sin(num);num的正弦
◆Math.cos(num);num的余弦
16.九尺龙泉万卷书,上天生我意如何。不能报国平天下,枉为男儿大丈夫。
17.事件绑定的方式
◆对象.事件名=匿名函数/普通函数【
//第一种方式 使用对象.属性的方式定义的,后面定义的事件会覆盖掉前面的
btn1.onclick=function(){
console.log("九尺龙泉万卷书,上天生我意何如。");
}
btn1.onclick=function(){
console.log("不能报国平天下,枉为男儿大丈夫。");
}
】
◆对象[事件名]=匿名函数/普通函数【
//第二种方式 使用对象[属性名]的方式定义的,后面定义的事件会覆盖掉前面的
btn2["onclick"]=function(){
console.log("九尺龙泉万卷书,上天生我意何如。");
}
btn2["onclick"]=function(){
console.log("不能报国平天下,枉为男儿大丈夫。");
}
】
◆对象.addEventListener(event,fn)或者对象.attachEvent(fullevent,fn)【
//第三种方式 底层原理是不断保存每次事件的地址,递归执行或者循环执行方法 所以不会被覆盖掉
//使用addEventListener更加有助于团队开发 因为前面两种方式都会采取最后面的覆盖掉前面的 但是IE678不支持
btn3.addEventListener("click",fn1);//直接写click 底层会加on
btn3.addEventListener("click",fn2);
//使用attachEvent 只有IE678支持
// btn3.attachEvent("onclick",fn1);//直接写onclick 底层不会再加on
// btn3.attachEvent("onclick",fn2);
】
★addEventListener与attachEvent区别在于, 在前者中用this关键字代表是的事件源对象,在后者中用this关键字代表的是window对象,内部机制不一样,在IE678中不能使用addEventListener来绑定事件,只能使用attachEvent绑定事件,但是在IE678之后addEventListener取代了attachEvent,因为内部机制进行了更新,更新的地方是,将函数里面的this不代表当前事件源的bug修改了。
18.事件解绑的方式
◆对象.事件=null【
//针对第一种绑定的方式的解绑
btn1.onclick=null;
】
◆对象[事件]=null【
//针对第二种绑定的方式的解绑
btn1["onclick"]=null;
】
◆对象.removeEventListener(event,fn)或者对象.detachEvent(fullevent,fn)【
//针对第三种绑定方式的解绑
btn3.removeEventListener("click",fn1);//解绑单击事件中的fn1
btn3.removeEventListener("click",fn2);//解绑单击事件中的fn2
//兼容IE678 因为只有IE678支持
// btn3.detachEvent("click",fn1)////解绑单击事件中的fn1
// btn3.detachEvent("click",fn2)////解绑单击事件中的fn2
】
19.事件的本质
◆事件的本质就是方法
◆方法的定义方式有三种:
◇function fn(){};
◇var fn=function(){};
◇var fn=new Function("");
◆事件是属于一个对象自己的方法
◇对象.methoed=fn;
◇对象.methoed=function(){};
◇对象.methoed=new Function("");
◆addEventListener与attachEvent可以实现多个方法的绑定,实际原理是将前一个事件如(onclick)的地址存起来,然后重新给这个事件赋值,先调用前一个事件如(onclick)的地址执行的方法,然后再调用你之后绑定的方法【
//addEventListener的底层原理
function addEvent(action,fn,element){
//把上一个事件的方法指针存起来
var tempEvent=element["on"+action];
element["on"+action]=function(){
//如果之前的事件已经绑定了方法
if(tempEvent){
//那么先执行之前的事件中的代码 再继续下面的
// console.log(tempEvent);
tempEvent();
}
//执行绑定的代码
fn();
}
}
】
◆addEventListener与attachEvent的原理远远不止上面的那么简单,其实它还进行了将每一个方法存到一个数组里面的操作,当每绑定一个方法,都会进行判断,是有已经有了这个方法,如果有了就不会再绑定重复的方法【
//添加事件
function addEvent(action,fn,element){
//判断当前事件对象中是否有这个行为的数组
if(!eventObj[action]){
//没有就创建
eventObj[action]=[];
}
//判断函数数组中是否有这个函数
for(var i=0;i<eventObj[action].length;i++){
if(eventObj[action][i]===fn){
return;
}
}
// //把当前的事件存到数组中 ,然后等到
// if(element["on"+action]){
//
// }
//有的话每次添加方法都是放入一个事件对象的这种行为数组中(栈中)
eventObj[action].push(fn);
element["on"+action]=function(){
//遍历栈中的方法 按照顺序循环执行
for(var i=0;i<eventObj[action].length;i++){
//IE 678使用的是 attachEvent 原理就是这样的
// eventObj[action][i]();
//IE678之后用的是这个 addEventListener 原理就是这样的
//这样就把方法中的this转换成了当前的对象
element.actionMethod=eventObj[action][i];
//由当前对象来调用
element.actionMethod();
}
}
}
】
★将以上两种结合起来就是系统的addEventListener和attachEvent原理,因为第一种和第二种加起来就能够兼容btn1.onclick=function(){},把它给添加到事件队列中去。
◆removeEventListener与detachEvent的简单原理【
//移除事件
function removeEvent(action,fn,element){
//IE678以上
//根据对象找索引 从后往前找,因为添加的时候是从后面添加的
// var index= eventObj[action].lastIndexOf(fn);
//直接切掉这个对象
// eventObj[action].splice(index,1);
//为了兼容IE678 因为indexOf和lastIndexOf方法不能用了
for(var i=eventObj[action].length-1;i>=0;i--){
//判断对象是否一致
if(eventObj[action][i]===fn){
eventObj[action].splice(i,1);
return;
}
}
}
】
★removeEventListener与detachEvent的原理不止上面那么简单,上面的是种实现的方式,但是不是最好的实现方式,因为我做的这种实现其实 和系统的实现无不干扰,其实系统的removeEventListener与detachEvent貌似还是另一套机制,当你使用对象.事件=null;的时候,无法去除你用addEventListener和attachEvent绑定的事件,就像你用removeEventListener与detachEvent也无法去除对象.事件=方法名;但是addEventListener和attachEvent绑定的事件可以获取到对象.事件=方法名;绑定的事件,并且还会吧这种方式绑定的事件存到自己的队列里面去,但是无法移除,只能添加。
【
//默认当前时间的定义 方式
var date1 = new Date();
console.log(date1);
//指定某个时间的定义 方式
var date2 = new Date("2019/1/31 15:48:20");//兼容最强
console.log(date2);
//直接指定字面量 但是兼容性不是很好 因为每个浏览器渲染的Date对象的字面量可能不一样
var date3 = new Date("Thu Jan 31 2019 15:48:20 GMT+0800 (中国标准时间)");
console.log(date3);
//指定年月日的方式 来定义
var date4 = new Date(2019, 1, 31);
console.log(date4);
】
2.操作日期对象
【
console.log(date1.getDate()); // 获取日 1-31
console.log(date1.getDay()); // 获取星期 0-6(0代表周日)
console.log(date1.getMonth()); // 获取月 0-11(1月从0开始)
console.log(date1.getFullYear()); // 获取完整年份(浏览器都支持)
console.log(date1.getHours()); //获取小时 0-23
console.log(date1.getMinutes()); //获取分钟 0-59
console.log(date1.getSeconds()); //获取秒 0-59
console.log(date1.getMilliseconds()); // 获取毫秒 (1s = 1000ms)
console.log(date1.getTime()); //返回累计毫秒数(从1970/1/1午夜)
】
3.为什么时间都是从1970/1/1午夜开始的,因为那是一个纪念的日子,有一个实验室,c语言和unix系统都是从那个实验室出来的,unix是收费的,linux是开源的,诺贝尔的获得者有七八个都是这个实验室的,后台他们把这个1970/1/1午夜被制定成了一个计算机时间的基本点,后面的人一直沿用至今。
4.返回当前距离时间基本点的毫秒数的四种方式
【
var date = Date.now();
console.log(date);
var date = +new Date();
console.log(date);
var date = new Date().getTime();
console.log(date);
var date = new Date().valueOf();
console.log(date);
】
5.获取页面代码执行的毫秒值
【
//获取代码开始执行之前的时间的毫秒数
var start=Date.now();
//执行的代码
// //开始进行1加到100000之间的运算
// var sum=0;
// for(var i=1;i<=100000;i++){
// sum+=i;
// }
//获取代码结束执行之后的时间的毫秒数
var end=Date.now();
console.log(end-start);//定义代码执行的毫秒数
】
5.简单数据类型无法绑定属性和方法,字符串之所以能够使用length属性和indexOf方法,完全是因为,当你使用字符串.属性或者方法的时候,底层会进行隐式转换复杂类型,将简单的字符串转换成了String对象了,所以才能够使用length属性和indexOf方法,用完之后会再转换回来。
6.真正的String对象的实例内部其实是这样的【
//创建String对象实例
var strobj=new String("abcdefg");
//打印该对象实例
console.log(strobj);//{0: "a",1: "b",2: "c",3: "d",4: "e",5: "f",6: "g", length: 7,[[PrimitiveValue]]: "abcdefg"}
每一个字符串都有对应的数字作为key,所以能够使用[]的方式来访问字符串中的每个字符。
】
7.for..in 遍历对象的时候只会遍历,允许让你访问的属性的名称,也就是key,你可以通过对象名[key]来获取value,也就是属性值。。
8.字符串获取单个字符的方式除了[]这种方式之外(IE678不支持这种方式),还能够使用charAt()方法,方法中的参数与[]中的参数一样,也是通过索引(key)来获取值(value)的,如果你想获取字符串中某个值得unicode码(十进制纯数字)的话,可以使用charCodeAt(key)方法,也是传索引,内部会先调用charAt(key)获取值,然后再进行unicode码的转换,数组的sort方法底层默认就是使用unicode码来进行比较的。【
var str="abcdefg";
console.log(str);//abcdefg
//使用[]方式来遍历
for(var i=0;i<str.length;i++){
console.log(str[i]);
}
//使用charAt方法来遍历 根据索引找到字符串 也是根据String对象中数字的key来找到字符
for(var i=0;i<str.length;i++){
console.log(str.charAt(i));
}
//使用charCodeA方法来遍历 也是根据索引找到字符串
// 然后再进行unicode码转换,最终变成了变成了数字 48代表1 65代表A 97代表a
for(var i=0;i<str.length;i++){
console.log(str.charCodeAt(i));
}
】
9.获取一条字符串的字符占位的长度(不是字符串的长度),可以使用charCodeAt()方法,unicode码在0-127中的话,所占的字符占位长度为1,除此之外的字符占位长度为2。【
function getCharlength(str) {
var count = 0;
for (var i = 0; i < str.length; i++) {
//占一个字符位的字符 unicode码在0-127
// 其它的占两个及两个以上的字符位的字符 unicode码不在0-127之间
if (str.charCodeAt(i) < 128 && str.charCodeAt(i) >= 0) {
count++;
} else {
count += 2;
}
}
return count;
}
】
10.基本包装类型 String/Number/Boolean对应了string/number/boolean,所以当这些简单数据类型调用方法或者属性时,底层都会去转换为复杂数据类型的对象实例来操作,操作完毕之后会再转换回去,相当于底层做了包装一样。
11.操作字符串的方法
◆charAt:根据字符串位置的索引返回指定字符
◆charCodeAt:根据字符串位置的索引返回指定字符的十进制的unicode编码的数值。
◆indexOf:根据字符串的字符返回索引值, 从前往后搜索。
◆lastIndexOf:根据字符串的字符返回索引值,从后往前搜索
◆encodeURIComponent:对url地址进行uri编码,他是window对象的方法,方法参数是字符串。
◆decodeURICompoent:对uri编码后的url进行解码,他是window对象的方法,方法参数是字符串。
◆concat:连接两个字符串,+的底层用的就是这个方法
◆slice:截取划分,和数组的silce方法类似,也是指定起始索引和结束索引,来截取原字符串返回新字符串,也是截取时包括左边的索引不包括右边的索引,索引为负数则为字符串的长度加上这个负数索引,如果起始索引大于结束索引则会返回空字符串,如果只有一个起始索引则会截取到最后。
◆substr:截取,和数组的splice方法类似,只不过这个方法只有截取没有替换,指定起始索引和要截取的长度来截取字符串,长度过大就直接等于字符串的长度,长度为负数则为0,索引为负数则字符串的长度加上这个负数索引。
◆substring:截取划分,和字符串的slice方法类似,只不过不同的地方是,索引为负数,那么直接返回全部字符串,如果起始索引大于结束索引,就会智能替换,把小的索引作为起始索引,大的索引作为结束索引。
◆trim:去掉字符串开头部分与结束部分的空格(IE678不支持,但是可以自己封装)。
◆replace:替换原字符串返回新字符串,第一个参数是原字符串中的某一段字符串,第二个参数是替换掉原字符串中的某一段字符串的字符串,使用str.replace(/\s/gi,"")可以替换掉所有的空格,这种方式叫做正则表达式。
12.字符串与数组之间的相互转换
◆join与split方法,数组通过join方法变成字符串,字符串通过split方法变成数组
【
//数组
var arr=["吕布","赵云","关羽","张飞","刘备"];
console.log(arr);//["吕布", "赵云", "关羽", "张飞", "刘备"]
//数组通过分隔符号连接成了字符串
console.log(arr.join("|"));//吕布|赵云|关羽|张飞|刘备
//字符串
var str="吕布|赵云|关羽|张飞|刘备";
console.log(str);//吕布|赵云|关羽|张飞|刘备
//如果split方法中的参数为空字符串,那么就会对字符串中每一个字符进行分割然后放入数组中,否做就会以分割符号来分割每一段字符串(不包括分隔符)然后放入数组中。
console.log(str.split("|"));//["吕布", "赵云", "关羽", "张飞", "刘备"]
】
13.字符串中的字母大小写相互转换
◆str.toLowerCase();//将str变量中的字母无论大小写都变成小写的字母,这个方法会返回新的字符串。
◆str.toUpperCase();//将str变量中的字母无论大小写都变成大写的字母,这个方法会返回新的字符串。
14.字符串中的转换为html标签字符串的方法
◆anchor()方法: "我是一个锚点".anchor("mao");等于<a name="mao">我是一个锚点</a>
◆link()方法: "我是一个超链接".link("http://www.baidu.com");等<a href="http://www.baidu.com">我是一个超链接</a>(记住这个)
◆其它不常用的标签【
var str="你好";
console.log(str.anchor());//<a name="undefined">你好</a>
console.log(str.big());// <big>你好</big>
console.log(str.sub());// <sub>你好</sub>
console.log(str.sup());// <sup>你好</sup>
console.log(str.link());//<a href="undefined">你好</a>
console.log(str.bold());//<b>你好</b>
】
15.数学对象的方法
◆Math.abs(num);num取绝对值
◆Math.pow(num1,num2);num1的num2次方
◆Math.power(num1,num2);num1的num2次幂
◆Math.sqrt(num);num开平方,相当于Math.power(num,2);
◆Math.sin(num);num的正弦
◆Math.cos(num);num的余弦
16.九尺龙泉万卷书,上天生我意如何。不能报国平天下,枉为男儿大丈夫。
17.事件绑定的方式
◆对象.事件名=匿名函数/普通函数【
//第一种方式 使用对象.属性的方式定义的,后面定义的事件会覆盖掉前面的
btn1.onclick=function(){
console.log("九尺龙泉万卷书,上天生我意何如。");
}
btn1.onclick=function(){
console.log("不能报国平天下,枉为男儿大丈夫。");
}
】
◆对象[事件名]=匿名函数/普通函数【
//第二种方式 使用对象[属性名]的方式定义的,后面定义的事件会覆盖掉前面的
btn2["onclick"]=function(){
console.log("九尺龙泉万卷书,上天生我意何如。");
}
btn2["onclick"]=function(){
console.log("不能报国平天下,枉为男儿大丈夫。");
}
】
◆对象.addEventListener(event,fn)或者对象.attachEvent(fullevent,fn)【
//第三种方式 底层原理是不断保存每次事件的地址,递归执行或者循环执行方法 所以不会被覆盖掉
//使用addEventListener更加有助于团队开发 因为前面两种方式都会采取最后面的覆盖掉前面的 但是IE678不支持
btn3.addEventListener("click",fn1);//直接写click 底层会加on
btn3.addEventListener("click",fn2);
//使用attachEvent 只有IE678支持
// btn3.attachEvent("onclick",fn1);//直接写onclick 底层不会再加on
// btn3.attachEvent("onclick",fn2);
】
★addEventListener与attachEvent区别在于, 在前者中用this关键字代表是的事件源对象,在后者中用this关键字代表的是window对象,内部机制不一样,在IE678中不能使用addEventListener来绑定事件,只能使用attachEvent绑定事件,但是在IE678之后addEventListener取代了attachEvent,因为内部机制进行了更新,更新的地方是,将函数里面的this不代表当前事件源的bug修改了。
18.事件解绑的方式
◆对象.事件=null【
//针对第一种绑定的方式的解绑
btn1.onclick=null;
】
◆对象[事件]=null【
//针对第二种绑定的方式的解绑
btn1["onclick"]=null;
】
◆对象.removeEventListener(event,fn)或者对象.detachEvent(fullevent,fn)【
//针对第三种绑定方式的解绑
btn3.removeEventListener("click",fn1);//解绑单击事件中的fn1
btn3.removeEventListener("click",fn2);//解绑单击事件中的fn2
//兼容IE678 因为只有IE678支持
// btn3.detachEvent("click",fn1)////解绑单击事件中的fn1
// btn3.detachEvent("click",fn2)////解绑单击事件中的fn2
】
19.事件的本质
◆事件的本质就是方法
◆方法的定义方式有三种:
◇function fn(){};
◇var fn=function(){};
◇var fn=new Function("");
◆事件是属于一个对象自己的方法
◇对象.methoed=fn;
◇对象.methoed=function(){};
◇对象.methoed=new Function("");
◆addEventListener与attachEvent可以实现多个方法的绑定,实际原理是将前一个事件如(onclick)的地址存起来,然后重新给这个事件赋值,先调用前一个事件如(onclick)的地址执行的方法,然后再调用你之后绑定的方法【
//addEventListener的底层原理
function addEvent(action,fn,element){
//把上一个事件的方法指针存起来
var tempEvent=element["on"+action];
element["on"+action]=function(){
//如果之前的事件已经绑定了方法
if(tempEvent){
//那么先执行之前的事件中的代码 再继续下面的
// console.log(tempEvent);
tempEvent();
}
//执行绑定的代码
fn();
}
}
】
◆addEventListener与attachEvent的原理远远不止上面的那么简单,其实它还进行了将每一个方法存到一个数组里面的操作,当每绑定一个方法,都会进行判断,是有已经有了这个方法,如果有了就不会再绑定重复的方法【
//添加事件
function addEvent(action,fn,element){
//判断当前事件对象中是否有这个行为的数组
if(!eventObj[action]){
//没有就创建
eventObj[action]=[];
}
//判断函数数组中是否有这个函数
for(var i=0;i<eventObj[action].length;i++){
if(eventObj[action][i]===fn){
return;
}
}
// //把当前的事件存到数组中 ,然后等到
// if(element["on"+action]){
//
// }
//有的话每次添加方法都是放入一个事件对象的这种行为数组中(栈中)
eventObj[action].push(fn);
element["on"+action]=function(){
//遍历栈中的方法 按照顺序循环执行
for(var i=0;i<eventObj[action].length;i++){
//IE 678使用的是 attachEvent 原理就是这样的
// eventObj[action][i]();
//IE678之后用的是这个 addEventListener 原理就是这样的
//这样就把方法中的this转换成了当前的对象
element.actionMethod=eventObj[action][i];
//由当前对象来调用
element.actionMethod();
}
}
}
】
★将以上两种结合起来就是系统的addEventListener和attachEvent原理,因为第一种和第二种加起来就能够兼容btn1.onclick=function(){},把它给添加到事件队列中去。
◆removeEventListener与detachEvent的简单原理【
//移除事件
function removeEvent(action,fn,element){
//IE678以上
//根据对象找索引 从后往前找,因为添加的时候是从后面添加的
// var index= eventObj[action].lastIndexOf(fn);
//直接切掉这个对象
// eventObj[action].splice(index,1);
//为了兼容IE678 因为indexOf和lastIndexOf方法不能用了
for(var i=eventObj[action].length-1;i>=0;i--){
//判断对象是否一致
if(eventObj[action][i]===fn){
eventObj[action].splice(i,1);
return;
}
}
}
】
★removeEventListener与detachEvent的原理不止上面那么简单,上面的是种实现的方式,但是不是最好的实现方式,因为我做的这种实现其实 和系统的实现无不干扰,其实系统的removeEventListener与detachEvent貌似还是另一套机制,当你使用对象.事件=null;的时候,无法去除你用addEventListener和attachEvent绑定的事件,就像你用removeEventListener与detachEvent也无法去除对象.事件=方法名;但是addEventListener和attachEvent绑定的事件可以获取到对象.事件=方法名;绑定的事件,并且还会吧这种方式绑定的事件存到自己的队列里面去,但是无法移除,只能添加。