JavaScript面向对象

本文详细介绍了面向对象编程的基础概念,如面向过程与面向对象的区别,以及在ES6中如何使用类和对象。涵盖了类的创建、构造函数、方法添加、继承机制,以及面向对象编程在实际案例中的应用,如动态Tab栏切换的实现。
摘要由CSDN通过智能技术生成

目录

1.面向对象编程介绍

1.1 两大编程思想

1.2 面向过程编程POP(Process-oriented programming)

1.3 面向对象编程OOP(Object Oriented Programming)

1.4 面向过程和面向对象的对比

​编辑

2.ES6中的类和对象

面向对象

 2.1 对象

2.2 类 class

2.3 创建类

语法:

创建实例:

2.4 类constructor构造函数

2.5 类添加方法

语法:

3.类继承

3.2 super关键字

语法:

 ​编辑

三个注意点:

4. 面向对象案例

面向对象版tab栏切换


 

1.面向对象编程介绍

1.1 两大编程思想

  • 面向过程
  • 面向对象

1.2 面向过程编程POP(Process-oriented programming)

面向过程就是分析出解决问题所需要的步骤,然后用函数把这些步骤一步一步实现, 使用的时候再一个一个的依次调用就可以了

举个例子:把大象装进冰箱,面向过程做法

 面向过程,就是按照我们分析好了的步骤,按照步骤解决问题

1.3 面向对象编程OOP(Object Oriented Programming)

面向对象是把事物分解成为一个个对象,然后由对象之间分工合作

举个例子:把大象放进冰箱,面向对象做法

先找出对象,并且写出这些对象的功能:

1.大象对象

  • 进去

2.冰箱对象

  • 打开
  • 关闭 

3.使用大象和冰箱功能

面向对象是以对象功能来划分问题,而不是步骤 

在面向对象程序开发思想中,每一个对象都是功能中心,具有明确分工

面向对象编程具有灵活、代码可复用、容易维护和开发的优点,更适合多人合作的大型软件项目

面向对象的特性:

  • 封装性
  • 继承性
  • 多态性 

1.4 面向过程和面向对象的对比

2.ES6中的类和对象

面向对象

 2.1 对象

2.2 类 class

在ES6中新增加了类的概念,可以使用class关键字声明一个类,之后以这个类来实例化对象

抽象了对象的公共部分,它泛指某一大类

对象特指某一个,通过类实例化一个具体的对象

 面向对象的思维特点:

  1. 抽取(抽象)对象共用的属性和行为组织(封装)成一个(模板)
  2. 对类进行实例化,获取类的对象

2.3 创建类

语法:

class name {
    //class body
}

创建实例:

var xx = new name();

注意:类必须使用new实例化对象 

2.4 类constructor构造函数

constructor()方法是类的构造函数(默认方法),用于传递参数,返回实例对象,通过new命令生成对象实例时,自动调用该方法,如果没有显式定义,类内部会自动给我们创建一个constructor()

<script>
        class Star {
            constructor(uname, age) {
                this.uname = uname;
                this.age = age;
            }
        }
        var ldh = new Star("刘德华", 18);
        var zxy = new Star("张学友", 19);
        console.log(ldh);
        console.log(zxy);
    </script>

 

注意:

  1. 通过class关键字创建类,类名我们还是习惯性定义首字母大写
  2. 类里面有个constructor函数,可以接收传递过来的参数,同时返回实例对象
  3. constructor函数 只要new生成实例时,就会自动调用这个函数,如果我们不写这个函数,类也会自动生成这个函数
  4. 生成实例new不能省略
  5. 最后注意语法规范,创建类 类名后面不要加小括号,生成实例,类名后面加小括号,构造函数不需要加function

2.5 类添加方法

语法:

class Person {
    constructor (name,age) {
        this.name = name;
        this.age = age;
    }
    say() {
        console.log(this.name+'你好');
    }
}

<script>
        class Star {
            constructor(uname, age) {
                this.uname = uname;
                this.age = age;
            }
            sing(song) {
                // console.log("唱歌");
                console.log(this.uname + song);
            }
        }
        var ldh = new Star("刘德华", 18);
        var zxy = new Star("张学友", 19);
     
        ldh.sing("冰雨");
        zxy.sing("李香兰");
    </script>

 注意:

  1. 类里面所有的函数不需要写function
  2. 多个函数方法之间不需要添加逗号分隔

3.类继承

现实中的继承:子承父业,比如我们继承了父亲的姓

程序中的继承:子类可以继承父类的一些属性和方法

