js--创建对象与对象继承

创建对象

  1. 工厂模式:
    function createPerson(name, age, job){
    var o = new Object();
    o.name = name;
    o.age = age;
    o.job = job;
    o.sayName = function(){
    alert(this.name);
    };
    return o;
    }
    var person2 = createPerson("Greg", 27, "Doctor");
  • 解析:虽然能创造对象,但是无法从constructor和instanceOf获取更多关于构造函数的信息,因为这里生成的对象的constructor都指向Object,而instanceOf只有Object为true.
  1. 正常构造函数模式:
    function Person(name, age, job){
    this.name = name;
    this.age = age;
    this.job = job;
    this.sayName = function(){
    alert(this.name);
    };
    }
    var person2 = new Person("Greg", 27, "Doctor");
  • 解析:函数里面没有return语句,必须要用new才能创建对象,如果直接调用Person,则里面的this会指向window,造成的结果是会在window上生成全局变量。
  • 使用new可以生成新对象,具体步骤如下:
    1. 开辟内存,创建新对象
    2. 将构造函数的作用域赋给新对象(因此this指向这个新对象)
    3. 将新对象的proto指向构造函数默认的prototype指针指向的对象
    4. 执行构造函数中的代码(为这个新对象添加属性)
    5. 返回新对象。
  1. 特殊比较——寄生构造函数模式:
    function Person(name, age, job){
    var o = new Object();
    o.name = name;
    o.age = age;
    o.job = job;
    o.sayName = function(){
    alert(this.name);
    };
    return o;
    }

     var friend = new Person("Nicholas", 29, "Software Engineer");
     friend.sayName();  //"Nicholas"
    
  • 解析:这里想要强调的是,当使用new操作符来对一个函数进行操作时,系统默认会创建新对象,此时还要根据函数中是否存在return值,来判断。如果没有返回值,则返回系统后台新创建的对象,如果有返回值而且是一个对象,则程序猿指定的返回值会代替新创建对象返回。
  • 所以,上面new之后返回给friend的是o,与工厂模式中一样,不能依赖instanceof和constructor来确定对象的具体类型。
  1. 原型模式:
    function Person(){
    }

     Person.prototype.name = "Nicholas";
     Person.prototype.age = 29;
     Person.prototype.job = "Software Engineer";
     Person.prototype.sayName = function(){
         alert(this.name);
     };
     
     var person1 = new Person();
     var person2 = new Person();
     
     person1.name = "Greg";
     alert(person1.name);   //"Greg" – from instance
     alert(person2.name);   //"Nicholas" – from prototype
    

