JavaScript进阶面向对象ES6整合篇

整合篇整体内容比较多,详细点可以查询目录


一、JavaScript面向对象

在这里插入图片描述

1、面向对象编程介绍

1.1 两大编程思想
分为面向过程面向对象
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

两种编程思想是根据不同的程序需要来选择的,比较简单的还是推荐使用面向对象来写,因为实现的步骤要更加简单一些
如果项目比较大需要多人合作的,则选择面向过程,因为便于维护和复用

2、ES6中的类和对象

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

在这里插入图片描述

<script>
        // 1. 创建类 class  创建一个 明星类
        class Star {
            constructor(uname) {
                this.uname = uname
            }
        }

        // 2. 利用类创建对象 new
        var ldh = new Star ('刘德华')
        console.log(ldh.uname)
    </script>
 <script>
        // 1. 创建类 class  创建一个 明星类
        class Star {
            constructor(uname) {
                this.uname = uname
            }
        }

        // 2. 利用类创建对象 new
        var ldh = new Star ('刘德华')
        var zxy = new Star ('张学友')
        console.log(ldh.uname)
        console.log(zxy.uname)
    </script>
<script>
        // 1. 创建类 class  创建一个 明星类
        class Star {
            constructor(uname,age) {
                this.uname = uname
                this.age = age
            }
        }

        // 2. 利用类创建对象 new
        var ldh = new Star ('刘德华',18)
        var zxy = new Star ('张学友',20)
        console.log(ldh) // 输出对象的所有属性
        console.log(zxy)
    </script>

在这里插入图片描述
在这里插入图片描述
1.我们类里面所有的函数不需要写function
2.多个函数方法之间不需要添加逗号分隔

<script>
        // 1. 创建类 class  创建一个 明星类
        class Star {
            constructor(uname,age) {
                this.uname = uname
                this.age = age
            }
            sing(song){
                // console.log('我唱歌')
                console.log(this.uname + song)
            }
        }

        // 2. 利用类创建对象 new
        var ldh = new Star ('刘德华',18)
        var zxy = new Star ('张学友',20)
        console.log(ldh)
        console.log(zxy)
        // 1.我们类里面所有的函数不需要写function
        // 2.多个函数方法之间不需要添加逗号分隔
        ldh.sing('冰雨')
        zxy.sing('李香兰')
    </script>

3、类的继承

在这里插入图片描述

<script>
        // 1.类的继承
        class Father {
            constructor(){

            }
            money(){
                console.log(100)
            }
        }
        class Son extends Father {

        }
        var son = new Son()
        son.money()
    </script>

在这里插入图片描述
在这里插入图片描述

<script>
        
        class Father {
            constructor(x,y){
                this.x = x
                this.y = y
            }
            sum(){
                console.log(this.x + this.y)
            }
        }
        class Son extends Father {
            constructor(x,y){
                super(x,y) // 调用父类中的构造函数
            }
        }
        var son = new Son(1,2)
        var son1= new Son(11,22)
        son.sum()
        son1.sum()
    </script>

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

<script>
        

        // super 关键字调用父级普通函数
        class Father {
            say(){
                return '我是父级'
            }
        }
        class Son extends Father {
            say() {
                // console.log('我是子级')
                console.log(super.say())
                // super.say() 就是调用父级中的普通函数 say()
            }
        }
        var son = new Son()
        son.say()
        // 继承中的属性或者方法查找原则:就近原则
        // 1.继承中,如果实例化子类输出一个方法,先看子类有没有这个方法,如果有就先执行子类的
        // 2.继承中,如果子类里面没有,就去查找父类有没有这个方法,如果有,就执行父类的这个方法(就近原则)

    </script>

利用super 调用父类的构造函数,super必须在子类this之前调用

在这里插入图片描述
在这里插入图片描述

<body>
    <button>点击</button>
    <script>
        class Star {
            constructor(uname,age) {
                this.uname = uname
                this.age = age
                // 2.类里面的共有的属性和方法一定要加this使用.
                // this.sing()
                this.btn = document.querySelector('button')
                // 应该是this.sing而不是this.sing(),加了括号那么打开网页的瞬间会直接调用
                this.btn.onclick = this.sing
            }
            sing() {
            // 不能直接log我们的uname,因为uname是构造函数里的,需要使用this进行指定
                console.log(this.uname)
            }
        }
         // 1.在 ES6中类没有变量提升,所以必须先定义类,才能通过类实例化对象
        var ldh = new Star('刘德华')

    </script>

在这里插入图片描述

在这里插入图片描述

4、案例:面向对象案例

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
该方法网址:https://developer.mozilla.org/zh-CN/docs/Web/API/Element/insertAdjacentHTML
在这里插入图片描述
在这里插入图片描述

在这里插入图片描述

重点注意
重新获取全部元素的方法在这里插入图片描述

在这里插入图片描述

效果图:
在这里插入图片描述

Html文件,引入外部js文件
html代码和js代码如下

<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <meta http-equiv="X-UA-Compatible" content="ie=edge">
    <title>面向对象 Tab</title>
    <link rel="stylesheet" href="./styles/style.css">
    <link rel="stylesheet" href="./styles/tab.css">
</head>

