JS进阶阶段

1 DOM基础

1.1.1 创建节点

document.createElement();方法用于创建指定tagname的HTML元素,然后通过appendChild(); 或insertBefore(); 方法将孤儿节点挂载到DOM树上。

  1. appendChild():是指DOM树上现有的节点调用appendChild()方法,可以将孤儿节点挂载到它最后一个子节点。

    语法:父节点.appendChild(孤儿节点);

<body>
    <div id="mydiv" style=" width:600px;height:400px;">
        <p>段落1</p>
   		<p>段落2</p>
    	<p>段落3</p>
    </div>    
</body>
  <script>
var mydiv=document.getElementById('mydiv');
// 1.创建节点,添加内容innerHTML和innerText
var op=document.createElement('p');
// 2.设置孤儿节点的内部文字
op.innerText='我是新来的';
// 3.上树
mydiv.appendChild(op);
  </script>
  1. insertBefore():是指DOM树上现有的节点调用insertBefore()方法,可以将孤儿节点挂载到标杆节点位置

​ 语法:父节点.insertBefore(孤儿节点,标杆节点);

<body>
    <div id="mydiv" style=" width:600px;height:400px;">
        <p>段落1</p>
   		<p>段落2</p>
    	<p>段落3</p>
    </div>    
</body>
  <script>
var mydiv=document.getElementById('mydiv');
var ops=mydiv.getElementsByTagName('p');
// 1.创建节点,添加内容innerHTML和innerText
var op=document.createElement('p');
// 2.设置孤儿节点的内部文字
op.innerText='我是新来的';
// 3.上树
mydiv.insertBefore(op,ops[0]);
  </script>

1.1.2 移动节点

一个节点不能同时位于DOM树的两个位置

所以将已经挂载到DOM树上的节点成为appendChild() 或insertBefore()的参数,这个节点将被移动

语法:新父节点.appendChild(已经有父亲的节点);

语法:新父节点.insertBefore(已经有父亲的节点,标杆子节点);

//   // appendChild()方法
<style>
    #box1 {width: 300px;height: 200px;background:red}
    #box2 {width: 300px;height: 200px;background:blue}
  </style>  
  <body>
    <div id="box1">1111
      <p id="p">p段落</p>
    </div>
    <div id="box2">2222</div>
  </body>
  <script>
    var box1=document.getElementById('box1');
    var box2=document.getElementById('box2');
    var p=document.getElementById('p');
    box2.appendChild(p);
  </script>
  // insertBefore()方法
<body>
    <div id="box1">1111
      <p id="p">p1段落</p>
    </div>
    <div id="box2">2222
      <p >p2段落</p>
      <p >p2段落</p>
      <p >p2段落</p>
    </div>
  </body>
  <script>
    var box1=document.getElementById('box1');
    var box2=document.getElementById('box2');
    var p=document.getElementById('p');
    var ps=box2.getElementsByTagName('p');
  
    box2.insertBefore(p,ps[1]);
  </script>

1.1.3 删除节点

removeChild()方法从DOM树删除指定节点,节点不能主动删除自己,必须由父节点调用removeChild进行删除。

语法:父节点.removeChild(要删除的子节点);

1.1.4 克隆节点

cloneNode方法可以克隆节点,克隆出的节点是孤儿节点,参数为布尔值,表示是否深度克隆。默认值为false

  • false:表示只克隆该节点本身
  • true:表示该节点的所有后代节点都会被克隆

语法:var 变量=老节点.cloneNode();

var 变量=老节点.cloneNode(true);

1.1.5 改变元素节点的CSS样式

	// css属性要写成'驼峰'形式
	// css属性值要设置成完整形式
	// 注意单位
   box1.style.backgroundColor='red';
   box1.style.backgroundImage='url(images/1.jpg)';
   box1.style.fontSize ='32px';

1.1.5 改变元素节点的HTML属性

  • 标准的W3C属性,如:src、href、 等,直接调用就可以

    语法:节点.src='images/1,jpg';

  • 不符合W3C标准的属性,通过setAttribute、getAttribute、removeAttribute进行设置、读取、删除元素的属性

    var box1=document.getElementById('box1');	//设置元素属性
    	box1.setAttribute('data-n',10);
    var n=box1.getAttribute('data-n');	//获取元素属性
    	alert(n);
    box1.removeAttribute('data-n');	//删除元素属性
    

1.1.6 nodeType常用属性值

nodeType值节点类型
1元素节点:

3文字节点
8注释节点
9document节点
10DTD节点
  1. 访问元素节点主要依靠 document

    • 几乎所有的DOM功能都封装在 document对象中
    • document对象也表示整个HTML文档,它是DOM节点树的根
    • document对象的nodeType属性值是9
  2. 访问元素节点的常用方法

    访问元素节点的常用方法功能
    document.getElementById()通过id得到元素
    document.getElementsByTagName()通过标签名得到元素数组
    document.getElementsByClassName()通过类名得到元素数组
    document.querySelector()通过选择器得到元素节点
    document.querySelectorAll()通过选择器得到元素节点数组
  3. 节点的关系
    | 节点的关系 | 考虑所有节点 | 只考虑元素节点 |
    | -------------- | --------------- | -------------------------- |
    | 子节点 | childNodes | children |
    | 父节点 | parentNode | parentNode |
    | 第一个子节点 | firstChild | firstElementChild |
    | 最后一个子节点 | lastChild | lastElementChild |
    | 前一个兄弟节点 | previousSibling | previousElementSibling |
    | 后一个兄弟节点 | nextSibling | nextElementSibling |

  4. 常见的节点关系函数

body>
    <div id="box">
        <p>我是段落</p>
        <p>我是段落</p>
        <p>我是段落</p>
        <p id="fpara">我是段落fpara</p>
        我是文本
        <!-- 我是注释 -->
        <p id="para">
            我是段落para
            <span>1</span>
            <span>2</span>
            <span>3</span>
        </p>
        <p>我是段落</p>
        <p>我是段落</p>
        <p>我是段落</p>
    </div>
<script>
        var box = document.getElementById('box');
        var para = document.getElementById('para');
        var fpara = document.getElementById('fpara');

        // 封装一个函数,这个函数可以返回元素的所有子元素节点(兼容到IE6),类似children的功能
        function getChildren(node) {
            // 结果数组
            var children = [];
            // 遍历node这个节点的所有子节点,判断每一个子节点的nodeType属性是不是1
            // 如果是1,就推入结果数组
            for (var i = 0; i < node.childNodes.length; i++) {
                if (node.childNodes[i].nodeType == 1) {
                    children.push(node.childNodes[i]);
                }
            }
            return children;
        }

        console.log(getChildren(box));
        console.log(getChildren(para));

        // 封装一个函数,这个函数可以返回元素的前一个元素兄弟节点(兼容到IE6),类似previousElementSibling的功能
        function getElementPrevSibling(node) {
            var o = node;
            // 使用while语句
            while (o.previousSibling != null) {
                if (o.previousSibling.nodeType == 1) {
                    // 结束循环,找到了
                    return o.previousSibling;
                }

                // 让o成为它的前一个节点,就有点“递归”的感觉
                o = o.previousSibling;
            }
            return null;
        }

        console.log(getElementPrevSibling(para));
        console.log(getElementPrevSibling(fpara));

        // 封装第三个函数,这个函数可以返回元素的所有元素兄弟节点
        function getAllElementSibling(node) {
            // 前面的元素兄弟节点
            var prevs = [];
            // 后面的元素兄弟节点
            var nexts = [];
            
            var o = node;
            // 遍历node的前面的节点
            while(o.previousSibling != null) {
                if(o.previousSibling.nodeType == 1){
                    prevs.unshift(o.previousSibling);
                }
                o = o.previousSibling;
            }

            o = node;

            // 遍历node的后面的节点
            while(o.nextSibling != null) {
                if(o.nextSibling.nodeType == 1){
                    nexts.push(o.nextSibling);
                }
                o = o.nextSibling;
            }

            // 将两个数组进行合并,然后返回
            return prevs.concat(nexts);
        }

        console.log(getAllElementSibling(para));
    </script>
</body>

1.2 事件监听

1.2.1 常见的鼠标事件监听:

序号时间名事件描述
1onclick当鼠标点击某个对象时
2ondblclick当用户双击某个对象时调用的事件句柄。
3onmousedown鼠标按键在某个对象上被按下
4onmouseup鼠标按键在某个对象上被松开
5onmousemove鼠标在某个对象上被移动
6onmouseenter当鼠标进入某个对象(相似事件 onmouseover)
7onmouseleave当鼠标离开某个对象(相似事件 onmouseout)
8oncontextmenu在用户点击鼠标右键打开上下文菜单时触发
9onmousewheel鼠标滚轮事件,滚轮滚动e.deltaY属性表示滚轮向下滚动时返回正值,向上滚动返回负值
  <script>
    var box1=document.getElementById('box1');
    var box2=document.getElementById('box2');
      // onclick 当鼠标点击某个对象时
    box1.onclick=function(){
      console.log('我是onclick');
    }
      // onmousedown 鼠标按键在某个对象上被按下
    box2.onmousedown=function(){
      console.log('我是onmousedown');
    }
  </script>

1.2.2 常见的键盘监听事件

序号事件名事件描述
1onkeypress当某个键盘的键被按下(系统按钮 如箭头键,及功能键无法得到识别)
2onkeydown当某个键盘的键被按下(系统按钮可以识别,并先于onkeypress发生)
3onkeyup当某个键盘的键被松开
<body>
    姓名:
    <input type="text" id="nameField">

    <script>
        var nameField = document.getElementById('nameField');

        nameField.onkeydown = function () {
            console.log('我是onkeydown');
        };

        nameField.onkeypress = function() {
            console.log('我是onkeypress');
        }

        nameField.onkeyup = function() {
            console.log('我是onkeyup');
        }
    </script>

</body>

1.2.3 常见的表单事件

序号事件名事件描述
1onchange当用户改变区域内容
2onfocus当元素获得焦点(比如tab键或者鼠标点击)
3onblur当元素失去焦点
4onsubmit当表单被提交
5onreset当表单被重置 HTML 5 不支持。
6oninput正在修改区域内容
<body>
    <form id="myform">
        <p>
            姓名:
            <input type="text" name="nameField">
        </p>
        <p>
            年龄:
            <input type="text" name="ageField">
        </p>
        <p>
            <input type="submit">
        </p>
    </form>

    <script>
        var myform = document.getElementById('myform');
        var nameField = myform.nameField;
        var ageField = myform.ageField;

        nameField.onchange = function () {
            console.log('你已经修改完了姓名');
        };

        nameField.oninput = function () {
            console.log('你正在修改姓名');
        };

        nameField.onfocus = function() {
            console.log('姓名框已经得到了焦点');
        }

        nameField.onblur = function() {
            console.log('姓名框已经失去了焦点');
        }

        myform.onsubmit = function() {
            alert('你正在尝试提交表单');
        }
    </script>
</body>

1.2.4 常见的页面监听事件

序号事件名事件描述
1onload当页面或图像加载完成
2onunload当用户退出页面

更多有关窗口或页面的事件在BOM课程中介绍

1.3 事件传播