class Father { //父类
}
class Son extends Father { //子类继承父类
}
<script>
        // 1. 类的继承
        class Father {
            money() {
                console.log(100);
            }
        }
        class Son extends Father {
        }
        var son = new Son();
        son.money();
    </script>

 

3.2 super关键字

super关键字用于访问和调用对象父类上的函数。可以调用父类的构造函数,也可以调用父类的普通函数

<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);
        son.sum();  //3
    </script>

语法:

 

 继承中的属性或者方法查找原则:就近原则

  1. 继承中,如果实例化子类输出一个方法,先看子类有没有这个方法,如果有就先执行子类的
  2. 继承中,如果子类里面没有,就去查找父类有没有这个方法,如果有,就执行父类的这个方法

 

 注意:子类在构造函数中使用super,必须放到this前面(必须先调用父类的构造方法,再使用子类的构造方法)

<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调用父类的构造方法
                // super必须在子类this之前使用
                super(x, y);
                this.x = x;
                this.y = y;
            }
            substract() {
                console.log(this.x - this.y);
            }
        }
        var son = new Son(5, 3);
        son.sum();
        son.substract();
    </script>

三个注意点:

1.在ES6中类没有变量提升,所以必须先定义类,才能通过类实例化对象

2.类里面的共有的属性和方法一定要加this使用,因为类里面的属性和方法是属于实例化对象的,而this指代的就是当前的实例化的对象

 

3.类里面的this指向问题

4.constructor里面的this指向实例对象,方法里面的this指向这个方法的调用者

<body>
    <button></button>
    <script>
        var that;

        class Star {
            constructor(uname, age) {
                that = this;
                console.log(that);
                this.uname = uname;
                this.age = age;
                this.btn = document.querySelector("button");
                this.btn.onclick = this.sing;
            }
            sing() {
                // 这里的this指的是btn,因为是btn调用的sing方法
                console.log(this);
                console.log(that.uname);//that里面存的是constructor里面的this
            }
        }
        var ldh = new Star("刘德华", 18);
        console.log(ldh === that);

    </script>
</body>

 

如果在一些情况中,想要在某个方法中的this已经不是constructor里面的this了,但还是想调用constructor里面的属性,可以定义一个全局变量that,在constructor里面把this赋值给that,就可以在其他的函数里使用constructor里面的this了

类比Java中的知识

  1. 构造函数用于在实例化对象的时候为这个对象的属性赋值
  2. 当成员变量(类中的变量)和局部变量(函数里的变量)重名的时候,使用this关键字分辨成员变量和局部变量,比如说,this.name = name,this.name 表示的是当前类中的变量,等号后面的name只是形参
  3. this指代当前对象,指当前正在调用类中方法的对象

4. 面向对象案例

面向对象版tab栏切换

 

-----------------------------------------------------------模块划分--------------------------------------------------- 

步骤:

  1. 先定义一个类Tab,实例化Tab对象,传入的参数是大盒子
  2. 在构造函数中进行属性的获取,分别要获取所有的li,section
  3. 类中还要定义四个函数,分别代表着切换、添加、修改、删除功能
  4. 在页面一加载的时候,就要给相关的元素绑定事件,因此我们设置一个init方法,给相关元素绑定点击事件
  5. 在构造函数中调用init(),为了在页面一加载的时候就给相关元素绑定事件

tab.js 

class Tab {
    constructor(id) {
        this.main = document.querySelector(id);
        this.lis = this.main.querySelectorAll('li');
        this.secitons = this.main.querySelectorAll('section');
        this.init();
    }
    init() {
        for (var i = 0; i < this.lis.length; i++) {
            this.lis[i].setAttribute('index', i);
            this.lis[i].onclick = function () {
                console.log(this.getAttribute('index'));
            }
        }
    }
    // 1.切换功能
    toggleTab() { }
    // 2.添加功能
    addTab() { }
    // 3.删除功能
    removeTab() { }
    // 4.修改功能
    editTab() { }
}
new Tab("#tab");

--------------------------------------------------------切换功能模块-----------------------------------------------------

步骤:

  1. 当点击tab栏的时候,就调用toggleTab()方法,注意不要加括号,不然会直接调用
  2. 点击tab栏的时候,就给当前点击的li设置liactive类名,同时,对应索引号的section显示出来
  3. 利用排他思想,先把所有的li和section的类名去掉,在设置当前的。因为li和section都去除了类名,可以封装在一个函数中clearClass()实现
  4. clearClass()中,利用for循环,将所有的类名清空,注意要在给li和section设置类名之前调用这个函数