对象继承

  1. 原型继承:
    function SuperType(){
    this.property = true;
    }

     SuperType.prototype.getSuperValue = function(){
         return this.property;
     };
     
     function SubType(){
         this.subproperty = false;
     }
     
     //inherit from SuperType
     SubType.prototype = new SuperType();
     
     SubType.prototype.getSubValue = function (){
         return this.subproperty;
     };
     
     var instance = new SubType();
     alert(instance.getSuperValue());   //true
    
     alert(instance instanceof Object);      //true
     alert(instance instanceof SuperType);   //true
     alert(instance instanceof SubType);     //true
    
     alert(Object.prototype.isPrototypeOf(instance));    //true
     alert(SuperType.prototype.isPrototypeOf(instance)); //true
     alert(SubType.prototype.isPrototypeOf(instance));   //true
    
  • 缺点:对象实例共享所有继承的属性和方法。
  1. 借用构造函数:
    function SuperType(){
    this.colors = ["red", "blue", "green"];
    }

     function SubType(){  
         //inherit from SuperType
         SuperType.call(this);
     }
    
     var instance1 = new SubType();
     instance1.colors.push("black");
     alert(instance1.colors);    //"red,blue,green,black"
     
     var instance2 = new SubType();
     alert(instance2.colors);    //"red,blue,green"
    
  • 解析:这里实际上是在(未来将要)新创建的SubType实例的环境下调用了SuperType构造函数,这样就会在新SubType对象上执行SuperType()函数中定义的所有对象初始化代码,这样每个SubType实例都会具有自己的colors属性。
  • 解决了上一方法中所有对象共享同一实例的缺点,每个对象都会调用SubType(),因此也都有自己的环境,会形成自己的实例属性。
  1. 组合继承(原型链+借用构造函数)

     function SuperType(name){
         this.name = name;
         this.colors = ["red", "blue", "green"];
     }
     
     SuperType.prototype.sayName = function(){
         alert(this.name);
     };
    
     function SubType(name, age){  
         SuperType.call(this, name);  //第二次调用超类构造函数
         
         this.age = age;
     }
    
     SubType.prototype = new SuperType();    //第一次调用超类构造函数
      SubType.prototype.constructor = SubType;
     SubType.prototype.sayAge = function(){
         alert(this.age);
     };
     
     var instance1 = new SubType("Nicholas", 29);
     instance1.colors.push("black");
     alert(instance1.colors);  //"red,blue,green,black"
     instance1.sayName();      //"Nicholas";
     instance1.sayAge();       //29
    
     var instance2 = new SubType("Greg", 27);
     alert(instance2.colors);  //"red,blue,green"
     instance2.sayName();      //"Greg";
     instance2.sayAge();       //27
    
  • 解析:这种方法通过使用原型链实现对原型属性和方法的继承(需共享的数据),通过借用构造函数来实现对实例属性的继承(需要每个实例独立的数据)。
  • 最终,子类会包含超类的全部实例属性。子类的原型与超类的原型会分开,是不同的对象,更改不会相互影响,但通过子类原型的proto可以取得超类的原型。
  • 组合继承最大的问题:无论什么情况下都会两次调用超类构造函数,一次是在创建子类原型的时候,第二次是在子类构造函数内部call。
  • instanceof和isPrototypeOf()也能够用于识别基于组合继承创建的对象。
  1. 原型式继承:
    function object(o){
    function F(){};
    F.prototype = o;
    return new F();
    }
  • 这意味着被传入的对象o成为了新对象的原型,新对象的proto直接指向o,并且当新对象对实例中不存在但原型上存在的属性操作时,对象o也会被随之改变。
  • 原型式继承还可以使用ES5中的Object.create(),如下:
    Square.prototype = Object.create(Rect.prototype,{
    constructor:{
    configure:true,
    enumerable:true,
    writable:true,
    value:Square
    }
    })
  • 解析:这样就实现了Square继承Rectangle的操作,还免去了生成中间构造函数F()的操作。
  1. 原型式继承进阶版——寄生式继承
    function createAnother(original){
    var clone = object(original); //这里沿用上面的object()函数
    clone.sayHi = function(){
    alert("hi");
    }
    return clone;
    }
    var person = {
    name: "json",
    age: 23
    }
    var newPerson = createAnother(person);
    newPerson.sayHi(); //"hi"
  • 解析:其实就是在上面基础上,增加了可以对继承后的对象添加实例方法或实例属性,然后这里继承以后,newPerson的proto指向person,两者修改其一,另一都会受到影响。
  1. 寄生组合式继承:
  • 完整版代码:
    function object(o){
    function F(){}
    F.prototype = o;
    return new F();
    }

            function inheritPrototype(subType, superType){
                var prototype = object(superType.prototype);   //返回一个新对象,新对象的__proto__指向superType.prototype            
                subType.prototype = prototype;                 //修改子类的原型,之后,subType的实例的__proto__会指向这个原型,而这个原型的__proto__会指向父类的原型superType.prototype,由此实现继承
                subType.prototype.constructor = subType;   //修正constructor
            }
                                    
            function SuperType(name){
                this.name = name;
                this.colors = ["red", "blue", "green"];
            }
            
            SuperType.prototype.sayName = function(){
                alert(this.name);
            };
    
            function SubType(name, age){  
                SuperType.call(this, name);
                this.age = age;
            }
    
            inheritPrototype(SubType, SuperType);
            
            SubType.prototype.sayAge = function(){
                alert(this.age);
            };
            
            var instance1 = new SubType("Nicholas", 29);
            instance1.colors.push("black");
            alert(instance1.colors);  //"red,blue,green,black"
            instance1.sayName();      //"Nicholas";
            instance1.sayAge();       //29
           
            var instance2 = new SubType("Greg", 27);
            alert(instance2.colors);  //"red,blue,green"
            instance2.sayName();      //"Greg";
            instance2.sayAge();       //27
    
  • 解析: 与组合继承相比的高效率在于,只调用一次SuperType构造函数,并且避免了在SuperType.prototype上面创建不必要的多余属性。与此同时,原型链保持不变,能够用instanceof和isPrototypeOf()来判断。是引用类型最理想的继承范式。

  • 这里说只调用一次SuperType构造函数,被省略掉那那一次是new SuperType(),因为这里如果直接new一个出来的话,虽然可以得到一个proto指向SuperType.prototype对象的对象,但是会存在冗余的SuperType实例属性,而本模式中的做法是,另用一个空函数,使它的prototype也指向SuperType.prototype,这样除了实例属性为空以外,其他都与SuperType()一样,此时new一个新函数的实例对象,就能取得一个proto指向SuperType.prototype对象的对象,最后将subType的prototype指向这个新new的实例对象,此时subType.prototype.proto = SuperType.prototype, 就实现了subType.prototype到SuperType.prototype的链接,形成了继承链,而subType.prototype里也没有冗余的值为空的SubperType的实例属性,Perfect!!!

  • 代码注释:

    • var prototype = object(superType.prototype); //返回一个新对象,新对象的proto指向superType.prototype
    • subType.prototype = prototype; //修改子类的原型,之后,subType的实例的proto会指向这个原型,而这个原型的proto会指向父类的原型superType.prototype,由此实现继承

补充:寄生组合式继承的图解:

111 (1).png
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 194,670评论 5 460
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 81,928评论 2 371
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 141,926评论 0 320
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 52,238评论 1 263
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 61,112评论 4 356
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 46,138评论 1 272
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 36,545评论 3 381
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 35,232评论 0 253
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 39,496评论 1 290
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 34,596评论 2 310
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 36,369评论 1 326
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 32,226评论 3 313
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 37,600评论 3 299
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 28,906评论 0 17
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 30,185评论 1 250
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 41,516评论 2 341
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 40,721评论 2 335

推荐阅读更多精彩内容