事件传播:捕获阶段 ---- 先从外到内 ,冒泡阶段 ---- 然后再从内到外

  • DOM0 级事件监听:只能监听冒泡阶段 box.onclick=function(){}; DOM0 级事件监听同名函数存在覆盖现象

  • DOM2 级事件监听: box.addEventListener('click',function(){//事件处理函数},true);

    注释: **‘click’**不加on的事件名

    true监听捕获阶段

    false监听冒泡阶段

<style>
        #box1{width: 202px;height: 202px;border: 1px solid #000;padding: 50px;}
        #box2{width: 100px;height: 100px;border: 1px solid #000;padding: 50px;}
        #box3{width: 100px;height: 100px;border: 1px solid #000;}
    </style>
<body>
    <div id="box1">
        <div id="box2">
            <div id="box3"></div>
        </div>
    </div>
    <script>
        var oBox1 = document.getElementById('box1');
        var oBox2 = document.getElementById('box2');
        var oBox3 = document.getElementById('box3');

      

        oBox2.addEventListener('click', function() {
            console.log('我是box2的冒泡阶段');
        }, false);
      
      
        oBox3.addEventListener('click', function() {
            console.log('我是box3的捕获阶段');
        }, true);

        oBox3.addEventListener('click', function() {
            console.log('我是box3的冒泡阶段');
        }, false);

        oBox3.onclick = function () {
            console.log('我是box3的onclick');
        };

        oBox1.addEventListener('click', function() {
            console.log('我是box1的冒泡阶段');
        }, false);

        oBox2.addEventListener('click', function() {
            console.log('我是box2的捕获阶段');
        }, true);

        oBox1.addEventListener('click', function() {
            console.log('我是box1的捕获阶段');
        }, true);

        oBox1.onclick = function () {
            console.log('我是box1的onclick');
        };

        oBox2.onclick = function () {
            console.log('我是box2的onclick');
        };

     
    </script>
</body>

1.3.1 事件对象

  • 事件处理函数提供了一个形式参数,它是一个对象,封装了本次事件的细节
  • 这个参数通常用字母e 或单词event来表示

// onmousemove鼠标移动事件
obox.onmousemove=function(e){
    // e 就是这次事件的“事件对象”
};

鼠标位置相关的属性

属性事件描述
clientx(clienty)鼠标指针相对于浏览器的水平(垂直)坐标
pagex(pagey)鼠标指针相对于整张网页的水平(垂直)坐标
offsetx(offsety)鼠标指针相对于事件源元素的水平(垂直)坐标
<style>
        *{margin: 0;padding: 0;}
        #box{width: 200px;height: 200px;background-color: #333; margin: 100px;}
        body{height: 2000px;}
        #info{font-size: 30px;}         
    </style>
<body>
    <div id="box">        
    </div>
    <div id="info"></div>
    <script>
        var oBox = document.getElementById('box');
        var oInfo = document.getElementById('info');
		// onmousemove鼠标移动事件
        oBox.onmousemove = function (e) {
            oInfo.innerHTML = 'offsetX/Y:' + e.offsetX + ',' + e.offsetY + '<br>'
                            + 'clientX/Y:' + e.clientX + ',' + e.clientY + '<br>'
                            + 'pageX/Y:' + e.pageX + ',' + e.pageY;
        };
    </script>
</body>

1.3.2 e.charCode和e.keyCode属性

  • e.charCode属性通常用于onkeypress事件中,表示用户输入字符的“字符码”

    字符charCode 字符码
    数字0~948-57
    大写字母A~Z65~90
    小写字母a~z97~122
  • e.keyCode属性通常用于onleydown事件和onkeyup中,表示用户按下的按键的“键码”

按键keyCode键码
数字0~948~57
字母不分大小写65~90
方向键←↑→↓37、38、39、40
回车键13
空格键32
<body>
    <input type="text" id="field1">
    <h1 id="info1"></h1>
    <input type="text" id="field2">
    <h1 id="info2"></h1>

    <script>
        var oField1 = document.getElementById('field1');
        var oInfo1 = document.getElementById('info1');
        var oField2 = document.getElementById('field2');
        var oInfo2 = document.getElementById('info2');

        oField1.onkeypress = function (e) {
            oInfo1.innerText = '你输入的字符的字符码是:' + e.charCode;
        };

        oField2.onkeydown = function (e) {
            oInfo2.innerText = '你按下的按键的键码是:' + e.keyCode;
        };
    </script>
</body>
// 小案例-按键使盒子移动
<style>
        #box {position: absolute;top: 200px;left: 200px;width: 100px;height: 100px;background-color: orange;}
</style>


<body>
    <div id="box"></div>
    <script>
        var oBox = document.getElementById('box');
        // 全局变量t、l,分别表示盒子的top属性值和left属性值
        var t = 200;
        var l = 200;
        // 监听document对象的键盘按下事件监听,表示当用户在整个网页上按下按键的时候
        document.onkeydown = function (e) {
            switch (e.keyCode) {
                case 37:
                    l -= 3;
                    break;
                case 38:
                    t -= 3;
                    break;
                case 39:
                    l += 3;
                    break;
                case 40:
                    t += 3;
                    break;
            }

            // 更改样式
            oBox.style.left = l + 'px';
            oBox.style.top = t + 'px';
        };
    </script>
</body>

1.3.3 阻止事件产生“默认动作”e.preventDefault

  • e.preventDefault()方法用来阻止事件产生“默认动作”

  • 一些特殊的业务需求,需要阻止事件的“默认动作”

// 小案例-preventDefault()方法1
<body>
    <p>
        只能输入小写字母和数字:
        <input type="text" id="field">
    </p>
    <script>
        var oField = document.getElementById('field');

        oField.onkeypress = function (e) {
            console.log(e.charCode);
            
            // 根据用户输入的字符的字符码(e.charCode)
            // 数字0~9,字符码48~57
            // 小写字母a~z,字符码97~122
            if (!(e.charCode >= 48 && e.charCode <= 57 || e.charCode >= 97 && e.charCode <= 122)) {
                // 阻止浏览器的默认行为
                e.preventDefault();
            }
        };
    </script>
</body>
// 小案例-preventDefault()方法2
<style>
        #box {width: 200px;height: 200px;background-color: #333;        }
        body{height: 2000px;        }
    </style>
<body>
    <div id="box"></div>
    <h1 id="info">0</h1>

    <script>
        var oBox = document.getElementById('box');
        var oInfo = document.getElementById('info');

        // 全局变量就是info中显示的数字
        var a = 0;

        // 给box盒子添加鼠标滚轮事件监听onmousewheel
        oBox.onmousewheel = function (e) {
            // 阻止默认事件:就是说当用户在盒子里面滚动鼠标滚轮的时候,此时不会引发页面的滚动条的滚动
            e.preventDefault();
            // deltaY属性表示鼠标滚轮向下滚动时返回正值,向上滚动返回负值
            if (e.deltaY > 0) {
                a++;
            } else {
                a--;
            }
            oInfo.innerText = a;
        }
    </script>
</body>

1.3.4 e.stopPropagation()方法

  • e.stopPropagation()方法用来阻止事件传播

  • 在一些场合需要切断事件传播,否则会造成页面特效显示出bug

<style>
        div{
            width: 200px;
            height: 200px;
            background-color: #333;
        }
    </style>
</head>

<body>
    <div id="box">
        <button id="btn">按我</button>
    </div>
    <script>
        var oBox = document.getElementById('box');
        var oBtn = document.getElementById('btn');

        // oBox.onclick = function () {
        //     console.log('我是盒子');
        // };

        // oBtn.onclick = function (e) {
        //     阻止事件继续传播
        //     e.stopPropagation();
        //     console.log('我是按钮');
        // };
        
		// 捕获阶段的阻止事件继续传播
        oBox.addEventListener('click', function(e) {
            // 阻止事件继续传播
            e.stopPropagation();
            console.log('我是盒子');
        }, true);

        oBtn.addEventListener('click', function() {
            console.log('我是按钮');
        }, true);
    </script>
</body>
 <style>
        .modal {
            width: 400px;
            height: 140px;
            background-color: #333;
            position: absolute;
            top: 50%;
            left: 50%;
            margin-top: -70px;
            margin-left: -200px;
            display: none;
        }
    </style>

<body>
    <button id="btn">按我弹出弹出层</button>
    <div class="modal" id="modal"></div>

    <script>
        var oBtn = document.getElementById('btn');
        var oModal = document.getElementById('modal');

        // 点击按钮的时候,弹出层显示
        oBtn.onclick = function (e) {
            // 阻止事件继续传播到document身上
            e.stopPropagation();
            oModal.style.display = 'block';
        };

        // 点击页面任何部分的时候,弹出层关闭
        document.onclick = function () {
            oModal.style.display = 'none';
        };

        // 点击弹出层内部的时候,不能关闭弹出层的,所以应该阻止事件继续传播
        oModal.onclick = function (e) {
            // 阻止事件继续传播到document身上
            e.stopPropagation();
        };
    </script>
</body>

1.4 事件委托

批量事件监听

  • 每个事件监听都会消耗一定的系统内存,而批量事件监听导致监听数量太多,内存消耗会非常大
  • 实际上,每个li元素的事件处理函数都是不同的函数,这些函数本身也会占用内存
<body>
    <ul id="list">
        <li>列表项</li>
        <li>列表项</li>
        <li>列表项</li>        
    </ul>
    <script>
        var oList = document.getElementById('list');
        var lis = oList.getElementsByTagName('li');
        // 书写循环语句,批量给元素添加监听
        for (var i = 0; i < lis.length; i++) {
            lis[i].onclick = function () {
                this.style.color = 'red';
            };
        }
    </script>
</body>

动态元素添加监听

  • 新增元素必须分别添加事件监听,不能自动获得事件监听
  • 大量事件监听,事件处理函数会产生大量的消耗内存
// 案例/动态元素添加监听 

<body>
    <button id="btn">按我添加新的li列表项</button>
    <ul id="list"></ul>

    <script>
        var oBtn = document.getElementById('btn');
        var oList = document.getElementById('list');
        var lis = oList.getElementsByTagName('li');

        // 按钮的点击事件
        oBtn.onclick = function () {
            // 创建一个新的li列表项,孤儿节点
            var oLi = document.createElement('li');
            oLi.innerHTML = '我是列表项';
            // 上树
            oList.appendChild(oLi);
            // 给新创建的这个li节点添加onclick事件监听
            oLi.onclick = function () {
                this.style.color = 'red';
            };
        };
    </script>
</body>

**事件委托:**是利用事件冒泡的机制,将后代元素事件委托给祖先元素

  • 大量类似元素需要批量添加事件监听时,使用事件委托可以减少内存开销
  • 动态元素节点上树时,使用事件委托可以让新上树的元素具有事件监听

e.target和e.currentTarget属性

属性描述
target触发此事件的最早元素,即"事件源元素"
currentTarget事件处理程序附加到的元素
<body>
    <button id="btn">按我创建一个新列表项</button>
    <ul id="list">
        <li>列表项</li>
        <li>列表项</li>
        <li>列表项</li>
    </ul>
    <script>
        var oList = document.getElementById('list');
        var oBtn = document.getElementById('btn');

        oList.onclick = function (e) {
            // e.target表示用户真正点击的那个元素
            e.target.style.color = 'red';
        };
        
       
        oBtn.onclick = function () {
            // 创建新的li元素
            var oLi = document.createElement('li');
            // 写内容
            oLi.innerText = '我是新来的';
            // 上树
            oList.appendChild(oLi);
        };
    </script>
</body>

事件监听注意事项:

  • onmouseenter 和 onmouseover都表示鼠标进入,区别为onmouseenter不冒泡 , onmouseover冒泡。

  • 使用事件委托时:不能委托不冒泡的事件给祖先元素

  • 最内层元素不能再有额外的内层元素,比如:

    // 事件委托,span元素会执行,li元素无效
    <ul>
        <li><span>span元素0<span>列表元素0</li>
        <li><span>span元素1<span>列表元素1</li>
    </ul>
    

1.5 定时器、延时器

1.5.1 setInterval()定时器

setInterval() - 间隔指定的毫秒数不停地执行指定的代码。

// 语法,第一个参数为要执行的函数,第二个参数为间隔的时间,毫秒为单位
// 下例为间隔2秒弹出 hello
setInterval(function(){alert("Hello")},2000)// setInterval可以直接调用函数fun作为第一参数
var a=0
function fun(){console.log(++a)}
setInterval(fun,2000)

clearInterval() 方法用于停止 setInterval() 方法执行的函数代码

<p id="demo"></p>
// 点击事件绑定函数调用clearInterval() 方法,停止定时器
<button onclick="myStopFunction()">停止</button>
<script>
    // 设置定时器,用变量接收
var myVar=setInterval(function(){console.log(++a)},1000);
function myStopFunction(){
    clearInterval(myVar);
}
</script>
<body>
    <h1 id="info">0</h1>
    <button id="btn1">开始</button>
    <button id="btn2">暂停</button>

    <script>
        var oInfo = document.getElementById('info');
        var oBtn1 = document.getElementById('btn1');
        var oBtn2 = document.getElementById('btn2');

        var a = 0;

        // 全局变量
        var timer;

        oBtn1.onclick = function () {
            // 为了防止定时器叠加,我们应该在设置定时器之前先清除定时器
            clearInterval(timer);
            // 更改全局变量timer的值为一个定时器实体
            timer = setInterval(function () {
                oInfo.innerText = ++a;
            }, 1000);
        };

        oBtn2.onclick = function () {
            clearInterval(timer);
        };
    </script>
</body>

1.5.2 setTimeout()延时器

setTimeout() - 在指定的毫秒数后执行指定代码,不再重复。

// 这个函数会在2秒后执行一次
setTimeout(function(){
console.log(++a)
},2000); 

clearTimeout()函数可以清除延时器

<body>
    <button id="btn1">2秒后弹出你好</button>
    <button id="btn2">取消弹出</button>

    <script>
        var btn1 = document.getElementById('btn1');
        var btn2 = document.getElementById('btn2');
        var timer;

        btn1.onclick = function() {
            timer = setTimeout(function () {
                alert('你好');
            }, 2000);
        }
        
        btn2.onclick = function() {
            clearTimeout(timer);
        }
    </script>
</body>

初步认识异步语句

  • setInterval()和setTimeout()是两个异步语句
  • 异步:不会阻塞CPU继续执行其他语句,当异步完成时,会执行 ”回调函数“(callback)

函数节流

		// 函数节流锁
        var lock = true;

        // 事件监听
         function 需要节流的函数() {
            // lock不为true直接跳出函数
            if (!lock) return;
           
             // 该区域为函数执行内容
             
             
            // 关锁
            lock = false;
            // 指定时间后,将锁打开
            setTimeout(function() {
                lock = true;
            }, 2000);
        };

1.6 案例

1.6.1 JS和CSS3结合实现动画

// 案例JS和CSS3结合实现动画.html
<style>
        #box {
            width: 100px;
            height: 100px;
            background-color: orange;
            position: absolute;
            top: 100px;
            left: 100px;
        }
    </style>
</head>

<body>
    <button id="btn">按我运动</button>
    <div id="box"></div>

    <script>
        // 得到元素
        var btn = document.getElementById('btn');
        var box = document.getElementById('box');

        // 标识量,指示当前盒子在左边还是右边
        var pos = 1;    // 1左边,2右边

        // 函数节流锁
        var lock = true;

        // 事件监听
        btn.onclick = function () {
            // 首先检查锁是否是关闭
            if (!lock) return;

            // 把过渡加上
            box.style.transition = 'all 2s linear 0s';
            if (pos == 1) {
                // 瞬间移动,但是由于有过渡,所以是动画
                box.style.left = '1100px';
                pos = 2;
            } else if (pos == 2) {
                // 瞬间移动,但是由于有过渡,所以是动画
                box.style.left = '100px';
                pos = 1;
            }

            // 关锁
            lock = false;
            // 指定时间后,将锁打开
            setTimeout(function() {
                lock = true;
            }, 2000);
        };
    </script>
</body>

1.6.2 无缝连续滚动特效

    <style>
        * {
            margin: 0;
            padding: 0;
        }

        .box {
            width: 1000px;
            height: 130px;
            border: 1px solid #000;
            margin: 50px auto;
            overflow: hidden;
        }

        .box ul {
            list-style: none;
            /* 设置大一点,这样li才能浮动 */
            width: 5000px;
            position: relative;
        }

        .box ul li {
            float: left;
            margin-right: 10px;
        }
    </style>


<body>
    <div id="box" class="box">
        <ul id="list">
            <li><img src="https://s.cn.bing.net/th?id=OHR.HedgehogNest_ZH-CN0781850458_1920x1080.jpg&rf=LaDigue_1920x1080.jpg&qlt=50" style ="width:300px"alt=""></li>
            <li><img src="https://cn.bing.com/th?id=OHR.YiPeng_ZH-CN0652265903_1920x1080.jpg&rf=LaDigue_1920x1080.jpg" style ="width:300px"alt=""></li>
            <li><img src="	https://cn.bing.com/th?id=OHR.LiDong2022_ZH-CN9929478283_1920x1080.jpg&rf=LaDigue_1920x1080.jpg" style ="width:300px"alt=""></li>
            <li><img src="https://cn.bing.com/th?id=OHR.MarathonSunday_ZH-CN9833453732_1920x1080.jpg&rf=LaDigue_1920x1080.jpg" style ="width:300px"alt=""></li>
            <li><img src="	https://cn.bing.com/th?id=OHR.Trossachs_ZH-CN9299955040_1920x1080.jpg&rf=LaDigue_1920x1080.jpg" style ="width:300px"alt=""></li>
            <li><img src="https://cn.bing.com/th?id=OHR.PeytoIce_ZH-CN7517633327_1920x1080.jpg&rf=LaDigue_1920x1080.jpg" style ="width:300px"alt=""></li>
        </ul>
    </div>
    <script>
        var box = document.getElementById('box');
        var list = document.getElementById('list');

        // 复制多一遍所有的li
        list.innerHTML += list.innerHTML;

        // 全局变量,表示当前list的left值
        var left = 0;

        // 定时器,全局变量
        var timer;

        move();

        // 动画封装成函数
        function move() {
            // 设表先关,防止动画积累
            clearInterval(timer);

            timer = setInterval(function () {
                left -= 4;
                // 验收
                if (left <= - 1260) {
                    left = 0;
                }
                list.style.left = left + 'px';
            }, 20);
        }

        // 鼠标进入停止定时器
        box.onmouseenter = function () {
            clearInterval(timer);
        };

        // 鼠标离开继续定时器
        box.onmouseleave = function () {
            move();
        };
    </script>
</body>

1.6.3 轮播图特效

 <style>
        *{
            margin: 0;
            padding: 0;
        }
        .carousel {
            width: 650px;
            height: 360px;
            border: 1px solid #000;
            margin: 50px auto;
            position: relative;
            overflow: hidden;
        }
        .carousel ul {
            list-style: none;
            width: 6000px;
            position: relative;
            left: 0px;
            transition: left .5s ease 0s;
        }
        .carousel ul li {
            float: left;
        }
        .carousel .leftbtn {
            position: absolute;
            left: 20px;
            top: 50%;
            margin-top: -25px;
            width: 50px;
            height: 50px;
            background-color: rgb(28, 180, 226);
            border-radius: 50%;
        }
        .carousel .rightbtn {
            position: absolute;
            right: 20px;
            top: 50%;
            margin-top: -25px;
            width: 50px;
            height: 50px;
            background-color: rgb(28, 180, 226);
            border-radius: 50%;
        }
    </style>
</head>
<body>
    <div class="carousel">
        <ul id="list">
         <li><img src="https://s.cn.bing.net/th?id=OHR.HedgehogNest_ZH-CN0781850458_1920x1080.jpg&rf=LaDigue_1920x1080.jpg&qlt=50"style ="width:650px"alt=""></li>
        <li><img src="https://cn.bing.com/th?id=OHR.YiPeng_ZH-CN0652265903_1920x1080.jpg&rf=LaDigue_1920x1080.jpg" style ="width:650"alt=""></li>
        <li><img src="	https://cn.bing.com/th?id=OHR.LiDong2022_ZH-CN9929478283_1920x1080.jpg&rf=LaDigue_1920x1080.jpg" style ="width:650px"alt=""></li>
        <li><img src="https://cn.bing.com/th?id=OHR.MarathonSunday_ZH-CN9833453732_1920x1080.jpg&rf=LaDigue_1920x1080.jpg" style ="width:650px"alt=""></li>
        <li><img src="	https://cn.bing.com/th?id=OHR.Trossachs_ZH-CN9299955040_1920x1080.jpg&rf=LaDigue_1920x1080.jpg" style ="width:650px"alt=""></li>
        <li><img src="https://cn.bing.com/th?id=OHR.PeytoIce_ZH-CN7517633327_1920x1080.jpg&rf=LaDigue_1920x1080.jpg" style ="width:650px"alt=""></li>
        </ul>
        <a href="javascript:;" class="leftbtn" id="leftbtn"></a>
        <a href="javascript:;" class="rightbtn" id="rightbtn"></a>
    </div>
    <script>
        // 得到按钮和ul,ul整体进行运动
        var leftbtn = document.getElementById('leftbtn');
        var rightbtn = document.getElementById('rightbtn');
        var list = document.getElementById('list');

        // 克隆第一张图片
        var cloneli = list.firstElementChild.cloneNode(true);
        list.appendChild(cloneli);

        // 当前ul显示到第几张了,从0开始数
        var idx = 0;

        // 节流锁
        var lock = true;

        // 右边按钮监听
        rightbtn.onclick = function () {
            // 判断锁的状态
            if (!lock) return; 

            lock = false;

            // 给list加过渡,为什么要加??css中不是已经加了么??这是因为最后一张图片会把过渡去掉
            list.style.transition = 'left .5s ease 0s';
            idx ++;
            if (idx > 4) {
                // 设置一个延时器,延时器的功能就是将ul瞬间拉回0的位置,延时器的目的就是让过渡动画结束之后
                setTimeout(function() {
                    // 取消掉过渡,因为要的是瞬间移动,不是“咕噜”回去
                    list.style.transition = 'none';
                    list.style.left = 0;
                    idx = 0;
                }, 500);
            }
            list.style.left = -idx * 650 + 'px';

            // 函数节流
            setTimeout(function() {
                lock = true; 
            }, 500);
        }

        // 左边按钮监听
        leftbtn.onclick = function () {
            if (!lock) return;

            lock = false;

            // 判断是不是第0张,如果是,就要瞬间用假的替换真的
            if (idx == 0) {
                // 取消掉过渡,因为要的是瞬间移动,不是“咕噜”过去
                list.style.transition = 'none';
                // 直接瞬间移动到最后的假图片上
                list.style.left = -5 * 650 + 'px';
                // 设置一个延时器,这个延时器的延时时间可以是0毫秒,虽然是0毫秒,但是可以让我们过渡先是瞬间取消,然后再加上
                setTimeout(function() {
                    // 加过渡
                    list.style.transition = 'left .5s ease 0s';
                    // idx改为真正的最后一张
                    idx = 4;
                    list.style.left = -idx * 650 + 'px';
                }, 0);
            } else {
                idx --;
                list.style.left = -idx * 650 + 'px';
            }
            
            // 函数节流
            setTimeout(function() {
                lock = true; 
            }, 500);
        }
    </script>
</body>

1.6.4 呼吸轮播图特效

<style>
        * {
            margin: 0;
            padding: 0;
        }

        .carousel {
            width: 650px;
            height: 360px;
            border: 1px solid #000;
            margin: 50px auto;
            position: relative;

        }

        .carousel ul {
            list-style: none;
        }

        .carousel ul li {
            position: absolute;
            top: 0;
            left: 0;
            /* 透明度都是0 */
            opacity: 0;
            transition: opacity 1s ease 0s;
        }

        /* 只有第一张透明度是1 */
        .carousel ul li:first-child {
            opacity: 1;
        }

        .carousel .leftbtn {
            position: absolute;
            left: 20px;
            top: 50%;
            margin-top: -25px;
            width: 50px;
            height: 50px;
            background-color: rgb(28, 180, 226);
            border-radius: 50%;
        }

        .carousel .rightbtn {
            position: absolute;
            right: 20px;
            top: 50%;
            margin-top: -25px;
            width: 50px;
            height: 50px;
            background-color: rgb(28, 180, 226);
            border-radius: 50%;
        }
    </style>
</head>

<body>
    <div class="carousel">
        <ul id="list">
            <li><img src="https://s.cn.bing.net/th?id=OHR.HedgehogNest_ZH-CN0781850458_1920x1080.jpg&rf=LaDigue_1920x1080.jpg&qlt=50"style ="width:650px"alt=""></li>
        <li><img src="https://cn.bing.com/th?id=OHR.YiPeng_ZH-CN0652265903_1920x1080.jpg&rf=LaDigue_1920x1080.jpg" style ="width:650"alt=""></li>
        <li><img src="	https://cn.bing.com/th?id=OHR.LiDong2022_ZH-CN9929478283_1920x1080.jpg&rf=LaDigue_1920x1080.jpg" style ="width:650px"alt=""></li>
        <li><img src="https://cn.bing.com/th?id=OHR.MarathonSunday_ZH-CN9833453732_1920x1080.jpg&rf=LaDigue_1920x1080.jpg" style ="width:650px"alt=""></li>
        <li><img src="	https://cn.bing.com/th?id=OHR.Trossachs_ZH-CN9299955040_1920x1080.jpg&rf=LaDigue_1920x1080.jpg" style ="width:650px"alt=""></li>
        <li><img src="https://cn.bing.com/th?id=OHR.PeytoIce_ZH-CN7517633327_1920x1080.jpg&rf=LaDigue_1920x1080.jpg" style ="width:650px"alt=""></li>
        </ul>
        <a href="javascript:;" class="leftbtn" id="leftbtn"></a>
        <a href="javascript:;" class="rightbtn" id="rightbtn"></a>
    </div>
    <script>
        // 得到按钮和ul,ul整体进行运动
        var leftbtn = document.getElementById('leftbtn');
        var rightbtn = document.getElementById('rightbtn');
        var list = document.getElementById('list');
        var lis = list.getElementsByTagName('li');

        // 当前是第几张图显示
        var idx = 0;

        // 节流
        var lock = true;

        // 右按钮
        rightbtn.onclick = function () {
            // 判断节流
            if (!lock) return;

            lock = false;

            // 还没有改idx,此时的idx这个图片就是老图,老图淡出
            lis[idx].style.opacity = 0;
            idx++;
            if (idx > 4) idx = 0;
            // 改了idx,此时的idx这个图片就是新图,新图淡入
            lis[idx].style.opacity = 1;

            // 动画结束之后,开锁
            setTimeout(function () {
                lock = true;
            }, 1000);
        }

        // 左按钮
        leftbtn.onclick = function () {
            // 判断节流
            if (!lock) return;

            lock = false;

            // 还没有改idx,此时的idx这个图片就是老图,老图淡出
            lis[idx].style.opacity = 0;
            idx--;
            if (idx < 0) idx = 4;
            // 改了idx,此时的idx这个图片就是新图,新图淡入
            lis[idx].style.opacity = 1;

            // 动画结束之后,开锁
            setTimeout(function () {
                lock = true;
            }, 1000);
        }
    </script>
</body>

2 BOM基础

  • BOM(Browser Objece Model,浏览器对象模型)是JS与浏览器窗口交互的接口
  • 与浏览器改变尺寸,滚动条滚动相关的特效,都要借助BOM技术

2.1 window对象

  • window对象是JS脚本运行所处的窗口,而这个窗口中包含的DOM结构,window.document属性就是document对象

  • 在有标签功能的浏览器中,每一个标签都拥有自己的window对象;

  • 全局变量是window的属性,意味着多个js文件之间是共享全局作用域的,即js文件没有作用域隔离功能

var a=10;
console.log(window.a==a); // true
  • 内置函数普遍都是window的方法

    如:setInterval()、alert()等内置函数,普遍是window的方法

窗口尺寸相关属性描述
innerHeight浏览器窗口的内容区域高度,包含水平滚动条
innerWidth浏览器窗口的内容区域宽度,包含垂直滚动条
outerHeight浏览器窗口的外部高度
outerWidth浏览器窗口的外部宽度

获取不包含滚动条的窗口宽,高,左,上:

document.documentElement.clientWidth

console.log('窗口内宽(包含滚动条)', window.innerWidth);
console.log('窗口外宽', window.outerWidth);
console.log('窗口内宽(不含滚动条)', document.documentElement.clientWidth);
2.1.1 resize事件

当窗口大小改变就会触发resize事件,可以使用window.onresize或者window.addEventListener(‘resize’)来绑定事件处理函数

  // 监听窗口改变尺寸事件
window.onresize = function () {
  var root = document.documentElement;
  console.log('窗口改变尺寸了', root.clientWidth, root.clientHeight);
};
2.1.2 已卷动高度

window.scrollY属性表示在垂直方向已经滚动的像素值

console.log(window.scrollY);
console.log(document.documentElement.scrollTop);

短路计算: var scrollop=window.scrollY || document.documentElement.scrollTop;

document.documentElement.scrollTop=0 滚动条自动到顶端,可以实现页面的上下移动

注意:

var scrollop=window.scrollY 是只读的

document.documentElement.scrollTop不是只读的

  1. scroll事件

在窗口被卷动后,就会触发scroll事件,可以使用window.scroll或者window.addEventListener(‘scroll’)来绑定事件处理函数

 window.onscroll = function () {
            console.log('窗口卷动了', window.scrollY);
        };
2.1.3 navigator对象

window.navigator属性可以检测navigator对象,它内部含有用户此次活动的浏览器的相关属性标识。

属性描述
appName返回浏览器的名称。
appVersion返回浏览器的版本信息。
userAgent浏览器用户代理(含有内核信息和封装壳信息)。
platform返回操作系统。
console.log('浏览器品牌', navigator.appName);
console.log('浏览器版本', navigator.appVersion);
console.log('用户代理', navigator.userAgent);
console.log('操作系统', navigator.platform);
返回信息:
浏览器品牌 Netscape
浏览器版本 5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/107.0.0.0 Safari/537.36
用户代理 Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/107.0.0.0 Safari/537.36
操作系统 Win32
2.1.4 history对象
  • window.history对象提供了操作浏览器会话历史的接口

  • 常用操作是模拟浏览器回退按钮

    history.back(); //等同于点击浏览器的回退按钮

    history.go(-1); //等同于history.back();

    // temp.html
    <body>
        <h1>我是temp网页</h1>
        <a href="history方法.html">去看history方法页面</a>
    </body>
    
// history方法.html
<body>
    <h1>我是history方法网页</h1>
    <button id="btn">回退</button>
    <a href="javascript:history.back();">回退</a>
    
    <script>
        var btn = document.getElementById('btn');

        btn.onclick = function() {
            // history.back();
            history.go(-1);
        };
    </script>
</body>

2.1.5 location对象

  • window.location标识当前所在网址,可以通过给这个属性赋值命令浏览器进行页面跳转

    window.location = ‘http://www.imooc.com’;

    window.location.href= ‘http://www.imooc.com’;

<body>
    <button id="btn1">点我去看慕课</button>
    <button id="btn2">刷新</button>
    <script>
        var btn1 = document.getElementById('btn1');
        var btn2 = document.getElementById('btn2');

        btn1.onclick = function () {
            window.location = 'http://www.imooc.com';
        };

        btn2.onclick = function () {
            // reload(true)刷新页面
            window.location.reload(true);
        };
    </script>
</body>

GET请求查询参数

window.location.search属性为当前浏览器的GET请求查询参数

如:https://www.baidu.cim?a=1&b=2

console.log(window.location.search); // 可以本地获取GET请求查询参数的内容a=1&b=2

2.2 BOM特效开发案例

2.2.1 BOM特效开发-----返回顶部
 <style>
        body {
            height: 5000px;
            background-image: linear-gradient(to bottom, blue, green, yellow);
        }

        .backtotop {
            width: 60px;
            height: 60px;
            background-color: rgba(255, 255, 255, .6);
            position: fixed;
            bottom: 100px;
            right: 100px;
            /* 小手状 */
            cursor: pointer;
        }
    </style>


<body>
    <div class="backtotop" id="backtotopBtn">返回顶部</div>

    <script>
        var backtotopBtn = document.getElementById('backtotopBtn');

        var timer;
        backtotopBtn.onclick = function () {
            // 设表先关
            clearInterval(timer);

            // 设置定时器
            timer = setInterval(function () {
                // 不断让scrollTop减少
                document.documentElement.scrollTop -= 200;
                // 定时器肯定要停
                if (document.documentElement.scrollTop <= 0) {
                    clearInterval(timer);
                }
            }, 20);
        };
    </script>
</body>
2.2.2 楼层导航效果
<style>
       *{margin:0;padding:0}#box{width:400px;height:400px;margin:60px auto;border:4px solid red}ul{padding-top:10px}li{padding-top:10px}#para{width:80px;height:80px;background-color:orange}
    </style>


    <script>
        var para = document.getElementById('para');
        // 净top值,使用这个属性的时候,所有的祖先元素不要有定位,有定位就不好用啦。
        console.log(para.);
    </script>
</body>
<style>
       *{margin:0;padding:0}.content-part{width:1000px;margin:0px auto;margin-bottom:30px;background-color:#ccc;font-size:50px}.floornav{position:fixed;right:40px;top:50%;margin-top:-100px;width:120px;height:200px;background-color:orange}.floornav ul{list-style:none}.floornav ul li{width:120px;height:40px;line-height:40px;text-align:center;font-size:26px;cursor:pointer}.floornav ul li.current{background:purple;color:white}
    </style>
</head>

<body>
    <nav class="floornav">
        <ul id="list">
            <li data-n="科技" class="current">科技</li>
            <li data-n="体育">体育</li>
            <li data-n="新闻">新闻</li>
            <li data-n="娱乐">娱乐</li>
            <li data-n="视频">视频</li>
        </ul>
    </nav>

    <section class="content-part" style="height:674px;" data-n="科技">
        科技栏目
    </section>

    <section class="content-part" style="height:567px;" data-n="体育">
        体育栏目
    </section>

    <section class="content-part" style="height:739px;" data-n="新闻">
        新闻栏目
    </section>

    <section class="content-part" style="height:574px;" data-n="娱乐">
        娱乐栏目
    </section>

    <section class="content-part" style="height:1294px;" data-n="视频">
        视频栏目
    </section>

    <script>
        // 使用事件委托给li添加监听
        var list = document.getElementById('list');
        var contentParts = document.querySelectorAll('.content-part');
        var lis = document.querySelectorAll('#list li');

        list.onclick = function (e) {
            if (e.target.tagName.toLowerCase() == 'li') {
                // getAttribute表示得到标签身上的某个属性值
                var n = e.target.getAttribute('data-n');

                // 可以用属性选择器(就是方括号选择器)来寻找带有相同data-n的content-part
                var contentPart = document.querySelector('.content-part[data-n=' + n + ']');

                // 让页面的卷动自动成为这个盒子的offsetTop值
                document.documentElement.scrollTop = contentPart.offsetTop;
            }
        }


        // 在页面加载好之后,将所有的content-part盒子的offsetTop值推入数组
        var offsetTopArr = [];
        
        // 遍历所有的contentPart,将它们的净位置推入数组
        for (var i = 0; i < contentParts.length; i++) {
            offsetTopArr.push(contentParts[i].offsetTop);
        }
        // 为了最后一项可以方便比较,我们可以推入一个无穷大
        offsetTopArr.push(Infinity);

        console.log(offsetTopArr);

        // 当前所在楼层
        var nowfloor = -1;

        // 窗口的卷动
        window.onscroll = function () {
            // 得到当前的窗口卷动值
            var scrollTop = document.documentElement.scrollTop;

            // 遍历offsetTopArr数组,看看当前的scrollTop值在哪两个楼层之间
            for (var i = 0; i < offsetTopArr.length; i++) {
                if (scrollTop >= offsetTopArr[i] && scrollTop < offsetTopArr[i + 1]) {
                    break;
                }
            }
            // 退出循环的时候,i是几,就表示当前楼层是几
            // 如果当前所在楼层,不是i,表示环楼了
            if (nowfloor != i) {
                console.log(i);
                // 让全局变量改变为这个楼层号
                nowfloor = i;

                // 设置下标为i的项有cur
                for (var j = 0; j < lis.length; j++) {
                    if (j == i) {
                        lis[j].className = 'current';
                    } else {
                        lis[j].className = '';
                    }
                }
            }
        };
    </script>
</body>

3 面向对象

3.1 认识对象

3.1.1 认识对象

对象对象是 键值对的集合,表示属性和值的映射关系

// 认识对象
var xiaoming = {
            name: '小明',
            age: 12,
            sex: '男',
            hobbies: ['足球', '编程'],
    		// 属性命名不符合js规范,必须使用引号包裹
            'favorite-book': '舒克和贝塔'
        };

        console.log(xiaoming.name);
        console.log(xiaoming.age);
        console.log(xiaoming.sex);
        console.log(xiaoming.hobbies);
		// 属性命名不符合js规范,必须使用方括号的写法访问
        console.log(xiaoming['favorite-book']);
        
        var key = 'sex';
		// 属性使用变量存储,必须使用方括号形式访问,否则返回undefined
        console.log(xiaoming[key]);
// 对象属性的修改增加和删除  
var obj = {
            a: 10,
            b: 20
        };

        // 对象属性的修改
        obj.b = 40;
        obj.b++;
        console.log(obj.b);

        // 对象属性的增加
        obj.c = 60;
        console.log(obj);
        
        // 对象属性的删除
        delete obj.a;
        console.log(obj);
3.1.2 对象的方法
  • 某个属性值是函数,则它被成为对象的方法
  • 方法是对象的“函数属性”,他需要用对象打点调用
// 对象的方法
var xiaoming = {
            name: '小明',
            age: 12,
            sex: '男',
            sayHello: function () {
                console.log('你好我是小明,我12岁了,我是一个男生');
            },
            sleep: function () {
                console.log('小明开始睡觉zzzzz');
            }
        };

        var xiaohong = {
            name: '小红',
            age: 11,
            sex: '女',
            sayHello: function () {
                console.log('你好我是小红,我11岁了,我是一个女生');
            },
            sleep: function () {
                console.log('小红开始睡觉zzzzz');
            }
        };

        xiaoming.sayHello();
        xiaohong.sayHello();
        xiaohong.sleep();
3.1.3 对象的遍历
var obj = {a: 11,b: 22,c: 88};
// for循环遍历对象
for (var k in obj) {
    console.log('对象obj的属性' + k + '的值是' + obj[k]);
}
3.1.4 对象的浅克隆与深克隆
举例当var a=b变量传值时当==比较时
基本数据类型数字、字符串、布尔、undefined、null内存中产生新的副本比较值是否相等
引用数据类型对象、数组等内存中不产生新的副本,而是让新变量指向同一个对象比较内存地址是否相同,即比较是否为同一个对象
// 例子1
var obj1 = {a: 1,b: 2,c: 3};
var obj2 = {a: 1,b: 2,c: 3};
console.log(obj1 == obj2);      // false
console.log(obj1 === obj2);     // false
        
console.log({} == {});          // false
console.log({} === {});         // false
        
// 例子2
var obj3 = {a: 10};
var obj4 = obj3;
obj3.a ++;
console.log(obj4);      // {a: 11}
  1. 浅克隆
    • 浅克隆是只克隆对象的表层,对象的某些属性是引用类型则不进一步克隆它们,知识传递它们的引用
    • 使用for…in…循环即可实现对象的浅克隆
var obj1 = {a: 1,b: 2, c: [44, 55, 66]};
		// obj2=obj1 没有实现克隆,而是让新变量指向同一个对象的内存地址

        // 实现浅克隆
        var obj2 = {};
        for (var k in obj1) {
            // 每遍历一个k属性,就给obj2也添加一个同名的k属性
            // 值和obj1的k属性值相同
            obj2[k] = obj1[k];
        }

        // 为什么叫浅克隆呢?比如c属性的值是引用类型值,那么本质上obj1和obj2的c属性是内存中的同一个数组,并没有被克隆分开。
        obj1.c.push(77);
        console.log(obj2);                  // obj2的c属性这个数组也会被增加77数组
        console.log(obj1.c == obj2.c);      // true,true就证明了数组是同一个对象
    </script>
  1. 深克隆
    • 深克隆是克隆对象的全貌,不论对象的属性值是否为引用类型值,都将实现克隆
    • 和数组的深克隆类似,对象的深克隆需要用到递归
		var obj1={a:1,b:2,c:[33,44,{m:55,n:66,p:[77,88]}]};

        // 深克隆
        function deepClone(o) {
            // 要判断o是对象还是数组
            if (Array.isArray(o)) {
                // 数组
                var result = [];
                for (var i = 0; i < o.length; i++) {
                    result.push(deepClone(o[i]));
                }
            } else if (typeof o == 'object') {
                // 对象
                var result = {};
                for (var k in o) {
                    result[k] = deepClone(o[k]);
                }
            } else {
                // 基本类型值
                var result = o;
            }
            return result;
        }

        
        var obj2 = deepClone(obj1);
        console.log(obj2);
        
        console.log(obj1.c == obj2.c);      // false
        
        obj1.c.push(99);
        console.log(obj2);                  // obj2不变的,因为没有“藕断丝连”的现象

        obj1.c[2].p.push(999);
        console.log(obj2);                  // obj2不变的,因为没有“藕断丝连”的现象

3.2认识函数的上下文

  • 函数中可以使用this关键字,他表示函数的上下文
  • 函数中的this具体指代什么必须通过调用函数时的“前言后语”来判断。与中文的“这”类似
var xm={name:'小明',age:'12',
       sayhello:function(){console.log('我是:'+this.name+',我'+this.age+'岁了')};};
xm.sayhello(); // 我是小明,我12岁了

// 将sayhello方法提炼出来,单独赋值给变量
var sh=xm.sayhello;
sh();  // 我是undefined,我undefined岁了
 

函数的上下文由调用方式决定;同一个函数,用不同的方式调用,则函数的上下文不同。

情形1:对象打点调用函数,函数中的this指代打点的对象

xm.sayhello();

情形2:圆括号直接调用函数,函数中的this指代window对象

var sh=xm.sayhello;
sh();

3.2.1 上下文规则1
  • 对象打点调用它的方法函数,则函数的上下文就是打点的对象

    function so(){console.log(this.a+this.b)};
    var xm={a:10,b:12,so:so};
    xm.so(); // 22
    
    var x1={a:1,b:2,
            fn:function(){console.log(this.a+this.b)}
           };
    var x2={a:3,b:4,
            fn:x1.fn
           };
    x2.fn(); // 7
    
    function outer(){
        var a=11;
        var b=22;
        return {a:33,b:44,fn:function(){console.log(this.a+this.b)}};
    }
    outer().fn(); // 77
    
    function so(){console.log(this.a+this.b)};
    var xm={a:10,b:12,c:[{a:3,b:4,c:so}]};
    var a=5
    xm.c[0].c(); // 7
    
3.2.2 上下文规则2
  • 圆括号直接调用函数,则函数的上下文是window对象

    var xm={a:10,b:12,so:function(){console.log(this.a+this.b)}};
    var a=3; // 全局变量都是window对象的属性
    var b=4;
    var fn=xm.so;
    fn(); // 7
    
    function fun(){return this.a+this.b;}
    var a=1;
    var b=2;
    var obj={a:3,b:fun()// 适用规则2,所以本例b:3
             ,fun:fun};
    var result=obj.fun();// 对象打点调用函数,适用于规则1,返回obj,a+b的结果3+3最终得6
    console.log(result); // 6
    
3.2.3 上下文规则3
  • 数组(类数组对象)枚举出函数进行调用,上下文是这个数组(类数组对象)

    var arr=['a','b','c',function(){console.log(this[0])}];
    // this[0]表示当前数组下标为0的内容,arr[0]是a
    arr[3]();// a
    
    • **类数组对象:**所有键名为自然数序列(从0开始),且有length属性的对象。

    • arguments对象是最常见的类数组对象,他是函数的实参列表

    function fun(){arguments[3]();}
    fun('a','b','c',function(){console.log(this[1]);}); // b
    
3.2.4 上下文规则4
  • IIFE中的函数中上下文是window对象

    IIFE中的函数(立即可执行函数):

    (function(){ })();

var a=1;
var obj={a:2,fun:(function(){var a=this.a; // 适用于规则4,this.a是window对象的a=1
    return function(){console.log(a+this.a);}// 这里的this.a是obj对象中a:2
})()};
obj.fun(); // 3
3.2.5 上下文规则5
  • 定时器,延时器调用函数,上下文是window对象

    setInterval(函数,时间);
    setTimeout(函数,时间);

var obj={a:1,b:2,fun:function(){console.log(this.a+this.b);}}
var a=3;
var b=4;
// setInterval(obj.fun,2000);
setTimeout(obj.fun,2000); // 两秒后弹出一次window对象ab的和7
var obj={a:1,b:2,fun:function(){console.log(this.a+this.b);}}

var a=3;
var b=4;
setTimeout(function(){obj.fun};,2000); // 现在适用于规则1,obj.fun不是由延时器调用,而是匿名函数调用。
3.2.6 上下文规则6
  • 事件处理函数的上下文是绑定事件的DOM元素

    DOM元素.onclick=function(){};
    
    // 点击哪个盒子哪个盒子就便红,要求使用同一个时间函数实现
    <style>
    div{width:200px;height:200px;float:left;border:1px solid #000;margin-right:10px}
     </style>
    
    <body>
        <div id="box1"></div>
        <div id="box2"></div>
        <div id="box3"></div>
    
        <script>
            function setColorToRed() {
                this.style.backgroundColor = 'red';
            }
    
            var box1 = document.getElementById('box1');
            var box2 = document.getElementById('box2');
            var box3 = document.getElementById('box3');
    
            box1.onclick = setColorToRed;
            box2.onclick = setColorToRed;
            box3.onclick = setColorToRed;
        </script>
    </body>
    
    // 点击哪个盒子哪个盒子2000毫秒后就变红
    <style>
        div{width:200px;height:200px;float:left;border:1px solid #000;margin-right:10px}
    </style>
    <body>
        <div id="box1"></div>
        <div id="box2"></div>
        <div id="box3"></div>
    
        <script>
            function setColorToRed() {
                // 备份上下文
                var self = this;
                setTimeout(function() {
                    self.style.backgroundColor = 'red';
                }, 2000);
            }
    
            var box1 = document.getElementById('box1');
            var box2 = document.getElementById('box2');
            var box3 = document.getElementById('box3');
    
            box1.onclick = setColorToRed;
            box2.onclick = setColorToRed;
            box3.onclick = setColorToRed;
        </script>
    </body>
    
3.2.7 call和apply
  • call和apply能指定函数的上下文

    函数名.call(对象);

    函数名.apply(对象);

  function sum(b1, b2) {alert(this.c + this.m + this.e + b1 + b2);};
  
  var xiaoming = {c: 100,m: 90e: 80};
  
  sum.call(xiaoming, 3, 5);
  sum.apply(xiaoming, [3, 5]);
function fun1(){fun2.apply(this,arguments);}// arguments是类数组传递的实参也是类数组,所以要用apply
function fun2(a,b){alert(a+b);}
fun1(33,44);
3.2.8 上下文总结
规则上下文
对象.函数()对象
函数()window
数组[下标]()数组
IIFEwindow
定时器window
DOM事件处理函数绑定DOM的元素
call和apply任意指定
用new调用函数秘密创建出的对象

3.3 构造函数

3.3.1 用new调用函数的四步走

JS规定,使用new操作符调用函数会进行“四步走”:

  1. 函数体内会自动创建出一个空白对象{}

  2. 函数的上下文(this)会指向这个对象

  3. 函数体内的语句会执行

  4. 函数会主动返回上下文对象,计时函数没有return语句

    function fun(){this.a=3;this.b=5;}
    var obj=new fun();
    console.log(obj); // fun {a: 3, b: 5}
    
    // ---------------------------------
    
    
    function fun(){
        this.a=3;this.b=5;var m=34;
        if(this.a>this.b){this.c=m}else{this.c=m*2}}
    var obj=new fun();
    console.log(obj);
    

3.4 原型和原型链

3.4.1 prototype和原型链查找
  • 任何函数都有prototype属性,prototype是英语“原型”的意思
  • prototype属性是个对象,它默认拥有construcyor(构造器)属性指回函数
function sum(a, b){
    return a + b;}

console.log(sum.prototype);//{constructor: ƒ}
console.log(typeof sum.prototype);	// 5 object
console.log(sum.prototype.constructor === sum);	// 6 true
  • 普通函数的prototype属性没有任何作用,而构造函数的prototype属性是它的实例原型

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-aSZ5zooo-1674716107557)(C:\Users\popla\AppData\Roaming\Typora\typora-user-images\image-20221111132301197.png)]

function People(name, age, sex) {
    this.name = name;
    this.age = age;
    this.sex = sex;}
// 实例化
var xiaoming = new People('小明', 12, '男');
// 测试三角关系是否存在
console.log(xiaoming.__proto__ === People.prototype);  // true
  • 原型链查找:实例可以打点访问它的原型的属性和方法,这被称为“原型链查找”
function People(name,age,sex){
    this.name=name;
    this.age=age;
    this.sex=sex}

// 往原型上添加nationality属性
People.prototype.nationality='中国';
var xm=new People('小明',18,'男');
console.log(xm.nationality)// 返回"中国" #实例可以打点访问它的原型的属性和方法
  • 原型链的遮蔽效应
function People(name,age,sex){
    this.name=name;
    this.age=age;
    this.sex=sex}
People.prototype.nationality='中国';
var xm=new People('小明',18,'男');
var tom=new People('汤姆',19,'男');
tom.nationality='俄罗斯';
// 原型链的遮蔽效应,tom有nationality属性,就直接返回该属性 俄罗斯
// 如果tom没有nationality属性,就直接返回prototype的nationality属性
console.log(tom.nationality);   
  • hasOwnProperty方法可以检测对象是否真的“自己拥有”某属性或方法

tom.hasOwnProperty(‘name’); // true
tom.hasOwnProperty(‘age’); // true
tom.hasOwnProperty(‘sex’); // true
xm.hasOwnProperty(‘nationality’); // false
tom.hasOwnProperty(‘nationality’); // true

  • in运算符只能检查某个属性或方法是否可以被对象访问,不能检查是不是自己的属性或方法

‘name’ in xiaoming // true

‘sex’ in xiaoming // true

‘nationality’ in xiaoming // true

// hasOwnProperty和in.html 
function People(name, age, sex) {
            this.name = name;
            this.age = age;
            this.sex = sex;
        }
        // 往原型上添加nationality属性
        People.prototype.nationality = '中国';

        // 实例化
        var xiaoming = new People('小明', 12, '男');

        console.log(xiaoming.hasOwnProperty('name'));
        console.log(xiaoming.hasOwnProperty('age'));
        console.log(xiaoming.hasOwnProperty('sex'));
        console.log(xiaoming.hasOwnProperty('nationality'));

        console.log('name' in xiaoming);
        console.log('age' in xiaoming);
        console.log('sex' in xiaoming);
        console.log('nationality' in xiaoming);
3.4.2 在prototype上添加方法

将方法添加在对象身上的缺点:每个实例和每个实例的方法函数都是内存中的不同函数,造成内存的浪费。

解决方法:将方法写在prototype

function People(name, age, sex) {
        this.name = name;
        this.age = age;
        this.sex = sex;
}
    // 把方法要写到原型上
People.prototype.sh=function(){console.log('我是' + this.name + '今年' + this.age + '岁');}
People.prototype.growup = function () {this.age ++;}
var xiaoming = new People('小明', 12, '男');
var xiaohong = new People('小红', 11, '女');
console.log(xiaoming.sayHello === xiaohong.sayHello);


xiaoming.sh(); //我是小明今年12岁
xiaohong.sh(); //我是小红今年11岁
xiaoming.growup();
xiaoming.growup();
xiaoming.growup();
xiaoming.growup();
xiaoming.growup();
xiaoming.sh(); //我是小明今年17岁
xiaohong.sh(); 我是小红今年11岁
3.4.3 原型链的终点

Object.prototype就是原型的终点

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-6UrQlc0D-1674716107560)(C:\Users\popla\AppData\Roaming\Typora\typora-user-images\image-20221111143100835.png)]

