当前位置: 首页 > news >正文

js之原型及原型链

如果js没有构造函数

首先不考虑构造函数这个鬼东西,当他不存在。

这个时候,创建对象的方式就是

    <script type="text/javascript">
        var dog = {
            name: 'hachi',
            age: 3
        }

    </script>

然后在浏览器上观察该对象,可以看到该对象包含三个属性,age,name和[[Prototype]],这里的[[Prototype]]其实就是__proto__,咱也不知道为啥浏览器要显示[[Prototype]],但是确实这里只用使用dog.__proto__访问,好,下面就记住这个属性是__proto__就行了。这个属性的值是一个对象,该对象就是原型对象,类似于java中的父类的概念,只是类似。

接下来创建父对象animal,js没有父子的概念,这里应该称之为原型对象,然后让dog对象继承animal对象

    <script type="text/javascript">
        var dog = {
            name: 'hachi',
            age: 3
        }

        var animal = {
            shout: function (){}
        }

        dog.__proto__ = animal

    </script>

继续观察dog对象,红框表示,dog对象的原型对象是animal对象,即dog对象继承了animal对象,绿框表示,animal对象继承了Object对象。是的,任何对象,最终都会继承自Object对象。

这里的__proto__属性就表示了继承关系,例如a继承了b,那么a.__proto__ == b所以,上面这个例子,我们可以判断一下是否确实有该继承关系,如下图所示,确实存在继承关系。

为了更清楚的表示原型链的链条关系,下面创建myDog对象,继承dog对象

    <script type="text/javascript">
        var dog = {
            name: 'hachi',
            age: 3
        }

        var animal = {
            shout: function (){}
        }

        dog.__proto__ = animal

        var myDog = {
            host: '张三'
        }
        myDog.__proto__ = dog

    </script>

可以看到myDog的原型对象的原型对象就是animal

到这里,原型链就变得超级简单了,其实就是由__proto__属性串起来的一系列对象。

但是,实际上我们不会生硬的设置__proto__属性,而是通过Object.reate函数来实现继承,如下代码所示

    <script type="text/javascript">
        

        var animal = {
            shout: function (){}
        }
        
        dog = Object.create(animal)
        dog.name = 'hachi'
        dog.age = 3
        
        myDog = Object.create(dog)
        myDog.host = '张三'

    </script>

但是,加入构造函数后,事情又变麻烦了

如果js有了构造函数

那就复杂一些了。上一章提到,可以使用obj = {a:1}的形式来产生对象,也可以通过obj = Object.create(obj_proto)来产生obj对象,并且该对象的原型对象是obj_proto,其实,还有第三种方式来产生对象,那就是通过构造函数。如下代码所示,构造函数可以粗糙的认为理解为java中的类,例如这里的Animal,Dog和MyDog,都是构造函数,使用new关键字可以生成实例对象,例如这里的dog = new Dog(),就生成了构造函数Dog的一个实例对象。一个构造函数可以生成许许多多的实例对象。

    <script type="text/javascript">
        var Animal = function (){
            this.shout = function (){}
        }
        var Dog = function (name,age){
            this.name = name
            this.age = age
        }
        var MyDog = function (host){
            this.host = host
        }
        
        
        var animal = new Animal()
        var dog = new Dog('hachi', 3)
        var myDog = new MyDog('张三')

    </script>

在语句var dog = new Dog()的时候,实际上发生了什么呢?

  1. 创建一个空对象,作为将要返回的对象实例。
  2. 将这个空对象的原型,指向构造函数的prototype属性。也就是将Dog的prototype属性赋值给dog的__proto__属性
  3. 将这个空对象赋值给函数内部的this关键字。
  4. 开始执行构造函数内部的代码。例如这里的this.name = name, this.age=age

可以检查一下是否构造函数的prototype属性和实例对象的__proto__属性相等,如图所示,的确是这样。

接下来进一步研究prototype属性到底是个啥,如图所示,prototype属性其实就是一个对象,这里叫做原型对象,该原型对象包括两个属性,一个是constructor,一个是[[Prototype]]。那么问一个问题,这里的[[Prototype]]是prototype还是__proto__呢?哈哈,当然是__proto__了,因为这是一个原型对象,不是函数,只有函数才拥有prototype属性。(在js中,函数其实也是对象,但是这里为了讨论起来简单,我说的对象是指的非函数的对象,函数就直接叫做函数好了)。

