js构造函数和原型对象,ES6中的class,四种继承方式

一、构造函数

1.构造函数是一种特殊的函数,主要用来初始化对象
2.使用场景

常见的{...}语法允许创建一个对象。可以通过构造函数来快速创建多个类似的对象

javascript 复制代码
 const Peppa = {
        name: '佩奇',
        age: 6,
        sex: '女'
    }
    const George = {
        name: '乔治',
        age: 3,
        sex: '男'
    }
    const Mum = {
        name: '佩奇妈妈',
        age: 30,
        sex: '女'
    }
    const Dad = {
        name: '佩奇爸爸',
        age: 32,
        sex: '男'
    }

    function Pig(name, age, sex) {
        this,
        name = name;
        this.age = age;
        this.sex = sex;
    }
    const Peppa1 = new Pig('佩奇', 6, '女')
    const George1 = new Pig('乔治', 3, '男')
    const Mum1 = new Pig('佩奇妈妈', 30, '女')
    const Dad1 = new Pig('佩奇爸爸', 32, '男')

构造函数在技术上是常规函数

命名以大写字母开头。

通过new关键字来执行。

3.创建构造

使用new关键字调用函数的行为被称为实例化

实例化构造函数时没有参数可以省略()

构造函数内部无需写return,返回值即为新创建的对象

构造函数内部的return返回的值无效,所以不需要写return

new Object()和new Date()也是实例化的构造函数

4.实例化执行过程!

a.创建新空对象,

b.构造函数this指向新对象,

c.执行构造函数代码,修改this,添加新的属性,

d.返回新对象

5.实例成员和静态成员

实例成员:通过构造函数创建的对象称为实例对象,实例对象中的属性和方法称为实例成员(实例属性和实例方法)

为构造函数传入参数,创建结构相同但值不同的对象

构造函数创建的实例对象彼此独立互不影响

javascript 复制代码
    //实例对象上的属性和方法属于实例成员
    function Pig(name) {
        this.name = name;
    }
    const peiqi = new Pig('佩奇')
    const qiaozhi = new Pig('乔治')
    peiqi.name = '小猪佩奇' //实例属性
    peiqi.sayHi = () => { //实例方法
        console.log('hi~');
    }
    console.log('peiqi', peiqi);
    console.log('qiaozhi', qiaozhi);

构造函数的属性和方法称为静态成员(静态属性和静态成员)

静态成员只能通过构造函数来访问

静态方法中的this指向构造函数

javascript 复制代码
    //构造函数上的属性和方法称为静态成员
    function Pig(name) {
        this.name = name;
    }
    Pig.eyes = 2 //静态属性
    console.log('Pig.eyes', Pig.eyes);
    Pig.sayHi = function() { //静态方法
        console.log(this);
    }
    console.log('Pig.sayHi', Pig.sayHi);

6.内置构造函数

引用类型Object,Array,RegExp,Date等

包装类型String,Number,Boolean等

Object的静态方法Object.keys(),Object.values(),Object.assign()

javascript 复制代码
    const o = {
            name: '佩奇',
            age: 6
        }
    //获取所有属性名,返回数组
    console.log(Object.keys(o));

    //获取所有属性值,返回数组
    console.log(Object.values(o));

    //拷贝对象
    const oo = {}
    Object.assign(oo, o)
    //给对象添加属性
    console.log(oo);
    Object.assign(o, {
        gender: '女'
    })
    console.log(o);

二、编程思想

1.面向过程

分析解决问题所需要的步骤,然后用函数把这些步骤一步一步实现,使用的时候再一个一个的依次调用。按照分析好了的步骤,按照步骤解决问题。

优点:性能比面向对象高,适合和硬件联系很紧密的东西

缺点:没有面向对象易维护、易复用、易扩展

2.面向对象

把食物分解成一个一个对象,然后由对象之间分工与工作。

以功能来划分问题,而不是步骤。

面向对象编程(oop):在面向对象程序开发中,每一个对象都是功能中心,具有明确分工。面向对象编程具有灵活,代码可复用,容易维护和开发的优点,更适合多人合作的大型软件项目。

优点:封装性,继承性,多态性。可以使系统更加灵活,更加易于维护

缺点:性能比面向过程低

js实现面向对象需要借助构造函数来实现

构造函数实例创建的对象彼此独立,互不影响。