function People(name, age) {
    this.name = name;
    this.age = age;}
var xiaoming = new People('小明', 12);

// 检查xiaoming的原型的原型是不是Object.prototype对象
console.log(xiaoming.__proto__.__proto__ === Object.prototype);     // true
// Object.prototype就是原型的终点,所以它的原型为null
console.log(Object.prototype.__proto__);                            // null


console.log(Object.prototype.hasOwnProperty('hasOwnProperty'));     // true
console.log(Object.prototype.hasOwnProperty('toString'));           // true

关于数组的原型链

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-leUBNPco-1674716107564)(C:\Users\popla\AppData\Roaming\Typora\typora-user-images\image-20221111144039900.png)]

var arr = [344, 45, 34, 23];
console.log(arr.__proto__ === Array.prototype);                 // true
console.log(arr.__proto__.__proto__ === Object.prototype);      // true
console.log(Array.prototype.hasOwnProperty('push'));            // true
console.log(Array.prototype.hasOwnProperty('splice'));          // true
3.4.4 继承
// 实现继承 

// 父类,人类
        function People(name, age, sex) {
            this.name = name;
            this.age = age;
            this.sex = sex;
        }
        People.prototype.sayHello = function () {
            console.log('你好,我是' + this.name + '我今年' + this.age + '岁了');
        };
        People.prototype.sleep = function () {
            console.log(this.name + '开始睡觉,zzzzz');
        };

        // 子类,学生类
        function Student(name, age, sex, scholl, studentNumber) {
            this.name = name;
            this.age = age;
            this.sex = sex;
            this.scholl = scholl;
            this.studentNumber = studentNumber;
        }
        // 关键语句,实现继承
        Student.prototype = new People();

        Student.prototype.study = function () {
            console.log(this.name + '正在学习');
        }
        Student.prototype.exam = function () {
            console.log(this.name + '正在考试,加油!');
        }
        // 重写、复写(override)父类的sayHello
        Student.prototype.sayHello = function () {
            console.log('敬礼!我是' + this.name + '我今年' + this.age + '岁了');
        }

        // 实例化
        var hanmeimei = new Student('韩梅梅', 9, '女', '慕课小学', 100556);

        hanmeimei.study();
        hanmeimei.sayHello();
        hanmeimei.sleep();

        var laozhang = new People('老张', 66, '男');
        laozhang.sayHello();