<body>

    <main>
        <h4>
            Js 面向对象 动态添加标签页
        </h4>
        <div class="tabsbox" id="tab">
            <!-- tab 标签 -->
            <nav class="firstnav">
                <ul>
                    <li class="liactive"><span>测试1</span><span class="iconfont icon-guanbi"></span></li>
                    <li><span>测试2</span><span class="iconfont icon-guanbi"></span></li>
                    <li><span>测试3</span><span class="iconfont icon-guanbi"></span></li>
                </ul>
                <div class="tabadd">
                    <span>+</span>
                </div>
            </nav>

            <!-- tab 内容 -->
            <div class="tabscon">
                <section class="conactive">测试1</section>
                <section>测试2</section>
                <section>测试3</section>
            </div>
        </div>
    </main>

    <script src="../js/tab.js" ></script>
</body>

</html>
// 设置一个全局变量,方便调用
var that
class Tab {
    constructor(id){
        // 把我们constructor中的this赋值给全局变量that
        that = this

        // 获取元素 
        // 实例化一个最大的对象
        this.main = document.querySelector(id)
        
        this.add = this.main.querySelector('.tabadd')
        
        // li的父元素
        this.ul = this.main.querySelector('.firstnav ul:first-child')
        // section的父元素
        this.fsection = this.main.querySelector('.tabscon')
        this.init()
    }
    init(){
        this.updateNode()
        // init 初始化操作让相关的元素绑定事件
        // add不需要循环 所以不放在for里面
        this.add.onclick = this.addTab
        for(var i = 0; i < this.lis.length; i++){
            this.lis[i].index = i
            this.lis[i].onclick = this.toggleTab
            this.remove[i].onclick = this.removeTab
            this.spans[i].ondblclick = this.editTab
            this.sections[i].ondblclick = this.editTab

        }
    }
    // 因为动态添加元素 需要重新获取
    updateNode() {
        this.lis = this.main.querySelectorAll('li')
        this.sections = this.main.querySelectorAll('section')
        this.remove = this.main.querySelectorAll('.icon-guanbi')
        this.spans = this.main.querySelectorAll('.firstnav li span:first-child')
    }
    // 1.切换功能
    toggleTab(){
        // this指的是init中对接的这个小li
        // console.log(this.index)
        // this.index即代表小li的索引号
        // 先调用清空函数,注意不能直接使用,要用that调用
        that.clearClass()
        this.className = 'liactive'
        that.sections[this.index].className = 'conactive'
    }
    // tab点击之后,使用 '' 来清空其他的样式
    clearClass(){
        for(var i = 0; i < this.lis.length; i++){
            this.lis[i].className = ''
            this.sections[i].className = ''
            this.remove[i].onclick = this.removeTab
        }
    }
    // 2.添加功能
    addTab(){
        that.clearClass()
        var random = Math.random()
        // (1)创建li元素和section元素
        var li = '<li class="liactive"><span>新选项卡</span><span class="iconfont icon-guanbi"></span></li>'
        var section = '<section class="conactive">新选项卡内容'+ random +'</section>'
        // (2)把这两个元素追加到对应的父元素里面
        that.ul.insertAdjacentHTML('beforeend', li)
        that.fsection.insertAdjacentHTML('beforeend', section)
        // 点击加号之后添加的小li和section属于后来的,init方法没法获取
        // 点击加号之后再调用一次init 重新拿一次小li,给他们绑定事件获取最新的元素
        that.init()
        
        // that.className = 'liactive'
        // that.sections[this.index].className = 'conactive'
    }
    // 3.删除功能
    removeTab(e){
        e.stopPropagation() //添加阻止冒泡,这样就不会触发点击事件
        // 把父元素的索引号赋给它
        var index = this.parentNode.index
        console.log(index);
        // 根据索引号删除对应的li 和section 
        // remove方法可以直接删除指定的元素
        that.lis[index].remove()
        that.sections[index].remove()
        // 重新再调用init
        that.init()
        // 当我们删除的不是选中状态的li 的时候,原来的选中状态li保持不变
        if(document.querySelector('.liactive')) return // return 则不再执行下面的代码
        // 当我们删除了选中状态的这个li 的时候,让它的前一个li 处于选定状态
        // 思路可以是当我删除这个li时,触发一个点击事件,点击这个li前面的li
        index--
        // 手动调用我们的点击事件 不需要鼠标触发
        // 判断逻辑是that.lis[index]为真时 后面的点击事件才会触发
        that.lis[index] && that.lis[index].click()
    }
    // 4.修改功能
    editTab(){
        var str = this.innerHTML
        // 双击禁止选定文字
        window.getSelection ? window.getSelection().removeAllRanges() : document.selection.empty()

        // alert(11)
        // 双击生成输入框
        this.innerHTML = '<input type="text" />'
        var input = this.children[0]
        input.value = str
        input.select() //文本框里面的文字处于选定状态
        // 当光标离开了文本框就把文本框里面的值给span
        input.onblur = function(){
            this.parentNode.innerHTML = this.value 
        }
        // 按下回车也可以把文本框里面的值给span
        input.onkeyup = function(e) {
            // 按下回车键时keyCode为13
            if(e.keyCode === 13){
                this.blur()
            }
        }
    }
}
new Tab('#tab')


二、构造函数和原型

在这里插入图片描述

1. 构造函数和原型

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

 <script>
        // 1. 利用 new Object() 创建对象
        var obj1 = new Object()

        // 2. 利用 对象字面量创建对象
        var obj2 = {}

        // 3. 利用构造函数创建对象
        function Star(uname, age){
            this.uname = uname
            this.age = age
            this.sing = function(){
                console.log('我会唱歌')
            }
        }

        var ldh = new Star('刘德华',18)
        var zxy = new Star('张学友',19)
        console.log(ldh)
        console.log(zxy)
        ldh.sing()
        zxy.sing()
    </script>

在这里插入图片描述

