JavaScript
1.基本认识
1.1 可以直接在标签中添加事件
<div id="wrap" onclick="alert(123);">123</div> <a href="javascript:void(0);">gogogogogo</a> <a href="javascript:alert('a标签被点击了!');">233333</a>
1.2 script标签只能放在head或者body里面
- 一个html中可以放无数个script标签
- script标签载入有先后顺序
- 如果一个script标签已经用来引入外部的js文件了,那么在标签内写代码无用
<script type="text/javascript" language="JavaScript"> //type和language是声明语言格式,推荐不写 alert('潭州教育!转义字符\\') </script>
1.3 系统弹窗 confirm prompt
let a = confirm('确认不要媳妇啦?'); console.log(a); //获得用户选择的结果, "boolean" let b = prompt('请输入你媳妇的名字:'); console.log(b); //用户输入的结果, 取消-> null -> "object" 确定 -> string
2.数据类型
2.1 ES5、ES6定义变量的区别
ES6 定义变量,4种方式 - let - const 常量,不可更改,初始必须赋值 - function - class ES5 定义变量,2种方式 - var - function
变量命名规则:
- 严格区分大小写;
- 只能包含 字母 数字 _ $ 四大类,且不能以数字开头;
- 不能使用 关键词/保留词/已有的API;
- 见名知意。
- 函数声明时会覆盖掉同名变量
2.2 ES6的七大数据类型
- number 数字
- string 字符串
- boolean 布尔值
- undefined 未定义
- null 空 但是在浏览器上 typeof null = "object"
- symbol ES6新增的一种数据类型
- object 对象
// number let x = 10; // string let x = "10"; let x = '10'; let x = `456`; //模板字符串 ES6中新的字符串定义方式 let x = "true"; // boolean let x = true; let x = false; //布尔值有两种,true 和 false // undefined let x; let x = undefined; const x = undefined; //const不允许初始不赋值 // symbol let x = Symbol(123); let y = Symbol(123); // x和y都是独一无二的 Symbol(变量) // object let x = [10,true,10]; //数组是对象的一种 function 和 class 定义的变量 typeof 显示"function", 其本质是object
2.3 typeof
let a = typeof NaN; //number let b = typeof a; //b是string类型 let c; //undefined let d = null; //typeof在检测null类型时返回object类型,其本质是null类型 let e = Symbol(123); //symbol let f = document; //object let g = function(){ //typeof在检测function数据会返回"function",其本质是object }; console.log(typeof(b)); console.log(typeof c); console.log(typeof d); console.log(typeof e); console.log(typeof f); console.log(typeof g);
3. js获取dom元素节点
let a = document.getElementById("wrap"); //a就是节点对象 console.log(a); // <div id="wrap"></div> //ClassName不唯一,所以Elements有s let b = document.getElementsByClassName("box"); console.log(b); //类(似)数组 HTMLCollection(3) [div.box, div.box, div.box] console.log(b.length); // 3 console.log(b[0]); // <div class="box">1</div> b[1].innerHTML = '阿飞'; let d = document.getElementsByName('name'); //应用在input标签 console.log(d); // NodeList [input]
3.1 html唯一标签获取
注意:打印的是标签,不是类数组节点
console.log(document.body); // body标签及其子标签 console.log(document.head); // head标签及内容 console.dir(document.title); //标题 //获取HTML,此处是引用,尽管在后面改变,但引用也会跟上 console.log(document.documentElement); //打印的是整个html页面 document.title = "阿飞"; document.documentElement.id = '狗蛋';
3.2 querySelector
let aaP = document.getElementById("wrap") .getElementsByTagName("div")[0] .getElementsByTagName("p"); //一层层查找,但性能仍然较querySelector好 console.log(aaP); // HTMLCollection(3) [p, p, p] let aP = document.querySelectorAll("#wrap div p"); //All多个,参数类似css选择器 console.log(aP); // NodeList(3) [p, p, p] /*最低支持IE8*/ let sP = document.querySelector("#wrap div p"); //只能选到第一个 打印出的就是标签
3.3 操作元素属性
w3c允许的标签属性,既是合法
合法的标签属性,直接 . 操作
- `class`比较特殊,需要使用`className`代替操作 - `style` 这个属性非常特殊,是个对象
let oDiv = document.getElementById("wrap"); //oDiv.title = '狗蛋'; 直接写 oDiv.className = 'wrap'; //属性class是保留词,这里用className oDiv.id = 'goudan'; oDiv.innerHTML = '<p>666</p>'; //虽然改了id,但oDIV仍指向这个节点对象整体,id只是其中一个属性,不能影响整体 //oDiv.innerHTML += '456'; 可以直接+= let oGouDan = document.getElementById("goudan"); console.log(oDiv === oGouDan); //完全相等
/* css样式 js中css属性名用驼峰法: 操作复合样式的时候,去掉 - 号,后面的第一个字母大写 */ let oWrap = document.getElementById("wrap"); console.log(oWrap.style); oWrap.style.height = "100px"; oWrap.style.backgroundColor = "pink"; //将background-color改为驼峰命名法,backgroundColor oWrap.style.cssText +="width:100px;height:100px;background-color:pink"; // 也可以这样写 / oWrap.className += " box"; / /* 操作className代替复杂的css样式设置 */
3.4 自定义标签属性
- getAttribute()
- setAttribute()
- removeAttribute() 移除属性
let oWrap = document.getElementById("wrap"); //获取自定义标签属性 console.log(oWrap.getAttribute("afei")); //设置自定义标签属性 oWrap.setAttribute("afei","456"); oWrap.setAttribute("zhuque","123"); //移除自定义属性 oWrap.removeAttribute("afei"); //可以操作合法属性,但一般不用,没有直接.操作方便 console.log(oWrap.getAttribute("id"));
3.5 标签数据 innerHTML、innerText(火狐低版本,textContent)、value
<div id="wrap"><i>阿飞飞</i> 小浪浪 海文文 银时时 朱雀雀</div> <input id="inp" type="text"> <button id="btn">按钮</button> <script> /* .innerHTML .innerText 火狐低版本 .textContent .value */ // let oWrap = document.getElementById("wrap"); // // console.log(oWrap.innerHTML); //获取HTML // console.log(oWrap.innerText); //只获取文本 // // oWrap.innerHTML = "<b>朱雀雀真...</b>"; //oWrap.innerText = "<b>猪八戒...</b>"; document.getElementById("btn").onclick = function(){ alert(document.getElementById("inp").value); } </script>
3.6 css超级选择器
<head> <meta charset="UTF-8"> <title>Title</title> <style> div#wrap.on.goudan[title]{ width:150px; height:150px; background-color:pink; border:5px solid #999; } </style> </head> <body> <div id="wrap" class="on goudan" title=""></div> </body>
4. 算术运算
4.1 隐式类型转换
- "+" 两边任意一边有字符串,那么都会进行隐式字符串转换,再进行拼接
- 布尔值和数字运算,会转换成 1 0
- 除开number string boolean之外,比如去讨论一个对象和另一个对象的加法等是没有实际意义的
let a = 10; let b = "20"; //alert(a + b); // 弹出 "1020",结果是字符串 //alert("123" + {}); // 123[object Object] //alert("123" + document); // 123[object HTMLDocument] //alert("123" + undefined); // 123undefined console.log(true + 8); // 返回9 //没意义 // console.log({}+{}); //[object Object][object Object] // console.log([]+{}); //[object Object] // console.log([]+[]); // //同级运算从左到右 alert(1 + 2 + "3"); // 返回 "33" alert("8" + 1 + 2); // 返回 "812" // 将字符串转成数字, 前面带一个 + console.log(+"86867");
// 数组和任何东西相加,都会转换成字符串 [1,2,3]+3 // "1,2,33" [1,2,3]+true // "1,2,3true" [1,2,3]+null // "1,2,3null" [1,2,3]+undefined // "1,2,3undefined" [1,2,3]+'' // "1,2,3" [1,2,3]+NaN // "1,2,3NaN" undefined+[] // "undefined" {}+[] // "[object Object]"
- * / %都会变成数字再进行运算 出现非法运算(字符串非数字字符串进行计算)时,会出现NaN(not a number) let a = '50p'; //非数字 let b = "40"; console.log(a - b); // NaN console.log(a * b); // NaN console.log(a / 20); // NaN console.log(a % 20); // NaN console.log(typeof(NaN)); //NaN是number类型,但不是一个数字 console.log(NaN + 10); // 返回NaN
4.2 自增和自减
let a = 10; console.log(a++); //返回10 // let a = 10; // let b = 5; // console.log(a++ + b); //返回15 let a = "5"; a ++; // ++ 或 -- 会强行变数字进行计算,最终a的值也是数字 console.log(a); // 6
4.3 模板字符串
/* 单引号 或者 双引号 描述的字符串,内部不能换行 ` ` 模板字符串可以 */ let oWrap = document.getElementById("wrap"); let x = "阿飞老师有点皮" // oWrap.innerHTML = "<ul>"+ // "<li>"+ // "<p>"+ // "<a href=''>"+x+"</a>"+ // "</p>"+ // "</li>" + // "</ul>"; /* ${}可以直接引用变量,{}内是JS代码 */ oWrap.innerHTML = `<ul> <li> <p> <i>${x}</i> <b>\$\{\}</b> </p> <p>\`\`是ES6的特点</p> </li> </ul>`;
5. 运算符
5.1 赋值运算 a+=1
let a = 15; a -= 1; //a = a - 1 a *= 2; //a = a * 2 a /= 3; //a = a / 3 a %= 4; //a = a % 4 alert(a);
5.2 比较=运算
/* == 只比较值相等与否,不关心数据类型 === 值与数据类型都要相等 != 不等 !== 不全等 */ let a = 10; let b = "10"; console.log(a == b); //true console.log(a === b); //false console.log(a !== b); //true console.log(a != b); //false
5.3 对象引用类型比较地址
/* 基础数据类型 (栈内存) 只比较值与类型 number string boolean undefined null symbol 引用数据类型 (堆内存) 比较地址 object */ /* a 和 b 是不同的对象,有自己的地址 */ let a = [10]; let b = [10]; console.log(a == b); //false console.log(a === b); //false console.log(a[0] === b[0]); //true //console.log(false == [0]); //这样不科学的,建议 ===
5.4 字符串比较
/* > < >= <= */ //字符串比较,从首位的ASCII码开始比较 let a = '3'; let b = '20'; console.log(a > b); // '3' > '2' 的ACSII码,所以返回 true
5.5 逻辑运算&& || !
/* && 与 //只考虑布尔值时:真真为真,其他都是假 真正的作用:遇到假就停,然后取假值,否则取后面的值 || 或 //只考虑布尔值时:假假为假,其他都是真 真正的作用:遇到真就停,然后取真值,否则取后面的值 ! 非 //只考虑布尔值时:取反 真正的作用:取数据对应布尔值的非 那些数据在被转成布尔值的时候是 false: 0 undefined null false '' "" NaN */ // let a = true && false; // console.log(!a); let a = 5 && 0; console.log(a); //取假值,返回 number 0 let b = 8 && 9; console.log(b); // 返回 9 console.log(NaN || 7 || 0); //返回7 console.log(!1); //返回 false console.log(!0); //返回 true //取一个数据对应的布尔值 console.log(!! NaN); //NaN的布尔值是false
5.6 逗号运算
/* 逗号运算符,从左到右 从上到下 运算 */ let a = (4,5,6); //算到6后停止 console.log(a); //返回 6
5.7 运算优先级
/* 运算符优先级 . [] () ++ -- - ~ ! delete new typeof void * / % + - < <= > >= == != === !== && || 三目 = , */ // let a = 8 || 9 && 0; // console.log(a); //返回8 let a = 10; let b = 5; //浏览器会惰性运算,||的左边已经确定,右边不会计算 // a = 8 || 5 && (b=6); //console.log(a); //8 //console.log(b); //5 ,b的赋值没有计算 a = 0 || 5 && (b=6); //返回 a = 6, b = 6 console.log( 6 || (b=6) && 10 || 11); //返回6 console.log(b); //b仍未赋值
5.8 位运算(先取整再算)
/* 二进制 位运算时会将数值转换为32位整型来进行运算, 所以位运算遇到小数时,直接处理掉小数部分当成整数来运算。 */ let a = 15; // 00000000 00000000 00000000 00001111 // -15的二进制 // 11111111 11111111 11111111 11110000 取反码 // 11111111 11111111 11111111 11110001 反码+1 /** * 位运算操作符:按位非`~` 、按位与`&`、按位或`|`、按位异或`^` *左移`<<`、 无符号右移`>>>`、 有符号右移`>>` */ //按位取反, 满足条件 a + ~a=-1 let a = 7; // 0111 console.log(~a); // -8 ( 1..1000 结果 取反 +1 ) (0...0111 -> 0...1000) //按位与 console.log(10 & 8); // 1010 & 1000 = 1000 => 8 //按位或 //左移 a << 2; //a * 2^2;
6. 判断和循环
6.1 三目运算
/* 哪些数据是假: 六大假 0 undefined null false "" NaN 当条件、真语句、假语句都是一条语句时,我们可以使用三目来改写if 条件 ? 真语句 :假语句 */ 4<5 ? alert('真') : alert('假'); let val = true; //三目运算符的 优先级 低于 + let name = "val is" + val ?"狗蛋":"大锤"; //"val is true" ? "狗蛋" : "大锤" console.log(name); // 多级三目,从左到右 function f(n) { let a = n>100 ? n-100 : n>99 ? n-99 : n; console.log(a); }
6.2 switch
let a = '海文'; // if (a === "阿飞") { // alert(a + "老师一般帅!"); // }else if (a === "小浪") { // alert(a + "老师??"); // }else if (a === "海文") { // alert(a + "斯文"); // }else if (a === "朱雀") { // alert(a + "可爱"); // }else{ // alert(a + "喵喵喵"); // } //switch 是全等判断 switch (a) { case "阿飞": alert(a + "帅!"); break; case "小浪": alert(a + "也帅!"); break; case "海文": alert(a + "斯文"); break; case "朱雀": alert(a + "可爱"); break; default: alert("喵喵喵?"); break; }
6.3 逻辑运算符代替判断
let a = true; function b() { console.log(123); } // if (a) { // b(); // } a && b(); //遇到假停止,b()会运行 //先与再或,勉强可以代替三目运算 let x = 5; let y = 6; //console.log(x<y ? 2 : 3); console.log(x<y && 2 || 3);
6.4 for循环
// 2+2^2+2^3+2^4+...+2^10 let a = 1; let sum = 0; for(let i = 1; i <= 10; i++){ console.log("i=" + i); a *= 2; //a = a*2 sum += a; console.log("sum=" + sum); }
6.5 综合案例,生成尖括号
/* 5 3 6 3 7 4 8 4 9 5 10 5 */ let oWrap = document.getElementById("wrap"); let HTML = ''; let num = 11; let mid = 0; //计算oWrap的宽高 if(num & 1){ //奇数判断 mid = (num+1)/2; }else{ mid = num/2; } oWrap.style.width = num*50 + 'px'; oWrap.style.height = mid*50 + 'px'; for(let i = 0; i < num; i++){ let x = i>(mid-1) ? num-i-1 : i; //箭头朝下 let y = i>(mid-1) ? i+1-mid : mid-1-i; //箭头朝上 HTML += `<div style="margin-top:${y*50}px;">${i+1}</div>`; } oWrap.innerHTML = HTML;
7. 循环
7.1 break vs continue
/* break: switch里面的break只对switch有用,不会影响到外面的for if里面的 break ,结束上级 for 循环 continue: 该次循环结束,进入下一个循环 */ for(let i=0;i<10;i++){ switch (i) { case 5: break; //作用不到for } console.log(i); } for(let i=0;i<9;i++){ if(i===5){ //continue; //执行到continue,即刻停止,进入下一个循环 break; //结束 for 循环 } console.log(i); } //break只能跳出一个for,要想跳出第二层,定义一个伪变量表示for循环,break aaa; aaa:for(let i=0;i<5;i++){ for(let j=0;j<4;j++){ if(i*j ===6){ break aaa; } console.log(`i===${i},j===${j}`); } }
7.2 while
//let在for循环内定义时,是局部变量 for(let i = 0;;) //var在for循环内定义时,相当于全部变量 for(var i = 0;;) let i = 0; for(;i<4;i++){ } console.log(i); //返回4 //全局变量下的for循环可以用while代替 var i =0; for(;i<5;){ console.log(i); i++; } var j=0; while(j<5){ console.log(j); j++; } // do while let x = 5; do{ console.log(x); x++; }while(x<5);
8. 函数
8.1 定义函数方式
/* let a = function () { alert(1); } a(); //函数加括号,自执行 */ /* a(); //可以放在function之前,let、var函数表达式定义的函数,不允许提前调用 function a () { alert(1); } */ /* let、var定义的函数,不允许提前调用 a(); var a = function () { alert(1); } */ let a = function b() { alert(2); console.log(b); //在函数里面可以获取b,此时b === a console.log(b === a); } a(); //b(); //显示b()未定义,函数外面不能使用b
8.2 函数表达式
/* 使用fuction直接定义的函数不能直接加括号执行,只能通过名字再加括号执行 function a() { alert(3); } a(); */ //let定义的函数表达式,可以直接在后面加括号执行 let b=function () { console.log(4); }(); console.log(b); //undefined,此时b不能代表函数,b成为了函数的返回值 //匿名函数只能传参或赋值,不允许直接出现 //[function () {}] //允许 // function{} //不允许 //匿名函数加括号变成函数表达式,括号可内可外 (function () { console.log(5); })(); (function () { console.log(6); }()); //改变函数的返回值,也是函数表达式 +function(){ //一元运算符 + - 可以变为函数表达式 console.log(7); }(); ~function () { //位运算非 console.log(8); }(); !function () { //逻辑运算非 console.log(9); }();
8.3 参数
/* 参数 形参 实参 不定参 */ //定义函数的时候()里面是允许写变量名字的,这些变量名字就是形参,形参只针对函数内容起作用 function a(x,y=200){ alert(x+y); } //函数调用的时候,()里面写的就是实参 a(7); function f(a,b,c) { console.log(a); console.log(b); console.log(c); } f(1,8); //从左到右进入形参,没有传入实参的,显示undefined f(1,5,3,4); //实参多了,多的那个无效 function sum(a,b) { console.log(a+b); } sum(7,8); sum(45,1); //求 n 个数的和,每次不一定是相同的个数,实参传入几个数,就求几个数的和 function sum(a,b,c,d) { //不定参 它是一个类数组,存储着 所有实参 // console.log(arguments); let s = 0; for(let i=0,len=arguments.length;i<len;i++){ s+=arguments[i]; } console.log(s); } sum(4,5,6); sum(1,2,3,4); sum(8,6);
8.4 上下文this
console.log(this); //打印Window 是顶层属性 // window.alert(4); // alert(a); //未定义 报错 // alert(requestAnimationFrame); //不存在即报错 if(window.requestAnimationFrame){ //直接判断时,如果不支持,直接报错,不能进行判断,此时最好用类属性判断 console.log(requestAnimationFrame); } /* 函数声明,默认位于window对象内 函数(声明式or表达式一样)自执行,this指向window */ function a() { console.log(this === window); } console.log(a === window.a); // 相等 a(); //let定义的函数不挂载window内,var定义的函数在window内 let b = function () { console.log(this); //this都是指向window } console.log(b === window.b); b(); /* 对象内的this,找 爸爸 所在的对象 */ let afei = { name:'阿飞', x : function () { console.log(this); } } afei.x(); //对象方法自执行。this指向对象 let zhuque = { xx:{ name:"朱雀的xx", a:function () { console.log(this === zhuque.xx); } } } zhuque.xx.a(); // 打印 父级的对象
8.5 每个函数都有返回值,默认 return undefined
//函数运行到return即停止,return后面的不执行 function a() { alert(1); return 2; alert(3); } console.log(a());
8.6 ES6{}就是一个作用域
let关键词定义的变量,起作用的范围就是包含这个变量最近的{}
var关键词,只有在function内定义的变量,才是局部变量,while、if、for内定义的都是全局变量
同名函数会被覆盖
function a() { alert(2); } function a(x,y) { //覆盖上一个定义 alert(x+y); } a();
8.7 获取元素的动态方法
动态有三个,意思是存储节点的变量会随着页面的改变实时改变,少了或者多了
- getElementsByClassName()
- getElementsByTagName()
- getElementsByName()
静态有以下,获取后,选择器被改也指向原对象
- querySelectorAll
- getElementById
<div id="wrap"> <p class="goudan"></p> <p class="goudan"></p> <p class="goudan"></p> </div> <script> //getElementsByClassName是个动态的 let aP = document.getElementsByClassName("goudan"); // 有三个 let oP = aP[0]; // 这种就是静态的,不存在因修改而改变 oP.className = 'dachui'; //改变了 aP,只剩两个 “goudan” oP.title = 'ppp'; aP[0].className = "a2121"; //aP是动态数组, aP[0].innerHTML = "112121212"; //因为aP[0]的类名被改变了,动态获取的内容更新了,现在又少了一个 </script>
9.作用域与解析顺序
9.1 var、function在全局作用域定义的变量默认是window对象的属性
//script标签是最大的作用域,也是全局作用域 //如果var、function在全局作用域里面定义变量。相当于给window添加了属性 var b = 10; console.log(window.b,this); //window的属性 //ES5定义变量的关键词 var funtion //作用域是往上离变量最近的函数 function a() { var g = 10; // var在function内定义的才是局部作用域 console.log(g,this.b,this); //函数自执行this指向window } a(); //任意作用域里面,如果不加var直接产生了没有定义过的变量,那么这个变量相当于window的属性 //但是实际开发不允许这样写 function aa() { bb = "bb"; // 赋值产生变量才行,如果直接使用则会报错 } aa(); console.log(bb); //面试题 function f() { var x=y=5; console.log(x); //x是局部的5 } f(); console.log(y); //y未声明,因此算是window的属性,y=5 //console.log(x); //因为x是局部变量,外部访问不到,故报错
9.2 作用域链
//作用域链,操作变量时,一层层的作用域查找变量声明的地方, //如果没有找到,调用变量会报错 //如果是赋值,在非严格模式下,变量成为windows的属性 var x=10; function a() { x=15; //本作用域找不到,去父级作用域找,找到后并赋值 var y=5; //局部作用域 return x+y; } console.log(a()); console.log(x); //全局变量被修改了,因此是15
9.3 es5解析顺序
var、function解析顺序 第一步:定义 找出当前作用域里面所有的var和function定义的变量名,不赋值,赋值是执行 此时function定义的函数,是个完整的函数,所以函数定义可以在函数执行的前后任意位置 第二步:执行 从上到下执行
alert(x); // 返回不是报错,是undefined,变量已有,但未赋值 var x=10; //let x=10; // 报错 /* 1.定义 var x; 2.执行 alert(x); //undefined x=10; */ alert(b); function b() { } /* 1.定义 function b(){} 2.执行 alert(b); */
9.4 变量重名与覆盖
/* 定义过程中,多个var声明同一个变量,我们只需要留一个 多个函数声明重名,只留最后一个函数 var和function重名,无论先后,只留function */ function v() { alert(5); } function v() { // 被打印 alert(10); } var v; console.log(v);
9.5 闭包
函数执行每次都会产生一个新的作用域 (父级也是新的) ,彼此不相干
/* JS自带变量回收机制,只有全局变量不会被回收,除非网页关闭; 闭包:一个使用了外部函数的参数或者变量的函数(作用域嵌套作用域,ES5只有函数能产生作用域) */ function a() { var x=10; function b() { x++; console.log(x,this); } return b; } //函数执行每次都会产生新的作用域, var c=a(); //c指向函数体内的函数b,局部变量x只在a()时产生了一次,所以不会回收 //alert(c); c是函数 //c()每次执行使用的都是同一个父级作用域下的变量x c(); //11 c(); //12 c(); //13 c(); //14 //a()每次执行后返回的都是新的作用域,使用的变量x是新的, a()(); //11 a()(); //11 a()(); //11
闭包的作用之一,闭包避免局部变量被回收
<div id="wrap"> <p>aaaaaaaaaaa</p> <p>bbbbbbbbbbb</p> <p>cccccccccc</p> <p>dddddddddd</p> <p>eeeeeeeeee</p> </div> <script> //闭包的作用之一,闭包避免局部变量被回收,onclick不知道什么时候触发,所以num不会被回收 (function () { var num=10; document.onclick = function () { num++; alert(num); } })(); let oP = document.getElementsByTagName("p"); //ES6只要是{}就是作用域,{}内的onclick使用了父级的i,所以i没有回收,子作用域可以调用 //产生了5次闭包,存了5个i,子作用域之间的i不相干 //5个闭包是5个单独的作用域,let i是5个父级作用域 for(let i=0;i<5;i++){ oP[i].onclick = function () { alert(++i); } } //var不能产生作用域,不是闭包,函数作用域用的是同一个变量i,(扩号内执行的结果是5) //i最后是5,所以每次点击都是同一个i for(var i=0;i<5;i++){ oP[i].onclick = function () { alert(i); } }; //强行给var加闭包,创造一个父级作用域,使得点击事件用上父级作用域的变量,产生5个闭包,存下5个变量 for(var i=0;i<5;i++){ function a(x){ oP[x].onclick = function() { alert(x); }; } a(i); //a(i)执行了5次,产生了5次闭包 } //等于自执行 for(var i=0;i<5;i++){ (function a(x){ oP[x].onclick = function() { alert(x); }; })(i);//a(i)执行了5次,产生了5次闭包 } //可以简写自执行 for(var i=0;i<5;i++){ !function a(x){ oP[x].onclick = function() { alert(x); }; }(i);//a(i)执行了5次,产生了5次闭包 } </script>
练习
fn()(); var a = 0; function fn(){ alert(a); var a = 3; function c(){ alert(a) } return c; } /* 1.定义 var a; function fn(){} 2.执行 fn() ===> 新作用域 1.定义 var a; function c(){} 2.执行 alert(a) undefined a=3 return c; fn()() ===>新作用域 alert(a),a从父级找,弹出3 */
var a = 5; function fn(){ var a = 10; alert(a); function b(){ a ++; alert(a); } return b; } var c = fn(); c(); fn()(); c(); /* 10 11 10 11 12 */
9.6 function属于对象类型,相等判断时是比较地址
console.log(5 === 5); console.log([] === []); //引用,需要比地址,false var h = function () {}; var l = function () {}; console.log(h === l); //false function a() { return function () {}; } var b = a(); var c = a(); console.log(b ===c); //false
9.7 ES6的作用域
只要是在作用域里面let过的变量,那就不允许提前使用
/* ES6定义变量 let const function class let 和 const没有预解析,不同于var 先对定义function,进行解析 */ alert(a); //报错 let a = 10; let a = 20; function b() { //从上到下读 //暂时性死区,只要是在作用域里面let过的变量,那就不允许提前使用 alert(a); let a = 30; //死区了,报错 } b(); //var的作用域只认函数 if(false){ var a=10; } console.log(a); //undefined //let的作用域是大括号 if(true){ let a=1; } console.log(a); //无法访问局部作用域 //for的小括号()是{}的父级作用域,但var不认这是作用域 const g = 10; //不允许重新赋值的常量 const a = []; //对象不能被重新赋值 a[0] = 10; //但允许改变其内容 console.log(a);
10. call apply bind
10.1 函数内的this
function a() { console.log(this); } a(); /* 普通模式下, 自执行this指向window, 严格模式下, this指向undefined 被动模式下,this指向触发事件的对象 */ document.onclick = a;
10.2 call apply
/* call()内第一个参数是函数内this的指向,后面跟的是实参,调用后会执行 */ function a(x,y) { console.log(x); console.log(y); console.log(this); } a(8,9); //自执行 //a.call(); //call()不加参数与自执行a()效果一样 a.call(document,8,9); //this指向document /* apply(),与call()类似,但是只有两个参数,第二个是传参的数组 */ a.apply(document,[8,9]); //this指向document //多个参数要用数组的形式传入
10.3 bind
bind之后相当于生成一个新的函数,还未执行
/* bind不能执行 */ function a(x,y) { console.log(x); console.log(y); console.log(this); } //a.bind(document); 这样直接写是没有效果的,只是相当于生成了一个新的函数,将this绑定给document function b(x) { x(); } b( a.bind(document) ); //bind后生成了有this新指向的函数,bind是定义函数,不能直接执行,需要手动执行 a.bind(document)(10,20); //新的函数内的this指向 document a.bind(10)(0,1); //新的函数内this指向number 10
//bind定义this指向 document.onclick = function (x,y) { console.log(x); console.log(y); console.log(this); }.bind({name:'阿飞'},200,250); //这里类似于call传参,bind产生新函数
10.4 H5新API classList
oWrap.classList.[attr] attr = add、remove、toggle
let aDiv = document.querySelectorAll("#wrap .title"), aList = document.querySelectorAll("#wrap .friend .list"), len=aDiv.length; for(let i=0;i<len;i++){ aDiv[i].onclick = function () { //toggle,如果有就删除,没有就添加 aList[i].classList.toggle("show"); } } //classList 方法还有 add remove oWrap.classList.add("afei"); //直接添加类名
11. ES6的解构赋值
// ES6 定义变量的解构赋值 let a = 10, b = 20, c = 30; //结构模式对应,赋值效果同上 let [a,b,c] = [10,20,30]; //多了 显示 undefined let [a,b,c,d] = [20,30,41]; // d = undefined //不用let的变量赋值语句,变量默认是window的属性 [a,b] = [20,30,41]; //变种写法 function fn() { let x=[]; for(let i =0;i<5;i++){ x[i]=i*i; } return x; } let [a,b,c,d,e] = fn(); //对象的解构赋值,属性是用来对应的,只有变量定义了 let {a:x,b:y} = {a:10,b:20}; console.log(x); console.log(a); // 报错,属性不是定义的 let {x:x,y:y} = {a:10,b:20,y:30}; console.log(x); //找不到右边对象的x属性,所以变量x显示未定义 console.log(y); //属性对应才能赋值 y=30 //注,属性和变量相同时,可以只写一个 let {x,y} = {x:10,y:30}; console.log(x,y); //ES6新增简写规则 let a = 10, b = 20, c = { a, b }; /*属性名和变量名相同,可以简写,对象c的定义同下 c = { a:a, b:b } */ console.log(c);
11.1 深层解构赋值
let [a,[b,c],d] = [1,[2,3],4]; console.log(a,b,c,d); let [a,b,{x,y}] = [1,2,{x:10,y:20}]; console.log(a,b,x,y);
11.2 解构赋值时默认值写法
只有右边**严格等于undefined**时取默认值 不允许前面的默认值用后面的变量值
/*使用解构赋值,取默认值的方法*/ function fn(data) { let a = data.a; let b = data.b; let c = data.c || 100; // 要确保传入的值不是假,否则走默认值,这种不严格 // 必须严格的走 全等undefined,再添加默认值 if(c === undefined){ c = 100; } } fn({a:10,b:20}); //ES6的默认值写法,左边写一个等于,只有右边严格等于undefined时取默认值 function fn(data) { let {a:a=1,b:b=2,c:c=100} = data; //左边严格上说不能认为是对象,只有右边是对象 console.log(a,b,c); // c = null } fn({a:10,b:20,c:null}); // 这里 null===undefined 不成立, //数组默认值写法 let [d,e,f=90] = [7,8]; console.log(d,e,f); //默认值是表达式时,如果不需要默认值,则默认值无法执行 function f1() { console.log("12"); } let [x = f1()] = [1]; // x=1, f1未执行 let [y = f1()] = [undefined]; // y=undefined, 是f1执行后的返回值 //不允许前面的默认值用后面的变量值 let [m=n,n]=[undefined,1]; console.log(m,n); //报错
12. 字符串方法
12.1 基本数据类型包装对象无意义
undefined null 等不能进行点操作,会报错的
//基础数据类型虽然可以点操作,但没有意义,拿不到包装对象; //点操作本质上是针对数据的包装对象进行操作,每一次的包装对象都是新产生的对象,不能确定 //undefined null 等不能进行点操作,会报错的 let a='阿飞'; a.index = 20; //不能给字符串添加自定义属性,不会报错,显示undefined,这是操作的a的包装对象,用了即消失,存不下来 console.log(a,a.index); //第二次的点操作是操作的全新的包装对象,用后即消失 /* let b={}; b.index = 20; //对象可以添加自定义属性 console.log(b); */ let a = new String("阿飞"); //这里的a是字符串对象 a.index = 20; //a的包装对象是 new String(),可以添加自定义属性 console.log(a+"朱雀",a.index);
12.2 str.charAt() 取出字符串中的第几位字符,不能赋值
let a = '阿飞'; //console.log(new String(a)); //可以打印包装对象属性为String的对象 //针对a的包装对象的charAt console.log(a.charAt(1)); //取出字符串中的第几位字符 console.log(a[0]); // a的包装对象有a的内容,对象访问时可以的,但是IE6,7不支持,最好用charAt //包装对象不能赋值,没有意义;使用即消失,存不了 a[0]="5"; console.log(a[0]);
12.3 str.charCodeAt() 获取ASCII编码
/* charCodeAt() 调用第几个字符的编码, String.fromCharCode() 与之相反,从ASCII码到字符 */ let a = "阿A飞"; let b = `阿 飞`; console.log(a.charCodeAt(1)); // 65 console.log(b.charCodeAt(1)); //换行码是 10 console.log(String.fromCharCode(39134)); // 飞 //加密,利用ASCII码 let c = "小卑鄙我爱你"; let d = ""; for(let i=0,len=c.length;i<len;i++){ let code = c.charCodeAt(i)+520; console.log(code); d += String.fromCharCode(code); } console.log(d);
12.4 str.substring(x,y) 字符串截取一段[x,y),xy大小无所谓,y默认为len
/*substring(x,y) 从第x位到第y位截取,x要,y不要*/ // slice可以倒着截取 let a="abc阿飞789456"; let b = a.substring(3,5); //包含3,不包含5 console.log(b); //阿飞 let bb=a.substring(5,3); //没有前后要求,会自动调整 console.log(bb); //阿飞 let c = a.substring(3); //从3到结尾 console.log(c); //阿飞789456 let d = a.substring(-2,3); //没有负值,会认为0 console.log(d); // abc let e = a.slice(-5,-2); //负值表示从后开始数,倒着数,要满足先后顺序 console.log(e); // 894
12.5 大小写转换toLocaleUpperCase
//主要针对英文字母大小写 /* toLocaleUpperCase() */ let a = "abc"; let b = a.toLocaleUpperCase(); console.log(b); console.log(b.toLocaleLowerCase());
12.6 indexof()返回坐标,默认-1
/* 找位置,返回num */ let a = "我觉得阿飞老师是最皮的"; console.log(a.indexOf("阿飞")); //指定位置开始找,后面加一个参数 console.log(a.indexOf("老师",3)); //找不到返回 -1
12.7 split(",")切割返回数组,join(",")拼接成字符串
// 使用split()进行切割,返回一个包含分开特定字符前后元素的数组 let a = "阿飞,朱雀,心艾,岚岚,丫丫,艾米"; let b = a.split(","); console.log(b); console.log(b.join("php")) // let c = a.split(""); // console.log(c);
12.8 ES字符串扩展
function fn(a,b) { console.log(arguments); // [ 0:["1,2", "阿飞", ""], 1:5 , 2:6 ] console.log(a+b); // 1,2,阿飞,5 } //fn(1,2); let b=5; let a = 6; fn`1,2${b}阿飞${a}`;
13. 数组方法
13.1 push pop shift unshift
增加,返回数组长度,删除,返回被删的内容
/* push 参数:任意数据 功能:直接改变原数组 返回值:返回改变后数组的长度 pop 参数:null 功能:改变原数组,删除最后一位 返回值:删除的元素 shift 功能:改变原数组,删除第一位 返回值:删除的元素 unshift 功能:改变原数组,从第一位添加 返回值:数组长度 */ let a = ["阿飞","无虑"]; console.log(a.push("小浪","海文")); console.log(a); console.log(a.pop()); console.log(a); console.log(a.shift()); console.log(a); console.log(a.unshift("first")); console.log(a); //骚操作, // pop()返回的是删除的最后一个函数,后面跟参数执行 let x = [5,function(x){alert(x)}]; x.pop()(x[0]);
13.2 a.indexOf() a.slice()
//类数组除了.length,不能执行其他api let a = ["阿飞","无虑","朱雀"]; console.log(a.indexOf("朱雀")); //2 //数组截取 let b = a.slice(1); console.log(b); // ["无虑", "朱雀"]
13.3 数组切割splice()
返回被切割的部分
//数组切割,单个参数时,包含前面而不含后面的 let a = ['阿飞','无虑','小浪']; console.log(a.splice(1)); //从第1个切,切到最后 ["无虑", "小浪"] console.log(a); // ["阿飞"] //第二个参数表示切除几个 console.log(a.splice(1,1)); //从第1个开始切除1个 ["无虑"] console.log(a); // ["阿飞", "小浪"] //添加与替换 a.splice(1,1,'朱雀','心艾'); //切除一个,替换了2个 ["无虑"] console.log(a); // ["阿飞", "朱雀", "心艾", "小浪"]
13.4 数组排序a.sort() a.reverse()
二分法排序
不加参数,默认升序, 数组被改变了,但引用地址没变,仍相等
//一般只是针对num类型数组排序,其他类型没有意义 // sort()从小到大排序,升序 // reverse() 反序,元素从后往前排,可以先升序再反序实现降序排列 // 返回值是排列完成之后的数组, let a=[12,78,50,20,32,40,90]; a.sort(); console.log(a); a.reverse(); console.log(a); console.log(a.sort() === a); //改变之后还是a本身,相等 //降序 console.log(a.sort().reverse()); //JS二分法排序 a-b: 为升序 b-a: 为降序 a.sort(function (a,b) { return a-b; }) console.log(a);
let a = [ {name:'阿飞',age:20}, {name:'朱雀',age:30}, {name:'丫丫',age:18}, {name:'心艾',age:80}, {name:'岚岚',age:22}, {name:'发发',age:22}, {name:'艾米',age:28} ]; //a.sort(); 无法直接对对象排序 a.sort(function (a,b) { return b.age-a.age; //后者减前者,降序 }) console.log(a);
13.5 数组拼接数组 a.concat(b)
// concat 拼接数组 let a = [1,2,3]; let b = [4,5,6]; let c = a.concat(b); //将b拼接到a的后面 console.log(a,b); //不改变原数组 console.log(c);
13.6 数组方法 Array.isArray(a)
// 只有 Array 可以使用 isArray()方法 let a = [1]; let b = document.querySelectorAll("div"); console.log(Array.isArray(a)); console.log(Array.isArray(b)); //常用于判断类数组,其没有数组常用的方法
13.7 数组拼接字符串 a.join(",")
let a = ["阿飞",'心艾','朱雀']; let b = a.join("<==>"); //通常将字符串数组元素拼接成一个新的长的字符串 console.log(a); //不改变原数组 console.log(b); //b是返回的长字符串 阿飞<==>心艾<==>朱雀
13.8 数组遍历 a.forEach(v,i)
//forEach 遍历数组,必须要有函数作为参数,自动执行length次,不支持函数return /* 接收函数,该函数第一个形参代表每一位的数据, 第二个形参代表每一位的序号, 第三个形参代表原数组 */ let a = [4,5,6,7,8,9]; let b=a.forEach(function (x,y,z) { console.log(x,y,z); }); console.log(b); //undefined //与forEach效果一样 for(let i=0;i<a.length;i++){ (function (x,y,z) { console.log(x,y,z); })(a[i],i,a); }
13.9 数组遍历 a.map(cb(val,i))
//map是有返回值的数组遍历,返回一个基于原数组结构的数组 let a = [1,2,4,5]; let b = a.map(function (x,y,z) { console.log(x,y,z); return x*x; }); console.log(a); //map不改变a console.log(b); //b是基于a结构的新生数组 [1, 4, 16, 25]
**ES6 的Map对象,可用于记数**
let a = [1,2,4,5]; let c = new Map() a.map(function (val,index) { c.set(index,value) // 实例方法,设置 map数据 }) console.log(c); // Map对象实例 Map(4) {0 => 1, 1 => 2, 2 => 4, 3 => 5} c.get(3); // 5 按set时的index查找 c.has(0); // true index c.keys(); // MapIterator {0, 1, 2, 3} c.values(); // MapIterator {1, 2, 4, 5} [...c]; // [[0,1], [1,2], [2,4], [3,5]]
13.10 过滤 a.filter(cb(val,i))
/* 遍历 func:过滤 生成一个过滤后的数组,return true 留下,false 过滤掉 */ let a=[10,20,5,7,89,41]; let b=a.filter(function (x,y,z) { console.log(x,y,z); return x>10; //将x>10的留下,新生一个数组 }); console.log(a); //不改变原数组 console.log(b); //b是过滤后的数组
13.11 ES6 ...扩展
let a = [1,2,3]; console.log(...a); //去除数组括号,拆解成单个,ES6兼容,babel可转 console.log(1,2,3); //与上等价 //param形参 function f(param) { console.log(param); // 只有一个形参 1 } //f(1,2,3); f(...a); /* let c = (...a); //这样做是不允许的 console.log(c); */
...数组拼接
// ...数组拼接 let a = [1,2,4]; let b = [5,6,7]; //let c = a.concat(b); let c = [...a,...b]; console.log(c);
...解构赋值
//b是数组,拆开后赋值, ...b只能放在后面,表示接受全部 let [a,...b]=[1,2,3,4]; console.log(a); // 1 console.log(b); // [2, 3, 4]
...与iterator接口
//常见的Iterator遍历器接口 / Array String nodelist arguments / // Nodelist是节点对象集合 function fn(a,b,c) { console.log(a,b,c); } fn("753"); // 753 undefined undefined fn(..."753"); // 7 5 3
...与NodeList
let aP = document.getElementsByTagName("p"); // 类数组不能用数组的遍历 // aP.forEach(function (val,index) { // }); //ES5就有办法将 aP 先搞成数组,之后再使用forEach //slice不传参数就不切割,数组切割,方法slice使用call改变切割对象执行,返回一个真数组 let x = [].slice.call(aP); //生成一个新数组 console.log(x); x.forEach(function (node,index) { node.onclick = function () { alert(index); } }) //ES5 类数组=>真数组遍历遍历 [].slice.call(aP).forEach(function (node,index) { node.onclick = function () { alert(index); } }); //ES6 , ...将Nodelist拆了,加上[]成为真数组 console.log([...aP]); [...aP].forEach(function (n,i) { n.onclick = function () { alert(i); } });
...与function
//任何拆解都要放在最后面 function fn(a,...b) { console.log(a); console.log(b); } fn(4,5,6); function fn(...argu) { console.log(argu); //ES6的...可以直接代替arguments } fn(7,8,9);
14. 数学对象
14.1 Math
- Math.pow(x,y)表示x的y次方
- Math.trunc(-5.999991) 去小数点取整 -5
/* Math 对象 属性: 方法: */ //小数表示方式: 0表示正负 10表示小数点的位置为2 10.1010的整数部分为2,小数部分是1010/2^4, 10/16 //var a = 0 10 10.1010; //表示 2.625 /* Math.abs() 1.传入的值是正确的,返回一个新的绝对值结果,字面量赋值 2.传入的值是数字字符串,会取value值取绝对值 3.单个数字元素的数组也会取值,多个数字元素也是NaN 4.传入的值其他类型,返回NaN,表示not a number */ console.log(Math.abs(["-888"])); // 888 //Math.pow(x,y)表示x的y次方 console.log(Math.pow(2,10)); // 1024 //开方,y小于1 console.log(Math.pow(256,1/2)); // 16 //过气的开二次方 console.log(Math.sqrt(256)); // 16 //取整,floor接地气,往小了取整,正负一样 console.log(Math.floor(2.5),Math.floor(-2.01)); //2 -3 //向上取整 console.log(Math.ceil(2.5),Math.ceil(-2.9)); //3 -2 //四舍五入取整 console.log(Math.round(4.49),Math.round(-4.6)); //4 -5 //去小数点取整 console.log(Math.trunc(-5.999991)); // -5
14.2 三角函数
/* 圆周 2PI 360deg PI/6 30deg PI/3 60deg sin 正弦 对/斜 cos 余弦 邻/斜 tan 正切 对/邻 反三角 arcsin */ console.log(Math.sin(Math.PI/6),Math.cos(Math.PI/3),Math.tan(Math.PI/4)); //结果有小数,不精确 console.log(Math.asin(0.5),Math.PI/6);
14.3 随机数 Math.random()
/* Math.random() 返回一个[0,1)的数 */ //console.log(Math.random(),Math.round(Math.random()*10)); // 生成一个n到m的随机数,m取不到 const rdm = function(n=0,m=n+1){ return Math.floor((m-n)*Math.random()+n); } console.log(rdm(1,7));
14.4 parseInt() parseFloat()
/* 非数学对象 parseInt(a,b) a 需要转换的数值,隐式转换成字符串 b 进制(2,36) 10+26 */ console.log(parseInt("123abc")); //返回字符串中的整数部分,遇到NaN停止 返回123 //第二个可选参数是进制 console.log(parseInt("ff",16)); //按16进制解析,结果是10进制255 console.log(parseInt("0xa")); // 10 /* parseFloat()返回带小数点的 在底层parseFloat会先判断传入的参数类型,转换成字符串; 如果传入对象,会默认调用对象的toString()方法 */ console.log(parseFloat("12.3254a")); console.log({}.toString()); // 打印 "[object Object]" //ES6次方简写 console.log(3**2); //表示3的平方
14.5 立方根cbrt,e的幂exp, 单精度浮点 fround
// cbrt是取立方根结果,类似 Math.pow(27,1/3) console.log(Math.cbrt(27),Math.pow(27,1/3)); //3 3 // exp是自然对数e的幂函数,e^ln2 = 2; console.log(Math.exp(Math.LN2),Math.exp(Math.LN10)); //2 10 // 转成最近的单精度浮点数的数字 console.log(Math.fround(100.1));
15. 对象方法
15.1 对象遍历 for in
let a={ name:'阿飞', 'age':18, marry:true, handsome:true } //for in循环,遍历对象,也可以是数组 for(let key in a){ console.log(key,":",a[key]); }
15.2 对象的增删改查
//对象的属性是无序的,一般按照字母先后顺序展示 let a={ name:'阿飞', age:18, marry:true } console.log(a); //这里a是引用,后面改了,这边也能相应 //增 a.GF = ['朱雀','丫丫','岚岚','茜茜']; //改 a.age = 21; //删除 delete a.marry; //查找,是否存在 console.log('age' in a);
15.3 json JSON.stringify() JSON.parse()
//json 是一个格式非常标准长得像对象的字符串 //通用字符串,基本上所有语言都能识别的字符串格式 //let a = '{"name":"阿飞","age":18,"marry":true}'; let a={ 'name':'阿飞', age:18, marry:true }; //将一个对象转为JSON格式 JSON.stringify() 反序列化 let b=JSON.stringify(a) console.log(b); //将JSON序列化, JSON.parse() 解析成对象 let c = '{"a":10,"b":"狗蛋","c":true}'; //必须是标准严格的JSON格式才能解析,里面必须用双引号包起来"" console.log(JSON.parse(c));
15.4 es6的默认值
let [a,b=10]=[5]; // a=5 b=10 let {x:hh,y:tt=20} = {x:11}; // hh=11 tt=20 // function fn([a,b]) { // console.log(a,b,arguments); // } // fn([5]); function fn({a=10,b=100}) { console.log(a,b,arguments); }; //fn({a:5}); //fn({a,b}); fn({}); // 10 100 //无实参取形参的默认值{x,y},有实参对应实参的默认值{x:10,y:30} function ffn({x,y} = {x:10,y:30}) { console.log(x,y); } ffn(); //10 30 ffn({}); //un un
15.5 箭头函数
/* ES6 箭头函数 () => */ /* 形参 返回值 */ let fn = v => v; //ES5写法如下 var fn = function (v) { return v; } /* 加法写法 */ var fn = function (a,b) { return a+b; } let fn = (a,b) => {a+b}; /* 函数内容较多 */ var fn=function (a,b) { let c = a+2; let d = b-3; return(c+d); } let fn = (a,b)=>{ let c = a+2; let d = b-3; return(c+d); } /* 如果返回对象时,需要再加上() */ var fn = function () { return {a:10,b:20}; } let fn = () => ({a:10,b:20}) /* document.onclick = function () { console.log(this); } */ //通常用在回调函数内,方便使用 let arr=[1,2,3]; arr.forEach((value,key)=>{ console.log(value); }); let b = arr.map(v=>v*v); //结果平方返回 console.log(b);
16.定时器
16.1 setTimeout(cb,T)
/* setTimeout 一次执行定时器 param1:回调函数,或者是字符串包裹的js脚本,或者是引号包裹的全局作用域内的函数自执行 param2:数字(毫秒单位) */ setTimeout(function () { alert("timeout!"); },1000); let fn = function () { alert(1); return function () { alert(3); } }; setTimeout(fn(),1000); //不能是返回值,第一个参数是函数 let str = "let a=10;alert(a);"; //可以接收字符串参数,当成JS代码来读 setTimeout(str,2000); let fn = function () { // 如果fn不在全局作用域内,则 "fn()"找不到将报错 alert(4); }; setTimeout("fn()",1500); //默认在全局中执行引号的内容,作用域在全局
16.2 setInterval(cb,T)
/* setInterval 循环定时 */ let num=0; let fn = function () { console.log(++num); } setInterval(fn,1000/60); //最小时间间隔最多是60次每秒,
16.3 定时器队列
主程序执行完之后才执行定时器队列
//读到setTimeout时将定时器放入队列中,主程序执行完之后才执行队列 //所以先弹出3,后弹出4 setTimeout(function () { //进入队列后执行 alert(4); },0); !function fn() { //先执行 alert(3); }();
16.4 参数
回调函数带参时,放在定时器第二个参数之后
//定时器的函数有参数时,直接放在第二个参数之后 (function () { function fn(a,b) { console.log(a+b); } //setInterval(fn(2,7),1000); //这样会直接执行 /* setInterval('fn(2,7)',1000); //双引号内的内容默认丢在全局中执行,全局中找不到fn,报错 window.fn = fn; //将局部fn赋值到window */ setInterval(fn,1000,25,14); //参数接着放 })();
16.5 返回值
/* 返回值是按定时器出现顺序从1号开始的编号,作用是用来清除定时器的 编号不区分作用域 */ let x = setTimeout(function(){alert(3);},50000); let y = setTimeout(function(){alert(4);},60000); let z = setTimeout(function(){alert(5);},70000); let w = setInterval(function(){alert(6);},80000); let v = setInterval(function(){alert(7);},90000); console.log(x,y,z,w,v); // 1 2 3 4 5
16.6 清除定时器
clearInterval(t)
clearTimeout(t)
let x = setTimeout(function(){console.log("a")},2000); // 1 let y = setTimeout(function(){console.log("b")},2000); // 2 console.log(x,y); clearTimeout(y); //清除编号为2的定时器 // 定时器的队列编号跟作用域没关系,按出现顺序编号 (function () { let x = setTimeout(function(){console.log("a")},2000); // 3 let y = setTimeout(function(){console.log("b")},2000); // 4 console.log(x,y); })();
16.7 requestAnimationFrame(cb)
canselAnimationFrame()
/* H5出的新的API requestAnimationFrame() 不需要第二个参数,间隔是浏览器的刷新频率,单次执行 主要是让动画很流畅,浏览器准备好就刷新 CSS3关于动画的API底层都是它 取消 canselAnimationFrame() */ let oW = document.getElementById("wrap"); let start=0; function m(){ start += 2; oW.style.left = start+'px'; requestAnimationFrame(m); } requestAnimationFrame(m); // 兼容 window.requestAnimationFrame = window.requestAnimationFrame || function (fn) { setTimeout(fn,1000/60); }; window.cancelAnimationFrame = window.cancelAnimationFrame || clearTimeout;
16.8 案例,自动无缝轮播图
关键点: oImg.style.transitionDuration = 0+'s';
<!DOCTYPE HTML> <html> <head> <meta charset="UTF-8"> <title>Title</title> <style> body{font-family: "Microsoft YaHei",serif;} body,dl,dd,p,h1,h2,h3,h4,h5,h6{margin:0;} ol,ul,li{margin:0;padding:0;list-style:none;} img{border:none;} #wrap{ overflow: hidden; position: relative; width:520px; height:280px; margin:50px auto; -webkit-user-select: none; -moz-user-select: none; -ms-user-select: none; user-select: none; } #wrap .img{ position: absolute; top:0; left:0; width:1000%; height:100%; transition-property: left; } #wrap .img li{ position: relative; float:left; width:10%; height:100%; } #wrap .tab{ position: absolute; left:0; right:0; bottom:15px; z-index:5; width:80px; margin:auto; background-color: rgba(255,255,255,.3); border-radius: 10px; font-size:0; text-align:center; } #wrap .tab li{ float: left; width:10px; height:10px; margin:3px; border-radius: 10px; background-color: white; cursor:pointer; } #wrap .tab li.active{ background-color: #ff5000; } #wrap>span{ display: none; z-index:5; position: absolute; top:0; bottom: 0; width:30px; height:30px; margin:auto; background-color: rgba(0,0,0,.3); border-radius: 30px; color:#fff; font-size:22px; line-height:30px; cursor: pointer; } #wrap:hover>span{ display: block; } #wrap>span:hover{ background-color: rgba(0,0,0,.5); } #wrap .left{ left:-12px; text-indent: .5em; } #wrap .right{ right:-12px; text-indent: .1em; } </style> </head> <body> <div id="wrap"> <ul class="img"> <li> <img src="img/05.webp"> </li> <li> <img src="img/01.webp"> </li> <li> <img src="img/02.jpg"> </li> <li> <img src="img/03.jpg"> </li> <li> <img src="img/04.png"> </li> <li> <img src="img/05.webp"> </li> <li> <img src="img/01.webp"> </li> </ul> <ul class="tab"> <li class="active"></li> <li></li> <li></li> <li></li> <li></li> </ul> <span class="left"><</span> <span class="right">></span> </div> <script> (function () { //定义变量 let oWrap = document.getElementById("wrap"), aTabLi = document.querySelectorAll("#wrap .tab li"), oImg = document.querySelector("#wrap .img"), aImgLi = document.querySelectorAll("#wrap .img li"), //做不到无缝 oLeft = document.querySelector("#wrap .left"), oRight = document.querySelector("#wrap .right"), len = aImgLi.length-1, lastIndex=1, timer=''; //做一个定时器动画 window.requestAnimationFrame = window.requestAnimationFrame || function (fn){setTimeout(fn,1000/60);}; window.cancelAnimationFrame = window.cancelAnimationFrame || clearTimeout; oImg.style.left = '-'+lastIndex*100+'%'; //初始是显示第一个 //封装动作事件 function move(x) { aTabLi[x].classList.remove("active"); oImg.style.transitionDuration = .3+'s'; //动画时间 oImg.style.left = '-'+lastIndex*100+'%'; //动画步骤 if(lastIndex === len){ //到达最后一个了!len = 6 requestAnimationFrame(function () { //偷天换日,改到第一个 lastIndex=1; aTabLi[0].classList.add("active"); }); //坐标移动该怎么不被动画发现呢,延时吧,等于duration 0.3s setTimeout(function () { oImg.style.left = '-'+lastIndex*100+'%'; oImg.style.transitionDuration = 0+'s'; },300); }else if(lastIndex === 0){ //到达第0个了!len-1 = 5 requestAnimationFrame(function () { //偷天换日,改到第一个 lastIndex=len-1; aTabLi[4].classList.add("active"); }); //坐标移动该怎么不被动画发现呢,延时吧,等于duration 0.3s setTimeout(function () { oImg.style.left = '-'+lastIndex*100+'%'; oImg.style.transitionDuration = 0+'s'; },300); }else{ aTabLi[lastIndex-1].classList.add("active"); } } //tab点击事件 [...aTabLi].forEach((oTabLi,index)=>{ oTabLi.onclick = function () { if(index === lastIndex-1)return; let x = lastIndex-1; lastIndex = index+1; move(x); } }); //left点击事件 , lastIndex = 0 1 2 3 4 oLeft.onclick = function () { let x = lastIndex-1; // [0,4] lastIndex --; move(x); } //right点击事件 oRight.onclick = function () { let x = lastIndex-1; //x从0开始 lastIndex++; move(x); } //定时器 timer = setInterval(function () { let x = lastIndex-1; //x从0开始 lastIndex++; move(x); },3000); //停止 oWrap.onmouseenter=function () { clearInterval(timer); } //继续 oWrap.onmouseleave=function () { timer = setInterval(function () { let x = lastIndex-1; //x从0开始 lastIndex++; move(x); },3000); } })(); </script> </body> </html>
17. 时间API
17.1 Date类
/*Date类 new Date() 本地此时此刻的日期对象/时间戳 */ let d = new Date(); //获取的是对象 console.dir(d); console.log(d.getTime()); // 时间戳 let t = new Date(d.getTime()); console.log(t); //以下API返回数字 number console.log(d.getFullYear()); //得到 年 console.log(d.getMonth()+1); //得到 月(从0开始计数,应该+1返回) console.log(d.getDate()); //日 console.log(d.getHours()); //时 console.log(d.getUTCHours()); //0时区 UTC console.log(d.getMinutes()); //分 console.log(d.getSeconds()); //秒 console.log(d.getDay()); //星期 星期日是0 console.log("===================="); console.log(d.toUTCString());
let x = new Date(); //日期对象可以做减法,会自动调用getTime进行相减 //定时器都不是 精准的计时 setInterval(function () { let y=new Date(); console.log(y-x); x=y; },50); //周期很小,则误差较大
17.2 设置时间戳
//无参是获取,有参是设置 let x=new Date(2018,8-1,8,1,1,1); //月份从0开始的,所以设置8月应输入7 let x=new Date(2018); //只有一个参数时,会被认为是毫秒时间戳,从1970年开始加 let x=new Date(2018,5-1); //默认1日,0时0分0秒 console.log(x); let a = new Date().getTime(); console.log(new Date(a-3600000)); //打印一个小时前的时间
17.3 网页倒计时跳转
<p>404 页面未找到~~</p> <div><span>8</span>秒后,返回 <a href="">主页</a></div> <script> (function () { let oS = document.querySelector("div span"); let oVal = 8; setInterval(function () { oS.innerHTML = --oVal; if(oVal === 1){ window.location.href = "http://web.tanzhouedu.com"; } },1000) })(); </script>
17.4 新年倒计时
(function () { let oW = document.getElementById("wrap"); let x = new Date(2020,2-1,24); //过年时间 function fn(){ let d = x - new Date(); //差了多少秒 let DD = Math.floor(d/1000/60/60/24); //天 let HH = Math.floor(d/1000/60/60%24); let MM = Math.floor(d/1000/60%60); let SS = Math.floor(d/1000%60); if(DD<10) DD = '0'+DD; if(HH<10) HH = '0'+HH; if(MM<10) MM = '0'+MM; if(SS<10) SS = '0'+SS; oW.innerHTML = `距离过年还有 <span>${DD}</span> 天 <span>${HH}</span> 小时 <span>${MM}</span> 分 <span>${SS}</span> 秒;`; }; fn(); setInterval(fn,1000); })();
18. 运动框架
18.1 基础的移动
<!DOCTYPE HTML> <html> <head> <meta charset="UTF-8"> <title>Title</title> <style> #wrap{ position: absolute; left:0; top:0; width:100px; height:100px; background-color: pink; } </style> </head> <body> <div id="wrap"></div> <script> (function () { let oWrap = document.getElementById("wrap"); let startVal = 0; function m() { startVal +=3; oWrap.style.left = startVal + 'px'; requestAnimationFrame(m); //推荐,保证前一个动画执行完成后,进入下一次动画 //setTimeout(m,1000/60); } m(); })(); </script> </body> </html>
18.2 封装好的运动框架
<!DOCTYPE HTML> <html> <head> <meta charset="UTF-8"> <title>Title</title> <meta name="keywords" content="关键词"> <meta name="description" content="描述"> <meta name="author" content="Danew"> <style> body{font-family: "Microsoft YaHei",serif;} body,dl,dd,p,h1,h2,h3,h4,h5,h6{margin:0;} ol,ul,li{margin:0;padding:0;list-style:none;} img{border:none;} #wrap{ width:50px; height:50px; margin-left:300px; /*百分比在IE显示百分比,要转成px*/ background-color: pink; opacity: .8; filter:alpha(opacity=80); /*标准IE浏览器的兼容,opacity 0-100 */ } </style> <script src="move.js"></script> </head> <body> <div id="wrap"></div> <script src="move.js"></script> <script> var oW = document.getElementById("wrap"); Move(oW,"marginLeft",600,3); Move(oW,"width",500,3); Move(oW,"height",500,3); Move(oW,"opacity",0,.01); //opacity没有单位 </script> </body> </html>
'use strict'; /* * 运动框架 * param: * ele - object 必须 表示要进行运动的节点 * attr - string 必须 表示要改变的css属性 * target - number 必须 表示属性的终点值 * step - number 选填 表示运动速度的正值,默认5 *return: * */ window.Move = function () { //兼容定时器 window.requestAnimationFrame = window.requestAnimationFrame || function (fn) { setTimeout(fn, 1000 / 60); }; window.cancelAnimationFrame = window.cancelAnimationFrame || clearTimeout; //框架函数 return function Move(ele, attr, target) { var step = arguments.length > 3 && arguments[3] !== undefined ? arguments[3] : 5; //ele.style.marginLeft 只能获取行内样式 //console.log(getComputedStyle(ele)); //原生JS,存储着节点对象全部的样式 //获取存储着ele展示样式的对象 var cssObj = ele.currentStyle || getComputedStyle(ele); //解决兼容性问题IE8用currentStyle //初始的值 var sVal = parseFloat(cssObj[attr]); //去掉单位,变成数字 //兼容IE的opacity if(attr === "opacity" && isNaN(sVal))sVal=1; //考虑初始值与目标值大小的问题 var bool = sVal>target; if(sVal > target){ step = -Math.abs(step); //负值 }else if (sVal < target){ step = Math.abs(step); //正值 }else{ return; } function f() { sVal += step; // + -step if (bool?sVal<=target:sVal>=target) { sVal = target; }else { requestAnimationFrame(f); } if(attr === 'opacity'){ ele.style[attr] = sVal; ele.style.filter = "alpha(opacity="+100*sVal+")"; // IE浏览器兼容 }else if(attr === 'zIndex'){ ele.style[attr] = sVal; //不加单位 }else{ ele.style[attr] = sVal + 'px'; //加单位 } } requestAnimationFrame(f); }; }();
19. DOM
19.1 节点nodeType nodeName nodeValue
/* 元素节点的nodeType是 1 nodeName是 大写的标签名 nodeValue=null 元素属性的nodeType是 2 nodeName是 属性名 nodeValue=属性值 文本节点的nodeType是 3 nodeName是 #Text nodeValue=文本内容 注释节点的nodeType是 8 nodeName是 #comment nodeValue=注释内容 */ let oW = document.getElementById("wrap"), oC = document.getElementsByClassName("content")[0]; console.dir(oW); console.log(oW.nodeType); //1 元素节点 console.log(oW.nodeName); //DIV console.log(oW.childNodes); //子节点,包含所有节点类型 console.log(oW.attributes[2].nodeType); // 2 // console.log(typeof oC.childNodes[0].nodeValue); oW.attributes[2].nodeValue = '朱雀'; //不建议这样修改,应该坐标是不确定的 console.log(oW.attributes[2]); // afei='朱雀'
19.2 子节点 childNodes children
<div id="wrap"> 阿飞飞 <p>gg</p> <p>ss</p> <a href="">gg</a> </div> <script> let oW = document.getElementById("wrap"); console.log(oW.childNodes); //IE不兼容,谷歌连换行也能获取,不实用 //常用 !!!!!! console.log(oW.children); //只获取子元素节点哦 ,有三个子元素(标签) //innerHTML是刷新整个内容,要求必须刷新后再get元素,这样很不方便 </script>
19.3 插入节点createElement
- createElement 创建元素节点
- createTextNode 创建文本节点
- appendChild 创建的是子节点
- insertBefore(A,B) 创建子节点,A元素节点放在B元素节点之前
<div id="wrap"> 阿飞飞 <p>pp</p> <p id="on">gg</p> <a href="#">123</a> </div> <script> let oW = document.getElementById("wrap"); let oN = document.getElementById("on"); let oP = document.querySelector("p"); oP.onclick = function(){ //节点的变化不影响其操作和属性 alert(5); } //创建文本节点对象 let text = document.createTextNode("朱雀"); //console.dir(text); //创建元素节点对象 let oDiv = document.createElement("div"); //从后面添加子节点 appendChild oW.appendChild(text); //oW.appendChild(oDiv); //在某个子元素的前面添加 (a,b) a是新元素节点 //oW.insertBefore(oDiv,oP); //放在子元素第一位 oW.insertBefore(oDiv,oW.children[0]); //oW.childNodes[0] 所有的第一位 </script>
19.4 修改节点位置
<div id="wrap"> <p>阿飞</p> <p>朱雀</p> </div> <div id="box"></div> <script> let oBox = document.getElementById("box"); let aP = document.getElementsByTagName("p"); //转移子元素,原来元素的属性和方法不会改变 oBox.appendChild(aP[0]); </script>
19.5 删除节点 removeChild()
<div id="wrap"> <p>555</p> <p>666</p> </div> <div class="box"></div> <script> //只有父级节点才能删除子节点 let oW = document.getElementById("wrap"); let oBox = document.getElementsByClassName("box")[0]; let oP = document.getElementsByTagName("p")[1]; //父级删除了子节点,但变量oP 仍保留了被删除的节点 oW.removeChild(oP); document.onclick = function () { oBox.appendChild(oP); //oP仍可以使用 } </script>
19.6 获取节点
- firstChild 第一个子节点,一般获取的都是文本节点 ->换行符
- firstElementChild 第一个元素子节点
- nextSibling 下一个兄弟节点
- nextElementSibling 下一个兄弟元素节点
- previousElementSibling 上一个兄弟元素节点
<div id="wrap"> <p>11</p> <p>22</p> <p>33</p> </div> <script> let oW = document.getElementById("wrap"); console.log(oW.firstChild); //第一个子节点,文本节点->换行符 console.log(oW.firstElementChild); //第一个元素子节点, <p>11</p> console.log(oW.children[0]); //第一个元素子节点, <p>11</p> var aChild = oW.children; //是动态的更新,根据儿子变化而变化, var p1 = aChild[0]; //元素节点 var p2 = aChild[1]; var p3 = aChild[2]; console.log(p1.nextSibling); //下一个兄弟节点(文本换行符) //标准浏览器 console.log(p1.nextElementSibling); //下一个兄弟元素(如果没有,打印null) console.log(p2.nextElementSibling); // console.log(p3.nextElementSibling); //没有返回null //上一个兄弟节点 previousElementSibling console.log(p2.previousElementSibling); //IE兼容,只有nextSibing,下一个兄弟元素节点 function getNextSibing(ele) { if(ele.nextElementSibling !== undefined){ return ele.nextElementSibling; }else{ return ele.nextSibling; //IE的 } } console.log(getNextSibing(p1)); console.log(getNextSibing(p2)); console.log(getNextSibing(p3)); </script>
- parentNode 父亲节点
- offsetParent 定位父级,离得最近的拥有定位的父级,都没有时就是body
<head> <meta charset="UTF-8"> <title>Title</title> <style> #wrap{ position: relative; } </style> </head> <body> <div id="wrap"> <p id="btn">4545454</p> </div> <script> let oBtn = document.getElementById("btn"), oW = document.getElementById("wrap"); console.log(oBtn.parentNode); // === oW typeof oW = "object" console.log(oW.parentNode); // body //自删除 //oBtn.parentNode.removeChild(oBtn); console.log(oBtn.offsetParent); //定位父级 oW </script> </body>
19.7 克隆节点 oP.cloneNode()
节点的事件不被克隆
- oP.cloneNode() 默认不复制内容
- oP.cloneNode(true) 包括内容全部复制,但事件不会复制
<div id="wrap"> <p class="gg">745545</p> </div> <div id="btn">444</div> <script> //节点具有唯一性,只有clone可以 默认不复制里面的内容,参数为true内容也克隆, 事件不能复制 let oP = document.getElementsByClassName("gg")[0], oBtn = document.getElementById("btn"); oP.onclick =function () {alert(1);}; //事件不能clone //oBtn.appendChild(oP); 直接appendChild是移动节点,并未增加 let clone = oP.cloneNode(true); //克隆获得一个新的节点对象 oBtn.appendChild(clone); </script>
19.8 节点替换 replaceChild(A,B) , A将B替换掉
<div id="wrap"> 123 </div> <div id="btn">456</div> <script> let oW = document.getElementById("wrap"), oB = document.getElementById("btn"); oW.replaceChild(oB,oW.childNodes[0]); </script>
19.9 节点片段 document.createDocumentFragment()
(function () { let oW = document.getElementById("wrap"); //生成10个小球 (function () { //节点片段,暂时存放节点的对象 let oF = document.createDocumentFragment(); //生成十个小球,一次性append进oW内,省的渲染多次 for(let i=0;i<10;i++){ let oP = document.createElement("p"); oF.appendChild(oP); } oW.appendChild(oF); })(); //运动 (function () { let aP = [...oW.children]; let MaxL,MaxT; window.onresize = (function r(){ MaxL = window.innerWidth - 100; MaxT = window.innerHeight - 100; return r; })(); //随机初始速度 let speedArr = []; aP.forEach((ele,index)=>{ speedArr[index] = { stepX : Math.floor(Math.random()*12+4), stepY : Math.floor(Math.random()*12+4) }; }); //随机颜色 function changeColor(ele) { let [r,g,b] = [ Math.floor(Math.random()*256), Math.floor(Math.random()*256), Math.floor(Math.random()*256) ]; ele.style.backgroundImage = `radial-gradient(white,rgb(${r},${g},${b}))`; } //遍历运动 !function m(){ aP.forEach((ele,index)=>{ let left = ele.offsetLeft + speedArr[index].stepX; let top = ele.offsetTop + speedArr[index].stepY; if(left >= MaxL){ left = MaxL; speedArr[index].stepX = -speedArr[index].stepX; changeColor(ele); } if(left <=0){ left = 0; speedArr[index].stepX = -speedArr[index].stepX; changeColor(ele); } if(top>=MaxT){ top = MaxT; speedArr[index].stepY = - speedArr[index].stepY; changeColor(ele); } if(top<=0){ top = 0; speedArr[index].stepY = - speedArr[index].stepY; changeColor(ele); } ele.style.left = left+'px'; ele.style.top = top + 'px'; }); requestAnimationFrame(m); }(); })(); })();
20. DOM宽高属性与事件对象
20.1 视图页面宽高,无单位
window.innerWidth
document.documentElement.clientWidth
<div id="wrap"></div> <script> /* Window视图属性 获取页面显示区的宽高,IE8及以下不兼容 */ console.log(window.innerWidth); console.log(window.innerHeight); document.body; document.head; document.title; document.documentElement; //获取html元素节点 console.log(document.documentElement === document.querySelector("html")); // true //Document文档视图 下面的IE兼容 console.log(document.documentElement.clientWidth); //html页面宽度 console.log(document.documentElement.clientHeight); //html页面高度 </script>
20.2 元素宽高,无单位
- getComputedStyle(oWrap).width css样式宽度,有单位
- oWrap.clientWidth 客户端宽度 width+padding 无单位
- oWrap.offsetWidth 客户端宽度 + border
- oWrap.scrollWidth 滚动宽度,一般用在出现滚动条, = 客户端宽度
/* number类型 clientHeight/clientWidth width+padding offsetHeight/offsetWidth width + padding + border scrollHeight/scrollWidth 滚动宽度,超出视图仍显示正常宽度,不管有没有超出隐藏 */ let oWrap = document.getElementById("wrap"); console.log(getComputedStyle(oWrap).width); //获取元素css样式设置的宽度,有单位 console.log(oWrap.clientWidth); //width + padding console.log(oWrap.offsetWidth); //width + padding + border //scrollWidth 元素占用的宽度,生成滚动条,内容不超出时数值等于clientWidth console.log(oWrap.scrollWidth); //如果有超出部分,正确的反映出超过之后的宽度,不管有没有加超出隐藏
20.3 定位left值offsetLeft与top值offsetTop
只有距离定位父级的左值和上值 ,没有右下
<!DOCTYPE HTML> <html> <head> <meta charset="UTF-8"> <title>Title</title> <style> #wrap{ /*position: relative;*/ margin:50px; } p{ width:10px; height:10px; background-color: pink; } </style> </head> <body> <div id="wrap"> 8888 <p></p> </div> <script> /* 只有距离定位父级的左值和上值 */ let oP = document.querySelector("#wrap p"); //wrap没加position,则定位父级是body console.log(oP.offsetLeft); //到定位父级的距离 console.log(oP.offsetTop); //到定位父级的距离 //获取元素到文档body的距离 function getOffset(ele) { let dis = { top:0,left:0 }; while(ele !== document.body){ dis.top += ele.offsetTop; dis.left += ele.offsetLeft; ele = ele.offsetParent; //定位父级 } return dis; } console.log(getOffset(oP)); </script> </body> </html>
20.4 body的滚动高度,用document.documentElement.scrollTop
<body style="height:2000px;"> <div id="wrap"></div> <script> document.onclick = function () { //页面滚动高 console.log(document.documentElement.scrollTop); //document.body.scrollTop 谷歌弃用了,但是手机可能还未改 //最好这样写去获取页面的滚动高 console.log(document.body.scrollTop || document.documentElement.scrollTop); //保险起见 } </script> </body>
20.5 元素的滚动高,父级的scrollTop
<!DOCTYPE HTML> <html> <head> <meta charset="UTF-8"> <title>Title</title> <style> #wrap{ overflow-y:auto ; /*y竖直方向有滚动条*/ width:300px; height:500px; background-color: pink; margin:50px auto; } #wrap p{ height:2000px; } </style> </head> <body> <div id="wrap"> <p></p> </div> <script> let oW = document.getElementById("wrap"); oW.onclick = function(){ console.log(oW.scrollTop); //oW元素竖直方向的滚动高度 } </script> </body> </html>
20.6 scrollTop无单位,可以赋值
<body style="height: 2000px"> <div id="wrap"></div> <script> document.onclick = function () { document.documentElement.scrollTop = 500; } </script> </body>
20.7 getBoundingClientRect()获取边界客户端矩形,对象形式,无单位
<!DOCTYPE HTML> <html> <head> <meta charset="UTF-8"> <title>Title</title> <style> #wrap{ /*position: relative;*/ margin:50px; } p{ width:10px; height:10px; background-color: pink; } </style> </head> <body> <div id="wrap"> 8888 <p></p> </div> <script> let oP = document.querySelector("#wrap p"); console.log(oP.getBoundingClientRect()); // DOMRect {x: 50, y: 71, width: 10, height: 10, top: 71, bottom:81, left:50, right:60} //top bottom left right 对应 边 到 body的距离 // x y是坐标 </script> </body> </html>
20.8 oW.scrollIntoView(true) 滚动到可视区
<!DOCTYPE HTML> <html> <head> <meta charset="UTF-8"> <title>Title</title> <style> #wrap{ position: absolute; top:1500px; width:100px; height:100px; background-color: pink; } </style> </head> <body style="height: 3000px;"> <div id="wrap"></div> <script> let oW = document.getElementById("wrap"); document.onclick = function () { //oW.scrollIntoView(false); //true是跳转到视图顶部,false跳转到视野底部 oW.scrollIntoView(true); } </script> </body> </html>
20.9 event事件对象
/* event事件对象 事件函数执行时,第一个形参就是事件对象:存储着和该次事件相关的一些信息 IE8及以下浏览器,使用event全局变量来表示事件对象 该对象里面比较重要的一些属性 clientX/clientY 事件触发时,鼠标距离可视区的距离 pageX/pageY 鼠标距离文档的位置(IE8 不兼容) */ //IE的事件函数没有形参 //兼容 document.onclick = function (ev) { ev = ev || window.event; // IE的Event是全局变量 console.log(ev); }
20.10 鼠标事件 onmousedown onmousemove onmouseup
document.onmousedown = function () { console.log("down"); document.onmousemove = function () { console.log("move"); } } document.onmouseup = function () { console.log("up"); document.onmousemove = null; //默认属性是null }
20.11 案例:可拖拽的盒子
<!DOCTYPE HTML> <html> <head> <meta charset="UTF-8"> <title>Title</title> <style> #wrap{ position: absolute; left: 0; top:0; width:100px; height:100px; background-color: pink; } </style> </head> <body> <div id="wrap"></div> <script> let oW = document.getElementById("wrap"); oW.onmousedown = function (ev) { //获取鼠标初始的位置 let sX = ev.clientX; let sY = ev.clientY; //获取按下时,盒子的位置 let sLeft = oW.offsetLeft; let sTop = oW.offsetTop; document.onmousemove = function (eve) { //获取鼠标当前的位置 let mX = eve.clientX; let mY = eve.clientY; //计算鼠标位置的变化量 let x = mX - sX; let y = mY - sY; //盒子当前的位置 = 盒子按下的位置 + 变化量 oW.style.left = sLeft + x + 'px'; oW.style.top = sTop + y + 'px'; }; }; document.onmouseup = function () { console.log("up"); this.onmousemove = null; } </script> </body> </html>
20.12 案例:十球发射
<!DOCTYPE HTML> <html> <head> <meta charset="UTF-8"> <title>Title</title> <meta name="keywords" content="关键词"> <meta name="description" content="描述"> <meta name="author" content="Danew"> <style> body{font-family: "Microsoft YaHei",serif;} body,dl,dd,p,h1,h2,h3,h4,h5,h6{margin:0;} ol,ul,li{margin:0;padding:0;list-style:none;} img{border:none;} div.box{ position: absolute; left:0; top:0; width:100px; height:100px; background-color: #db3a45; border-radius: 50%; } </style> </head> <body> <div id="wrap"> <div class="box"></div> <div class="box"></div> <div class="box"></div> <div class="box"></div> <div class="box"></div> <div class="box"></div> <div class="box"></div> <div class="box"></div> <div class="box"></div> <div class="box"></div> </div> <script> (function () { let aBox = [...document.getElementsByClassName("box")]; //最大边距 let MaxLeft,MaxTop ; window.onresize = (function s() { MaxLeft = window.innerWidth-100; MaxTop = window.innerHeight-100; return s; })(); //步长 let step = [ { stepX:3, stepY:4, }, { stepX:5, stepY:6, }, { stepX:6, stepY:7, }, { stepX:7, stepY:8, }, { stepX:8, stepY:9, }, { stepX:9, stepY:10, }, { stepX:10, stepY:11, }, { stepX:11, stepY:12, }, { stepX:12, stepY:13, }, { stepX:13, stepY:14, }, ]; aBox.forEach((ele,index)=>{ ball(ele,index); }); //rgb函数 function color() { let r = Math.floor(Math.random()*256), g = Math.floor(Math.random()*256), b = Math.floor(Math.random()*256); return `rgb(${r},${g},${b})`; } //小球运动函数 function ball(ele,index) { //初始位置 let left = ele.offsetLeft, top = ele.offsetTop; !function m() { //步长 left += step[index].stepX; top += step[index].stepY; //left if(left>=MaxLeft){ left=MaxLeft; ele.style.backgroundColor = color(); step[index].stepX = -step[index].stepX; } if(left<=0){ left=0; ele.style.backgroundColor = color(); step[index].stepX = -step[index].stepX; } //top if(top>=MaxTop){ top = MaxTop; ele.style.backgroundColor = color(); step[index].stepY = -step[index].stepY; } if(top<0){ top = 0; ele.style.backgroundColor = color(); step[index].stepY = -step[index].stepY; } ele.style.left = left+'px'; ele.style.top = top+'px'; requestAnimationFrame(m); }(); } })(); </script> </body> </html>
21. DOM事件
21.1 事件冒泡
冒泡触发顺序:从子元素开始到窗口
- event.stopPropagation() 主流浏览器阻止冒泡:
- event.cancelBubble = true; IE阻止冒泡
<div id="box"> box1 <div id="box1"> box2 </div> </div> <script> /* 事件的冒泡 子元素触发了事件 如果父元素也有同类型的事件的话,父元素也会触发 触发的顺序是从子级开始,向父级方向 冒泡的触发顺序 box2 -> box1 -> body -> html 冒泡触发顺序 从子元素开始到窗口,(从儿子到爸爸)window 阻止冒泡 event.stopPropagation() 阻止传播 IE不兼容 box2 box1 */ var box = document.getElementById("box"), box1 = document.getElementById("box1"); document.onclick = function(){ console.log("我也触发了事件"); } box.onclick = function () { console.log(1); } box1.onclick = function (event) { event = event ||window.event; //event.stopPropagation(); //冒泡停止(主流浏览器) console.log(2); //ie阻止冒泡的 属性 //event.cancelBubble = true; //兼容性写法 event.stopPropagation ? event.stopPropagation():event.cancelBubble=true; } </script>
21.2 事件监听
- attachEvent("onclick",cb) IE8
- detachEvent(事件类型,回调方法);
- addEventListener(事件类型,回调方法,捕获|冒泡) 主流浏览器
- removeEventListener(事件类型,回调方法,捕获|冒泡);
捕获与冒泡顺序相反
var oW = document.getElementById("wrap"); //DOM 0级事件, 相同名字的事件被覆盖,只能监听一个事件 oW.onclick = function () { console.log(1); //被覆盖 }; oW.onclick = function () { console.log(2); }; /* 事件监听(默认事件冒泡) 用到事件的地方最好就是使用 DOM2级来监听 监听事件 主流浏览器 事件类型 "click" ele.addEventListener(事件类型,回调方法,捕获|冒泡) 1.this指向节点本身 2.捕获与冒泡 默认是false 冒泡事件 true为捕获,与冒泡的顺序相反,从最大父级到子级开始(也能使用阻止冒泡的方式) ie浏览 ie8 事件类型 "onclick" oW.attachEvent(事件类型,回调方法) 1.this不再指向节点本身,指向window ie8没有事件捕获,默认是冒泡事件 移除事件 主流浏览器 ele.removeEventListener(事件类型,回调方法,捕获|冒泡); IE8 ele.detachEvent(事件类型,回调方法); */ //事件监听DOM 2级事件 //IE8 oW.attachEvent("onclick",function (e) { e = e||window.event; console.log(this); //this 不再指向监听事件的节点本身 console.log(e); }); /* 主流浏览器 */ oW.addEventListener("click",function () { console.log("我又监听了一次点击事件,理解一下DOM 2级事件"); }) var callback = function () { console.log(1); }; oW.addEventListener("click",callback); //文档双击,注销事件 //该事件必须事先用变量保存,removeEventListener的参数必须与添加事件监听的参数一样,才能移除 document.ondblclick = function () { oW.removeEventListener('click',callback); }
21.3 事件默认行为
- oncontextmenu 默认行为指右键可选事件
- return false; dom0级阻止默认行为
- event.preventDefault(); 阻止默认行为
- event.returnValue = false; IE8阻止默认行为
<div id="wrap"> <p>默认行为</p> <p>默认行为</p> <p>默认行为</p> <p>默认行为</p> <p>默认行为</p> <p>默认行为</p> <p>默认行为</p> <p>默认行为</p> <p>默认行为</p> <p>默认行为</p> <p>默认行为</p> <p>默认行为</p> </div> <script> //事件的默认行为是右边鼠标显示的可选事件 oncontextmenu /* 阻止默认行为 DOM 0级在事件里面直接 return false, DOM 2级 主流浏览器 event.preventDefault(); ie event.returnValue = false; */ //DOM 0级在事件里面直接 return false, document.oncontextmenu = function () { return false; } //DOM 2级 preventDefault document.addEventListener('contextmenu',function (e) { e.preventDefault(); }) //IE8 document.attachEvent('oncontextmenu',function (e) { e = e||window.event; e.returnValue = false; }) //阻止默认的滚轮事件 wrap.addEventListener('mousewheel',function (e) { e.preventDefault(); }) </script>
21.4 滚轮事件
- mousewheel 滚轮事件 event.wheelDelta 滚动的幅度 下负上正 120 谷歌+ie
- DOMMouseScroll 火狐的滚轮事件 event.detail 滚轮的幅度 上负 3
let oW = document.getElementById("wrap"); /* 谷歌以及ie event.wheelDelta 滚动的幅度 -120 下滚 120 上滚 火狐的 滚动事件名都不一样 DOMMouseScroll 只能通过DOM 二级事件监听 event.detail 滚动的幅度 3 下滚 -3 上滚 只有谷歌和ie有onmousewheel事件属性,默认值为null 火狐根本就没有onmousewheel属性,为undefined */ /* oW.addEventListener("mousewheel",function (e) { console.log(e.wheelDelta); }) */ //只能通过创建一个新的节点来判断兼容 if(document.createElement("div").onmousewheel === null){ console.log("谷歌"); }else{ console.log("火狐"); }
兼容的事件监听
var oW = document.getElementById("wrap"); //元素的事件 var fn = addEvent(oW,'click',function () { this.style.backgroundColor = 'red'; }); //文档双击移除事件 addEvent(document,"dblclick",function () { removeEvent(oW,'click',fn); }) //滚轮事件 addEvent(oW,'mousewheel',function () { console.log('我滚动了'); }) //添加监听事件 function addEvent(ele,eType,callback,capture) { //主流浏览器 if(ele.addEventListener){ //兼容一下火狐的滚轮事件 if(eType === 'mousewheel' && document.createElement("div").onmousewheel === undefined){ eType = 'DOMMouseScroll'; } ele.addEventListener(eType,callback,capture); return callback; }else{ //处理ie的this指向,ie低版本不支持bind var codeCall = function(){ callback.call(ele); } ele.attachEvent('on'+eType,codeCall); return codeCall; } } //移除事件 function removeEvent(ele,eType,callback,capture) { ele.removeEventListener? ele.removeEventListener(eType,callback,capture) : ele.detachEvent("on"+eType,callback); }
兼容的文档滚动
//自定义滚动 addEvent(oW,'mousewheel',function (event) { event = event || window.event; var dir; if(event.detail){ //火狐的滚动值 dir = event.detail / 3; //下滚 }else{ dir = event.wheelDelta / -120; //下滚 } //console.log(dir); this.scrollTop += dir*50; })
21.5 案例 自定义滚动条
<!DOCTYPE HTML> <html> <head> <meta charset="UTF-8"> <title>Title</title> <meta name="keywords" content="关键词"> <meta name="description" content="描述"> <meta name="author" content="Danew"> <style> #wrap{ position: relative; width:400px; height:500px; margin:20px auto; overflow: hidden; user-select: none; } #wrap .content{ position: absolute; left:0; top:0; width:370px; font-size:14px; } #wrap .content li{ width:100%; padding:5px 0 5px 10px; margin-bottom:1px; background-color:#eeeef0; line-height:30px; } #wrap .scrollbar{ position: absolute; right:0; top:0; width:20px; height:496px; background-color: #ccc; background-color:rgba(200,200,200,.5); border-radius: 10px; border:2px solid #aaa; box-shadow: 0 0 20px #ccc ; } #wrap .scrollbar .bar{ position: absolute; left:0; top:0; width:20px; height:80px; background-color: white; border-radius:10px; } </style> </head> <body> <div id="wrap"> <ul class="content"> <li> <p>1. It is never too old to learn.</p> <p>活到老,学到老。</p> </li> <li> <p>2. There is no royal road to learning.</p> <p>学问无坦途。书山有路勤为径,学海无涯苦作舟.</p> </li> <li> <p>3. A man becomes learned by asking questions.</p> <p>不耻下问才能有学问。</p> </li> <li> <p>4. A young idler, an old beggar.</p> <p>少壮不努力,老大徒伤悲。</p> </li> <li> <p>5. Study to be what you wish to seem. </p> <p>学习可成为你所理想的人物。</p> </li> <li> <p>6. By reading we enrich the mind, by conversation we polish it.</p> <p>读书使人充实,交谈使人精明。</p> </li> <li> <p>7. Books and friends should be few but good.</p> <p>读书如交友,应求少而精。</p> </li> <li> <p>8. Readingis to the mind while exercise to the body.</p> <p>读书健脑,运动强身。</p> </li> <li> <p>9. A good beginning makes a good ending.</p> <p>良好的开端就是成功的一半。 善始者善终。</p> </li> <li> <p>10. No matter how bad your heart has been broken, the world doesn’t stop for your grief. The sun comes right back up the next day.</p> <p>不管你有多痛苦,这个世界都不会为你停止转动。太阳照样升起。</p> </li> <li> <p>11. Experience is the mother of wisdom.</p> <p>实践出真知。</p> </li> <li> <p>12. Don't trouble trouble until trouble troubles you.</p> <p>不要自寻烦恼。</p> </li> <li> <p>13. Everybody dies, but not everybody lives.</p> <p>人人都会死,但非人人都曾活过。</p> </li> <li> <p>14. Doing is better than saying.</p> <p>行胜于言。</p> </li> <li> <p>15. Commitment in many, can do. It is just a lie.</p> <p>承诺再多,都做不到。那也只不是是谎言。</p> </li> <li> <p>16. No cross, no crown.</p> <p>不经历风雨,怎能见彩虹。</p> </li> </ul> <div class="scrollbar"> <div class="bar"></div> </div> </div> <script> (function () { var oW = document.getElementById("wrap"), oContent = document.querySelector("#wrap .content"), oScroll = document.querySelector("#wrap .scrollbar"), oBar = document.querySelector("#wrap .bar"), docMove= function () {}; //内容区的滚动事件 createEvent(oW,"mousewheel",function (event) { event = event||window.event; //兼容IE var sT = oContent.offsetTop; var dir = 0; var top = 0; if(event.detail){ //火狐 dir = event.detail / 3; //下滑 }else{ //谷歌和IE dir = event.wheelDelta / -120; //下滑值 } if(dir>0){ top = Math.max((sT-dir*50),-756); }else{ top = Math.min((sT-dir*50),0) } oContent.style.top = top + 'px'; oBar.style.top = -top/1.82 + 'px'; event.stopPropagation ? event.stopPropagation():(event.cancelBubble=true); },false); //自定义滚轮区的点击事件 createEvent(oScroll,'click',function (event) { event = event||window.event; var y_ = event.clientY - oBar.offsetHeight/2 - oW.offsetTop; if(y_<=0) y_=0; if(y_>=416)y_=416; oBar.style.top = y_+'px'; oContent.style.top = -y_*1.82 + 'px'; //阻止子级冒泡 event.stopPropagation ? event.stopPropagation():(event.cancelBubble=true); },true); //自定义滚轮的滚动(点击时拖放) createEvent(oBar,'mousedown',function (event) { event = event||window.event; var sT = oBar.offsetTop; var sY = event.clientY; var top = 0; docMove = createEvent(document,'mousemove',function (event) { event = event||window.event; var _Y = event.clientY - sY; if(_Y>0) top = Math.min((sT+_Y),416); if(_Y<0) top = Math.max((sT+_Y),0); oBar.style.top = top+'px'; oContent.style.top = -top*1.82 + 'px'; }) }) //清除移动事件 createEvent(document,'mouseup',function (){ removeEvent(document,'mousemove',docMove); }); //双击清除移动事件 createEvent(document,"dblclick",function () { removeEvent(document,'mousemove',docMove); }); //生成监听事件 function createEvent(ele,eType,callback,capture) { if(ele.addEventListener){ //主流 //兼容火狐的滚动事件 if(eType === 'mousewheel' && document.createElement("div").onmousewheel === undefined){ eType = "DOMMouseScroll"; } //生成监听 ele.addEventListener(eType,callback,capture); return callback; }else{ //IE8 var codeCall = function(){ callback.call(ele); } ele.attachEvent('on'+eType,codeCall); return codeCall; } } //清除事件 function removeEvent(ele,eType,callback,capture) { if(ele.removeEventListener){ //兼容火狐的滚动事件 if(eType === 'mousewheel' && document.createElement("div").onmousewheel === undefined){ eType = "DOMMouseScroll"; } ele.removeEventListener(eType,callback,capture); }else{ ele.detachEvent("on"+eType,callback); } } })(); </script> </body> </html>
22. 表单事件
22.1 表单事件
<div id="wrap"></div> <a href="" id="x">455454</a> <form action="" method="" id="form"> <input type="text" name="user"> <input type="radio" name="sex"> <input type="radio" name="sex"> <input type="submit"> </form> <script> //表单是 .name 就能直接获取对象 // name相同,获取的是相同name的数组 // checkbox多选框,name不要一样 let oForm = document.getElementById("form"); console.log(oForm.sex); // RadioNodeList(2) [input, input, value: ""] oForm.user.value = "阿飞飞"; /* onfocus 焦点事件 onblur 失去焦点事件 并不是所有的元素都能添加焦点事件,只有能获得焦点的元素才能添加焦点事件 页面tab键能获得焦点的元素(document,a,window,以及表单相关的元素可以) */ let ox = document.getElementById("x"); x.onfocus = function () { // TAB键触发 console.log("我获得焦点了"); } x.onblur = function () { console.log("嘤嘤,我失去焦点了"); } //window的焦点事件 window.onfocus = function () { document.title = "乌龙阁的博客"; } window.onblur = function () { document.title = "握草,出BUG!快来看看啊"; }
22.2 定时器关闭
/* 页面失去焦点时,定时器setTimeout速度下降,可能影响布局渲染 requestAnimationFrame会立即停止 */ let num=0; let timer = null; function m(){ document.title = ++num; timer = setTimeout(m,50); //requestAnimationFrame(m); }; m(); window.onblur = function () { clearTimeout(timer); } window.onfocus = function () { clearTimeout(timer); m(); }
22.3 表单事件 onchange
- onchange 输入框,当失去焦点时与获取焦点时发生改变时触发
- oninput 输入框发生变化时,实时的触发
- onselectstart 选中文字,触发
- onsubmit 表单触发提交事件
- 用select标签的name属性获取选中的option值,如 oForm.gg.value
<form action="" method="" id="form"> <input type="text" name="user"> <input type="radio" name="sex"> <input type="radio" name="sex"> <input type="submit"> <select name="gg" id="h"> <option value="1">1</option> <option value="2">2</option> <option value="3">3</option> </select> </form> <div id="wrap"> 文字文字文字文字文字文字文字文字 </div> <script> /* 输入框的onchange,判断获取焦点时与失去焦点时的差异 oninput 响应实时变化事件 */ let oForm = document.getElementById("form"); /* oForm.user.onchange = function () { console.log(1); } */ /* oForm.user.oninput= function () { console.log(1); } */ oForm.gg.onchange=function () { console.log("选项发生了改变",oForm.gg.value); } //onselectstart 选中文字时,触发事件 document.getElementById("wrap").onselectstart = function () { console.log(2); return false; //阻止默认操作 } //表单的提交事件,提交后跳转一个页面 oForm.onsubmit = function () { console.log("提交"); return false; //拒绝提交(用于判断是否满足提交条件) } </script>
22.4 案例,限制焦点
<form action="" method="" id="form"> <input type="text" name="user"> <input type="text" name="user1"> <input type="text" name="user2"> </form> <button id="btn"> 按钮 </button> <script> let oForm = document.getElementById("form"); let oBtn = document.getElementById("btn") /* API focus() 加焦点 blur() 失去焦点 submit() 提交api */ let aInp = [...oForm.children]; //动态的 aInp.forEach((ele,index)=>{ ele.oninput = function () { if(this.value.length >= 5){ //大于5个离开 this.disabled = true; aInp[index+1] && aInp[index+1].focus(); //存在则转移到下一个焦点 } }; ele.onblur = function () { if(this.value.length<5){ //不满5个不能离开 this.focus(); } } })
22.5 window.onready window.onload
//等待全部加载完毕 window.onload = function () { } //只需要结构加载完毕即可 window.onready = function () { }
22.6 键盘事件
- keydown 按键按下
- keyup 按键抬起
/* 按键按下: keydown keypress keydown 在 keypress之前触发 down响应所有按键 press事件只响应能键入值的按键,enter也能响应,不响应功能键(退格,上下左右) 按键抬起: keyup 按键抬起时响应 */ document.onkeydown = function () { console.log("处于按下去状态ing"); } document.onkeypress = function(){ console.log("press"); } document.onkeyup = function () { console.log("按键抬起了!"); }
键值
- 回车键 keyCode = 13
- F12 keyCode = 123
/* 事件event只包含键盘的属性,没有鼠标的信息 e.keyCode */ document.onkeydown = function (e) { console.log(e); console.dir(e.keyCode); if(e.keyCode === 123){ // 123 阻止按键 F12 打开调试面板 return false; } }
按键控制盒子移动
<!DOCTYPE HTML> <html> <head> <meta charset="UTF-8"> <title>Title</title> <meta name="keywords" content="关键词"> <meta name="description" content="描述"> <meta name="author" content="Danew"> <style> body{font-family: "Microsoft YaHei",serif;} body,dl,dd,p,h1,h2,h3,h4,h5,h6{margin:0;} ol,ul,li{margin:0;padding:0;list-style:none;} img{border:none;} #wrap{ position: absolute; left:200px; top:200px; width:100px; height:100px; background-color: pink; } </style> </head> <body> <div id="wrap"></div> <script> let oW = document.getElementById("wrap"); document.onkeydown = function (ev) { let keyCode = ev.keyCode; switch (keyCode) { case 65: //a oW.style.left = oW.offsetLeft-5+'px'; break; case 87: //w oW.style.top = oW.offsetTop-5+'px'; break; case 83: //s oW.style.top = oW.offsetTop+5+'px'; break; case 68: //d oW.style.left = oW.offsetLeft+5+'px'; break; } }; //运动 </script> </body> </html>
多按键同时控制
<!DOCTYPE HTML> <html> <head> <meta charset="UTF-8"> <title>Title</title> <meta name="keywords" content="关键词"> <meta name="description" content="描述"> <meta name="author" content="Danew"> <style> body{font-family: "Microsoft YaHei",serif;} body,dl,dd,p,h1,h2,h3,h4,h5,h6{margin:0;} ol,ul,li{margin:0;padding:0;list-style:none;} img{border:none;} #wrap{ position: absolute; left:200px; top:200px; width:100px; height:100px; background-color: pink; } </style> </head> <body> <div id="wrap"></div> <script> let oW = document.getElementById("wrap"); let ifDown = { 65:{ bool:false, timer:null, setTimer(){ oW.style.left = oW.offsetLeft-5+'px'; this.timer = requestAnimationFrame(this.setTimer.bind(this)); }, clearTimer(){ cancelAnimationFrame(this.timer); } }, 87:{ bool:false, timer:null, setTimer(){ oW.style.top = oW.offsetTop-5+'px'; this.timer = requestAnimationFrame(this.setTimer.bind(this)); }, clearTimer(){ cancelAnimationFrame(this.timer); } }, 83:{ bool:false, timer:null, setTimer(){ oW.style.top = oW.offsetTop+5+'px'; this.timer = requestAnimationFrame(this.setTimer.bind(this)); }, clearTimer(){ cancelAnimationFrame(this.timer); } }, 68:{ bool:false, timer:null, setTimer(){ oW.style.left = oW.offsetLeft+5+'px'; this.timer = requestAnimationFrame(this.setTimer.bind(this)); }, clearTimer(){ cancelAnimationFrame(this.timer); } } } //按下启动定时 document.onkeydown = function (ev) { let keyCode = ev.keyCode; if(!ifDown[keyCode]) return; ifDown[keyCode].bool || ifDown[keyCode].setTimer(); ifDown[keyCode].bool = true; }; //抬起清除定时 document.onkeyup = function (ev) { let keyCode = ev.keyCode; if(!ifDown[keyCode]) return; ifDown[keyCode].clearTimer(); ifDown[keyCode].bool = false; } </script> </body> </html>
23. 正则表达式
23.1 有什么用
不用正则时,一般用 !isNaN(121)=true 来提取数字
/* 正则表达式 对象 配合一些api来验证字符串 */ let a="456阿飞789朱雀12"; //["456","789,"12"] let b="454sjjdsiaodao5454"; let c="sds45sd45s21a"; console.log(fn(a)); console.log(fn(b)); console.log(fn(c)); console.log("*************************"); console.log(zz(a)); console.log(zz(b)); console.log(zz(c)); function zz(str) { return str.match(/\d+/g); } function fn(str) { let len = str.length; let s =""; let arr = []; for(let i=0;i<len;i++){ let n=str.charAt(i); if(!isNaN(n)){ //数字字符不是NaN,其他字符是NaN //console.log(n); s+=n; }else{ //NaN s && arr.push(s); s=""; } } s && arr.push(s); return arr; }
23.2 正则表达式
创建正则表达式对象的两种方法
- let a = /abc/
- let b = new RegExp("abc")
验证正则的方法
- a.test(str) 返回 true or false
/* 正则表达式对象 两种创建方式 1. 双斜杠包括的字符 2. new RegExp("") */ let a = /abc/; console.log(typeof a); //object let b = new RegExp("abc"); //和第一种创建的效果一样 /*api1 test() 正则.test(字符串) 匹配成功返回true,否则返回false */ let a = /abc/; //匹配完整连续的abc字符串 console.log(a.test("阿飞abc朱雀"));; /* / /不能传变量 */ let s = "abc"; //let a=/s/; //这种定义方式,是没有办法根据变量来制定正则的 let a = new RegExp(s); //可以用变量 console.log(a.test("阿飞abc朱雀"));
23.3 转移符号\的用法
字符串中实现转义 "\\" 打印 "\"
/* \转义符号,默认在字符串中的\有特殊意义,不能单独出现 */ console.log("阿飞\"love\"朱雀\n123456\t789021"); //转义符号将有特殊意义的字符变成没有特殊意义 let str = "/\\"; let a=/\/\\/; //用转义字符"\"转义"/"和"\" console.log(a.test(str)); /* 转义符号配合一些字母使用,有非常独特的意思 \s 各种空格, \t \n \r " " \S 非空格,\s的补集 \d 数字 \D \d的补集 \w 数字、字母、下划线 一般匹配用户名的 \W \b 连词符 起始 结束 (空格 以及 (除了\w之外) 所有内容都是独立部分) \B */ let a = /\s/; let s = " "; let a=/\d\d/; //连续两个数字 let s="fffff79pppp"; let a = /\w/; let s = "abc"; /* 算作"afei"独立的字符 "I am afei-1" "I am afei阿飞" "I am afei.abc" \b只能用于匹配英文,中文本身就不独立,不能使用\b独立匹配 */ let a = /\bafei\b/; //要匹配独立的部分 let s = "I am afei.abc"; //可以 "afeihui"就不行 console.log(a.test(s));
23.4 修饰符 i m g
验证正则的方法 match
- str.match(a) 返回所有匹配正则的元素组成的数组,(a是加了g的全局匹配)
/* 修饰符(写在//后面 i 不区分大小写 g 全局匹配 m 换行匹配 */ let a = /g/i; let s = "G"; console.log(a.test(s)); /* .match() 字符串.match(正则) 寻找匹配的内容,拿到并组成一个数组返回,否则返回null */ let a = /xx/ig; let s = "XxgfxxhxXx"; //xXx只能识别前两个 console.log(s.match(a)); //加了g后是纯粹的数组,没有别的属性
23.5 量词 {n}
/* 量词 { } {n} n个 {n,m} n~m包含n也包含m {n,} n~无穷大 包含n 默认按多的取(贪婪模式) 量词后面加问号"?"就成了(惰性模式) 几个特殊量词有专属的符号代替 {1,} + 至少一个 {0,} * 至少0个 {0,1} ? 要么有要么没有 */ let a = /\d\d\d\d\d\d\d\d\d\d\d/g; let s = "阿飞18860900316阿飞"; let a = /飞\d{11}/g; //{11}只表示\d重复11次 let s = "阿飞18860900316阿飞15357875321"; let a = /\d{2,4}?/g; // 惰性匹配,所以只匹配2个的 let s = "1-23-234-456阿飞18860900316阿飞15357875321"; // ["23", "23", "45", "18", "86", "09", "00", "31", "15", "35", "78", "75", "32"] let a = /\d{2,}/g; //至少2个 let s = "1-23-234-456阿飞18860900316阿飞15357875321"; // ["23", "234", "456", "18860900316", "15357875321"] //let a = /\d+/g; //最少一个取 //let a = /\d+?/g; //匹配到后,一个个取 //let a = /\d*?/g; //返回的全是空 相当于 //g let a = /\d??/g; //全是空 let s = "gh777scsf45dsdsd454s35"; console.log(s.match(a));
23.6 子项()
加了括号的成为子项,使用全局g,则不打印子项
获取第一个子项 s.match(a)[1]
匹配失败,返回null
/* 子项 ( ) */ let a = /(ab)+/; // 不加括号,只能匹配abbbb let s = "abababab"; // ["abababab", "ab", index: 0, input: "abababab", groups: undefined] let a = /gg(ab)+f(f)/; //加了括号的子项也会打印出来,使用全局g,则不打印子项 let s = "ababggababffababa"; //不加g,打印子项["ggababff", "ab", "f", index: 4, input: "ababggababffababa", groups: undefined] //利用子项,只获取子项中的号码 let a = /阿飞:(\d{11})/; let s = "阿飞:12121212321,朱雀:32321232123"; console.log(s.match(a),s.match(a)[1]); // ["阿飞:12121212321", "12121212321", index: 0, input: "阿飞:12121212321,朱雀:32321232123", groups: undefined] "12121212321"
23.7 一个[]字符集只匹配一个字符
/* | 或者 [] 字符集,一个字符集只匹配一个字符 [a-z] 字符区间("-"在"[]"内有了特殊意义) [abc] 或者的意思 [^abc] 放在正则字符的首位,表示除了这些,(不放在首位表示一个单纯的字符) [{}?*.+()] 这些原来有特殊意义的字符放在[]内没有了特殊意义,就是单纯的字符 [a-z] [A-Z]所有的字母 */ let a = /abc|edf/g; //或者匹配 let s = "edfabcedfabc"; let a = /(阿飞|朱雀)老师/g; //用子级匹配,加外面公用的 let s = "朱雀老师美,阿飞老师帅"; let a = /1|2|3|4|5|6|7/g; //1或者7中的任何一个 let a = /[1-7]{5}/g; //区间,代表1到7之间任何一个连续5个 //ascii码的匹配,注意要有前后顺序 let a = /[2-B]/g; let s = "23456789:;C<=>?@AB"; //unicode字符编码也能匹配 let a = /[阿-飞]/g; //意义不大 let s = "阿飞老师"; let a = /[abc0-9]/g; //a或b或c,0到9 let s = "2345abc"; //匹配所有的数字和字母 let a = /[a-zA-Z0-9]/g; let s = "2345abcAfejnfjGHjfbj"; let a = /[^abc]/g; //除了,取反只能放在首位 let s = "2345abcAfejnfjGHjfbj"; let a = /[{}?*./+()[\]\-]/g; //字符集内的字符失去了特殊意义 let s = "2345abcAfejnfjGHjfbj"; console.log(s.match(a));
23.8 起止字符
/* 起止字符 ^ 起始位置 $ 结束位置 */ let a = /^abc$/; //只能匹配"abc",空格都不可以有 //let s = "gabcgg"; //必须是a起始的字符串才能匹配 let s = "gabcgg"; console.log(s.match(a)); // console.log("ddabcdd".match(/(a|^)bc/)); // abc a是子项 console.log("bcjfjfjfjfj".match(/(a|^)bc/)); // bc 子项为""
23.9 .字符
/* . 匹配任意字符 除了换行等之外 \n \r [.]的.是单纯的匹配字符. */ let a = /./g; let s = ".123\r46\n5"; console.log(s.match(a)); //匹配所有的字符 /[\s\S]/ /[\w\W]/
23.10 应用案例
/* /a/ new RegExp() \s \S \w \W \d \D \b \B 修饰词 igm 量词{} *+? 惰性?取短的 子项() 字符集[] | ^ $ . */ let reg={ //qq:5~10,只能是数字,第一位不是0 qq: /^[1-9]\d{4,9}$/, //用户名:6~18,数字字母_,必须要字母开头 user:/^[a-z]\w{5,17}$/i, //密码:6~18,数字字母_所有符号 pwd:/^[\w<>,.?/\-+=*@#$%^&()[\]`~|\\{}<>]{6,18}$/, //手机号: tel:/^1[3-9]\d{9}$/, //邮箱 mail:/^[a-z1-9_]\w{0,17}@[0-9a-z]{2,}(\.[a-z]{2,4}){1,2}$/i, //身份证 IDCard:/^[1-9]\d{5}(18|19|20)\d{2}(0[13578]|1[02])(0[1-9]|[12][0-9]|3[01])|(0[469]|11)(0[1-9]|[12][0-9]|30)|(02(0[1-9]|[12][0-9]))\d{3}[0-9x]$/i, } // let a = reg.qq; let b = reg.mail; // let s = "3045282682"; // console.log(a.test(s)); console.log(b.test("1886@163.com")); // let a = reg.IDCard; // console.log(a.test("341221199411178765")); //大月 //31天 /* /(0[13578]|1[02])(0[1-9]|[12][0-9]|3[01])/ //30天 /(0[469]|11)(0[1-9]|[12][0-9]|30)/ //2月 /(02(0[1-9]|[12][0-9]))/ //一起 /(0[13578]|1[02])(0[1-9]|[12][0-9]|3[01])|(0[469]|11)(0[1-9]|[12][0-9]|30)|(02(0[1-9]|[12][0-9]))/ /((0[13578]|1[02])(0[1-9]|[12][0-9]|3[01])|(0[469]|11)(0[1-9]|[12][0-9]|30)|(02(0[1-9]|[12][0-9])))/ */
23.11 捕获组
捕获的是子项 RegExp.$1 最近的一次正则结果中的子项
捕获用 \n 表示,代表一个子项
let a = /a(b)(c)/; let s = "abc"; console.log(a.test(s)); let a = /(阿飞|朱雀)老师/; let s = "abc阿飞老师"; let s1 = "abc朱雀老师"; a.test(s); s1.match(a); console.log(RegExp.$1); //离它最近一次正则结果的子项1 朱雀 console.log(RegExp.$9); //最多存9个子项 没有就是 空 //捕获组,1表示重复第1个子项,没有加g,所以第一次匹配到后就结束匹配 let a = /(\d)\1/; //捕获组,重复子项 aa bb let a = /(\d\d)\1/; //捕获组,重复子项 abab let a = /(\d(\d))\1\2/; //捕获组,重复子项 ababb let a = /(\d\d)\1{9}/; //捕获组,可以用量词 abab*n let a = /(\d(\d))\2\1/; //捕获组,重复子项 abbab let s = "797994545545"; console.log(s.match(a));
23.12 案例 表单验证
<!DOCTYPE HTML> <html> <head> <meta charset="UTF-8"> <title>Title</title> <meta name="keywords" content="关键词"> <meta name="description" content="描述"> <meta name="author" content="Danew"> <style> body{font-family: "Microsoft YaHei",serif;} body,dl,dd,p,h1,h2,h3,h4,h5,h6{margin:0;} ol,ul,li{margin:0;padding:0;list-style:none;} img{border:none;} #wrap{ position: relative; width:430px; height:550px; margin:50px auto 0; background-color: #00baff; } #wrap .title{ height:40px; line-height: 40px; background-color: #008ccf; color:white; font-size: 24px; text-align: center; font-weight: bold; } #wrap input{ display: inline-block; width:250px; height:40px; padding-left: 20px; margin-left: 80px; margin-top:20px; } #wrap input.error{ border: 2px solid red; } #wrap input.ok{ border:2px solid #479552; } #wrap img{ display: none; width:30px; height:30px; vertical-align: middle; } #wrap input[type=submit]{ width:270px; padding:0; background-color: #008ccf; outline-style: none; color:white; font-weight: bold; cursor:pointer; } #wrap label{ overflow: hidden; display: block; height:0; margin-left: 80px; color:red; font-size: 12px; } #alert{ position: absolute; left: 0; right:0; top:0; bottom:100px; visibility: hidden; width:150px; height:40px; margin: auto; background-color: black; color:white; text-align: center; line-height: 40px; opacity: 0; transition: bottom .4s,opacity .5s ; } #alert.on{ visibility:visible; bottom: 0; opacity: 1; } </style> </head> <body> <div id="wrap"> <p class="title">注册</p> <form action="" method="" id="form"> <input type="text" name="name" id="name" placeholder="请输入您的用户名"> <img src="img/yes.png"> <label for="name">错误:用户名以字母开头,长度为6~16位</label> <input type="password" name="pwd" id="pwd" placeholder="请输入您的密码"> <img src="img/yes.png"> <label for="name">错误:注意密码长度为6~18位</label> <input type="password" name="repwd" id="repwd" placeholder="请再次输入您的密码"> <img src="img/yes.png"> <label for="name">错误:两次密码输入不一致</label> <input type="text" name="phonenumb" id="phonenumb" placeholder="请输入您的手机号"> <img src="img/yes.png"> <label for="name">错误:手机号格式不正确</label> <input type="text" name="qq" id="qq" placeholder="请输入您的QQ号"> <img src="img/yes.png"> <label for="name">错误:QQ号码格式不正确</label> <input type="text" name="mail" id="mail" placeholder="请输入您的邮箱"> <img src="img/yes.png"> <label for="name">错误:邮箱格式不对,请重新输入</label> <input type="submit" value="提交" id="submit"> <div id="alert">请先输入密码!</div> </form> </div> <script> (function () { let aLabel = document.querySelectorAll("#wrap label"), aImg = document.querySelectorAll("#wrap img"), oName = document.getElementById("name"), oPwd = document.getElementById("pwd"), oRepwd = document.getElementById("repwd"), oPnumb = document.getElementById("phonenumb"), oQQ = document.getElementById("qq"), oMail = document.getElementById("mail"), alert = document.getElementById("alert") ; //验证规则 let reg={ //用户名 name:/^[a-z]\w{5,15}$/i, //密码 pwd:/^[\w~`!@#$%^&*()\-+=[\]{}'";:,.<>/?\\]{6,18}$/, //手机号 tel:/^1[3-9]\d{9}$/, //qq qq:/^[1-9]\d{4,9}$/, //邮箱 mail:/^[a-z1-9_]\w{0,17}@[0-9a-z]{2,}(\.[a-z]{2,6}){1,2}$/i }; let isCheck=[]; //定义添加验证函数 function addCheckEvent(ele,reg,index) { //输入时,取消对勾 ele.addEventListener("focus",function () { aImg[index].style.display = "none"; }) //离开时验证 ele.addEventListener("blur",function () { if(!ele.value){ ele.className=""; ele.className=""; aImg[index].style.display = "none"; //不显示对勾 aLabel[index].style.height = 0+'px'; //错误不提示 return; }; //未填写则返回 //验证成功 if(reg.test(ele.value)){ ele.className="ok"; //绿框 isCheck[index]=true; //表示验证过 aImg[index].style.display = "inline-block"; //对勾 aLabel[index].style.height = 0+'px'; //错误不提示 }else{ ele.className="error"; //红框 isCheck[index]=false; //未验证通过 aImg[index].style.display = "none"; //不显示对勾 aLabel[index].style.height = 16+'px'; //提示错误 ele.focus(); //锁定焦点 } }); } //1. 验证用户名 addCheckEvent(oName,reg.name,0); //2.验证密码 addCheckEvent(oPwd,reg.pwd,1); //3.二次密码focus oRepwd.addEventListener("focus",function () { //先取消对勾 //aImg[2].style.display = "none"; //判断第一次密码输入状态 if(!oPwd.value){ //密码未输入 setTimeout(function () { alert.classList.add("on"); //动画提示 oPwd.focus(); }); setTimeout(function () { alert.classList.remove("on"); //定时消失 },2000); } }) //二次密码验证blur oRepwd.addEventListener("blur",function () { if(!oRepwd.value){ oRepwd.className=""; aImg[2].style.display = "none"; //对勾 aLabel[2].style.height = 0+'px'; //错误不提示 return; }; //未填写则返回 if(isCheck[1]){ //第一次密码已经验证 if(oRepwd.value === oPwd.value){ //密码一样 oRepwd.className="ok"; //绿框 isCheck[2]=true; //表示验证过 aImg[2].style.display = "inline-block"; //对勾 aLabel[2].style.height = 0+'px'; //错误不提示 }else{ //不通过 oRepwd.className="error"; //绿框 isCheck[2]=false; //表示验证过 aImg[2].style.display = "none"; //对勾 aLabel[2].style.height = 16+'px'; //错误不提示 oPwd.focus(); //锁定第一次输入密码 } } }) //4.验证手机号 addCheckEvent(oPnumb,reg.tel,3); //5.验证QQ号 addCheckEvent(oQQ,reg.qq,4); //6.验证邮箱 addCheckEvent(oMail,reg.mail,5); })(); </script> </body> </html>
23.13 正向断言(?=a)
/* 断言,JS只有正向断言,放在后面,括号不算子项 (?=a) 格式限定后面紧接着必须是a,但不希望匹配结果中有a (?!a) 格式限定后面紧接着不能是a */ // let r = /(阿飞|朱雀)老师,\1老师/; // let s = "阿飞老师,阿飞老师"; let r = /Window(?=XP)/; //希望格式是WindowXP,但不希望结果中有XP,所有用? 这个()不是子项 let s = "WindowXPx"; let r = /Window(?!XP)/; //不希望格式是Window后接着XP,?! 这个()不是子项 let s = "Window111"; console.log(s.match(r)); console.log(RegExp.$1);
23.14 replace
/* replace str.replace(正则,字符串|函数); 替换匹配到的内容变成第二个参数的内容 (如果是字符串直接替换,如果是函数,替换返回值) */ let reg = /(阿飞|风屿|海文)(老师|先生|大大)/g; let str = "阿飞老师是个纯粹的大猪蹄,风屿先生真自恋,海文大大脾气不好"; let newStr = str.replace(reg,"小浪"); let newStr = str.replace(reg,(...rest)=>{ //经过了很多运算之后,得到用来替换的字符串 console.log(rest); return '小浪'; }); let newStr = str.replace(reg,(a,b,c)=>{ //经过了很多运算之后,得到用来替换的字符串 console.log(a,b,c); //a表示正则形参,b表示第一个子项,c表示第二个子项 return '小浪'+c+c.slice(1); }); console.log(newStr);
23.15 脏字过滤器
<div id="wrap"> <input type="text"> <button class="btn">发送</button> <ul></ul> </div> <script> let Btn = document.getElementsByClassName("btn")[0], Input = document.getElementsByTagName("input")[0], oU = document.getElementsByTagName("ul")[0] ; Btn.onclick = function () { let str = Input.value; if(str === "")return; let reg=/傻[,./<>\-+=\d]*逼|草|操|cao|艹|你[爸妈]|王八蛋|/g; str = str.replace(reg,($0)=>{ let s=""; [...$0].forEach(()=>s+="□") return s; }) let oLi = document.createElement("li"); oLi.innerText = str; oU.appendChild(oLi); Input.value=""; } </script>
24. cookie
24.1 cookie
/* 适合小数据的存储,字符串缓存,存在本地的 1.不同的浏览器存放的cookie位置不一样,也是不能通用的 2.cookie的存储是以域名形式进行区分的 3.cookie的数据可以设置名字的 4.一个域名下存放的cookie的个数是有限制的,不同的浏览器存放的个数不一样 5.每个cookie存放的内容大小也是有限制的,不同的浏览器存放大小不一样 本地文件不支持cookie,火狐浏览器支持,通过Webstorm打开支持 */ /* 如果不给cookie设置过期时间,那么浏览器关闭之后,cookie就清除了 在存储本地cookie肯定是需要设置过期时间的 expires 一个日期对象转换成的字符串,默认是UTC时间 */ //每条cookie都需要单独设置,不支持一次赋值设置多个cookie document.cookie = "user=阿飞;pwd=123"; 没用的 let date = new Date(new Date().getTime()-7*24*3600*1000); document.cookie = "goudan=阿飞;expires="+date.toUTCString(); //date转成UTC时间进行赋值 document.cookie = "pwd=123"; //获取全部cookie console.log( document.cookie );
24.2 封装cookie的增删改查API
let Cookie ={ //设置,修改 set(mJson,day) { //设置过期时间,不设置day默认undefined,计算时 = NaN,此时设置的cookie关闭浏览器释放 let date = new Date(new Date().getTime()+day*24*3600*1000).toUTCString(); Object.entries(mJson).forEach(([key,value])=>{ document.cookie = `${key}=${value};expires=${date}`; }) }, //获取 get(key) { //不传值返回全部cookie构建的对象 if(!key){ let json=document.cookie; let obj={}; while(json){ let reg=/(^|\s)(\w+)=([^;]+)(;|$)/; let arr = json.match(reg); json = json.split(arr[0])[1].toString(); obj[RegExp.$2]=RegExp.$3; } return obj; }else{ let str = document.cookie; // /(^|\s)aaa=([^;]+)(;|$)/ let reg = new RegExp("(^|\\s)"+key+"=([^;]+)(;|$)"); if(reg.test(str)){ return RegExp.$2; }else{ return undefined; //匹配不成功返回undefined } } }, //删除 remove(key) { Cookie.set({ [key]:"" //清空 },-1); } }; Cookie.set({ a:'阿飞', b:123, goudan:"afei" },7); //7天 //测试get console.log(Cookie.get("goudan")); //删除cookie Cookie.remove("b");
24.3 ES6的Object的API
//ES6写法 ,属性名的变量写法 let a = "name"; let obj= { [a]:"阿飞" // 这里a加【】代指变量 “name” }; console.log(obj); let afei= { name :"阿飞", age:18, sex:0 }; console.log(Object.keys(afei)); //提取对象所有属性名,返回数组 console.log(Object.values(afei)); //提取对象所有值,返回数组 console.log(Object.entries(afei)); //提取对象的键值,返回二位数组
24.4 案例,cookie记录访问时间
<!DOCTYPE HTML> <html> <head> <meta charset="UTF-8"> <title>Title</title> <meta name="keywords" content="关键词"> <meta name="description" content="描述"> <meta name="author" content="Danew"> </head> <body> <div id="wrap"> 欢迎访问本站!您上次访问本站的时间为:2018-11-10,20:50:20 </div> <script> //封装的cookie let Cookie ={ set(mJson,day) { let date = new Date(new Date().getTime()+day*24*3600*1000).toUTCString(); Object.entries(mJson).forEach(([key,value])=>{ document.cookie = `${key}=${value};expires=${date}`; }) }, get(key) { //不传值返回全部cookie构建的对象 if(!key){ let json=document.cookie; let obj={}; while(json){ let reg=/(^|\s)(\w+)=([^;]+)(;|$)/; let arr = json.match(reg); json = json.split(arr[0])[1].toString(); obj[RegExp.$2]=RegExp.$3; } return obj; }else{ let str = document.cookie; let reg = new RegExp("(^|\\s)"+key+"=([^;]+)(;|$)"); if(reg.test(str)){ return RegExp.$2; }else{ return undefined; //匹配不成功返回undefined } } }, remove(key) { Cookie.set({ [key]:"" //清空 },-1); } }; (function () { //lastTime存储上一次的时间 let oW = document.getElementById("wrap"); let lastTime = Cookie.get("lastTime"); let date = new Date().getTime(); function showText(lastTime) { if(lastTime){ let date = new Date(lastTime-0); return `欢迎访问本站!您上次访问本站的时间为:${date.getFullYear()}-${date.getMonth()+1}-${date.getDate()},${date.getHours()}:${date.getMinutes()}:${date.getSeconds()}`; }else{ return "您是第一次访问本站哟!"; } } oW.innerText = showText(lastTime) ; Cookie.set({ lastTime:date },9999); })(); </script> </body> </html>
25. ajax
/* Ajax 异步的javascript和xml 同步 一心一意 异步 三心二意 功能 无刷新页面的情况下,实现与后台的数据交互,同时在页面进行更新 跨域 默认不允许跨域请求资源 安全问题 前端能不能请求到数据,是后端说了算 */ //创建 ajax对象 const ajax = new XMLHttpRequest(); //监听状态的改变 /* 0~4 0 初始化状态 ajax已经被创建 1 open()方法已经调用 2 send()方法已调用 3 所有的响应已经收到 4 http响应已经完全接收 */ ajax.onreadystatechange = function(){ if(ajax.readyState === 4 & ajax.status === 200){ //前端 就能够接收到数据了 //console.log(ajax.response); //把对应的数据 转换成对应的数据类型 console.log(JSON.parse(ajax.response)); } } //路径不能出现中文 //本地测试 //ajax.open('get','./data.php',true); //通过什么样的方式,向什么样的后端服务器 发送什么的请求(true表示异步) //ajax.send(); //执行请求命令 //真实服务器, 数据先行, 数据驱动视图 ajax.open('get','http://www.tanzhouweb.com/48/data.php',true); ajax.send();
25.1 封装一个ajax
//调用方式 ajax({ url:"www.baidu.com", method:"get", data:{ name:"peter", age:18 }, success:function(msg){ }, error:function(err){ } })
function ajax(json){ var method = json.method.toUpperCase() || "GET"; var data = json.data || {}; var xhr = new XMLHttpRequest(); switch(method){ case 'GET': xhr.open(method,json.url+"?"+jsonUrl(json.data),true); xhr.send(null); break; case 'POST': xhr.open(method,json.url,true); //设置post请求头 xhr.setRequestHeader('content-type','application/x-www-form-urlencoded'); xhr.send(jsonUrl(json.data)); break; } } xhr.onreadystatechange=function(){ if(xhr.readyState === 4){ if(xhr.status>=200 || xhr.status<=300 || xhr.status === 304){ json.success(xhr.responseText); //responseText 才是服务器返回数据 }else{ json.error(xhr.status); } } } function jsonUrl(data){ var arr=[]; for(var key in data){ arr.push(key+'='+data[key]); } return arr.join("&"); }
25.2 jsonp跨域解决方案
/* jsonp 一种跨域问题的解决方案 */ function getData(data) { console.log(data); } //核心本质 就是后端服务器 返回一个函数调用 getData('js') createJsonp(); function createJsonp() { const s = document.createElement('script'); // s.src='http://www.tanzhouweb.com/48/jsonp.php?callback=getData'; s.src='http://localhost/data.php?callback=getData'; document.body.appendChild(s); }
25.3 CORS服务器允许跨域
设置响应头,res.writeHeader(200,{'Access-Control-Allow-Origin':'*'})
/* cmd检测nodejs的安装 node -v npm -v */ //通过 node的原生模块 http 搭建服务器 提供数据接口 const http = require('http'); http.createServer(function () { res.writeHeader(200,{ 'Access-Access-Control-Allow-Origin':'*' }) res.end("hi~这里是node服务器返回的数据"); }).listen(6060);
express
const express = require('express'); const app = express(); //在中间件设置允许前后端跨域 app.use((req,res,next)=>{ res.set('Access-Control-Allow-Origin','*'); next(); })
26. 面向对象
26.1 封装一个对象函数
/* OOP 封装 继承 多态 */ var afei = teacher(1111,'阿飞',18); var zhuque = teacher(2222,'朱雀',20); //封装一个对象函数 function teacher(id,name,age) { var o = {}; o.id = id; o.name=name; o.age=age; o.showID = function () { //堆内存占用了多个 alert(this.id); } return o; }
26.2 new
/* new Date() new Image() new XMLHttpRequest() new RegExp() new Array() new 关键词 后面紧跟一个 函数 通过new执行函数对函数的影响: ①:函数内部生成一个全新的对象,函数的this指向这个对象 ②:函数默认返回上述对象 */ //fn(); //函数自执行;非严格模式指向window,严格模式指向undefined function fn() { // console.log(this); //new的this指向全新的对象,只有this能指向这个对象 this.x = 10; } new fn(); //先初始化一个空{},然后将{}的proto即隐式原型指向fn的prototype,然后执行了fn.call({})将this作用域交予{},完成实例化 console.log(new fn()); // fn {x: 10}
var afei = new Teacher(1111,'阿飞',18); var zhuque = new Teacher(2222,'朱雀',20); console.log(afei.showID === zhuque.showID); //false 每个对象都占用一个堆内存存放函数 //构造函数 / 类 function Teacher(id,name,age) { this.id = id; this.name=name; this.age=age; this.showID = function () { //堆内存占用了多个 alert(this.id); } }
26.3 原型
/* 原型 prototype 是一个对象数据类型 它是构造函数的一个属性 每一个实例都共享这个原型的属性 */ //构造函数 function Teacher(id,name,age) { this.id = id; this.name=name; this.age=age; } // console.dir(Teacher); Teacher.prototype.x = 10; //原型也是对象 Teacher.prototype.showID=function () { //所有实例公用一个showID属性 alert(this.id); } var afei = new Teacher(1111,'阿飞',18); // 实例 var zhuque = new Teacher(2222,'朱雀',20); console.log(afei); console.log(afei.x,zhuque.x,afei.__proto__===zhuque.__proto__); console.log(afei.__proto__ === Teacher.prototype); //实例的__proto__ 全等于 构造函数的 prototype属性 // 构造函数的原型是对象数据类型 {x: 10, showID: ƒ, constructor: ƒ} //afei.showID(); console.log(afei.showID === zhuque.showID); // true 共享一个堆内存
26.4 原型链
/* 当访问对象的属性时,先从自身找,自身没有,才进入原型中找;原型找不到去原型的原型里面找,直到Object.prototype 为止,因为Object没有__proto__属性,原型链到此结束 实例没有 prototype 属性;Object没有__proto__属性 构造函数的隐式原型是Function: A.__proto__ === Function.prototype 构造函数的原型是其实例的隐式原型:A.prototype === new A().__proto__ Function比较特殊 Function.__proto__.__proto__ === Object.prototype goudan.__proto__ = Fn.prototype Fn.prototype.__proto__ = Object.prototype */ function Fn() { this.x=10; //实例的x属性 } Fn.prototype.x=20; //原型的x属性 console.log(Fn.prototype.constructor === Fn); //true 构造属性 var goudan = new Fn(); // 实例 console.log(goudan.x); console.log(goudan.__proto__ === Fn.prototype); //true Fn的原型 console.log(Fn.prototype); console.log(Fn.prototype.__proto__ === Object.prototype); //true Fn.prototype 是 Object的原型 // 构造函数的原型 是 Object 的实例化对象 //Object不存在 .__proto__ 属性,
/* 私有属性写在构造函数里面 公共属性写在原型上 先有原型,再有实例 */ function Teacher(n,a,i) { this.name = n; this.age = a; this.id = i; } /* Teacher.prototype.showID=function () { alert(this.id); } */ Teacher.prototype = { //重写了 prototype属性,注意保留constructor属性 constructor:Teacher, showID:function () { alert(this.id); }, showName:function () { alert(this.name); }, showAge:function () { alert(this.age); } } var afei = new Teacher("阿飞",18,1111); afei.showID();
/* 原型链继承 构造函数的原型 = 一个函数的实例化对象 实现继承该函数的原型链 */ function C() {} C.prototype.x = 10; function B() {} B.prototype = new C(); // B的原型指向C function A() {} A.prototype = new B(); Object.prototype.x = 40; //顶层链 var a = new A(); console.log(a.x); // 10 原型链查找,就近原则
/* 得到一个实例的构造函数 */ function A() {} var a = new A(); console.log(a.constructor); // A(){} a没有constructor,实际上是原型 A.prototype.constructor //注意。原型链可能导致不确定 function B() {} B.prototype = {}; // 这里将原型置为{},上一级原型不存在constructor, var b = new B(); console.log(b.constructor); // Object() { [native code] } 再向上级找,找到Object原型的构造函数 //所有大括号构建对象都相当于new一个Object对象 // let x={}; // 原型 = Object.prototype let x = new Object();
26.5 ES5的继承
先继承私有属性,然后是原型
function A(n,a) { this.name = n; this.age = a; } A.prototype.getName = function () { return this.name; } //ES5实现继承(组合继承) function B(n,a,i) { A.call(this,n,a); //1.继承了A的私有属性 this.id = i; //新增的私有属性 } //2.再继承A的原型,这里只继承原型,所以使用中间构造函数,如果用new A(),原型会有A的属性 function Fn(){}; Fn.prototype = A.prototype; B.prototype = new Fn(); //这里只传递了原型,但原型缺少构造函数 // 新增原型 B.prototype.constructor = B; // 补上构造函数 B.prototype.xx=10; // 新增原型 var afei = new A('阿飞',18); var xinai = new B('心艾',20,5555); console.log(afei); console.log(xinai);
26.6 instanceof实现对象的深拷贝
/* instanceof运算符用于测试构造函数的prototype属性是否出现在对象的原型链中的任何位置 对象 instanceof 构造函数(一般是顶层构造函数Object,Array,Function) */ function deepclone(obj) { var o = obj instanceof Array?[]:{}; for(var key in obj){ if(typeof obj[key] === 'object'){ o[key] = deepclone(obj[key]); }else{ o[key] = obj[key]; } } return o; } var a = { a:20, b:50, k:{ //不支持复杂类型 aa:1, cc:{ h:2333 } } } var b = deepclone(a); //深拷贝后,引用值类型不会被影响 b.c = 40; b.k.bb = 2; b.k.cc.i=120; console.log(a); console.log(b);
26.7 JSON的API实现对象拷贝
/* JSON只会传数字和字符串,不能存 函数 等其他类型 */ var a = { a:20, b:50, k:{ aa:1, cc:{ h:2333 } }, f:function () { //JSON 不支持函数类型,所以过滤掉 console.log(1) } } console.log(JSON.stringify(a)); //没有函数 f var b = JSON.parse(JSON.stringify(a)); //这样实现的b也和a不一样了 b.z=30; b.k.o=80; console.log(b); console.log(a);
26.8 对象案例,选项卡
<!DOCTYPE HTML> <html> <head> <meta charset="UTF-8"> <title>Title</title> <meta name="keywords" content="关键词"> <meta name="description" content="描述"> <meta name="author" content="Danew"> <style> body{font-family: "Microsoft YaHei",serif;} body,dl,dd,p,h1,h2,h3,h4,h5,h6{margin:0;} ol,ul,li{margin:0;padding:0;list-style:none;} img{border:none;} #wrap,#wrap1{ position: relative; width:300px; height:300px; border:1px solid pink; margin:50px auto; } #wrap .con,#wrap1 .con{ position: absolute; top:30px; left:0; width:300px; height: 270px; } #wrap .con ul,#wrap1 .con ul{ width:100%; height:100%; } #wrap .con li,#wrap1 .con li{ display: none; position: absolute; width:100%; height:100%; } #wrap .con li.active,#wrap1 .con li.active{ display: block; } #wrap .tab,#wrap1 .tab{ position: absolute; left:0; top:0; width:100%; height:30px; } #wrap .tab li,#wrap1 .tab li{ float: left; width:99px; height:30px; border-right:1px solid red; background-color: #abcded; cursor: pointer; } #wrap .tab li.active,#wrap1 .tab li.active{ background-color: pink; } </style> </head> <body> <div id="wrap"> <div class="con"> <ul> <li class="active" style="background-color: red">A内容</li> <li style="background-color: #06aaff">B内容</li> <li style="background-color: #f9a886">C内容</li> </ul> </div> <div class="tab"> <ul> <li class="active">A</li> <li>B</li> <li>C</li> </ul> </div> </div> <script> (function () { //对象 function Tab({conEle,tabEle,conClass='active',tabClass='active'}){ this.conEle = conEle; this.tabEle = tabEle; this.conClass = conClass; this.tabClass = tabClass; this.lenth = this.tabEle.length; this.index = 0; this.addClick(); } Tab.prototype = { constructor : Tab, addClick:function () { for(var i=0;i<this.lenth;i++){ (function (i) { //函数作用域形成闭包 this.tabEle[i].onclick=function () { this.change(i); }.bind(this); }).call(this,i); } }, change:function (i) { this.conEle[this.index].classList.remove(this.conClass); this.tabEle[this.index].classList.remove(this.tabClass); this.index = i; this.conEle[this.index].classList.add(this.conClass); this.tabEle[this.index].classList.add(this.tabClass); } }; //实例化 new Tab({ conEle:document.querySelectorAll("#wrap .con li"), tabEle:document.querySelectorAll("#wrap .tab li"), }); })(); </script> </body> </html>
26.9 对象继承案例,自动轮播选项卡
<!DOCTYPE HTML> <html> <head> <meta charset="UTF-8"> <title>Title</title> <meta name="keywords" content="关键词"> <meta name="description" content="描述"> <meta name="author" content="Danew"> <style> body{font-family: "Microsoft YaHei",serif;} body,dl,dd,p,h1,h2,h3,h4,h5,h6{margin:0;} ol,ul,li{margin:0;padding:0;list-style:none;} img{border:none;} #wrap2{ position:relative; width:780px; height:380px; margin:50px auto 0; user-select:none; } #wrap2 .img{ width:100%; height:330px; } #wrap2 .img ul{ width:100%; height:330px; } #wrap2 .img ul li{ position:absolute; width:100%; height:330px; opacity: 0; transition: opacity .3s; } #wrap2 .img ul li.active{ opacity: 1; z-index:2; } #wrap2 .tab{ position:absolute; left:0; bottom:0; width:100%; height:50px; } #wrap2 .tab ul{ width:100%; height:50px; } #wrap2 .tab ul li{ float:left; z-index:4; width:20%; height:100%; background-color: #121112; color:#fff; text-align:center; line-height:50px; cursor: pointer; } #wrap2 .tab ul li.active{ background-color: #303030; color:#e9c06c; } </style> </head> <body> <div id="wrap2"> <div class="img"> <ul> <li class="active"><img src="img/1.jpg" alt=""></li> <li><img src="img/2.jpg" alt=""></li> <li><img src="img/3.jpg" alt=""></li> <li><img src="img/4.jpg" alt=""></li> <li><img src="img/5.jpg" alt=""></li> </ul> </div> <div class="tab"> <ul> <li class="active">开黑吗?</li> <li>我压缩贼六</li> <li>只要E的够快</li> <li>队友的问号</li> <li>就追不上我</li> </ul> </div> </div> <script> (function () { //对象 function Tab({conEle,tabEle,conClass='active',tabClass='active'}){ this.conEle = conEle; this.tabEle = tabEle; this.conClass = conClass; this.tabClass = tabClass; this.lenth = this.tabEle.length; this.index = 0; this.addClick(); } Tab.prototype = { constructor : Tab, addClick:function () { for(var i=0;i<this.lenth;i++){ (function (i) { this.tabEle[i].onclick=function () { this.change(i); }.bind(this); }).call(this,i); } }, change:function (i) { if(this.index === i)return; this.conEle[this.index].classList.remove(this.conClass); this.tabEle[this.index].classList.remove(this.tabClass); this.index = i; this.conEle[this.index].classList.add(this.conClass); this.tabEle[this.index].classList.add(this.tabClass); } }; function TabAuto({conEle,tabEle,conClass='active',tabClass='active',wrap}) { Tab.call(this,{conEle,tabEle,conClass,tabClass}); //继承私有属性 this.wrap = wrap; this.timer = null; this.autoplay(); this.addTimer(); } function Fn(){} Fn.prototype = Tab.prototype; TabAuto.prototype = new Fn(); //继承原型 TabAuto.prototype.constructor = TabAuto; TabAuto.prototype.autoplay = function () { this.timer = setInterval(function () { var i = this.index; i++; i = i%this.lenth; this.change(i); }.bind(this),2000); } TabAuto.prototype.addTimer = function () { this.wrap.onmouseenter = function () { clearInterval(this.timer); }.bind(this); this.wrap.onmouseleave = function () { this.autoplay(); }.bind(this); } //实例化 new TabAuto({ conEle:document.querySelectorAll("#wrap2 .img li"), tabEle:document.querySelectorAll("#wrap2 .tab li"), wrap:document.getElementById("wrap2") }); })(); </script> </body> </html>
26.10 多态
function fn(x) { if(x<10){ return '0'+x; }else{ return ''+x; } }
26.11 ES6面向对象class
/* ES5的构造函数和原型是分开的,ES6的类都定义在一个{}内 */ function A(n,a) { this.name = n; this.age = a; } A.showName = function () { // 这里的函数是 构造函数A的属性,并不属于原型的函数 alert(this.name); } A.prototype.showName = function(){ //这里才是给原型的函数,A的实例化对象能调用的函数 alert(this.name) } /*ES6 构造函数,也就是私有属性,写在constructor里面 其他内容就是原型里面的内容 原型里面加属性(可以在外面加,A.prototype.x=10) 每个方法结束后,千万不要写 , 号 */ class A{ constructor(n,a) { this.name = n; this.age = a; } showName(){ } x(){ return 10; } } // A.prototype.x=10;
26.12 ES6继承extends
class A{ constructor(n,a) { this.name = n; this.age = a; } showName(){ alert(this.name); } showAge(){ alert(this.age); } x(){ return 10; } } class B extends A{ constructor(n,a,id){ super(n,a); //必要的super,把父类的私有属性继承一下(传参),类似ES5的 call this.id = id; } x(){ //return A.prototype.x()+10; //原型的super代表 A.ptototype return super.x()+10; } } let a = new A('阿飞',18); let b = new B('朱雀',81,111); console.log(a); // {name: "阿飞", age: 18} console.log(b); // {name: "朱雀", age: 81, id: 111} console.log(a.x()); // 10 console.log(b.x()); // 20
26.13 选项卡案例 ES6对象
<!DOCTYPE HTML> <html> <head> <meta charset="UTF-8"> <title>Title</title> <meta name="keywords" content="关键词"> <meta name="description" content="描述"> <meta name="author" content="Danew"> <style> body{font-family: "Microsoft YaHei",serif;} body,dl,dd,p,h1,h2,h3,h4,h5,h6{margin:0;} ol,ul,li{margin:0;padding:0;list-style:none;} img{border:none;} #wrap2{ position:relative; width:780px; height:380px; margin:50px auto 0; user-select:none; } #wrap2 .img{ width:100%; height:330px; } #wrap2 .img ul{ width:100%; height:330px; } #wrap2 .img ul li{ position:absolute; width:100%; height:330px; opacity: 0; transition: opacity .3s; } #wrap2 .img ul li.active{ opacity: 1; z-index:2; } #wrap2 .tab{ position:absolute; left:0; bottom:0; width:100%; height:50px; } #wrap2 .tab ul{ width:100%; height:50px; } #wrap2 .tab ul li{ float:left; z-index:4; width:20%; height:100%; background-color: #121112; color:#fff; text-align:center; line-height:50px; cursor: pointer; } #wrap2 .tab ul li.active{ background-color: #303030; color:#e9c06c; } </style> </head> <body> <div id="wrap2"> <div class="img"> <ul> <li class="active"><img src="img/1.jpg" alt=""></li> <li><img src="img/2.jpg" alt=""></li> <li><img src="img/3.jpg" alt=""></li> <li><img src="img/4.jpg" alt=""></li> <li><img src="img/5.jpg" alt=""></li> </ul> </div> <div class="tab"> <ul> <li class="active">开黑吗?</li> <li>我压缩贼六</li> <li>只要E的够快</li> <li>队友的问号</li> <li>就追不上我</li> </ul> </div> </div> <script> (function () { class Tab{ constructor({conEle,tabEle}) { this.conEle = conEle; this.tabEle = tabEle; this.index = 0; this.lenth = this.tabEle.length; this.addClick(); } addClick(){ [...(this.tabEle)].forEach((ele,index)=>{ ele.onclick = function() { this.change(index); }.bind(this); }) } change(i){ if(this.index === i)return; this.conEle[this.index].classList.remove('active'); this.tabEle[this.index].classList.remove('active'); this.index = i; this.conEle[this.index].classList.add('active'); this.tabEle[this.index].classList.add('active'); } } class TabAuto extends Tab{ constructor({conEle,tabEle,wrap}){ super({conEle,tabEle}); // 继承父类私有属性 this.wrap = wrap; this.timer = null; this.autoplay(); this.addTimer(); } autoplay() { this.timer = setInterval(()=>{ let i = this.index; i++; i = i%this.lenth; this.change(i); },2000); } addTimer() { this.wrap.onmouseenter = ()=> { clearInterval(this.timer); }; this.wrap.onmouseleave = ()=>{ this.autoplay(); }; } }; //实例化 new TabAuto({ conEle:document.querySelectorAll("#wrap2 .img li"), tabEle:document.querySelectorAll("#wrap2 .tab li"), wrap:document.getElementById("wrap2") }); })(); </script> </body> </html>
27. ES6
27.1 默认值
/* 函数的小括号,会被当成作用域 */ let x=5; function fn(x,y=x) { console.log(x,y); } fn(1); // 1 1
27.2 for of只限于遍历iterator接口
作用是遍历拿到数组的value值
对象使用for of
遍历时,先拿到对象的成员, for (let [key,value] of Object.entries(obj))
let arr=[ '阿飞', '风屿', '小浪', '海文' ]; console.log(Object.keys(arr),Object.values(arr)); // 一维数组 console.log(Object.entries(arr)); // 二维数组 let obj={ name:'afei', age:18, sex:'男', marry:true } /* for of只限于用于 iterator接口 */ for (let string of arr) { console.log(string); //拿到数组的值value } for (let objElement of Object.values(obj)) { console.log(objElement); } for (let objElement of Object.entries(obj)) { //打印数组 console.log(objElement); } for (let [key,value] of Object.entries(obj)) { //结合使用适合遍历obj的键和值 console.log(key,value); }
27.3 ...用于对象合并
- 合并后的对象是新的对象
- 合并对象时同名属性被覆盖
let x={ aa:1, bb:2 }; let y={ bb:0, cc:3, dd:4 }; let z = {...x,...y} x.aa=10; console.log(x); console.log(z);
27.4 symbol
Object.getOwnPropertySymbols(obj);
获取对象内部所有的symbol属性,返回一个数组
Symbol.for('a')
的名字比如a相同,就代表相同的Symbol
//独一无二的Symbol console.log(Symbol() === Symbol()); //false let obj = {name:'阿飞'}; let n=Symbol(); //独一 obj[n]='朱雀'; n=Symbol(); //n被赋值,只是‘朱雀’取不到了,但仍存在 obj[n]='心艾'; console.log(obj[n]); //获取的是 心艾 obj[Symbol.for('name')]='朱雀'; // obj中添加一个属性 // {name: "阿飞", Symbol(): "朱雀", Symbol(): "心艾", Symbol(name): "朱雀"} //获取所有的Symbol let symbolArr = Object.getOwnPropertySymbols(obj); // [Symbol(), Symbol(), Symbol(name)] console.log(obj[symbolArr[0]]); //找到朱雀 //传值是别名 console.log(Symbol('a'),Symbol.for('a')); //Symbol的名字相同,仍不同 console.log(Symbol('a')===Symbol('a')); //false //Symbol.for的名字相同,就代表相同的Symbol console.log(Symbol.for('a') === Symbol.for('a')); //true
27.5 Set
new Set(data)
data必须是能被迭代的数据
实例的add方法,可以追加数据到对象内
/* 对象 特点:没有重复值 只接收一个参数,必须能被迭代的数据,字符串,数组,NodeList,argument,Set,Net */ let set = new Set([1,2,3]); let set = new Set("阿飞老师阿飞老师朱雀老师"); //默认去重 set.add('心艾'); //add的内容被 当成一条数据传入,不被拆开 set.add('心'); console.log(set); // 心艾 和 心 并不会去重 //去重 let arr=[1,2,4,5,6,1,2,4,2,3]; let s = new Set(arr); console.log([...s]);
27.6 Map
map.set(key,value);
队列设置
/* Map可以使用一个对象当成键名 支持各种数据类型当键使用,对象,实例, */ let map = new Map(); // let key = 'name'; // let value = '阿飞'; let key = {goudan:'狗蛋'}; //对象当成键 let value = 'afei'; map.set(key,value); console.log(map); //取值 console.log(map.get(key));
27.7 Object的toString方法
var a={m:'n'}; var b= a.toString(); console.log(b,typeof b,b.length); // [object Object] string 15
27.8 Proxy
//Proxy 可以理解成,在目标对象之前架设一层“拦截”,外界对该对象的访问,都必须先通过这层拦截, // 因此提供了一种机制,可以对外界的访问进行过滤和改写。 let o ={ a:1, b:2 } let obj = new Proxy(o,{ //拦截对象的获取 get(target,key,receive){ console.log(target); // {a: 1, b: 2} console.log(key); // a console.log(receive); // Proxy {a: 1, b: 2} return target[key]; // 1 }, //拦截对象的赋值 set(target,key,value,receive){ console.log(target); //{a: 1, b: 2} console.log(key); //a console.log(value); //123 console.log(receive); // Proxy {a: 1, b: 2} } //... 针对框架开发使用 }) console.log(obj.a); //触发get obj.a = 123; //触发set
27.9 Promise
new Promise()
返回一个promise对象
Promise.all([new Promise(),newPromise()]).then().catch()
批量执行,all传入的是数组包裹的promise对象,都成功走then回调,都失败,走catch回调,只显示失败的
Promise.race([new Promise(),newPromise()]).then().catch()
竞争执行,哪个实例先改变状态(不论resolve还是reject),那么then和catch就走哪个实例
//回调地狱->解决异步编程 setTimeout(()=>{ console.log("123") },0) console.log("abc") //resolve 表示成功 reject表示失败 let x = new Promise((resolve,reject)=>{ setTimeout(()=>{ resolve("200"); //成功,永远在当前环境的最后执行 console.log("我先打印,再进入resolve事件") },0) }).then((val)=>{ //then表示成功回调 console.log("成功回调"+val); },(err)=>{ //第二个函数是失败回调 console.log("失败回调"+err); }) //all 只有数组成员都成功才执行then let p = Promise.all([ new Promise((resolve,reject)=>{ resolve('1'); }),new Promise((resolve,reject)=>{ resolve('1'); }),new Promise((resolve,reject)=>{ reject('0'); }) ]).then(data=>{ console.log(data); //都成功[1,1,0] }).catch(err=>{ console.log(err); //有一个失败,就走失败,这里只显示失败的 '0' }) /* Promise.race() 有竞争的意思 只要`p1`、`p2`、`p3`之中有一个实例率先改变状态,`p`的状态就跟着改变。 那个率先改变的 Promise 实例的返回值,就传递给`p`的回调函数。 */ let p1 = Promise.race([ new Promise((resolve,reject)=>{ setTimeout(()=>{ resolve('1'); },100); }),new Promise((resolve,reject)=>{ setTimeout(()=>{ resolve('2'); },101); }),new Promise((resolve,reject)=>{ setTimeout(()=>{ reject('3'); },99); }) ]).then(data=>{ console.log('resolve '+data); //只显示最快的 3,无论是 resolve还是reject }).catch(err=>{ console.log('reject '+err); })