3.3.5 面向对象
  • 面向对象的本质:定义不同的类,让类的实例工作
  • 面向对象的优点:程序编写更清晰,代码结构更严密,是代码更健壮利与维护
  • 面向对象常用到的场合:需要封装和复用性很强的场合(组件思维)

案例1:红绿灯小案例.html

<style>
        #box img{            width: 80px;        }
    </style>
<body>
    <div id="box"></div>

    <script>
        // 定义红绿灯类
        function TrafficLight() {
            // 颜色属性,一开始都是红色
            // 红色1、黄色2、绿色3
            this.color = 1;
            // 调用自己的初始化方法
            this.init();
            // 绑定监听
            this.bindEvent();
        }
        // 初始化方法
        TrafficLight.prototype.init = function() {
            // 创建自己的DOM
            this.dom = document.createElement('img');
            // 设置src属性
            this.dom.src = 'images/' + this.color + '.jpg';
            box.appendChild(this.dom);
        };
        // 绑定监听
        TrafficLight.prototype.bindEvent = function() {
            // 备份上下文,这里的this指的是JS的实例
            var self = this;
            // 当自己的dom被点击的时候
            this.dom.onclick = function () {
                // 当被点击的时候,调用自己的changeColor方法
                self.changeColor();
            };
        }
        // 改变颜色
        TrafficLight.prototype.changeColor = function () {
            // 改变自己的color属性,从而有一种“自治”的感觉,自己管理自己,不干扰别的红绿灯
            this.color ++;
            if (this.color == 4) {
                this.color = 1;
            }
            // 光color属性变化没有用,还要更改自己的dom的src属性
            this.dom.src = 'images/' + this.color + '.jpg';
        };

        // 得到盒子
        var box = document.getElementById('box');


        // 实例化100个
        var count = 100;

        while(count--){
            new TrafficLight();
        }
         
    </script>

