原型、继承

本文详细讲解了JavaScript中对象的创建、构造函数的作用、原型的运用、继承机制以及其在代码复用和内存优化中的重要性。通过实例阐述了如何利用构造函数创建对象、原型来共享属性和方法,以及如何实现构造函数的继承以减少代码冗余。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

1,对象和构造函数

1.1对象

对象有两种创建方式:1,字面量创建 2,使用new+构造函数创建

1.2构造函数

构造函数和普通函数的区别主要有三点:

  • 构造函数的函数名以大写字母开头,用于和普通函数区分

  • 构造函数中的this指向的是构造函数创建的空对象

  • 构造函数不用return,默认的返回值就是this指向的对象

function Student(name, age, sex, phone, address) {
            // this.name指的是对象的属性名,后边的name指的是构造函数的参数名
            this.name = name;
            this.age = age;
            this.sex = sex;
            this.phone = phone;
            this.address = address
}//这就是Student的一个构造函数
 var zhansgan = new Student("张三", 20, "男", "110", "河南");

总结:使用对象结构和构造函数的好处

对象结构的好处:可以把一些关联性高的数据结合成一个整体,数据调用,查找更方便,快速,准确。

构造函数的好处:在创建大量相似对象时,使用构造函数,可减少代码量,简化结构

2,原型

原型:每一个构造函数都有一个属性prototype,它的值就是一个对象,叫做原型对象。原型对象又有一个属性叫constructor(构造器),指向构造函数本身。

可以把构造函数相同的属性和方法,定义在原型对象中,当把属性或方法定义到构造函数原型中之后,构造函数创建的对象就不再包含这些属性和方法了,内存中不再出现重复的数据,而这些数据只有在构造函数原型中独一份。

属性和方法定义到原型对象中的语句:

        Student1.prototype.address = "河南"
        Student1.prototype.study = function(){
            console.log(this.name + "在学前端")
        }
  function Student1(name, age, sex, phone){
            this.name = name;   
            this.age = age;
            this.sex = sex;
            this.phone = phone;
        }
Student1.prototype.address = "河南"
        Student1.prototype.study = function(){
            console.log(this.name + "在学前端")
        }
        var zhangsan = new Student1("张三", 20, '男', '110')
        var lisi = new Student1("李四", 22, '男', '120')
        var wangwu = new Student1("王五", 23, '女', '130')
        var zhaoliu = new Student1("赵六", 24, '女', '140')
        var array = [zhangsan, lisi, wangwu, zhaoliu];
        console.log(3, array)

2.2 js对象调用属性方法的流程

js对象调用属性方法时的流程是,对象会先从对象本身查找对象的属性或方法,如果有,直接调用,如果没有,呢去构造函数中查找,如果有直接调用,如果没有,再去构造函数的原型中查找,原型中有这个属性或方法就可以直接调用

所以,对象中不存在address属性和study方法,但是可以调用

2.3修改原型中属性或者方法

想要修改原型中属性或者方法可以单独修改这个对象的属性和方法(相当于在对象中添加属性或者方法),对象中属性方法的修改,并不影响构造函数原型中的数据。

function Student1(name, age, sex, phone){
            this.name = name;   
            this.age = age;
            this.sex = sex;
            this.phone = phone;
        }
Student1.prototype.address = "河南"
        Student1.prototype.study = function(){
            console.log(this.name + "在学前端")
        }
var xiaoqiang = new Student1("小强", 25, '男', '150')
        console.log(xiaoqiang.address)
        // 此时可以单独修改这个对象的地址
        xiaoqiang.address = "四川"
        // 对象中属性方法的修改,并不影响构造函数原型中的数据
        console.log(xiaoqiang, zhangsan, Student1.prototype)
        // 此时,对象中有address,原型中也有address, 优先调用对象自身的address

3,构造函数的继承

当两个构造函数中有共同的属性或者方法,可以在父构造函数中定义这些共同的属性或者方法,然后用子构造函数继承父构造函数,这样子构造函数就可以用父构造函数的属性和方法,但是父构造函数不能使用子构造函数的属性和方法。

 // (一)以下是两个构造函数, 他们有一些共同的属性name,age
        function People(name, age){
            this.name = name;
            this.age = age;
            this.eat = function(){
                // 此处省略600行代码
                console.log(this.name + "是吃货")
            }
        }
        var zhangsan = new People("张三", 20)
        function Student(name, age, stuID){
            this.name = name;
            this.age = age;
            this.stuID = stuID;
            this.eat = function(){
                // 此处省略600行代码
                console.log(this.name + "是吃货")
            }
            this.study = function(){
                console.log(this.name + "是学渣")
            }
        }
        var lisi = new Student("李四",21, "123456")
        console.log(zhangsan, lisi)
        
        // 此时,People构造函数中有很多属性和方法, Student构造函数中也有People中的属性和方法,还有Student自己独有的, 如果People中属性和方法非常多非常复杂, Student中再写一遍太麻烦了, 此时可以通过继承的形式, 让 Student 继承 People, 这样Student中不用再写重复的属性和方法, 创建的对象可直接调用People中的属性和方法
        

