JS原型

163 阅读3分钟

一 什么是原型,原型是做什么的

  1. 请看如下代码,定义了一个构造函数Beauty,用来构造对象,每当新创建一个对象let b1=new Beauty('小美')就会开辟内存,将新对象的name属性以及doSomething方法存进去,name如果创建100个新对象,doSomething方法是不是要存100次?浪费内存!!!!
  function Beauty(name){
     this.name=name;
     this.doSomething=function(){
        console.log(this.name+`在喝茶`);
     }
  }
  
  1. 有没有什么方法可以只将doSomething在内存中存一次,而所有新创建的对象都可以使用呢?
  2. 有,原型。

二 使用原型的构造函数

  1. 将上文中代码改为如下格式
  function Beauty(name){
     this.name=name;
     
  }
  Beauty.prototype.doSomething=function(){
        console.log(this.name+`在喝茶`);
     }
  1. 为构造函数设置一个prototype属性,这个属性包含一个对象,所有实例对象需要共享的属性和方法,都放在这个对象里面;那些不需要共享的属性和方法,就放在构造函数里面。

三 怎么获取定义在prototype上的共享属性呢

  1. 实例对象一旦创建,将自动引用prototype对象的属性和方法。故直接使用共享属性和方法即可
  1. 实例对象是怎么自动引用的prototype对象上的属性和方法呢?如图所示,新创建的实例上有一个__proto__属性,可通过该属性获取到doSomething方法。
  1. 观察上图执行结果不难发现,b1.__proto__===Beauty.prototype

四 问题

  1. 如果执行以下代码,那么运行结果分别是什么?

    let b1=new Beauty('小美')
    let b2=new Beauty('大美')
    b2.doSomething=function(){
       console.log('大美改了doSomething了吗')
    }
    b1.doSomething();
    b2.doSomething();
    
  1. 根据输出,可以发现,b2并没有更改prototype上面的doSomething方法,所以b1输出的依旧是'小美在喝茶',b2只是在自身上面增加了一个doSomething方法,因此,当b2调用doSomething方法时,优先使用自身上面的方法,如果没有,才会通过原型链访问原型上的doSomething。

  2. 如果b2想要修改原型上面的doSomething方法,怎么修改呢?通过__proto__属性修改

b2.__proto__.doSomething=function(){
   console.log('大美改了doSomething了吗')
}

五 关于普通对象x的原型

  1. [x的原型]等价于[x.__proto__所指的对象],有时为了方便,我们可以认为[x的原型]等价于[x.__proto__]

  2. 一个对象的原型指的是这个对象和其他同类对象的公有属性的集合,比如obj1和obj2同时拥有toString/valueOf/,那么toString、valueOf等属性组成的对象,就是obj1和obj2的原型,这个原型的地址一般存储在构造函数的prototype里。

  3. x.__proto__和Object.prototype存储着同一个对象的地址,这个就是x的原型。 每个对象都有原型,但除了[根对象Object.prototype]比较特殊,Object.prototype这个对象的原型为空null。

六 关于prototype

  1. 所有函数一出生就有一个prototype属性
  2. 所有prototype一出生就有一个constructor属性
  3. 所有constructor属性一出生就保存了对应的函数的地址
  4. 如果一个函数不是构造函数,他依然拥有prototype属性,只不过这个属性暂时没什么用
  5. 如果一个对象不是函数,那么这个对象一般来说没有prototype属性,但这个对象一般一定会有__proto__属性

七 关于Object.prototype

  1. Object.prototype是[Object构造出来的对象obj]的原型,即obj.__proto__===Object.prototype
  2. Object.__proto__是Object的原型,由于Object是函数,而所有函数的原型都是Function.prototype,所以Object.__proto__===Function.prototype
  3. Object.prototype不是Object的原型,Object.__proto__才是Object的原型(还记得之前答过[x.原型 等价于x.__proto__]吗,现在只不过是把x替换成Object。很多人都搞不清楚Object.__proto__和Object.prototype哪一个才是Object的原型,其实只要记住公式就好办了。)

八 实现,不使用new调用构造函数,就报错

    function Con(name){
        this.name=name;
        if(this.__proto__!==Con.prototype){
                console.log(new Error('创建对象失败'))
        }
    }

    Con.prototype={
         print:function(){
            console.log(this.name)
    }
    }

    // let b=new Con('www');
    // b.print();

    Con('qqq')