</body>

案例2:炫彩小球小案例.html

<style>
        body {
            background-color: black;
        }

        .ball {
            position: absolute;
            border-radius: 50%;
        }
    </style>

<body>
    <script>
        // 小球类
        function Ball(x, y) {
            // 属性x、y表示的是圆心的坐标
            this.x = x;
            this.y = y;
            // 半径属性
            this.r = 20;
            // 透明度
            this.opacity = 1;
            // 小球背景颜色,从颜色数组中随机选择一个颜色
            this.color = colorArr[parseInt(Math.random() * colorArr.length)];
            // 这个小球的x增量和y的增量,使用do while语句,可以防止dX和dY都是零
            do {
                this.dX = parseInt(Math.random() * 20) - 10;
                this.dY = parseInt(Math.random() * 20) - 10;
            } while (this.dX == 0 && this.dY == 0)

            // 初始化
            this.init();
            // 把自己推入数组,注意,这里的this不是类本身,而是实例
            ballArr.push(this);
        }
        // 初始化方法
        Ball.prototype.init = function () {
            // 创建自己的dom
            this.dom = document.createElement('div');
            this.dom.className = 'ball';
            this.dom.style.width = this.r * 2 + 'px';
            this.dom.style.height = this.r * 2 + 'px';
            this.dom.style.left = this.x - this.r + 'px';
            this.dom.style.top = this.y - this.r + 'px';
            this.dom.style.backgroundColor = this.color;
            // 上树
            document.body.appendChild(this.dom);
        };
        // 更新
        Ball.prototype.update = function () {
            // 位置改变
            this.x += this.dX;
            this.y -= this.dY;
            // 半径改变
            this.r += 0.2;
            // 透明度改变
            this.opacity -= 0.01;
            this.dom.style.width = this.r * 2 + 'px';
            this.dom.style.height = this.r * 2 + 'px';
            this.dom.style.left = this.x - this.r + 'px';
            this.dom.style.top = this.y - this.r + 'px';
            this.dom.style.opacity = this.opacity;

            // 当透明度小于0的时候,就需要从数组中删除自己,DOM元素也要删掉自己
            if (this.opacity < 0) {
                // 从数组中删除自己
                for (var i = 0; i < ballArr.length; i++) {
                    if (ballArr[i] == this) {
                        ballArr.splice(i, 1);
                    }
                }
                // 还要删除自己的dom
                document.body.removeChild(this.dom);
            }
        };


        // 把所有的小球实例都放到一个数组中
        var ballArr = [];

        // 初始颜色数组
        var colorArr = ['#66CCCC', '#CCFF66', '#FF99CC', '#FF6666', 
    '#CC3399', '#FF6600'];

        // 定时器,负责更新所有的小球实例
        setInterval(function () {
            // 遍历数组,调用调用的update方法
            for (var i = 0; i < ballArr.length; i++) {
                ballArr[i].update();
            }
        }, 20);

        // 鼠标指针的监听
        document.onmousemove = function (e) {
            // 得到鼠标指针的位置
            var x = e.clientX;
            var y = e.clientY;

            new Ball(x, y);
        };
    </script>