构造函数存在浪费性能的问题
javascript 复制代码
    function Star(name, age, sex) {
        this.name = name;
        this.age = age;
        this.sing = function() {
            console.log('sing');
        };
    }
    const ldh = new Star('ldh', 55)
    const zxy = new Star('zxy', 58)
    console.log(ldh.sing === zxy.sing); //false

三、原型

原型可以解决构造函数里浪费内存的问题。

1.目的:能够利用原型对象实现方法共享

构造函数通过原型分配的函数是所有对象所共享的。

js规定:每一个构造函数都有一个prototype属性,指向另一个对象,我们也称为原型对象。

这个对象可以挂载函数,对象实例化不会多次创建原型上函数,节约内存。

我们可以把那些不变的方法,直接定义在prototype对象上,这样所有对象的实例就可以共享这些方法。

javascript 复制代码
    function Star(name, age, sex) {
      //公共属性写到构造函数里
        this.name = name;
        this.age = age;
    }
    const ldh = new Star('ldh', 55)
    const zxy = new Star('zxy', 58)
    console.log('Star.prototype', Star.prototype)
    //构造函数有一个prototype属性,公共方法写到原型对象身上
    Star.prototype.sing = function() {
        console.log('唱歌');
    };
    ldh.sing()
    zxy.sing()
    console.log(ldh.sing === zxy.sing); //true

构造函数和原型对象中的this都指向实例化对象。

javascript 复制代码
    let a;
    let b;

    function Star(name, age, sex) {
        a = this;
        this.name = name;
        this.age = age;
    }
    const ldh = new Star('ldh', 55)
    console.log(a === ldh);//true

    //构造函数里面的 this就是实例对象
    Star.prototype.sing = function() {
        b = this;
        console.log('唱歌');
    };
    ldh.sing()
    console.log(b === ldh);//true
javascript 复制代码
    //自己定义数组扩展方法max,min,sum
    Array.prototype.max = function() {
        //原型函数里面的this指向实例化对象arr
        return Math.max(...this)
    }
    Array.prototype.min = function() {
        //原型函数里面的this指向实例化对象arr
        return Math.min(...this)
    }
    Array.prototype.sum = function() {
        //原型函数里面的this指向实例化对象arr
        return this.reduce((sum, item) => sum + item, 0)
    }
    const arr = [1, 2, 3]
    console.log(arr.max());
    console.log(arr.min());
    console.log(arr.sum());
2.constructor属性

每个原型对象里面都有一个constructor属性,该属性指向该原型对象的构造函数.

javascript 复制代码
    function Star() {}
    const ldh = new Star()
    console.log(Star.prototype.constructor === Star);//true

如果有多个对象的方法,我们可以给原型对象采取对象形式赋值。

但是这样就会覆盖构造函数原型对象原来的内容,修改后原型对象constructor就不再指向当前构造函数了,我们需要在修改后的原型对象中,添加一个constructor指向原来的构造函数。

javascript 复制代码
    function Star() {}
    // Star.prototype.sing = function() {
    //     console.log('唱歌');
    // }
    // Star.prototype.dance = function() {
    //         console.log('跳');
    //     }
    Star.prototype = {
        //重新指回创造这个原型对象的构造函数
        constructor: Star,
        sing: function() {
            console.log('唱歌');
        },
        dance: function() {
            console.log('跳');
        }
    }
    console.log(Star.prototype);

3.原型

在每个对象都会有一个__proto_指向构造函数的prototype原型对象,之所以我们可以使用构造函数prototype原型对象的属性和方法,就是因为对象有__proto_原型存在。

__protp_是js非标准属性,[prototype]和__protp_意义相同

用来表明当前实例对象指向哪个原型对象prototype

__protp_对象原型里面也有一个constructor属性,指向创建该实例对象的构造函数。

javascript 复制代码
    function Star() {}
    const ldh = new Star()
    //每一个构造函数都有一个prototype属性,指向另一个对象,我们也称为原型对象。
    console.log('Star.prototype', Star.prototype);
    //每个原型对象里面都有一个constructor属性,该属性指向该原型对象的构造函数.
    console.log(Star.prototype.constructor === Star);//true
    //ldh.__proto__ 指向该构造函数的原型对象
    console.log(ldh.__proto__ === Star.prototype); //true
    //对象原型里面也有constructor属性,指向创建该实例对象的构造函数。
    console.log(ldh.__proto__.constructor === Star); //true

总结

prototype是原型对象,构造函数都自动有原型对象