注意:

1.要注意this的指向

// 1.切换功能
    toggleTab() {
        // console.log(this.getAttribute('index'));
        that.clearClass();
        this.className = 'liactive';
        that.secitons[this.getAttribute('index')].className = 'conactive';



    }
    clearClass() {
        for (var i = 0; i < this.lis.length; i++) {
            this.lis[i].className = '';
            this.secitons[i].className = '';
        }
    }

--------------------------------------------------------tab栏添加功能------------------------------------------------------

 步骤:

  1. 在构造函数里面获取加号元素
  2. 在init方法里面给加号元素绑定点击事件
  3. 点击的时候,动态创建元素li,添加到fisrstnav盒子的第一个子级ul(记得获取ul元素)里面的最后面(用insertAdjacentHTML('beforeend',li))
  4. 再用同样的方法,动态创建元素section,添加到tabscon盒子里面去
  5. 生成新的li和section的时候,同样利用排他思想,先清除掉所有li和section的类名,再设置对应的类名
  6. 因为后来添加的li和section没有绑定事件,所以我们把构造函数里面获取到的li和section拿出来单独定义一个函数updateEle,在init()里面调用
  7. 在添加功能的函数里调用init(),每次创建完新的li和section都会重新获取页面中的li和section并且进行绑定事件

 

注意:

1.获取元素都写在constructor里面,绑定事件都写在init()里,页面一加载就调用,一加载就绑定事件

2.我们动态添加li的时候,因为li元素里面的内容较多很长,按照以前的做法,必须先createElement()创建,然后通过innerHTML()赋值,才能通过appendChild()添加到父元素内容的最后,而且appendChild()方法只有用了createElement()之后才能使用,所以,我们采用insertAdjacentHTML()方法,可以直接定义一个动态添加元素的字符串,然后通过此方法直接添加

 

 3.添加完li和section之后,会发现排他和切换都是不起作用的,这是因为在页面一加载的时候,只获取到了当前页面写定了的3个li和section,并且绑定了事件,而我们后来创建的li和section并没有被获取到,也没有绑定事件。所以我们把获取li和section单独拿出来封装成一个函数,在init()里面调用,再在添加函数里面调用init函数,这样,添加功能之后,再对页面进行初始化,获取所有的li和section并且重新绑定事件

     updateEle() {
        this.lis = this.main.querySelectorAll('li');
        this.secitons = this.main.querySelectorAll('section');
    }
    init() {
        // init()初始化时就给相应元素绑定事件
        this.updateEle();
        this.add.onclick = this.addTab;
        for (var i = 0; i < this.lis.length; i++) {
            this.lis[i].setAttribute('index', i);
            this.lis[i].onclick = this.toggleTab;
        }
    }

 // 2.添加功能
    addTab() {
        that.clearClass();
        var random = Math.random();
        var li = '<li class="liactive"><span>新选项卡</span><span class="iconfont icon-guanbi"></span></li>';
        that.ul.insertAdjacentHTML('beforeend', li);
        var section = '<section class="conactive">测试' + random + '</section>';
        that.tabscon.insertAdjacentHTML('beforeend', section);
        that.init();

    }

 ------------------------------------------------------删除功能模块-------------------------------------------------------

步骤:

  1. 获取页面所有叉号元素,要放在updateEle()里
  2. 通过for循环给所有的叉号元素绑定点击事件
  3. 因为叉号元素和父元素li都绑定了点击事件,所以要阻止事件冒泡,单击叉号的时候不要切换
  4. 通过叉号元素的父元素li的index获取到叉号元素的index
  5. 获取到index之后,删除(remove())索引号对应的li和section
  6. 删除完成后,再调用一次init(),确保页面获取到的数据是最新的
  7. 我们想要删除当前内容之后,自动选中的是被删除内容的前一个内容,将index--,然后手动给对应的index的li调用click()
  8. 删除最后一个内容之后会报错,因为index当前是负值,所以只有在index不为负值的时候才调用click方法,用逻辑与 that.lis[index] && that.lis[index].click();
  9. 如果当前选中的li不是要删除的li,就保持当前选中的li不变,而不是显示被删除的内容的前一个 if (document.querySelector('.liactive')) return;

注意:

1.如果把获取叉号元素写在构造函数里,那么一开始只获取到页面中已经有的所有叉号元素,在进行添加的时候,新加入的li元素里面的叉号是没有获取到的,在进行for循环绑定事件的时候,就获取不到新的叉号元素,所以就会报错,因此要把获取叉号元素也放到updateEle()中,在执行init()的时候先获取到元素,再进行事件绑定