</body>

3.5 JS的内置对象

3.5.1 包装类
  • 包装类只对基本数据类型有效
  • Number()、String()、Boolean()分别是数字、字符串、布尔值的“包装类”
  • 很多编程语言都有包装类的设计,包装类的目的是为了让基本类型值可以从他们的构造函数的prototype上获得方法
        var a = new Number(123);
        var b = new String('慕课网');
        var c = new Boolean(true);
        
        console.log(a);
        console.log(typeof a);      // object
        console.log(b);
        console.log(typeof b);      // object
        console.log(c);
        console.log(typeof c);      // object

        console.log(5 + a);         // 128
        console.log(b.slice(0, 2)); // '慕课'
        console.log(c && true);     // true

        var d = 123;
        console.log(d.__proto__ == Number.prototype);       // true

        var e = '慕课网';
        console.log(e.__proto__ == String.prototype);       // true
        console.log(String.prototype.hasOwnProperty('toLowerCase'));    // true
        console.log(String.prototype.hasOwnProperty('slice'));          // true
        console.log(String.prototype.hasOwnProperty('substr'));         // true
        console.log(String.prototype.hasOwnProperty('substring'));      // true
  • Number()、String()、Boolean()的实例都是object类型,它们的primitiveValue属性存储它们的本身值
  • new出来的基本类型值可以正常参与运算
  • 包装类的目的就是为了让基本类型值可以从他们的构造函数的prototype上获得方法
3.5.2 math对象
  • 幂和开方:math.pow()、math.sqrt()

  • 向上取整和向下取整:math.ceil()、math.floor()

  • 四舍五入:math.round()

    console.log(Math.round(3.49));      // 3
    console.log(Math.round(3.51));      // 4
    
    var a = 3.7554;
    // 实现四舍五入到小数点某一位(a * 100) / 100)
    // 375.54四舍五入后,得到376,然后/100,返回结果3.76
    console.log(Math.round(a * 100) / 100); // 3.76
    
  • Math.max()和Math.min()

    console.log(Math.max(44, 55, 33, 22));      // 55
    console.log(Math.min(44, 55, 33, 22));      // 22
    

    利用math.max求数组最大值

    • Math.max()要求参数必须罗列出来,而不能是数组
    • 函数上下文中学到的apply方法,它可以指定函数的上下文,并且以数组的形式传入“零散值”当作函数的参数
    var arr = [3, 4, 4, 3, 2, 2, 1, 3, 5, 7, 4, 3];
    console.log(Math.max.apply(null, arr));     // 7
    
    // 在今后学习ES6之后,求数组最大值还可以
    console.log(Math.max(...arr));              // 7
    
  • 随机数Math.random()

    • Math.random()可以得到0~1之间的小数

    • 为了得到[a,b]区间的整数,可以使用公式:parseInt(Math.random() * (b - a + 1)) + a

console.log(Math.random());
console.log(Math.random());
console.log(Math.random());
console.log(Math.random());

 // 如果要生成[a, b]之内的整数,就要使用公式

	// parseInt() 函数解析字符串并返回整数。
// parseInt(Math.random() * (b - a + 1)) + a

 // [3, 8]
console.log(parseInt(Math.random() * 6) + 3);
3.5.3 Date对象
  • 使用new Date()即可得到当前时间的日期对象,它是object类型值
  • 使用new Date(2022,11,10)即可得到指定日期的日期对象,注意第二个参数表示月份,从0开始计算,11表示12月。
  • 也可以是new Date(‘2022-12-01’)这样的写法,注意,月份-日期必须是两位的,不足需要补0
  1. 日期对象
 // 什么参数都不加,自动得到今天此时此刻的日期对象
var d1 = new Date();
console.log(d1);
console.log(typeof d1);

// 得到六月一日
var d2 = new Date(2020, 5, 1);      // 不算时区
var d3 = new Date('2020-06-01');    // 算时区,8点

console.log(d2);
console.log(d3);
  1. 日期对象的常用方法
方法描述
getDate()从Date对象返回一个月中的某一天(1~31)
getDay()从Date对象返回一周中的某一天(0~6)
getMonth()从Date对象返回月份(0~11)
getFullYear()从Date对象以4位数字返回年份
getHours()返回Date对象的小时(0~23)
getMinutes()返回Date对象的分钟(0~59)
getSeconds()返回Date对象的秒数(0~59)
var d = new Date();

console.log('日期', d.getDate());
console.log('星期', d.getDay());
console.log('年份', d.getFullYear());
console.log('月份', d.getMonth() + 1);
console.log('小时', d.getHours());
console.log('分钟', d.getMinutes());
console.log('秒数', d.getSeconds());
  1. 时间戳
    • 时间戳表示1970年1月1日0点整距离某一时刻的毫秒数
    • 通过getTime()方法或者Date.parse()函数可以将时间戳对象变为时间戳
    • 通过new Date(时间戳)的写法,可以将时间戳变为日期对象
// 日期对象
var d = new Date();

// 显示时间戳的两种方法。时间戳表示1970年1月1日距离此时的毫秒数
var timestamp1 = d.getTime();       // 精确到毫秒
var timestamp2 = Date.parse(d);     // 精确到秒,也是毫秒数,只不过最后三位一定是000

console.log(timestamp1);
console.log(timestamp2);

// 如何把时间戳,比如1601536565000变回日期对象呢?
var dd = new Date(1601536565000);
console.log(dd);
console.log(dd.getFullYear());
3.5.4 倒计时小程序
<body>
    <h1>2021年高考倒计时</h1>
    <h2 id="info"></h2>
    <script>
        var info = document.getElementById('info');

        setInterval(function(){
            // 现在的日期
            var nd = new Date();
            // 目标的日期,5表示六月
            var td = new Date(2021, 5, 7);
            // 毫秒差
            var diff = td - nd;

            // 任务很简单,就是把diff换算为天、小时、分钟、秒
            // 换算为多少天,除以一天的总毫秒数,不就是换算为多少天么
            var day = parseInt(diff / (1000 * 60 * 60 * 24));
            // 零多少小时呢??差的总毫秒数与1天的毫秒数的相除的余数,就是零头的毫秒数
            var hours = parseInt(diff % (1000 * 60 * 60 * 24) / (1000 * 60 * 60));
            // 零多少分钟呢??
            var minutes = parseInt(diff % (1000 * 60 * 60) / (1000 * 60));
            // 零多少秒呢??
            var seconds = parseInt(diff % (1000 * 60 * 60) % (1000 * 60) / 1000);

            
            info.innerText = day + '天' + hours + '时' + minutes + '分' + seconds + '秒';
        }, 1000);
        
    </script>
</body>

3.6 内置构造函数

  • JavaScript有很多内置构造函数,比如Array就是数组类型的构造函数,Function就是函数类型的构造函数,Object就是对象类型的构造函数
  • 内置构造函数非常有用,所有该类型的方法都是定义在它的内置构造函数的prototype上的,我们可以给这个对象添加新的方法,从而拓展某类型的功能
3.6.1 内置构造函数

nstanceof 运算符用于检测构造函数的 prototype 属性是否出现在某个实例对象的原型链上。(MDN)

instanceof 是另一种检测类型的手段,但通常用于检测对象之间的关系,如某个对象是不是由某个构造函数生成的。

 // 数组的内置构造函数,任何的数组字面量都可以看做是Array的实例
console.log([1, 2] instanceof Array); 	// true
console.log([] instanceof Array); 	// true

var arr = new Array(5); // 创建一个长度为5的空数组
console.log(arr); 	// (5)[empty x 5] 
console.log(arr.length); 	// 5

// 函数的内置构造函数,任何的函数字面量都可以看做是Function的实例
function fun() { }
function add(a, b) {    return a + b;}
console.log(fun instanceof Function); 	// true
console.log(add instanceof Function); 	// true

var jianfa = new Function('a', 'b', 'return a - b');
console.log(jianfa(8, 3)); 	// 5

// 对象的内置构造函数
console.log({a: 1} instanceof Object); 	// true
console.log({} instanceof Object); 	// true

var o = new Object();
o.a = 2;
o.b = 6;
console.log(o);
3.6.2 扩展数组的方法
 	// 检测Array是不是自身拥有以下几个方法
console.log(Array.prototype.hasOwnProperty('push')); 	// true
console.log(Array.prototype.hasOwnProperty('pop')); 	// true
console.log(Array.prototype.hasOwnProperty('splice')); 	// true

 // 拓展qiuhe方法
 Array.prototype.qiuhe = function () {
     // this表示调用qiuhe()方法的数组
     var arr = this;
     // 累加器
     var sum = 0;
     for (var i = 0; i < arr.length; i++) {sum += arr[i];} 
     // 返回结果
	return sum;
 };

var arr = [3, 6, 2, 1, 3];
var result = arr.qiuhe();
console.log(result);

var arr2 = [3, 8];
var result2 = arr2.qiuhe();
console.log(result2);
3.6.3 基本类型的包装类
  • Number、String、Boolean是三个基本类型的包装类,用new调用他们可以生成"对象"版本的基本类型值
// console.log(3);	//3
// console.log(typeof 3);	//number

var o = new Number(3);
console.log(o);		//Number
console.log(typeof o);	//object
console.log(5 + o);     // 8

var s = new String('abc');
console.log(s);	//String
console.log(typeof s);	//object
console.log(String.prototype.hasOwnProperty('slice'));	//true
console.log(String.prototype.hasOwnProperty('substring'));	//true
console.log(String.prototype.hasOwnProperty('substr'));	//true

var b = new Boolean(true);
console.log(b);	//Boolean
console.log(typeof b);	//object
3.6.4 Object.prototype是万物原型链的终点
  • object.prototype是万物原型链的重点。JavaScript中函数、数组都是对象,以数组为例,原型链是这样的:

    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-tINz6Xf7-1674716107574)(C:\Users\popla\AppData\Roaming\Typora\typora-user-images\image-20221112121803676.png)]

console.log([1, 2].__proto__ === Array.prototype);     // true
console.log([1, 2].__proto__.__proto__ === Object.prototype);     // true

console.log([] instanceof Object);     // true
console.log([] instanceof Array);     // true

console.log(Object.prototype.__proto__);     // null
console.log(null.__proto__);     // 会报错
3.6.5 内置构造函数的关系

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-190GAkUE-1674716107576)(C:\Users\popla\AppData\Roaming\Typora\typora-user-images\image-20221112122546023.png)]

console.log(Object.__proto__ === Function.prototype);   // true
console.log(Object.__proto__.__proto__ === Object.prototype);   // true
console.log(Function.__proto__ === Function.prototype);   // true