construvtor属性在原型对象(prototype)和对象原型(proto)上都有 ,指向创建实例对象/原型的构造函数。

__proto__属性在实例对象里面,指向原型对象。

4.原型继承

继承是面向对象编程的另一个特征,通过继承进一步提升代码封装的程度,js中大多是借助原型对象实现继承的特性。

javascript 复制代码
    const Person = {
        head: 1,
        eyes: 2
    }
    function Woman() {}
    //Woman通过原型来继承Person
    Woman.prototype = Person
    //指回原来的构造函数
    Woman.prototype.constructor = Woman
    const red = new Woman()
    console.log('red', red);

问题:同时使用了同一个对象,根据引用类型的特点,他们指向同一个对象,修改一个都会改变。

javascript 复制代码
    const Person = {
        head: 1,
        eyes: 2
    }

    function Woman() {}
    Woman.prototype = Person
    Woman.prototype.constructor = Woman
    Woman.prototype.sing = function() {
        console.log('唱歌');
    }
    const red = new Woman()
    console.log('red', red);


    function Man() {}
    Man.prototype = Person
    Man.prototype.constructor = Man
    const bob = new Man()
    console.log('bob', bob);
javascript 复制代码
    function Person() {
        this.head = 1
        this.eyes = 2
    }

    function Woman() {}
    //父构造函数(父类)子构造函数(子类) 子类的原型=new父类
    Woman.prototype = new Person()
    Woman.prototype.constructor = Woman
    Woman.prototype.sing = function() {
        console.log('唱歌');
    }
    const red = new Woman()
    console.log('red', red);

    function Man() {}
    Man.prototype = new Person()
    Man.prototype.constructor = Man
    const bob = new Man()
    console.log('bob', bob);
5.原型链

基于原型对象的继承使得不同的构造函数的原型对象关联在一起,并且这种关联的关系是一种链状结构,我们将原型对象的链状结构关系称为原型链。

javascript 复制代码
    function Person() {}
    const ldh = new Person()
    console.log(ldh.__proto__);
    console.log(Person.prototype);
    console.log(ldh.__proto__ === Person.prototype); //true
    console.log(Person.prototype.__proto__ === Object.prototype); //true
    console.log(Object.prototype.__proto__); //null
    console.log(ldh instanceof Person); //true
    console.log(ldh instanceof Object); //true

原型链查找规则

a.当访问一个对象的属性时,首先查找这个对象自身有没有该属性。

b.如果没有就查找它的原型,也就是__proto__指向的prototype原型对象。

c.如果没有找到就查找原型对象的原型。

d.依次类推一直找到Object为之。

e.__proto__对象原型的意义就在于为对象成员查找机制提供一个方向。

f.可以使用instanceof 运算符用于检测构造函数的prototype属性是否出现在某个实例对象的原型链上

四、示例

1.封装模态框
html 复制代码
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
</head>

<body>
    <button id="delete">删除</button>
    <button id="login">登录</button>
</body>
</html>

<script>
    //构造函数封装模态框
    function Modal(title = '', message = '') {
        // 1.创建div标签
        // 2.给div标签添加类名modal
        // 3.modal盒子内部填充2个div并且修改文字内容
        this.modalBox = document.createElement('div')
        this.modalBox.className = "modal"
        this.modalBox.innerHTML = `<div class="header">${title}<i>x</i></div>
    <div class="main">${message}</div>`
    }

    //挂载open方法
    Modal.prototype.open = function() {
        //准备open显示时先判断 页面中有没有modal,有就移除,没有就添加
        const box = document.querySelector('.modal')
        box && box.remove()
        document.body.append(this.modalBox)
            //等盒子显示出来,就可以绑定关闭事件
        this.modalBox.querySelector('i').addEventListener('click', () => {
            //需要用箭头函数,这个this指向实例对象
            this.close()
        })
    }
    Modal.prototype.close = function() {
        this.modalBox.remove()
    }

    document.querySelector('#delete').addEventListener('click', () => {
        const del = new Modal('温馨提示', '你没有权限删除')
        del.open()
    })
    document.querySelector('#login').addEventListener('click', () => {
        const login = new Modal('友情提示', '你没有注册')
        login.open()
    })
</script>
2.选项卡(构造函数写法)
javascript 复制代码
<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
    <style>
        #box div,
        #box1 div {
            width: 200px;
            height: 50px;
            background-color: palegreen;
            display: none;
        }
        
        .active {
            background-color: palevioletred;
        }
        
        #box .current,
        #box1 .current {
            display: block;
            background-color: palegreen;
        }
    </style>