3.1 继承的原理

原理:在子构造函数中调用父构造函数,需要保证父构造函数中的this和子构造函数中的this保持一致,需要使用call修改this指向

// (二)以下是 Student 继承 People 的写法,  Student为子, People为父
        function People2(name, age){
            this.name = name;
            this.age = age;
            this.eat = function(){
                console.log(this.name + "是吃货")
            }
        }
        var zhangsan = new People2("张三", 20)
        function Student2(name, age, stuID){
            People2.call(this, name, age)
            this.stuID = stuID;
            this.study = function(){
                console.log(this.name + "是学渣")
            }
        }
        var lisi = new Student2("李四",21, "123456")
        console.log(zhangsan, lisi)
        console.log(lisi.name, lisi.age, lisi.stuID)
        lisi.study(); lisi.eat();

3.2 构造函数的继承和构造函数的原型的区别

  • 原型中的属性和方法对于每一个对象值都相同,而继承来的属性或方法值可以不相同

  • 原型中的属性和方法不会创建显示到对象中,而继承来的属性和方法,会在对象中创建并显示

3.3 构造函数的继承和原型结合使用

构造函数继承时,只继承了父构造函数中的属性和方法,而父构造函数原型中的属性和方法并没有继承过来,所以子构造函数对象无法调用父构造函数原型中的方法.只有在继承不只要继承构造函数,也要继承原型

   // (三),继承和原型结合使用, 以上的继承结构存在不足: People和Student构造函数中都有固定的函数eat,study, 我们完全可以把这两个函数定义子对应的原型对象中
        function People3(name, age) {
            this.name = name;
            this.age = age;
        }
        People3.prototype.eat = function() {
            console.log(this.name + "是吃货")
        }
        var zhangsan = new People3("张三", 20)
​
        function Student3(name, age, stuID) {
            People3.call(this, name, age)
            this.stuID = stuID;
        }
        Student3.prototype.study = function() {
            console.log(this.name + "是学渣")
        }
        var lisi = new Student3("李四", 21, "123456")
        console.log(zhangsan, lisi)
        console.log(lisi.name, lisi.age, lisi.stuID)
        lisi.study();
        // 问题: 子构造函数对象不能调用父构造函数原型中的方法
        // lisi.eat();  // error : lisi.eat is not a function
        // 原因: 构造函数继承时, 只继承了父构造函数中的属性和方法, 而父构造函数原型中的属性和方法并没有继承过来, 所以子构造函数对象无法调用

原型的继承:通过Object.create()

 // (四) 解决方案: 原型的继承: 在继承时不只要继承构造函数,也要继承原型
        function People4(name, age) {
            this.name = name;
            this.age = age;
        }
        People4.prototype.eat = function() {
            console.log(this.name + "是吃货")
        }
​
        function Student4(name, age, stuID) {
            People4.call(this, name, age)
            this.stuID = stuID;
        }
        // 在设置子构造函数原型时, 需要先继承父构造函数原型
        // Student4.prototype = People4.prototype // 错误, 不能直接等, 因为这样子写是浅拷贝, 造成两个构造函数公用一个原型对象, 如果一个构造函数修改了原型, 另一个的原型也会被修改,造成数据混乱出错
        // Object.create()创建一个新的对象, 这个是空的, 属于深拷贝
        Student4.prototype = Object.create(People4.prototype)
            // 原型继承后, 原型的构造器constructor属性丢了, 需要重新设置(没啥用,不设置也行)
        Student4.prototype.constructor = Student4;
        Student4.prototype.study = function() {
            console.log(this.name + "是学渣")
        }
        console.log("prototype", People4.prototype, Student4.prototype)
        var lisi = new Student4("李四", 21, "123456")
        console.log(lisi.name, lisi.age, lisi.stuID)
        lisi.study();
        lisi.eat();
        // 原型继承之后, 子构造函数对象就可以调用父构造函数原型中的属性和方法了

3.4 总结

构造函数继承的步骤:

  • 在子构造函数中调用夫构造函数,并使用call修改父构造函数中this指向

  •  People4.call(this, name, age)
  • 在构造函数继承之后,构造函数的原型也要继承

  • Student4.prototype = Object.create(People4.prototype)
  • 重置子构造函数原型的构造器constructor

  • Student4.prototype.constructor = Student4;
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值