zoukankan      html  css  js  c++  java
  • 理解js中的原型和原型链

    不记下来总是容易忘记掉,相当于备忘吧。。本文主要参考高程3,以及一点自己的想法,如有不对,还望指正

    • 为什么要使用原型模式?

    我们创建对象最一开始用的工厂模式:

    function createPeople(name, age, job) {
            var o = new Object();
            o.name = name;
            o.age = age;
            o.job = job;
            o.sayName = function () {
                alert(this.name);
        }
    }    
    
    var person1 = createPeople('Nicolas', 29, 'SoftWare Engineer');
    var person1 = createPeople('Greg', 27, 'Doctor');

    该模式的缺点在于: 无法获知一个对象的类型

    为了解决该问题,就有了构造函数模式:

    function Person(name, age, job) {
            this.name = name;
            this.age = age;
            this.job= job;
            this.sayName = function() {
                alert(this.name);
        };
    } 
    
    var people1 = new Person('Nicholas', 29, 'Software Engineer');
    var people2 = new Person('Greg', 27, 'Doctor');

    new其实包含了一下4个步骤: 

    创建一个空对象 -> 将构造函数的作用域赋给新对象(this指向该对象) -> 执行构造函数中的代码(为新对象添加属性) -> 返回新对象

     person1 和 person2 分别保存着Person对象的一个不同的实例。这两个对象都有一个constructor属性,指向Person。

    该模式的缺点在于: 每个方法都要在每个实例上重新创建一遍。比如每个peson都有sayName这个方法,但是都指向不同的saynam实例,实际上并不需要这么多的sayname实例。那么一个解决方法是:

    function Person(name, age, job) {
        this.name = name;
        this.age = age;
        this.job= job;
        this.sayName = sayName;
    } 
    
    function sayName() {
        alert(this.name);
    }
    
    var people1 = new Person('Nicholas', 29, 'Software Engineer');
    var people2 = new Person('Greg', 27, 'Doctor');

    缺点: 实际上我们并不需要这样一个全局的sayName函数,并且如果对象有很多方法的话,全局函数就会变得非常多。

    由此,我们就可以用原型模式来解决这个问题。

    这里先介绍一下原型:

    • 什么是原型

    当我们创建一个函数的时候,会给该函数创建一个prototype属性,该属性指向函数的原型对象。所有的原型对象有一个constructor属性,指向prototype属性所在的函数。即:

    Person.prototype.constructor = Person;

    创建实例之后,该实例中含有_proto_这个属性,指向构造函数的原型对象。找不到person的图了,凑合着看吧,这里_proto_画的有点不准确,指向的应该是构造函数的原型对象:

    当代码读取对象的某个属性的时候,先查询实例是否有该属性,没有的话就搜索实例的原型对象。

    回到原型模式,我们就可以这么构造一个对象:

    function Person() {
    } 
    
    Person.prototype.name = 'Nicholas';
    Person.prototype.age = 29;
    Person.prototype.job = 'SoftWare Engineer';
    Person.prototype.sayName = function() {
        alert(this.name);
    }
    
    var people1 = new Person();
    var people2 = new Person();

    在这里无法为构造函数传参,为了解决这个问题可以组合使用构造函数和原型模式:

    function Person(name, age, job) {
            this.name = name;
            this.age= age;
            this.job= job;
    } 
    
    Person.prototype.sayName = function() {
        alert(this.name);
    }
    
    var people1 = new Person("Nicholas", 29, "SoftWare Engineer");
    var people2 = new Person("Greg", 27, "Doctor");

    这是目前比较通用的构造方法。。。

    •  下面介绍原型链

    在js中,继承主要靠原型链来实现。实现原型链可以通过将构造函数的原型对象指向另一个类型的实例。

    function SuperType() {
        this.property = true;  
    }
    
    SuperType.prototype.getSuperValue = function() {
        return this.property;
    }
    
    function subType() {
        this.subproperty = false;
    }
    
    // 继承Supertype
    subType.prototype = new SuperType();
    
    subType.prototype.getSubValue = function() {
        return this.subproperty;
    }
    
    var instance = new SubType();
    alert(instance.getSuperValue); // true

    继承的本质上是重写原型对象,原来存在于SuperType的实例中的所有属性和方法也将存在于Subtype.prototype中。需要注意到的是:1.property这个属性位于SubType.property中,这一点可以这么理解:property是一个实例属性,而SubType.property是SuperType的一个实例。2.instance.constructor此时指向SuperType。

    原型链实现继承有一个问题,就是包含引用类型的原型:

    在构造函数里面定义属性不会与所有实例共享,而原型中定义的话就会与所有实例共享。

    function SuperType() {
        this.color = ['red', 'blue'];
    }
    
    function SubType() {
          
    }
    
    SubType.protoType = new SuperType();
    var instance1 = new SubType();
    instance1.push('black');
    
    var instance2 = new SubType();
    alert(instance2.colors);  // 'red, blue,black'

    这里SubType的原型是SuperType的实例,因此相当于在原型中定义了colors这个属性,而该属性是引用类型。

    为了解决这样的问题,我们使用借用构造函数实现继承,实现只需要在子类型构造函数内调用超类类型结构函数。

    function SuperType() {
        this.color = ['red', 'blue'];
    }
    
    function SubType() {
          SuperType.call(this);
    }
    
    var instance1 = new SubType();
    instance1.push('black');
    
    var instance2 = new SubType();
    alert(instance2.colors);  // 'red, blue'

    同时还解决了传参的问题:

    function SuperType(name) {
        this.name= name;
    }
    
    function SubType() {
        SuperType.call(this ,'Nicholas');
    }
    
    var instance = new SubType();
    alert(instance.name);  // 'Nicholas'

    然而借用构造也有缺点:超类中属性包含方法的时候,如果将方法写在超类的prototype中,那么该方法对子类是不可见的,如果是写在构造函数里的话,那么就无法实现方法的复用,解决这个缺点可以采用组合继承的方式:

    function SuperType(name) {
        this.name= name;
        this.color = ['red', 'green', 'blue'];
    }
    
    // 定义超类的方法
    SuperType.prototype.sayName= function() {
        alert(this.name);
    }
    
    function subType(name, age) {
        // 继承属性
        SuperType.call(this, name);
        // 定义自己的实例属性
        this.age = age;
    }
    
    // 继承方法
    SubType.prototype = new SuperType();
    // 定义自己的方法
    SubType.prototype.sayAge = function(){
        alert(this.age);
    }

    简单来说就是超类的属性用借构的方式继承,超类的方法用原型的方式来继承。。一般这种方式称为组合继承,也是使用最为广泛的继承模式。

  • 相关阅读:
    093孤荷凌寒自学第179天区块链093天Dapp048
    092孤荷凌寒自学第178天区块链092Dapp047
    091孤荷凌寒自学第177天区块链091天Dapp046
    090孤荷凌寒自学第180天区块链090天Dapp045
    089孤荷凌寒自学第175天区块链089天Dapp044
    088孤荷凌寒自学第174天区块链088天Dapp043
    087孤荷凌寒自学第173天区块链087天Dapp042
    孤荷凌寒自学第172天区块链086天Dapp041
    孤荷凌寒自学第171天区块链085天Dapp040
    iOS开发——高级篇——内存分析,Instruments
  • 原文地址:https://www.cnblogs.com/doudoujun/p/6420528.html
Copyright © 2011-2022 走看看