<script>
        // 构造函数中的属性和方法我们称为成员,成员可以添加
        function Star(uname, age){
            this.uname = uname
            this.age = age
            this.sing = function(){
                console.log('我会唱歌')
            }
        }
        var ldh = new Star('刘德华',18)
        // 1.实例成员就是构造函数内部通过this添加的成员 uname age sing 就是实例成员
        // 实例成员只能通过实例化的对象来访问
        console.log(ldh.uname)
        ldh.sing()
        // console.log(Star.uname)  // 不可以通过构造函数来访问实例成员
        // 2. 静态成员 在构造函数本身上添加的成员 sex 就是静态成员
        Star.sex = '男'
        // 静态成员只能通过构造函数来访问
        console.log(Star.sex)
        console.log(ldh.sex)
    </script>

在这里插入图片描述
解决方案就是使用原型对象prototype:
在这里插入图片描述

在这里插入图片描述

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

在这里插入图片描述
在这里插入图片描述

在这里插入图片描述
查找规则:根据原型链一直往上找
在这里插入图片描述
在这里插入图片描述

在这里插入图片描述

可以给对象添加原来没有的功能,就是自定义一些自己想使用的功能

<script>
        // 原型对象的应用 扩展内置对象方法
        console.log(Array.prototype);
        Array.prototype.sum = function() {
            var sum = 0
            for(var i = 0; i < this.length; i++) {
                sum += this[i]
            }
            return sum
        }
        var arr = [1, 2, 3]
        console.log(arr.sum());
        // log输出的结果就是数组的和:6
    </script>

2. 继承

在这里插入图片描述
call()最主要的目的就是修改函数的this指向
在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述
在这里插入图片描述
注释:不能直接将父级的原型赋值给子级的原型,这样的话如果修改了子级原型对象父级原型对象也会跟着一起变化,修改思路是:创建一个父级实例对象作为中介,Son原型对象通过Father实例对象__proto__访问Father原型对象拿到money属性
在这里插入图片描述

ES6之前通过 构造函数 + 原型 实现面向对象编程
ES6 通过 类 实现面向对象编程

在这里插入图片描述
class本质上还是函数
语法糖:语法糖就是一种便捷写法,简单理解,有两种方法可以实现同样的功能,但是一种写法更加清晰、方便,那么这个方法就是语法糖。

3. ES5中的新增方法

3.1 ES5新增方法概述

在这里插入图片描述

3.2 数组方法

在这里插入图片描述
我们挑更加重要的方法进行讲解
以前都是使用for循环进行数组遍历,以后可以用forEach()

<script>
        // forEach 迭代(遍历)数组
        var arr = [1,2,3]
        var sum = 0
        arr.forEach(function(value, index, array){
            console.log('每个数组元素' + value)
            console.log('每个数组元素的索引号' + index)
            console.log('数组本身' + array)
            sum += value
        })
        console.log(sum)
    </script>

输出结果:
在这里插入图片描述

在这里插入图片描述
filter翻译过来就是“过滤器”的意思,主要用于筛选数组

<script>
        // filter 筛选数组
        var arr = [12, 88, 122, 201, 277]
        var newArr = arr.filter(function(value, index){
            // return value >= 20
            // 即筛选数组中能被2整除的数(偶数)
            return value % 2 === 0
        })
        console.log(newArr)
    </script>

输出结果:
在这里插入图片描述

在这里插入图片描述
注意:如果找到第一个满足条件的元素,则终止循环,不再继续查找了。(效率比较高)

<script>
        var arr = [10,30,4,40]
        var re = arr.some(function(value){
            return value >= 20
        })
        console.log(re)
        // if(re != 0) {
        //     alert(11)
        // }
        var arr1 = ['ts','ning','rookie','jk','bl']
        var re1 = arr1.some(function(value){
            return value == 'jk'  
        })
        console.log(re1)
    </script>

总结:
在这里插入图片描述

补充some方法的知识点:

some方法最后得到的是布尔值(true或false),如果返回布尔值是true则终止遍历,如果返回的是false则继续往下遍历,有一个使用技巧是可以在some里面添加if判断条件对想要的数据进行检测筛选。
思路(代码选自本文商品案例):
点击按钮时,函数对data数组里的数据进行检测,是否有某个数据的pname和我们输入的内容product.value相同,符合条件则把这段数据放进中介数组arr中,同时一定记得要return布尔值返给some(因为some需要得到布尔值)

searchTwo.addEventListener('click',function(){
            // alert(11)
            // 中介arr数组 存放符合条件的对象
            var arr = []
            data.some(function(value){
                if(value.pname === product.value) {
                    // console.log(value)
                    // 符合条件就把它放入到设置的数组arr中
                    arr.push(value)
                    return true  // return后面必须写true
                }
            })
            // 把数据渲染到页面中
            setDate(arr)

        })
案例:查询商品案例

在这里插入图片描述
在这里插入图片描述

案例代码如下:

<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <meta http-equiv="X-UA-Compatible" content="ie=edge">
    <title>Document</title>
    <style>
        table {
            width: 400px;
            border: 1px solid #000;
            border-collapse: collapse;
            margin: 0 auto;
        }
        
        td,
        th {
            border: 1px solid #000;
            text-align: center;
        }
        
        input {
            width: 50px;
        }
        
        .search {
            width: 600px;
            margin: 20px auto;
        }
    </style>
</head>