</head>

<body>
    <div id="box">
        <section>
            <button class="active">娱乐</button>
            <button>体育</button>
            <button>教育</button>
        </section>
        <section>
            <div class="current">娱乐</div>
            <div>体育</div>
            <div>教育</div>
        </section>
    </div>

    <hr>
    <div id="box1">
        <section>
            <button class="active">娱乐</button>
            <button>体育</button>
            <button>教育</button>
        </section>
        <section>
            <div class="current">娱乐</div>
            <div>体育</div>
            <div>教育</div>
        </section>
    </div>
</body>
<script>
    //构造函数
    function Tab(ele, type) {
        this.ele = document.querySelector(ele); // {ele:"元素"}
        this.btns = this.ele.children[0].children
        this.divs = this.ele.children[1].children
        this.changeColor(type); // 函数的调用  "click"
    }
    Tab.prototype.changeColor = function(type) {
            // this: 指向的是实例化对象的this
            for (let i = 0; i < this.btns.length; i++) {
                this.btns[i].addEventListener(type, () => {
                    console.log(2);
                    // this.btns[i]:i就可以用了,this.btns[i]代表具体点击的元素
                    for (var j = 0; j < this.btns.length; j++) {
                        this.btns[j].classList.remove("active")
                        this.divs[j].classList.remove("current")
                    }
                    this.btns[i].classList.add("active")
                    this.divs[i].classList.add("current")
                })
            }
        }
        // 创建对象
    let tab1 = new Tab("#box", "click")
    let tab2 = new Tab("#box1", "mouseover")

</script>

</html>

五、class类

从ES6开始,JavaScript引入了类(class)的语法,可以用来创建构造函数。这种语法更加简洁和清晰。

javascript 复制代码
    //写法一: 构造函数
    function Person(name, age) {
        this.name = name;   
        this.age = age;   
    }
    Person.prototype.say = function () {
        console.log("呵呵");
    }
    let p1 = new Person("zs", 10)
    let p2 = new Person("zs", 10)
    console.log(p1);
    console.log(p1 == p2); // false

    //写法二: class类的写法
    class Person {
        constructor(name, age) {
            this.name = name;
            this.age = age;
        }
        //相当于挂载在了构造函数的原型对象上, Person.prototype.say = function(){}
        say() {  
            console.log("呵呵");
        }
    }
    let p1 = new Person("zs", 10)
    let p2 = new Person("zs", 10)
    console.log(p1);
    console.log(p1 == p2); // false
选项卡(class类写法)
html 复制代码
<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
    <style>
        #box div,
        #box1 div {
            width: 200px;
            height: 50px;
            background-color: palegreen;
            display: none;
        }
        
        .active {
            background-color: palevioletred;
        }
        
        #box .current,
        #box1 .current {
            display: block;
            background-color: palegreen;
        }
    </style>
</head>

<body>
    <div id="box">
        <section>
            <button class="active">娱乐</button>
            <button>体育</button>
            <button>教育</button>
        </section>
        <section>
            <div class="current">娱乐</div>
            <div>体育</div>
            <div>教育</div>
        </section>
    </div>

    <hr>
    <div id="box1">
        <section>
            <button class="active">娱乐</button>
            <button>体育</button>
            <button>教育</button>
        </section>
        <section>
            <div class="current">娱乐</div>
            <div>体育</div>
            <div>教育</div>
        </section>
    </div>
</body>
<script>
    // class类
    class Tab {
        constructor(ele, type) {
            this.ele = document.querySelector(ele); // {ele:"元素"}
            this.btns = this.ele.children[0].children
            this.divs = this.ele.children[1].children
            this.changeColor(type); // 函数的调用  "click"
        }
        changeColor(type) {
            for (let i = 0; i < this.btns.length; i++) {
                this.btns[i].addEventListener(type, () => {
                    console.log(2);
                    // this.btns[i]:i就可以用了,this.btns[i]代表具体点击的元素
                    for (var j = 0; j < this.btns.length; j++) {
                        this.btns[j].classList.remove("active")
                        this.divs[j].classList.remove("current")
                    }
                    this.btns[i].classList.add("active")
                    this.divs[i].classList.add("current")
                })
            }
        }
    }
    let tab1 = new Tab("#box", "click")
    let tab2 = new Tab("#box1", "mouseover")
</script>

</html>