2.因为叉号元素remove和它的父级li都绑定了点击事件,所以会事件冒泡,点击叉号的时候会发现li也在切换,所以要阻止冒泡,防止li切换

3.点击叉号想要删除对应的li和section,就要获取到点击的叉号的下标,因为叉号的父级li已经自定义了index索引号属性,所以remove元素就不用再定义一个索引号了,直接获取parentNode的index属性即可

4.想要删除指定的li之后,显示被删除内容的前一个内容,只需要将被选中的对应index-1,然后再将当前index对应的li和section显示。我们可以根据index给对应的li和section设置className,也可以使用click()方法,可以在不点击的情况下手动调用点击事件

5.为了防止index--之后成为负值之后报错,我们利用逻辑与 that.lis[index] && that.lis[index].click(); 只有that.lis[index]为真才会执行点击

6.

 

如果当前页面中存在类名为liactive的元素,即还有被选中的li、上一步删除的不是当前被选中的元li,那么直接return结束整个函数,不再进行下面的步骤

 

  updateEle() {
        this.lis = this.main.querySelectorAll('li');
        this.secitons = this.main.querySelectorAll('section');
        this.remove = this.main.querySelectorAll('.icon-guanbi');

    }
 // 3.删除功能
    removeTab(e) {
        e.stopPropagation(); //阻止冒泡 防止触发li的切换事件
        var index = this.parentNode.getAttribute("index");
        // console.log(index);
        that.lis[index].remove();
        that.secitons[index].remove();
        that.init();
        // 如果我们删除的不是当前选中的li的时候,就将选中状态的li保持不变
        if (document.querySelector('.liactive')) return;
        // 当我们删除了选中状态的li的时候,使它前一个li处于选中状态
        index--;
        // 手动调用click()方法,不需要鼠标点击
        that.lis[index] && that.lis[index].click();
    }

-----------------------------------------------------编辑模块----------------------------------------------------------------

步骤:

  1. 获取所有的、fisrstnav盒子里的ul里的li里的第一个span,包括动态添加的,在updateEle()里面获取
  2. 给获取过来的所有span通过for循环绑定双击事件ondblclick
  3. 设置双击禁止选中文字
  4. 双击span的时候,使当前选中的span的内容变为文本框(innerHTML)
  5. 获取文本框input元素:input是span的第一个子级
  6. 获取在没点击的时候的span里的内容,赋值给文本框的内容value,并且使文本框里的内容自动全选
  7. 当文本框失去焦点的时候,将文本框里我们修改的内容重新赋值给span
  8. 给input绑定键盘事件,如果用户敲的是enter,就手动调用失去焦点事件
  9. 给页面中的所有section也绑定双击事件,调用编辑的函数即可

注意:

1.不管是给获取到的span还是叉号等等,给它们绑定事件的时候,它们的个数都和li相等,所以可以直接在一个for循环里写

2.双击之后生成的表单是span的第一个子级,可以通过children[0]获取

3.表单里的文本是通过value设置的

4.禁止双击选中文字

window.getSelection ? window.getSelection().removeAllRanges() : document.selection.empty();

5.

 

// 4.修改功能
    editTab() {
        var str = this.innerHTML;
        // 双击禁止选定文字
        window.getSelection ? window.getSelection().removeAllRanges() : document.selection.empty();
        this.innerHTML = '<input type="text">';
        var input = this.children[0];
        input.value = str;
        input.select(); //选中文本
        // 当我们离开文本框的时候就把文本框里的值重新赋给span
        input.onblur = function () {
            this.parentNode.innerHTML = this.value;
        }
        // 当用户按下的是enter键的时候,就把文本框里的值重新赋给span
        input.onkeyup = function (e) {
            if (e.keyCode === 13) {
                this.blur(); //手动失去焦点,不需要鼠标离开操作
            }
        }
    }

----------------------------------------------------完整代码----------------------------------------------------------

index.html

<!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/tab.css">
    <link rel="stylesheet" href="./styles/style.css">
</head>

<body>

    <main>
        <h4>
            Js 面向对象 动态添加标签页
        </h4>
        <div class="tabsbox" id="tab">
            <!-- tab 标签 -->
            <nav class="fisrstnav">
                <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>