<body>
    <div class="search">
        按照价格查询: <input type="text" class="start"> - <input type="text" class="end"> <button class="search-price">搜索</button> 按照商品名称查询: <input type="text" class="product"> <button class="search-pro">查询</button>
    </div>
    <table>
        <thead>
            <tr>
                <th>id</th>
                <th>产品名称</th>
                <th>价格</th>
            </tr>
        </thead>
        <tbody>


        </tbody>
    </table>
    <script>
        // 利用新增数组方法操作数据
        var data = [{
            id: 1,
            pname: '小米',
            price: 3999
        }, {
            id: 2,
            pname: 'oppo',
            price: 999
        }, {
            id: 3,
            pname: '荣耀',
            price: 1299
        }, {
            id: 4,
            pname: '华为',
            price: 1999
        }, ];
        
        // 1. 获取相应的元素
        var tbody = document.querySelector('tbody')
        var searchOne = document.querySelector('.search-price')
        var start = document.querySelector('.start')
        var end = document.querySelector('.end')
        var searchTwo = document.querySelector('.search-pro')
        var product = document.querySelector('.product')
        setDate(data)
        // 2. 把数据渲染到页面中 需要一个形参(mydata)来帮助与实参对接
        function setDate(mydata) {
            // 先清空里面的数据
            tbody.innerHTML = ''
            mydata.forEach(function(value){
            // console.log(value)
            var tr = document.createElement('tr')
            tr.innerHTML = '<td>'+ value.id +'</td><td>'+ value.pname +'</td><td>'+ value.price +'</td>'
            tbody.appendChild(tr) 
        })
        }

        // 3. 根据价格查询商品
        // 当我们点击了按钮,就可以根据我们的商品价格去筛选数组里面的对象
        searchOne.addEventListener('click',function(){
            // alert(11)
            // 筛选符合输入价格内的商品 放入newPrice数组中
            var newPrice = data.filter(function(value){
                // console.log(value.price)
                return value.price >= start.value && value.price <= end.value
            })
            console.log(newPrice)
            // 把筛选之后的对象渲染到页面上
            setDate(newPrice)
        })
        // 4. 根据商品名称查找商品
        // 如果查询数组中唯一的元素,用some方法更合适,因为它找到这个元素,就不再进行循环,效率更高。
        searchTwo.addEventListener('click',function(){
            // alert(11)
            // 中介arr数组 存放符合条件的对象
            var arr = []
            data.some(function(value){
                if(value.pname === product.value) {
                    // console.log(value)
                    // 符合条件就把它放入到设置的数组arr中
                    arr.push(value)
                    return true  // return后面必须写true
                }
            })
            // 把数据渲染到页面中
            setDate(arr)

        })
    </script>
</body>

</html>

some和forEach的区别:

forEach():
即使找到了元素或者遇到return,也不会终止迭代
在这里插入图片描述
在这里插入图片描述
some():
从头开始找,一找到元素就会返回布尔值true,终止遍历,迭代效率更高
(如果查询数组中唯一的元素,用some方法更合适)

在这里插入图片描述

3.3 字符串方法

在这里插入图片描述

<body>
    <input type="text"> <button>点击</button>
    <div></div>
    <script>
        // trim方法 去除字符串两侧空格
        var str = '  andy  '
        console.log(str);
        var str1 = str.trim()
        console.log(str1);

        var input = document.querySelector('input')
        var btn = document.querySelector('button')
        var div = document.querySelector('div')
        btn.onclick = function() {
            // 避免在只输入几个空格时,被判断为有内容,使用trim方法对空格进行去除
            // 使用str表示input.value.trim(),简化代码
            var str = input.value.trim()
            if(str === '') {
                alert('您还没有输入内容')
            } else {
                console.log(str)
                console.log(str.length)

                div.innerHTML =  str
            }
        }
    </script>
</body>

效果如下:
在这里插入图片描述

3.4 对象方法

1. Object.keys()

在这里插入图片描述

<script>
        // 用于获取对象自身所有的属性
        var obj = {
            id: 1,
            pname: '小米',
            price: 1999,
            num: 2000
        }
        // 把获取的属性放到数组arr里面
        var arr = Object.keys(obj)
        console.log(arr)
        // 遍历数组可以用forEach,拿到每个值
        arr.forEach(function(value){
            console.log(value)
        })
    </script>

(Object.keys)代码结果如下:
在这里插入图片描述

2. Object.defineProperty()

以往要给对象添加新属性或者修改原有属性,我们都是用=赋值来实现(如下图)
在这里插入图片描述
现在我们可以使用新方法Object.defineProperty()
在使用时,它的三个属性都是必要的
在这里插入图片描述

在这里插入图片描述
在这里插入图片描述
writable:(true/false):用于一些很重要的数据,false使这个数据不能被修改。
enumerable: (true/false):设置数据是否可以被遍历,比如一些地址和个人信息,它在对象中正常存在,但是在遍历时我们想保密隐私,给它设置enumerable: false(默认就是false)
configurable:数据属性是否可以删除或再次修改


三、函数进阶

在这里插入图片描述

1. 函数的定义和调用

1.1 函数的定义方式

在这里插入图片描述
在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

1.2 函数的调用方式

在这里插入图片描述

<script>
        // 函数的调用方式

        // 1. 普通函数
        function fn() {
            console.log('人生');
        }
        // 它有两种调用方式
        fn()  // 或者是:
        fn.call()

        // 2. 对象的方法
        var o = {
            sayHi: function() {
                console.log('人生')
            }
        }
        o.sayHi()

        // 3. 构造函数
        function Star() {}
        new Star()

        // 4. 绑定事件函数
        btn.onclick = function() {} // 点击了按钮就可以调用函数

        // 5. 定时器函数
        setInterval(function() {}, 1000) // 这个函数是定时器自动1秒钟调用一次

        // 6. 立即执行函数  
        (function() {
            console.log('人生')
        })()
        // 立即执行函数是自动调用
    </script>