console.log(Function instanceof Object);        // true
console.log(Object instanceof Function);        // true
console.log(Function instanceof Function);      // true
console.log(Object instanceof Object);          // true

3.7 继承的实现

3.7.1 通过原型链实现继承

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-GydwjOtO-1674716107580)(C:\Users\popla\AppData\Roaming\Typora\typora-user-images\image-20221112133159106.png)]

  • 让子类构造函数 prototype,指向父类的一个实例
// ##通过原型链实现继承.html

// 父类:People类
function People(name, age, sex) {
    // this.arr = [33, 44, 55];
    this.name = name;
    this.age = age;
    this.sex = sex;}

People.prototype.sayHello = function () {console.log('你好,我是' + this.name + '我今年' + this.age + '岁了');};
People.prototype.sleep = function () {console.log(this.name + '正在睡觉');};

// 子类:Student类
function Student(name, age, sex, school, sid) {
    this.name = name;
    this.age = age;
    this.sex = sex;
    this.school = school;
    this.sid = sid;}

// 实现继承的非常重要的语句。让子类的prototype指向父类的一个实例。
Student.prototype = new People();
// 追加方法
Student.prototype.exam = function () {
    console.log(this.name + '正在考试');
};
Student.prototype.study = function () {
    console.log(this.name + '正在学习');
};
// 子类可以更改父类的方法,术语叫做override“改写”、“重写”
Student.prototype.sayHello = function () {
    console.log('敬礼!您好,我是' + this.name + ',我是' + this.school + '的学生,我' + this.age + '岁了');
};

var xiaoming = new Student('小明', 12, '男', '小慕学校', 100666);
xiaoming.exam();
xiaoming.study();
xiaoming.sayHello();
xiaoming.sleep();
// xiaoming.arr.push(123);
// console.log(xiaoming.arr);
var xiaohong = new Student('小红', 11, '女', '小慕学校', 100667);
xiaohong.exam();
xiaohong.study();
xiaohong.sayHello();
xiaohong.sleep();
// console.log(xiaohong.arr);
var laowang = new People('老王', 56, '男');
laowang.sayHello();
3.7.2 通过构造函数实现继承
  • 为了解决原型中包含引用类型所带来的问题和子类构造函数不优雅的问题,可以使用借助构造函数的技术,也被称为“伪造对象”或“经典继承”
  • 借助构造函数:在子类构造函数的内部调用超类的构造函数,但是要注意使用cal()绑定上下文
function People(name, sex, age) {
    this.name = name;
    this.sex = sex;
    this.age = age;
    this.arr = [33, 44, 55];
}

function Student(name, sex, age, school, sid) {
    // 借用构造函数
    People.call(this, name, sex, age);	// 该语句可以调用 People的属性和方法
    this.school = school;
    this.sid = sid;}

var xiaoming = new Student('小明', '男', 12, '小慕学校', 100666);
console.log(xiaoming);
xiaoming.arr.push(77);
console.log(xiaoming.arr);
console.log(xiaoming.hasOwnProperty('arr'));

var xiaohong = new Student('小红', '女', 11, '小慕学校', 100667);
console.log(xiaohong.arr);
console.log(xiaohong.hasOwnProperty('arr'));
3.7.3 组合继承
  • 借用原型链和借用构造函数的技术组合到一起,叫做组合继承,也叫做伪经典继承
  • 组合继承是JavaScript中最常用的继承模式
  • 组合继承的问题:会调用两次超类的构造函数分别是创建子类原型时,子类构造函数内部

创建子类原型时调用第一次:Student.prototype = new People();

子类构造函数内部调用第二次:People.call(this, name, sex, age);

// 父类
function People(name, sex, age) {
    this.name = name;
    this.sex = sex;
    this.age = age;
}
People.prototype.sayHello = function () {
    console.log('你好,我是' + this.name + '今年' + this.age + '岁了');
}
People.prototype.sleep = function () {
    console.log(this.name + '正在睡觉');
}
// 子类
function Student(name, sex, age, school, sid) {
    // 借助构造函数
    People.call(this, name, sex, age);
    this.school = school;
    this.sid = sid;
}
// 实现继承,借助原型链
Student.prototype = new People();

// 添加exam方法,覆盖sayhello方法
Student.prototype.exam = function() {console.log(this.name + '正在考试');};
Student.prototype.sayHello = function() {
    console.log('敬礼!你好,我是' + this.name + '今年' + this.age + '岁了,我是' + this.school + '学校的学生');
};

var xiaoming = new Student('小明', '男', 12, '小慕学校', 100666);
xiaoming.sayHello();
xiaoming.sleep();
xiaoming.exam();

3.6.9 原型式继承

  • IE9+开始支持Object.create()可以根据指定的对象为原型创建出新对象

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-hXIzcLfQ-1674716107584)(C:\Users\popla\AppData\Roaming\Typora\typora-user-images\image-20221113164032673.png)]

  • 在没必要创建构造函数,而只是想让新对象与现有对象“类似”的情况下,使用Object.create()即可胜任
var obj1 = {
    a: 33,
    b: 45,
    c: 12,
    test: function() {
        console.log(this.a + this.b);
    }
};
var obj2 = Object.create(obj1, {
    d: {value: 99    }, 
    a: {value: 2    }
});
// 检查obj2的原型是否为obj1,结果为true
console.log(obj2.__proto__ === obj1);       // true
console.log(obj2.a);
console.log(obj2.b);
console.log(obj2.c);
console.log(obj2.d);
obj2.test();
3.7.4 Object.create()的兼容性写法
// 道格拉斯·克罗克福德写的一个函数,非常巧妙,面试常考
// 函数的功能就是以o为原型,创建新对象
function object(o) {
    // 创建一个临时构造函数
    function F() {}
    // 让这个临时构造函数的prototype指向o,这样一来它new出来的对象,__proto__指向了o
    F.prototype = o;
    // 返回F的实例
    return new F();
}
var obj1 = {
    a: 23,
    b: 5
};
var obj2 = object(obj1);
console.log(obj2.__proto__ === obj1);
console.log(obj2.a);
console.log(obj2.b);
3.7.5 寄生式继承
  • 编写一个函数,他接受一个参数o,返回以o为原型的新对象p,同时给p上添加预置的新方法

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-xR79HizT-1674716107588)(C:\Users\popla\AppData\Roaming\Typora\typora-user-images\image-20221113165451887.png)]

  • 寄生式继承就是编写一个函数,他可以“增强对象”,只要把对象传入这个函数,这个函数将以此对象为“基础”,创建出新的对象,并未新对象赋予新的预置方法。
  • 在主要考虑对象而不是自定义类型和构造函数的情况下,寄生式继承也是一种有用的模式
  • 使用寄生式继承来为对象添加函数,会由于不能做到函数复用而降低效率,即“方法没有写到prototype上”
var o1 = {
    name: '小明',
    age: 12,
    sex: '男'
};
var o2 = {
    name: '小红',
    age: 11,
    sex: '女'
};
// 寄生式继承
function f(o) {
    // 以o为原型创建出新对象
    var p = Object.create(o);
    // 补充方法
    p.sayHello = function () {
        console.log('你好,我是' + this.name + '今年' + this.age + '岁了');
    }
    p.sleep = function () {
        console.log(this.name + '正在睡觉');
    }
    return p;
}
var p1 = f(o1);
p1.sayHello();
var p2 = f(o2);
p2.sayHello();
console.log(p1.sayHello == p2.sayHello);
3.7.6寄生组合式继承
  • 寄生组合式继承:通过借用构造函数来继承属性,通过原型链的混成形式来继承方法
  • 背后思路:不必为了指定子类型的原型而调用超类型的构造函数,我们所需要的无非就是超类型原型的一个副本而已。本质上就是使用寄生式继承来继承超类型的原型,然后将结果指定给子类型的原型。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-ePOLVcOs-1674716107593)(C:\Users\popla\AppData\Roaming\Typora\typora-user-images\image-20221113171347133.png)]

// 这个函数接收两个参数,subType是子类的构造函数,superType是父类的构造函数
function inheritPrototype(subType, superType) {
    var prototype = Object.create(superType.prototype);
    // 通过父类的原型创建子类的原型
    subType.prototype = prototype;
}
// 父类
function People(name, sex, age) {
    this.name = name;
    this.sex = sex;
    this.age = age;
}
People.prototype.sayHello = function () {
    console.log('你好,我是' + this.name + '今年' + this.age + '岁了');
}
People.prototype.sleep = function () {
    console.log(this.name + '正在睡觉');
}
// 子类
function Student(name, sex, age, school, sid) {
    // 借助构造函数
    People.call(this, name, sex, age);
    this.school = school;
    this.sid = sid;
}
// 调用我们自己编写的inheritPrototype函数,这个函数可以让Student类的prototype指向“以People.prototype为原型的一个新对象”
inheritPrototype(Student, People);
 
Student.prototype.exam = function() {
    console.log(this.name + '正在考试');
};
Student.prototype.sayHello = function() {
    console.log('敬礼!你好,我是' + this.name + '今年' + this.age + '岁了,我是' + this.school + '学校的学生');
};

var xiaoming = new Student('小明', '男', 12, '小慕学校', 100666);
xiaoming.sleep();
xiaoming.exam();
xiaoming.sayHello();

instanceof运算符

  • instanceof运算符用来检测“某对象是不是某个类的实例”,

    比如:xiaoming instanceof Student 检测xiaoming是不是Student的实例

  • 底层机理:检查Student.prototype属性是否在xiaoming的原型链上(多少层都行,只要在就行)

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Jy9Bi3PM-1674716107598)(C:\Users\popla\AppData\Roaming\Typora\typora-user-images\image-20221113172230693.png)]

function People() {        }
function Student() {        }
Student.prototype = new People();
var xiaoming = new Student();

// 测试instanceof
console.log(xiaoming instanceof Student);
console.log(xiaoming instanceof People);

4 正则表达式

4.1 正则表达式的基本使用

  • 用一个例子快速演示正则表达式的基本用法:检查某个字符串是否是六位数
// 字符串
var str='123456';
//正则表达式
var regexp=/^\d{6}$/;// 开头:^、结尾:$、\d{6}:/d表示数字
//检查
if (regexp.test(str)){//正则可以调用test方法检查某个字符串是否达到规定的规则
    alert('符合规则')
}else{
    alert('不符合规则')}

  • 正则表达式“按位”描述规则,是指它是一位一位的描述字符串的构成形式

    如:以字母m开头,然后是三位数字,最后n结尾 /^m\d\d\dn$/

    快速感知正则表达式.html

    // 定义正则表达式
    // 这个正则表达式表示一个形式:以m开头,n结尾,量词:中间是{6}位数字
    var regexp = /^m\d{6}n$/;
    var str1 = 'm123456n';
    var str2 = 'm1234567n';
    var str3 = 'm123b56n';
    var str4 = '123456';
    console.log(regexp.test(str1));     // true
    console.log(regexp.test(str2));     // false
    console.log(regexp.test(str3));     // false
    console.log(regexp.test(str4));     // false
    

4.2 创建正则表达式

  • 使用 / 内容 / 的语法形式,可以快速创建正则表达式
  • 也可以使用**new RegExp(‘内容’)**的形式,创建正则表达式
  • 使用typrof运算符检查正则表达式的类型,结果为object
// 创建正则表达式方法1
var regexp1 = /^\d{6}$/;
// 创建正则表达式方法2
var regexp2 = new RegExp('^\\d{6}$');new RegExp()中\存在转义,所以需要写成\\
var str = '555666';
console.log(regexp1.test(str));     // true
console.log(regexp2.test(str));     // true
console.log(typeof regexp1);        // object
console.log(typeof regexp2);        // object
console.log( /^\d{6}$/== /^\d{6}$/);        // false