六、继承

发生在两个构造函数之间,如果说A构造函数使用了B构造函数中的属性或者方法,A继承自B,B就属于父类,A属于子类

可以把构造函数当成一个类

1.借用父类构造函数继承

优点:可以继承父类构造函数内的属性和方法 ​ 缺点:不可以继承父类原型对象上的属性和方法

javascript 复制代码
    // 定义父类构造函数
    function Father(name, age) {
        this.name = name;
        this.age = age;
    }
    // 父类原型方法
    Father.prototype.sayHello = function() {
        console.log(`Hello, my name is ${this.name} and I am ${this.age} years old.`);
    };

    // 定义子类构造函数
    function Child(name, age, grade) {
        // 借用父类构造函数
        Father.call(this, name, age); // 通过call()借用父类构造函数
        this.grade = grade; // 子类特有的属性
    }

    // 使用示例
    const child = new Child('Alice', 10, 5); //{name: 'Alice', age: 10, grade: 5}
    child.sayHello(); //  child.sayHello is not a function
2.原型链继承

优点:可以继承父类原型对象上的属性和方法 ​ 缺点:不可以继承父类构造函数内的属性和方法

javascript 复制代码
    // Father类
    function Father(money) {
        this.money = money
    }
    Father.prototype.smoke = function() {
            console.log("吸烟");
        }
    // Son类
    function Son(name, money) {
        this.name = name;
    }
    Son.prototype = new Father(); // 会覆盖掉Son构造函数原型对象的constructor属性
    Son.prototype.constructor = Son; // 手动加上
    Son.prototype.study = function() {
        console.log("study");
    }

    var f = new Father(10000)
    var s = new Son("小张")
    console.log(s);
    s.smoke();
3.组合继承

优点:实现函数复用,保证每个实例之间的属性不会相互影响。 ​

缺点:使用了两次继承,代码书写繁琐,还出现了constructor的覆盖

javascript 复制代码
    // Father类
    function Father(money) {
        this.money = money
    }
    Father.prototype.smoke = function() {
            console.log("吸烟");
        }

    // Son类
    function Son(name, money) {
        this.name = name;
        Father.call(this, money); // 继承父类属性的关键
    }
    Son.prototype = new Father(); // 继承父类原型对象方法的关键
    Son.prototype.study = function() {
        console.log("study");
    }

    var s = new Son("小张", 1000); // 即继承了属性
    console.log(s);
    s.smoke(); // 又继承了父类原型对象上的方法
4.es6继承:extends super

优点:1.既可以继承父类的属性,也可以继承父类原型对象上的方法 ​ 2.写法简单

javascript 复制代码
    class Father {
        constructor(money) {
            this.money = money;
        }
        smoke() {
            console.log('抽大烟');
        }
    }
    class Son extends Father {
        constructor(name, money) {
            super(money);
            this.name = name;
        }
    }
    // extends来继承父类的属性和方法,必须还需要使用 super()访问父类的构造器(构造器的代码走一遍)
    var son = new Son("小明", 10000)
    console.log(son);
    son.smoke()

    // 既可以继承父类的属性,也可以继承父类原型对象上的方法
相关推荐
乐闻x几秒前
最佳实践:如何在 Vue.js 项目中使用 Jest 进行单元测试
前端·vue.js·单元测试
遇到困难睡大觉哈哈12 分钟前
JavaScript面向对象
开发语言·javascript·ecmascript
檀越剑指大厂15 分钟前
【Python系列】异步 Web 服务器
服务器·前端·python
我是Superman丶17 分钟前
【前端】js vue 屏蔽BackSpace键删除键导致页面后退的方法
开发语言·前端·javascript
Hello Dam18 分钟前
基于 Spring Boot 实现图片的服务器本地存储及前端回显
服务器·前端·spring boot
小仓桑20 分钟前
利用 Vue 组合式 API 与 requestAnimationFrame 优化大量元素渲染
前端·javascript·vue.js
Hacker_xingchen20 分钟前
Web 学习笔记 - 网络安全
前端·笔记·学习
天海奈奈21 分钟前
前端应用界面的展示与优化(记录)
前端
多多*42 分钟前
后端并发编程操作简述 Java高并发程序设计 六类并发容器 七种线程池 四种阻塞队列
java·开发语言·前端·数据结构·算法·状态模式
ᥬ 小月亮44 分钟前
Layui表格的分页下拉框新增“全部”选项
android·javascript·layui