2. this

在这里插入图片描述
在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述
在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述
代码结果:
在这里插入图片描述

在这里插入图片描述
巧用bind(): 在bind括号里面添加this就可以对延迟函数的this进行修改,因为bind是在btn1的点击函数里面,所以延迟函数的this经过修改就指向onclick的this了,而onclick的this就是点击对象btn1
有了这个bind方法,就可以不用像以前要加var that = this来申明this对象
bind()的使用在后面是一个重点

bind应用面向对象tab栏:
使用bind()来替代原来的var that = this
思路解释:情况是在功能方法中有些元素还在使用this,所以不能一刀切直接用bind引入that,而是在绑定事件时,使用bind先绑定元素自己,然后跟上一个this参数,再在功能方法里写入(that)形参对接bind里面的this实参。
在这里插入图片描述

// var that;
class Tab {
    constructor(id) {
        // 获取元素
        // that = this;
        this.main = document.querySelector(id);
        this.add = this.main.querySelector('.tabadd');
        // li的父元素
        this.ul = this.main.querySelector('.fisrstnav ul:first-child');
        // section 父元素
        this.fsection = this.main.querySelector('.tabscon');
        this.init();
    }
    init() {
            this.updateNode();
            // init 初始化操作让相关的元素绑定事件
            this.add.onclick = this.addTab.bind(this.add, this);
            for (var i = 0; i < this.lis.length; i++) {
                this.lis[i].index = i;
                this.lis[i].onclick = this.toggleTab.bind(this.lis[i], this);
                this.remove[i].onclick = this.removeTab.bind(this.remove[i], this);
                this.spans[i].ondblclick = this.editTab;
                this.sections[i].ondblclick = this.editTab;

            }
        }
        // 因为我们动态添加元素 需要从新获取对应的元素
    updateNode() {
            this.lis = this.main.querySelectorAll('li');
            this.sections = this.main.querySelectorAll('section');
            this.remove = this.main.querySelectorAll('.icon-guanbi');
            this.spans = this.main.querySelectorAll('.fisrstnav li span:first-child');
        }
        // 1. 切换功能
    toggleTab(that) {
            // console.log(this.index);
            that.clearClass();
            this.className = 'liactive';
            that.sections[this.index].className = 'conactive';
        }
        // 清除所有li 和section 的类
    clearClass() {
            for (var i = 0; i < this.lis.length; i++) {
                this.lis[i].className = '';
                this.sections[i].className = '';
            }
        }
        // 2. 添加功能
    addTab(that) {
            that.clearClass();
            // (1) 创建li元素和section元素 
            var random = Math.random();
            var li = '<li class="liactive"><span>新选项卡</span><span class="iconfont icon-guanbi"></span></li>';
            var section = '<section class="conactive">测试 ' + random + '</section>';
            // (2) 把这两个元素追加到对应的父元素里面
            that.ul.insertAdjacentHTML('beforeend', li);
            that.fsection.insertAdjacentHTML('beforeend', section);
            that.init();
        }
        // 3. 删除功能
    removeTab(that, e) {
            e.stopPropagation(); // 阻止冒泡 防止触发li 的切换点击事件
            var index = this.parentNode.index;
            console.log(index);
            // 根据索引号删除对应的li 和section   remove()方法可以直接删除指定的元素
            that.lis[index].remove();
            that.sections[index].remove();
            that.init();
            // 当我们删除的不是选中状态的li 的时候,原来的选中状态li保持不变
            if (document.querySelector('.liactive')) return;
            // 当我们删除了选中状态的这个li 的时候, 让它的前一个li 处于选定状态
            index--;
            // 手动调用我们的点击事件  不需要鼠标触发
            that.lis[index] && that.lis[index].click();
        }
        // 4. 修改功能
    editTab() {
        var str = this.innerHTML;
        // 双击禁止选定文字
        window.getSelection ? window.getSelection().removeAllRanges() : document.selection.empty();
        // alert(11);
        this.innerHTML = '<input type="text" />';
        var input = this.children[0];
        input.value = str;
        input.select(); // 文本框里面的文字处于选定状态
        // 当我们离开文本框就把文本框里面的值给span 
        input.onblur = function() {
            this.parentNode.innerHTML = this.value;
        };
        // 按下回车也可以把文本框里面的值给span
        input.onkeyup = function(e) {
            if (e.keyCode === 13) {
                // 手动调用表单失去焦点事件  不需要鼠标离开操作
                this.blur();
            }
        }
    }

}
new Tab('#tab');

在这里插入图片描述

3. 严格模式

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述
在这里插入图片描述

在这里插入图片描述

<script>
        'use strict'
        // 1. 我们的变量名必须先声明再使用
        // num = 10   // 应该是 var num = 10
        // console.log(num)
        // 2. 我们不能随意删除已经声明好的变量
        // delete num    // 删除已经声明好的变量会报错
    </script>

在这里插入图片描述
在这里插入图片描述

在这里插入图片描述

在这里插入图片描述
在这里插入图片描述
函数必须声明在顶层,不能放到if或者for等的里面
在这里插入图片描述

网页链接:link

4. 高阶函数

在这里插入图片描述

<script>
        // 高阶函数 - 函数可以作为参数传递
        function fn(a, b, callback) {
            console.log(a + b)
            // 执行回调函数
            callback && callback()
        }
        fn(1, 2, function() {
            console.log('我是最后调用的')
        })