4.3 元字符

  • 元字符是指一位指定类型的字符

    元字符功能
    \d匹配一个数字
    \D匹配一个非数字字符
    \w匹配一个单字字符(字母、数字、下划线)
    \W匹配一个非单字字符
    \s匹配一个空白字符,包括空格、制表符、换行符
    .任意字符
    ^匹配开头
    $匹配结尾

    元字符注意事项:

    使用new RegExp(‘内容’)的写法,反斜杠需要多写一个即双反斜杠

    如使用**new RegExp('^\dKaTeX parse error: Got function '\\' with no arguments as superscript at position 10: ')**和**/^\̲\̲d/**是相同意思

// 题目1:	某快递公司运单号形式是这样的:123-4567-890,请使用正则表达式检查某字符串是否符合此格式
// 正则表达式
var regexp1 = /^\d\d\d-\d\d\d\d-\d\d\d$/;
// 待测试的字符串
var str1 = '444-5555-000';
console.log(regexp1.test(str1));     // true

// 题目2:	某产品的验证秘钥形式是这样的:□□□-□□□□-□□□,其中□表示字母数字或者下划线,请使用正则表达式检查某字符串是否符合此格式
// 正则表达式
var regexp2 = /^\w\w\w-\w\w\w\w-\w\w\w$/;
// 待测试的字符串
var str2 = 'abc-__ab-123';
console.log(regexp2.test(str2));     // true

4.4 字符的转义

  • 在特殊字符之前的反斜杠\表示下一个字符不是特殊字符,应该按照字面量理解

    示例注解
    /^.$/. 是特殊字符(元字符),在这里是检查字符串是不是任意字符
    /^\.$/. 前加上反斜杠表示转义,在这里是检查字符串是不是一个点
    /^\\$/在这里是检查字符是不是反斜杠
  • 不管一个符号有没有特殊意义,都可以加上一个反斜杠以确保他表达的是这个符号本身

    举例:

    // 某产品批号形式为:123.45^67#89,请使用正则表达式检查某字符串是否符合此格式
    var regexp3 = /^\d\d\d\.\d\d\^\d\d\#\d\d$/;
    // 待测试的字符串
    var str3 = '666.66^66#66';
    console.log(regexp3.test(str3));     // true
    

4.5 方括号表示法

  • 使用方括号,如[xuz],可以创建一个字符集合,表示匹配方括号中的任意字符
// 某学校的学号规定:第1位是一个字母,b表示本科生,y表示研究生,后面是7位数字,用正则表示为:
// 学号字符串
var str1 = 'm4444555';
var str2 = 'b4444555';
// 用正则表达式进行检查
console.log(/^[by]\d{7}$/.test(str1));     // false
console.log(/^[by]\d{7}$/.test(str2));     // true

  • 可以使用短横-来指定一个字符范围,^表示否定

    元字符等价的方括号表示法含义
    \d[0-9]匹配0-9的数字
    \D[^0-9]匹配非0-9的数字内容
    \w[A-Za-z0-9_]匹配大小写字母和0-9数字
    \W[^A-Za-z0-9_]匹配非字母和数字的内容
// 题目1:请验证某字符串是否是5位字母,大小写均可
var str2 = 'abcde';
var str3 = 'abcd5';
console.log(/^[a-zA-Z]{5}$/.test(str2));	 // true	\W不满足大小写字符匹配,只能使用方括号表示法
console.log(/^[a-zA-Z]{5}$/.test(str3));	//false
// 题目2:请验证某字符串是否是5位,且仅由小写字母、点构成
var str4 = 'mnp..';
var str5 = 'mnp.#';
console.log(/^[a-z\.]{5}$/.test(str4)); 	// true
console.log(/^[a-z\.]{5}$/.test(str5));		//false
// 题目3:请验证某字符串是否是4位小写字母,且最后一位不能是m字母
var str6 = 'abcd';
var str7 = 'abcm';
var str8 = 'ab1c';
console.log(/^[a-z]{3}[a-ln-z]$/.test(str6)); 	// true
console.log(/^[a-z]{3}[a-ln-z]$/.test(str7));	//false
console.log(/^[a-z]{3}[a-ln-z]$/.test(str8));	//false

4.6 量词

量词含义
*匹配前一个表达式0次或多次,等价于{0,}
+匹配前一个表达式1次或多次,等价于{1,}
匹配前一个表达式0次或1次,等价于{0,1}
{n}n是一个正整数,匹配前一个字符刚好出现了n次
{n,}n是一个正整数,匹配前一个字符至少出现了n次
{n,m}n和m都是正整数,匹配前面的字符至少n次,最多m次
// 题目1:请验证字符串是否符合手机号码的规则:11位数字,并且肯定以1开头
var str1 = '13812345678';
var str2 = '138123456789';
var str3 = '38123456789';
var regexp1 = /^1\d{10}$/;
console.log(regexp1.test(str1));
console.log(regexp1.test(str2));
console.log(regexp1.test(str3));

// 题目2:请验证某字符串是否是这样的:以字母开头,中间是任意位数字(最少1位)构成,并以字母结尾
var str4 = 'a123123123b';
var str5 = 'abcd';
var str6 = 'a1b';
var regexp2 = /^[a-zA-Z]\d+[a-zA-Z]$/;
console.log(regexp2.test(str4));
console.log(regexp2.test(str5));
console.log(regexp2.test(str6));

// 题目3:请验证某字符串是否符合网址规则:以www.开头,中间是任意位的字符(字母数字下划线,最少一位),最后以.com结尾,也可以以.com.cn结尾
var str7 = 'www.imooc.com';
var str8 = 'www.sina.com.cn';
var str9 = 'www.news.cn';
var regexp3 = /^www\.\w+\.com(\.cn)?$/;
console.log(regexp3.test(str7));
console.log(regexp3.test(str8));
console.log(regexp3.test(str9));

4.7 修饰符

  • 修饰符也叫标志(flags),用于使用正则表达式实现高级索索
修饰符含义
i不区分大小写搜索
g全局搜索
// 修饰符的使用:
// 方法1
var re=/m/gi;
//方法2
var re=new RegExp('m','gi');

4.8 正则表达式和字符串的相关方法

4.8.1 正则表达式的相关方法
方法简介
test()检测某个字符串是否匹配此正则表达式,返回布尔值
exec()根据正则表达式,在字符串中进行查找,返回结果数组或者null
  1. test()方法

正则表达式的test()方法:用正则表达式进行表单验证.html

/^www\.\w+\.com(\.cn)?$/.test('www.imooc.com)		// true
  1. exec()方法

exec()方法:在指定的字符串中搜索匹配查找,返回一个结果数组或null

var str = 'abc123def456ghi789';
var regexp = /\d+/;    
var result1 = regexp.exec(str);		// ["123",index:3,input:"abc123def456ghi789",groups:undefined]

exec()方法是有“g”修饰符的正则表达式将自动成为“有状态”的,这意味着可以对单个字符串中的多个匹配结果进行逐条的遍历

//exec()方法.html
var str = 'abc123def456ghi789';
var regexp = /\d+/g;    // +表示贪婪的,尽可能多的匹配
var result1 = regexp.exec(str);
var result2 = regexp.exec(str);
var result3 = regexp.exec(str);
var result4 = regexp.exec(str);
console.log(result1);
console.log(result2);
console.log(result3);
console.log(result4);
// 使用循环语句来循环执行exec,寻找所有的匹配结果
var result;
while (result = regexp.exec(str)) {
    console.log(result);
}
4.8.2 字符串的相关方法
方法简介
search()在字符串中根据正则表达式进行查找匹配,返回首次匹配到的位置索引,检测不到则返回-1
match()在字符串中根据正则表达式进行查找匹配,返回一个数组,找不到则返回null
replace()使用替换字符串替换掉匹配到的子字符串,可以使用正则表达式
split()分割字符串为数组,可以使用正则表达式
var str = 'abc123def4567ghi89';
     
// search()方法,很像indexOf(),返回查找到的第一个下标,如果找不到就是-1
var result1 = str.search(/\d+/g);
var result2 = str.search(/m/g);
console.log(result1);       // 3
console.log(result2);       // -1
// match()方法,返回查找到的数组,找不到就是null
var result3 = str.match(/\d+/g);
console.log(result3);       // ["123", "4567", "89"]
// replace()方法,进行替换
var result4 = str.replace(/[a-z]+/g, '*');      // 注意+表示贪婪的,尽可能多的连续匹配小写字母
var result41 = str.replace(/[a-z]/g, '*'); 
console.log(result4);       // *123*4567*89
console.log(result41);       // ***123***4567***89
// split()方法,进行字符串拆为数组
var result5 = str.split(/\d+/g);
console.log(result5);       // ["abc", "def", "ghi", ""]

4.9 正则表达式的应用

用正则表达式进行表单验证.html

<style>
        .warning {color: red;display: none;}
    </style>
<body>
    <div>
        <p>
            请输入中文姓名:
            <input type="text" id="nameField">
            <span class="warning" id="nameWarning">输入的姓名不合法</span>
        </p>
        <p>
            请输入手机号码:
            <input type="text" id="telField">
            <span class="warning" id="telWarning">输入的手机号码不合法</span>
        </p>
        <p>
            请输入Email:
            <input type="text" id="emailField">
            <span class="warning" id="emailWarning">输入的Email不合法</span>
        </p>
    </div>
    <script>
        var nameField = document.getElementById('nameField');
        var telField = document.getElementById('telField');
        var emailField = document.getElementById('emailField');
        var nameWarning = document.getElementById('nameWarning');
        var telWarning = document.getElementById('telWarning');
        var emailWarning = document.getElementById('emailWarning');

        // 当文本框失去焦点,就是光标不在文本框中了
        nameField.onblur = function () {
            // 得到姓名
            var name = nameField.value;
            if (/^[\u4E00-\u9FA5]{2,4}$/.test(name)) {
                // 如果通过校验
                nameWarning.style.display = 'none';
            } else {
                // 如果没有通过校验
                nameWarning.style.display = 'inline';
            }
        };

        telField.onblur = function () {
            // 得到电话
            var tel = telField.value;
            if (/^1\d{10}$/.test(tel)) {
                // 如果通过校验
                telWarning.style.display = 'none';
            } else {
                // 如果没有通过校验
                telWarning.style.display = 'inline';
            }
        };

        emailField.onblur = function () {
            // 得到email
            var email = emailField.value;
            // 合法的email都是abc_def123@abc.net

            if (/^\w{2,}\@\w{2,}\.[a-z]{2,4}(\.[a-z]{2,4})?$/.test(email)) {
                // 如果通过校验
                emailWarning.style.display = 'none';
            } else {
                // 如果没有通过校验
                emailWarning.style.display = 'inline';
            }
        };
    </script>
</body>

Ⅰ、蔡勒公式计算指定日期是星期几

y:是年份后两位数,如:1949取49

c:是年份前两位数,如1949取19

m:是月份

d:是日期

公式:W=Math.trunc([y+y/4+c/4-2c+13(m+1)/5+d-1]%7)

<body>    
    请输入日期:<input type="text"  id="input">
    <button onclick="hanshu()">计算星期几</button>
    <div id="div" class="carousel"></div>

<script>
    
    function hanshu(){
    var div= document.getElementById('div');
    var oinput= document.getElementById('input').value;
    var oi=oinput.split('.')
    var oi=oi.map(Number)
    var c=Math.trunc(oi[0]/100)
    var y=oi[0]-c*100
    var w=y+y/4+c/4-2*c+13*(oi[1]+1)/5+oi[2]-1;
    div.innerText= oinput+'是星期:'+Math.trunc(w%7)
    }
   </script>

分割字符串为数组,可以使用正则表达式 |

var str = 'abc123def4567ghi89';
     
// search()方法,很像indexOf(),返回查找到的第一个下标,如果找不到就是-1
var result1 = str.search(/\d+/g);
var result2 = str.search(/m/g);
console.log(result1);       // 3
console.log(result2);       // -1
// match()方法,返回查找到的数组,找不到就是null
var result3 = str.match(/\d+/g);
console.log(result3);       // ["123", "4567", "89"]
// replace()方法,进行替换
var result4 = str.replace(/[a-z]+/g, '*');      // 注意+表示贪婪的,尽可能多的连续匹配小写字母
var result41 = str.replace(/[a-z]/g, '*'); 
console.log(result4);       // *123*4567*89
console.log(result41);       // ***123***4567***89
// split()方法,进行字符串拆为数组
var result5 = str.split(/\d+/g);
console.log(result5);       // ["abc", "def", "ghi", ""]

4.9 正则表达式的应用

用正则表达式进行表单验证.html

<style>
        .warning {color: red;display: none;}
    </style>
<body>
    <div>
        <p>
            请输入中文姓名:
            <input type="text" id="nameField">
            <span class="warning" id="nameWarning">输入的姓名不合法</span>
        </p>
        <p>
            请输入手机号码:
            <input type="text" id="telField">
            <span class="warning" id="telWarning">输入的手机号码不合法</span>
        </p>
        <p>
            请输入Email:
            <input type="text" id="emailField">
            <span class="warning" id="emailWarning">输入的Email不合法</span>
        </p>
    </div>
    <script>
        var nameField = document.getElementById('nameField');
        var telField = document.getElementById('telField');
        var emailField = document.getElementById('emailField');
        var nameWarning = document.getElementById('nameWarning');
        var telWarning = document.getElementById('telWarning');
        var emailWarning = document.getElementById('emailWarning');

        // 当文本框失去焦点,就是光标不在文本框中了
        nameField.onblur = function () {
            // 得到姓名
            var name = nameField.value;
            if (/^[\u4E00-\u9FA5]{2,4}$/.test(name)) {
                // 如果通过校验
                nameWarning.style.display = 'none';
            } else {
                // 如果没有通过校验
                nameWarning.style.display = 'inline';
            }
        };

        telField.onblur = function () {
            // 得到电话
            var tel = telField.value;
            if (/^1\d{10}$/.test(tel)) {
                // 如果通过校验
                telWarning.style.display = 'none';
            } else {
                // 如果没有通过校验
                telWarning.style.display = 'inline';
            }
        };

        emailField.onblur = function () {
            // 得到email
            var email = emailField.value;
            // 合法的email都是abc_def123@abc.net

            if (/^\w{2,}\@\w{2,}\.[a-z]{2,4}(\.[a-z]{2,4})?$/.test(email)) {
                // 如果通过校验
                emailWarning.style.display = 'none';
            } else {
                // 如果没有通过校验
                emailWarning.style.display = 'inline';
            }
        };
    </script>
</body>

Ⅰ、蔡勒公式计算指定日期是星期几

y:是年份后两位数,如:1949取49

c:是年份前两位数,如1949取19

m:是月份

d:是日期

公式:W=Math.trunc([y+y/4+c/4-2c+13(m+1)/5+d-1]%7)

<body>    
    请输入日期:<input type="text"  id="input">
    <button onclick="hanshu()">计算星期几</button>
    <div id="div" class="carousel"></div>

<script>
    
    function hanshu(){
    var div= document.getElementById('div');
    var oinput= document.getElementById('input').value;
    var oi=oinput.split('.')
    var oi=oi.map(Number)
    var c=Math.trunc(oi[0]/100)
    var y=oi[0]-c*100
    var w=y+y/4+c/4-2*c+13*(oi[1]+1)/5+oi[2]-1;
    div.innerText= oinput+'是星期:'+Math.trunc(w%7)
    }
   </script>

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值