JavaScript笔记

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

变量命名规则:

  1. 严格区分大小写;
  2. 只能包含 字母 数字 _ $ 四大类,且不能以数字开头;
  3. 不能使用 关键词/保留词/已有的API;
  4. 见名知意。
  5. 函数声明时会覆盖掉同名变量

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">&lt;</span>
            <span class="right">&gt;</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>
/*
    适合小数据的存储,字符串缓存,存在本地的
    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);
})

转载于:https://www.cnblogs.com/danew/p/11415740.html

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值