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()

    // 既可以继承父类的属性,也可以继承父类原型对象上的方法
相关推荐
前端郭德纲6 分钟前
深入浅出ES6 Promise
前端·javascript·es6
就爱敲代码12 分钟前
ES6 运算符的扩展
前端·ecmascript·es6
天天进步201527 分钟前
Lodash:现代 JavaScript 开发的瑞士军刀
开发语言·javascript·ecmascript
王哲晓33 分钟前
第六章 Vue计算属性之computed
前端·javascript·vue.js
假装我不帅36 分钟前
js实现类似与jquery的find方法
开发语言·javascript·jquery
究极无敌暴龙战神X39 分钟前
CSS复习2
前端·javascript·css
风清扬_jd1 小时前
Chromium HTML5 新的 Input 类型week对应c++
前端·c++·html5
Ellie陈1 小时前
Java已死,大模型才是未来?
java·开发语言·前端·后端·python
GISer_Jing2 小时前
React面试常见题目(基础-进阶)
javascript·react.js·前端框架
想做白天梦2 小时前
双向链表(数据结构与算法)
java·前端·算法