tab.js

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.tabscon = this.main.querySelector('.tabscon');
        this.init();
    }
    updateEle() {
        this.lis = this.main.querySelectorAll('li');
        this.secitons = this.main.querySelectorAll('section');
        this.remove = this.main.querySelectorAll('.icon-guanbi');
        this.spans = this.main.querySelectorAll('.fisrstnav ul li span:first-child');
    }
    init() {
        // init()初始化时就给相应元素绑定事件
        this.updateEle();
        this.add.onclick = this.addTab;
        for (var i = 0; i < this.lis.length; i++) {
            this.lis[i].setAttribute('index', i);
            this.lis[i].onclick = this.toggleTab;
            this.remove[i].onclick = this.removeTab;
            this.spans[i].ondblclick = this.editTab;
            this.secitons[i].ondblclick = this.editTab;
        }
    }
    // 1.切换功能
    toggleTab() {
        // console.log(this.getAttribute('index'));
        that.clearClass();
        this.className = 'liactive';
        that.secitons[this.getAttribute('index')].className = 'conactive';

    }
    clearClass() {
        for (var i = 0; i < this.lis.length; i++) {
            this.lis[i].className = '';
            this.secitons[i].className = '';
        }
    }
    // 2.添加功能
    addTab() {
        that.clearClass();
        var random = Math.random();
        var li = '<li class="liactive"><span>新选项卡</span><span class="iconfont icon-guanbi"></span></li>';
        that.ul.insertAdjacentHTML('beforeend', li);
        var section = '<section class="conactive">测试' + random + '</section>';
        that.tabscon.insertAdjacentHTML('beforeend', section);
        that.init();

    }
    // 3.删除功能
    removeTab(e) {
        e.stopPropagation(); //阻止冒泡 防止触发li的切换事件
        var index = this.parentNode.getAttribute("index");
        // console.log(index);
        that.lis[index].remove();
        that.secitons[index].remove();
        that.init();
        // 如果我们删除的不是当前选中的li的时候,就将选中状态的li保持不变
        if (document.querySelector('.liactive')) return;
        // 当我们删除了选中状态的li的时候,使它前一个li处于选中状态
        index--;
        // 手动调用click()方法,不需要鼠标点击
        that.lis[index] && that.lis[index].click();
    }
    // 4.修改功能
    editTab() {
        var str = this.innerHTML;
        // 双击禁止选定文字
        window.getSelection ? window.getSelection().removeAllRanges() : document.selection.empty();
        this.innerHTML = '<input type="text">';
        var input = this.children[0];
        input.value = str;
        input.select(); //选中文本
        // 当我们离开文本框的时候就把文本框里的值重新赋给span
        input.onblur = function () {
            this.parentNode.innerHTML = this.value;
        }
        // 当用户按下的是enter键的时候,就把文本框里的值重新赋给span
        input.onkeyup = function (e) {
            if (e.keyCode === 13) {
                this.blur(); //手动失去焦点,不需要鼠标离开操作
            }
        }
    }
}
new Tab("#tab");

tab.css

* {
    margin: 0;
    padding: 0;
}

ul li {
    list-style: none;
}

main {
    width: 960px;
    height: 500px;
    border-radius: 10px;
    margin: 50px auto;
}

main h4 {
    height: 100px;
    line-height: 100px;
    text-align: center;
}

.tabsbox {
    width: 900px;
    margin: 0 auto;
    height: 400px;
    border: 1px solid lightsalmon;
    position: relative;
}

nav ul {
    overflow: hidden;
}

nav ul li {
    float: left;
    width: 100px;
    height: 50px;
    line-height: 50px;
    text-align: center;
    border-right: 1px solid #ccc;
    position: relative;
}

nav ul li.liactive {
    border-bottom: 2px solid #fff;
    z-index: 9;
}

#tab input {
    width: 80%;
    height: 60%;
}

nav ul li span:last-child {
    position: absolute;
    user-select: none;
    font-size: 12px;
    top: -18px;
    right: 0;
    display: inline-block;
    height: 20px;
}

.tabadd {
    position: absolute;
    /* width: 100px; */
    top: 0;
    right: 0;
}

.tabadd span {
    display: block;
    width: 20px;
    height: 20px;
    line-height: 20px;
    text-align: center;
    border: 1px solid #ccc;
    float: right;
    margin: 10px;
    user-select: none;
}

.tabscon {
    width: 100%;
    height: 300px;
    position: absolute;
    padding: 30px;
    top: 50px;
    left: 0px;
    box-sizing: border-box;
    border-top: 1px solid #ccc;
}

.tabscon section,
.tabscon section.conactive {
    display: none;
    width: 100%;
    height: 100%;
}

.tabscon section.conactive {
    display: block;
}

 

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值