美文网首页
JS-理解原型与原型链

JS-理解原型与原型链

作者: 爱学习的小仙女早睡早起 | 来源:发表于2021-12-29 14:18 被阅读0次

    构造函数

    构造函数就是一个普通的函数,创建方式和普通函数没有区别,不同的是构造函数习惯上首字母大写。另外就是调用方式的不同,普通函数是直接调用,而构造函数需要使用new关键字来调用

        function Person(name, age, gender) {
            this.name = name
            this.age = age
            this.gender = gender
            this.sayName = function () {
                alert(this.name);
            }
        }
        var per = new Person("孙悟空", 18, "男");
        function Dog(name, age, gender) {
            this.name = name
            this.age = age
            this.gender = gender
        }
        var dog = new Dog("旺财", 4, "雄")
        console.log(per);//当我们直接在页面中打印一个对象时,事件上是输出的对象的toString()方法的返回值
        console.log(dog);
    

    每创建一个Person构造函数,在Person构造函数中,为每一个对象都添加了一个sayName方法,也就是说构造函数每执行一次就会创建一个新的sayName方法。这样就导致了构造函数执行一次就会创建一个新的方法,执行10000次就会创建10000个新的方法,而10000个方法都是一摸一样的,为什么不把这个方法单独放到一个地方,并让所有的实例都可以访问到呢?这就需要原型(prototype)

    原型

    在JavaScript中,每当定义一个函数数据类型 (普通函数、类)时候,都会天生自带一个prototype属性,这个属性指向函数的原型对象,并且这个属性是一个对象数据类型的值。
    image.png

    原型对象就相当于一个公共的区域,所有同一个类的实例都可以访问到这个原型对象,我们可以将对象中共有的内容,统一设置到原型对象中。

     function Person(name,age){
                this.name=name
                this.age=age
                this.sayName = function () {
                    alert(this.name);
                }
            }
    

    改造下

     function Person(name,age){
                this.name=name
                this.age=age
            }
     
            Person.prototype.year=2021   // 往原型上添加一点东西
            Person.prototype.sayName =function(){
                alert(this.name);
            }
            const personA=new Person()
            const personB=new Person()
            console.log('personA',personA , personB)
            console.log(personA.hasOwnProperty('year'), 'year' in personA ) // false true
            console.log(personB.hasOwnProperty('year'), 'year' in personB) // false true
            // hasOwnProperty只会从实例本身上找属性, in会从实例所属类的原型身上找
            console.log('personA.year',personA.year , personB.year )  // 2021 2021
    

    原型的作用:
    1:实现对象之间的数据共享。
    2.在es6之前,没有class的情况下,模拟面向对象,构造函数中放私有属性,原型上放公有属性,一般放方法。

    通过原型添加的方法,可以完美的解决属性与方法共享问题,从而节省了内存空间..

    原型链

    每一个对象数据类型(普通的对象、实例、prototype......)也天生自带一个属性__proto__,这个属性的值是当前实例所属类的原型对象(prototype)。
    原型对象中有一个属性constructor, 它指向构造函数。

    image.png
        function Person() {}   // 构造函数
        var person = new Person()  // 实例person
        console.log(person.__proto__ === Person.prototype)  // true
        console.log(Person.prototype.constructor===Person)  // true
    
        //顺便学习一个ES5的方法,可以获得对象的原型
        console.log(Object.getPrototypeOf(person) === Person.prototype)   // true
    

    js实例对象的原型属性__proto__的属性值,指向这个实例对象所属类的原型

    例如 new Array 出来的数组,属于Array这个类

    const xArray=new Array()
    

    那么

    xArray.__proto__ === Array.prototype  // true
    

    Array.prototype拥有的方法,array的每一个实例都会有,直接通过xArray.push()这样调用,而不需要xArray._ proto _.push,不需要这样去调用。

     Array.prototype.__proto__ === Object.prototype  // true
    
    Object.prototype._proto_  // null
    

    所谓原型链,指的就是这一条指针链!

    原型链的顶层就是Object.prototype,而Object的原型对象的是没有原型属性的。 Object.prototype._proto_ ===null

    何为原型链

    在JavaScript中万物都是对象,对象和对象之间也有关系,并不是孤立存在的。对象之间的继承关系,在JavaScript中是通过prototype对象指向父类对象,直到指向Object对象为止,这样就形成了一个原型指向的链条,专业术语称之为原型链。

    举例说明:person → Person → Object ,普通人继承人类,人类继承对象类

    当我们访问对象的一个属性或方法时,它会先在对象自身中寻找,如果有则直接使用,如果没有则会去原型对象中寻找,如果找到则直接使用。如果没有则去原型的原型中寻找,直到找到Object对象的原型,Object对象的原型没有原型,如果在Object原型中依然没有找到,则返回undefined。

    我们可以使用对象的hasOwnProperty()来检查对象自身中是否含有该属性;使用in检查对象中是否含有某个属性时,如果对象中没有但是原型中有,也会返回true

    function Person() {}
        Person.prototype.a = 123;
        Person.prototype.sayHello = function () {
          alert("hello");
     };
        var person = new Person()
        console.log(person.a)//123
        console.log(person.hasOwnProperty('a'));//false
        console.log('a'in person)//true
    

    person实例中没有a这个属性,从 person 对象中找不到 a 属性就会从 person 的原型也就是 person.__proto__,也就是 Person.prototype中查找,很幸运地得到a的值为123。那假如 person.__proto__中也没有该属性,又该如何查找?

    当读取实例的属性时,如果找不到,就会查找与对象关联的原型中的属性,如果还查不到,就去找原型的原型,一直找到最顶层Object为止。Object是JS中所有对象数据类型的基类(最顶层的类),在Object.prototype上没有__proto__这个属性。

    console.log(Object.prototype.__proto__ === null) // true
    
    image.png

    所谓原型链,指的就是图中的proto这一条指针链!

    原型链的顶层就是Object.prototype,而Object的原型对象的是没有原型属性的。

    先找自身,找不到就沿着__proto__一直往上找原型,直到找到最顶层的类Object,Object类没有原型了,如果还找不到就返回undefined


    实战:用ES6的class定义一套对象/函数

    ES6提供了class,但是这个并不是类,而是 Function 的语法糖。
    目的是简化ES5里面,为了实现继承而采用的各种“神操作”。

    用class来定义,结构和关系会非常清晰,再也不会看着头疼了,建议新手可以跳过ES5的实现方式,直接用ES6的方式。

    我们先定义一个Base,然后定义一个Person继承Base,再定义一个Man继承Person。
    也就是说,可以深层继承。

    class Base {
      constructor (title) {
        this.title = '测试一下基类:' + title
      }
    
      baseFun1(info) {
        console.log('\n这是base的函数一,参数:', info, '\nthis:', this)
      }
    }
    
    class Person extends Base{
      constructor (title, age) {
        super(title)
        this.title = '人类:' + title
        this.age = age
      }
    
      personFun1(info) {
        console.log('\n这是base的函数一,参数:', info, '\nthis:', this)
      }
    }
    
    
    class Man extends Person {
      constructor (title, age, date) {
        super(title, age)
        this.title = '男人:' + title
        this.birthday = date
      }
    
      manFun3 (msg) {
        console.log('jim 的 this ===', this, msg)
      }
    }
    
    
    • 构造函数 constructor
      打印结果很清晰的表达了,构造函数就是我们定义的class。

    • 属性
      属性比较简单,统统都挂在 this 上面,而且是同一个级别。

    • 函数
      函数就有点复杂了,首先函数是分级别的,挂在每一级的原型上面

    • 原型链
      Man的实例 > Man的原型 > Person的原型 > Base 的原型 > Object 的原型。
      通过 __proto__ 连接了起来。

      image.png

    Man的实例 man1,可以通过这个“链条”,找到 baseFun1,
    直接用 man1.baseFun1() 即可(✔),
    而不需要使用__proto__
    man1.__ proto__.__ proto__.__ proto__.baseFun1() (✘)

    man1.__ proto__ === Man.prototype
    Man.prototype .__proto__ === Person.prototype
    Person.prototype .__proto__ === Base.prototype
    Base.prototype .__proto__ === Object.prototype
    Object.prototype .__proto__ === null

    image.png

    Object是js顶层类,原型链的顶端也就是Object.prototype ,Object.prototype没有__proto__属性



    相关文章

      网友评论

          本文标题:JS-理解原型与原型链

          本文链接:https://www.haomeiwen.com/subject/wwdvqrtx.html