</script>

5. 闭包

JS中的闭包和异步并称为两大难点
在这里插入图片描述
闭包本身就是一个函数

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述
案例1. 循环注册点击事件(很典型的闭包面试题)

<body>
    <ul class="nav">
        <li>榴莲</li>
        <li>臭豆腐</li>
        <li>鲱鱼罐头</li>
        <li>大猪蹄子</li>
    </ul>
    <script>
        // 闭包应用-点击li输出当前li的索引号
        // 方法1. 我们可以利用动态添加属性的方式
        var lis = document.querySelector('.nav').querySelectorAll('li')
        // for(var i = 0; i< lis.length; i++) {
        //     lis[i].index = i
        //     lis[i].onclick = function(){
        //         console.log(this.index)
        //     }
        // }
        // 方法2. 利用闭包的方式得到当前小li的索引号
        for(var i = 0; i < lis.length; i++){
            // 利用for循环创建了4个立即执行函数
            // 立即执行函数也称为小闭包 因为立即执行函数里面任何一个函数都可以使用它的i变量
            (function(i) {
                lis[i].onclick = function(){
                console.log(i)
            }
                // 为立即执行函数接收i
            })(i)
        }
    </script>

案例2. 循环中的setTimeout()

<body>
    <ul class="nav">
        <li>榴莲</li>
        <li>臭豆腐</li>
        <li>鲱鱼罐头</li>
        <li>大猪蹄子</li>
    </ul>
    <script>
        // 闭包应用-3秒钟之后,打印所有li元素的内容
        var lis = document.querySelector('.nav').querySelectorAll('li')
        for(var i = 0; i < lis.length; i++) {
            // 用立即执行函数这个小闭包配合定时器
            (function(i) {
                setTimeout(function() {
                console.log(lis[i].innerHTML)
            }, 3000)
            })(i)
        }
    </script>

案例3. 计算打车价格

<script>
        // 闭包应用-计算打车价格
        // 打车起步价13¥(3公里内),之后每公里5¥,用户输入公里数就可以计算打车价格
        // 如果有拥堵情况,总价格多收取10¥拥堵费
       var car = (function fn() {
            var start = 13  // 起步价
            var total = 0   // 总价
            return {
                // 正常的总价price
                price: function(n) {
                    if(n <= 3) {
                        total = start
                    } else {
                        total = start + (n - 3) * 5 
                    }
                    return total
                },
                yd: function(flag) {
                    return flag ? total + 10 : total
                }
            }
        })()
        console.log(car.price(5))  // 23
        console.log(car.yd(true)); // 33

        console.log(car.price(1)); // 13
        console.log(car.yd(false)); // 13

    </script>

在这里插入图片描述

6. 递归

在这里插入图片描述

在这里插入图片描述
递归实用1:求求1~n的阶乘 1 * 2 * 3 * 4 * ..n

<script>
        // 利用递归函数求1~n的阶乘 1 * 2 * 3 * 4 * ..n
        function fn(n) {
            if(n == 1) {
                return 1
            }
            // 原理就是 从n开始一直往前面减一,一直到fn(n)中的n=1就return结束
            return n * fn(n - 1)
        }
        // fn(3) = 3 * fn(2) = 3 * 2 * fn(1) = 3x2x1 =6
        console.log(fn(3));  // fn(3) = 6
        console.log(fn(4));  // fn(4) = 24
    </script>

递归实用2:求斐波那契数列

<script>
        // 利用递归函数求斐波那契数列(兔子数列) 1、1、2、3、5、8、13、21...
        // 用户输入一个数字n就可以求出 这个数字对应的兔子序列值
        // 我们只需要知道用户输入的n 的前面两项(n-1 n-2)就可以计算出n对应的序列值
        function fb(n) {
            if(n === 1 || n ===2) {
                return 1
            }
            return fb(n-1) + fb(n-2)
        }
        console.log(fb(3))  // fb(3) = 2
        console.log(fb(6))  // fb(6) = 8
    </script>

在这里插入图片描述

<script>
        var data =[{
            id: 1,
            name: '家电',
            goods: [{
                id: 11,
                gname: '冰箱',
                goods: [{
                    id: 111,
                    gname: '海尔'
                }, {
                    id: 112,
                    gname: '美的'
                }, ]
            }, {
                id: 12,
                gname: '洗衣机'
            }]
        }, {
            id: 2,
            name: '服饰'
        }]
        // 我们想要做输入id号,就可以返回的数据对象
        // 1. 利用forEach 去遍历里面的每一个对象
        function getID(json, id) {
            // o对象 是专门用来存放筛选完的数据的
            var o = {}
            json.forEach(function(item) {
                // console.log(item); // 两个数组元素
                if(item.id == id) {
                    // console.log(item);
                    o = item
                    // 2. 我们想得到里层的数据 11 12 可以用递归函数
                    // 里面应该有goods这个数组并且数组的长度不为0
                    // if()在外层找不到对应的id则else if往里面找id
                } else if (item.goods && item.goods.length > 0) {
                    // 满足条件的话则调用getID()返回item.goods里面的数据对象
                   o = getID(item.goods, id)
                }
            })
            return o
        }
        console.log(getID(data, 1));
        console.log(getID(data, 2));
        console.log(getID(data, 11));
        console.log(getID(data, 12));
        console.log(getID(data, 111));
        
    </script>

在这里插入图片描述
在这里插入图片描述

浅拷贝:

<script>
        // 浅拷贝只是拷贝一层,更深层次对象级别的只拷贝引用
        // 深拷贝拷贝多层,每一级别的数据都会拷贝
        var obj = {
            id: 1,
            name: 'andy',
            msg: {
                age: 18
            }
        }
        var o = {}
        // 方法1:for循环实现浅拷贝
        // for(var k in obj) {
            // k 是属性名  obj[k] 属性值
        //     o[k] = obj[k]
        // }
        // console.log(o)
        // o.msg.age = 20
        // console.log(obj)

        // 方法2:语法糖 es6新增方法也可以实现浅拷贝,并且更简单
        // 实际开发更推荐 Object.assign()
        Object.assign(o, obj)
        console.log(o)
        o.msg.age = 20
        console.log(obj)
    </script>

深拷贝:

<script>
        var obj = {
            id: 1,
            name: 'andy',
            msg: {
                age: 18
            },
            color: ['pink', 'red']
        }
        var o = {}
        // 封装函数
        function deepCopy (newobj, oldobj) {
            for(var k in oldobj) {
                // 判断我们的属性值属于哪种数据类型
                // 1. 获取属性值 oldobj[k]
                var item = oldobj[k]
                // 2. 判断这个值是否是数组 (数组也属于Object,需要先把数组筛出去)
                if (item instanceof Array) {
                    newobj[k] = []
                    deepCopy (newobj[k], item)
                } else if (item instanceof Object) {
                    // 3. 判断这个值是否是对象
                    newobj[k] = {}
                    deepCopy(newobj[k], item)
                } else {
                    // 4. 属于简单数据类型
                    newobj[k] = item
                }
               
            }
        }
        deepCopy(o, obj)
        console.log(o)

        var arr = []
        // 数组也属于Object
        console.log(arr instanceof Object);
        // 深拷贝是拷贝里面的数据,所以这里修改age=20,影响不了obj里的age,因为age=18已经拷贝过来了不会被修改
        o.msg.age = 20
        console.log(obj)
    </script>

四、ES6

在这里插入图片描述

1. ES6简介

旨在学习一些最新的、好用的语法规范
在这里插入图片描述

在这里插入图片描述

2. ES6的新增语法

2.1 let

在这里插入图片描述
let可以防止循环变量变成全局变量
在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

2.2 const

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

2.3 解构赋值

解构:分解数据结构
赋值:为变量赋值
在这里插入图片描述
在这里插入图片描述
如果解构不成功,变量的值为undefined
在这里插入图片描述

在这里插入图片描述
注意:{name: myName}中冒号左边的name只用于属性匹配,冒号右边的myName才是真正的变量

2.4 箭头函数

箭头函数是用来简化函数定义语法的
在这里插入图片描述

<script>
        // 箭头函数是用来简化函数定义语法的
        // const fn = () => {
        //     console.log(123);
        // }
        // fn()

        // 在箭头函数中 如果函数体中只有一句代码
        // 并且代码的执行结果就是函数的返回值,函数体大括号可以省略
        const sum = (n1, n2) =>  n1 + n2
            
        console.log(sum(1, 2)); 

    </script>

在这里插入图片描述

<script>
// 在箭头函数中,如果形参只有一个,那么形参外侧的小括号也是可以省略的
        const fn = v => {
            alert(v)
        }
        fn(20)
        </script>

在这里插入图片描述

<script>
        // 箭头函数不绑定this,箭头函数没有自己的this关键字
        // 如果在箭头函数中使用this,this关键字将指向箭头函数定义位置中的this
        const obj = { name: '张三'}
        function fn() {
            console.log(this)
            return () => {
                console.log(this)
            }
        }
        // fn.call(obj) call方法: fn的this指向obj
        const resFn = fn.call(obj)
        resFn()
    </script>
箭头函数面试题

在这里插入图片描述
问:alert弹出的值是多少?
答案是undefined,因为obj是一个对象对象是不能产生作用域的,所以箭头函数是被定义在全局作用域下,调用say方法的时候alert(this.age)中的this是指向window,而在window下没有age属性,因此弹出来的值是undefined

2.5 剩余参数

在这里插入图片描述

// ...args是形参(args是一个数组),与实参对应,
// 在args前面加省略号代表接收所有的实参。
const sum = (...args) => {
            let total = 0
            // forEach用于遍历数组(数组有多少个数就循环多少次)
            // item表示当前这一次循环的这一项
            args.forEach(item => total += item)
            return total
        }
        console.log(sum(10, 20))  // 输出结果30
        console.log(sum(10, 20, 30))  // 输出结果60

在这里插入图片描述

<script>
        let students = ['ts', 'ning', 'rk','jk', 'bl']
        let [top1, ...mid] = students
        console.log(top1);  // ts
        console.log(mid);   // ['ning', 'rk', 'jk', 'bl']

		let students = ['ts', 'ning', 'rk','jk', 'bl']
        let [top1, jug, ...mid] = students
        console.log(top1);  // ts
        console.log(jug);   // ning
        console.log(mid);   // ['rk', 'jk', 'bl']
    </script>

3. ES6的内置对象扩展

3.1 Array的扩展方法

3.1.1 扩展运算符

扩展运算符和剩余参数正好是相反的,剩余参数是将剩余的数组放到一个数组中,而扩展运算符是将数组或对象拆分成用逗号分隔的参数序列。
在这里插入图片描述

<script>
        let ary = [1, 2, 3]
        // ...ary 即为 1, 2, 3
        console.log(...ary);  //输出 1 2 3
        console.log(1, 2, 3); //输出 1 2 3
    </script>

console.log(…arg)输出结果中没有逗号是因为逗号被认作是console.log方法的分隔符。
在这里插入图片描述