如果该构造函数是自定义的函数且没有修改其prototype值,那么这里prototype对象的[[prototype]]属性其实就是Object对象,也就是说使用该构造函数生成的任何实例对象,其原型对象(即__proto__),都是Object。

constructor又是啥呢?其实就是构造函数本身,这个字段的作用就是为了让每个实例对象记住自己的构造函数。例如,构造函数Dog的原型对象的constructor属性,就是Dog,如下图所示,这里不要问为什么,反正就是这么规定的,规定prototype有个constructor属性,规定constructor属性的值就是构造函数本身。可以认为这个属性就是为了让实例对象记住自己的构造函数吧

那么如何通过构造函数来实现继承呢?

其实就是改变构造函数的prototype属性就好了,但是一定记得修改constructor属性。如下代码所示,Dog.prototype = animal将animal对象作为构造函数Dog的原型对象(prototype属性),Dog.prototype.constructor = Dog设置constructor属性的值为Dog;同理,MyDog.prototype = dog将dog对象作为构造函数MyDog的原型对象(prototype属性),MyDog.prototype.constructor = MyDog设置constructor属性的值为MyDog。

    <script type="text/javascript">
        var Animal = function (){
            this.shout = function (){}
        }
        var Dog = function (name,age){
            this.name = name
            this.age = age
        }
        var MyDog = function (host){
            this.host = host
        }


        var animal = new Animal()
        Dog.prototype = animal
        Dog.prototype.constructor = Dog

        var dog = new Dog('hachi', 3)
        MyDog.prototype = dog
        MyDog.prototype.constructor = MyDog

        var myDog = new MyDog('张三')

    </script>

可以检查一下继承关系是否构建好,如下图所示

其实也好理解,当我执行dog = new Dog()后,构造函数Dog的prototype属性赋值给dog的__proto__属性了,而Dog.prototype就是animal;当我执行myDog = new MyDog()后,构造函数MyDog的prototype属性赋值给myDog的__proto__属性了,而MyDog.prototype就是dog,所以继承关系就成立了。

总结起来就是,非函数对象都有一个__proto__属性,该属性指向其原型对象(也就是父对象),对象之间通过__proto__属性串联起来构成原型链,而对象可以通过构造函数产生,构造函数有一个prototype属性,使用构造函数生成示例对象后,prototype属性会赋值给实例对象的__proto__属性。

相关文章:

  • Linux之环境变量(超详细版)
  • 计算机毕业设计SpringBoot+Vue.js党员教育管理系统(源码+文档+PPT+讲解)
  • vue3之echarts仪表盘
  • JavaScript+vue 实现复制文字功能
  • Linux·数据库INSERT优化
  • 【MySQL、Oracle、SQLserver、postgresql】查询多条数据合并成一行
  • SpaCy处理NLP的详细工作原理及工作原理框图
  • Flutter 学习之旅 之 flutter 使用 carousel_slider 简单实现轮播图效果
  • JAVA毕设项目-基于SSM框架的百色学院创新实践学分认定系统源码+设计文档
  • chrome Vue.js devtools 提示不支持该扩展组件,移除
  • 【JQuery—前端快速入门】JQuery 操作元素
  • 如何使用 Ollama 的 API 来生成聊天
  • js基本功
  • 【软考-架构】2.2、进程调度-死锁-存储管理-固定分页分段
  • 网络安全需要学多久才能入门?
  • 大语言模型技术发展
  • Dify部署-(零基础)(个人体验)(Linux)(白嫖)(可部署大模型)
  • Vue前端开发- Vant之Card组件
  • visual studio 2022 手工写一个简单的MFC程序
  • 如何在Android中实现服务(Service)
  • 天长做网站的/网上推广专员是什么意思
  • 百度不收录网站首页/中国十大电商平台
  • 网站开发 电话/seo优化收费
  • 网站后台凡科建设/培训机构好还是学校好
  • 优秀网站建设方案/2020站群seo系统
  • 设计方案网站/网站怎么推广效果好一点呢