<script>
        // 合并数组
		// 方法1:
		let ary1 = [1, 2, 3]
        let ary2 = [4, 5, 6]
        // 将数组1和数组2合并,名为数组3
        let ary3 = [...ary1, ...ary2]
        console.log(...ary3) // 1, 2, 3, 4, 5, 6
        
        // 方法2:
        let ary1 = [1, 2, 3]
        let ary2 = [4, 5, 6]
        // push方法:将数组2中的456放到数组1中了
        ary1.push(...ary2)
        console.log(...ary1)  // 1, 2, 3, 4, 5, 6

    </script>

注意:JS中伪数组又叫类数组
在这里插入图片描述

<body>
    <div>1</div>
    <div>4</div>
    <div>3</div>
    <div>6</div>
    <div>2</div>
    <div>5</div>
    <script>
        let oDivs = document.getElementsByTagName('div')
        console.log(oDivs);  // 输出伪数组

        // oDivs 是一个伪数组,在oDivs前加...使其变成以逗号分隔的参数序列
        // 再在参数序列...oDivs外面加[],把它变成真正的数组
        // 为什么转真数组?因为真数组下面的方法才能被调用
        var ary = [...oDivs]
        console.log(ary);  // 输出真数组
        ary.push('a')
        console.log(ary)
    </script>
</body>

上面代码打印结果如下:
在这里插入图片描述

3.1.2 构造函数方法:Array.from()

注意:JS中伪数组又叫类数组
在这里插入图片描述

<script>
        // arr 的值是一个伪数组
        let arr = {
            '0': 'a',
            '1': 'b',
            '2': 'c',
            length: 3
        }
        // Array.from方法接收一个伪数组,返回值就是一个真正的数组
        let newArr = Array.from(arr)
        // 真数组newArr
        console.log(newArr);
    </script>

在这里插入图片描述
item => item*2 是第二参数,意为在函数内部将所处理的每个值乘以2
代码输出结果如下:
在这里插入图片描述

3.1.3 实例方法:find()

在这里插入图片描述

 <script>
        let ary = [{
            id: 1,
            name: '张三'
        }, {
            id: 2,
            name: '李四'
        }]
        // 可以通过id或name进行查找
        // item指当前循环到的那个值,index当前循环到的索引(当前没用到index所以可以省略)
        // let target = ary.find((item, index) => item.id == 2)
        let target = ary.find((item) => item.name == '李四')
        console.log(target)
3.1.4 实例方法:findIndex()

在这里插入图片描述

<script>
        let ary = [1, 5, 10, 15]
        // 寻找数组中第一个大于9的数的位置
        let index = ary.findIndex((value, index) => value > 9)
        console.log(index)  // 输出2(10大于9,10的索引号index是2)

        // let index1 = ary.findIndex((value, index) => value > 90)
        // console.log(index1)   // 输出-1(没有找到)
    </script>
3.1.5 实例方法:includes()

在这里插入图片描述

<script>
        let re = [1, 2, 3].includes(2)
        console.log(re);  // true
        let re1 = [1, 2, 3].includes(22)
        console.log(re1); // false

        let ary = ["a", "b", "c"]
        let ce = ary.includes('a')
        console.log(ce)     // true
        let ce1 = ary.includes('e')
        console.log(ce1)    // false
    </script>

3.2 String的扩展方法

3.2.1 模板字符串

在这里插入图片描述

在这里插入图片描述

<script>
        let name = '张三'
        let sayHello = `hello,my name is ${name}`
        console.log(sayHello);  // hello,my name is 张三
    </script>

在这里插入图片描述

在这里插入图片描述

3.2.2 实例方法:startsWith() 和 endsWith()及repeat()

在这里插入图片描述

在这里插入图片描述

3.3 Set数据结构

注意:Set不会存储重复值
在这里插入图片描述
set数据解构可以用作记录搜索历史记录,方便下次直接点击历史搜索。使用时Set会判断是否重复,如果重复则不会存储。

<script>
        const s1 = new Set()
        console.log(s1.size)  // 输出0,因为没有数据
        // size表示数据的数量
        const s2 = new Set(["a", "b"])
        console.log(s2.size) // 输出2,有a和b两个数据

        // 利用Set做数组去重
        const s3 = new Set(["a", "a","b", "b"])
        console.log(s3.size) // 仍然输出2,因为Set可以筛选重复值
        // 用剩余参数 得到数组
        const ary = [...s3]
        console.log(ary)
    </script>

在这里插入图片描述

<script>
        const s = new Set()
        s.add(1).add(2).add(3)  // 向Set结构中添加值
        console.log(s);         // Set(3) {1, 2, 3}
        s.delete(2)             // 删除Set结构中的2值
        console.log(s);         // Set(2) {1, 3}
        console.log(s.has(3));  // 输出true
        console.log(s.has(2));  // 输出false
        s.clear()               // 清空Set数据结构里的值
        console.log(s);         // Set(0) {size: 0}
    </script>

在这里插入图片描述

<script>
        const re = new Set(['a', 'b', 'c'])
        // 对re数组中的元素进行遍历,并且打印输出
        re.forEach(value => {
            console.log(value);
        })
    </script>

代码输出结果如下:
在这里插入图片描述


结语:到这里这篇博客终于结束了,这一部分的内容我觉得可以整合在一块,于是决定写一篇整合篇,这一写就是两个月(2022.5.19-2022.7.21),中间各种考试和工作耽误了很久,但总归是学完了,此时我一身轻松,感谢大家的阅读,有不对的地方欢迎指正。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值