资讯专栏INFORMATION COLUMN

ES5-对象创建与继承----《JavaScript高程3》

Heier / 2259人阅读

摘要:并且,在创建子类型的实例时,无法向超类型的构造函数传递参数。借用构造函数经典继承在子类型构造函数的内部调用超类型构造函数。缺点是同样具有构造函数模式创建对象的固有弊端构造函数中烦人方法函数对象重复创建。

创建对象的几种方式

在逻辑上从低级到高级:工厂模式、构造函数模式、原型模式、组合模式。当然还有其他模式,但是这四者逻辑关系强,总结起来很有感觉。之所以和继承一起分析,也是因为逻辑关系很清晰:原型模式对应原型链继承,构造函数模式对应借用构造函数模式继承,组合模式对应组合继承。逻辑上按照“哪个模式有什么缺点,为什么有这个缺点,我们怎么解决这个缺点”逐步分析,这样分析完后就会豁然开朗。

1)工厂模式

使用函数创建对象,该函数来封装创建对象的细节。

                function createObject(para1,para2,para3){
                  var o = new Object();//显式创建对象
                  o.colors = [para1, para2, para3];
                  o.sayColors = function() {
                    console.log(this.colors[0]);
                  }
                  return o;//返回对象
                }
                var ob1 = createObject("red", "green", "blue");
                console.log(ob1);
                console.log(ob1.colors);
                ob1.sayColors();
                console.log(ob1 instanceof createObject);//false,无法判断对象的类型


缺点:无法判断对象类型。

2)构造函数模式

通过构造函数创建的实例被标识为一种特定的类型,可以通过instanceof 判断对象类型。

            function createObject(para1, para2, para3) {
              //通过this完成函数属性定义,不用显示创建对象,因此也不用返回对象
              this.colors = [
                para1,
                para2,
                para3
              ];
              this.sayColors = function () {
                console.log(this.colors[0]);
              }
            }
            var ob1 = new createObject("red", "green", "blue");
            console.log(ob1);
            console.log(ob1.colors);
            ob1.sayColors();
            console.log(ob1 instanceof createObject); //true,可以判断对象的类型
               

缺点:通过构造函数创建的实例具有不同的方法和属性,属性可以不同,这对实例来说是好的,我们希望实例属性独立。但是方法即函数,函数即对象,也就是说创建了太多重复的对象。

  var ob1 = new createObject("red", "green", "blue");
  var ob2 = new createObject("red", "green", "blue");
  alert(ob1.sayColors == ob2.sayColors);//false,不同实例具有不同的方法

解决方式:把方法的定义放到构造函数外部,即在构造函数内部引用外部全局函数。这样就可以一次定义,多次引用。但是当外部全局函数增多时,明显降低了封装性,《JavaScript精粹》上提到,全局对象是EScript的一大败笔。

    

值得一提的是,构造函数创建的实例中的引用类型属性是很特殊的,这一点会随后提到。

3) 原型模式

:每一个函数都有一个prototype属性,这个属性指向通过构造函数创建的实例对象的原型对象。原型对象的方法和属性可以被它的所有实例共享。因此,通过把属性和方法添加到实例的原型对象上,可以实现属性和方法共享。


缺点:“成也萧何,败也萧何”,原型模式的缺点就在于过强的共享能力,方法的共享可以减少多余的对象实例创建。但是属性共享导致实例难以拥有自己独特属性。当然,如果是一些不会修改的属性值,共享也就罢了;但是如果是需要修改的属性,并且该属性值是引用类型(基本类型属性值可以在实例中定义,会覆盖掉原型属性,但是不会修改原型属性,其他的实例访问该属性依旧对应原型属性),那么实例对这个属性值的修改就会在原型中反映出来,这其实就是修改了原型。糟糕的是其他实例中的该属性也同步变化,然后就会出现奇怪的问题。


4) 组合模式(最常用的一种对象创建方式,兼顾优点,避免缺点)

:使用构造函数模式定义各个实例属性,使用原型模式定义方法和共享的属性。



补充关于对象的几个方法:
isPrototypeOf(): 确定一个对象是否是另一个对象的原型,只要是原型链中出现的原型,就返回true,使用方法:

  alert(createObject.prototype.isPrototypeOf(ob1));//rue

instanceof操作符:检测是否是某一构造函数的实例,前面的参数是实例, 后面的的参数是构造函数名,只要是原型链中出现的构造函数就返回true。

    alert(ob1 instanceof createObject);//true

hasOwnProperty(): 检测一个实例是否拥有某个属性,使用方法

      alert(ob1.hasOwnProperty("colors"));//true

in操作符:多带带使用时可以检查实例属性或者原型属性是否存在,in后跟的一般是实例,因此可以理解为检查实例以及实例对应的原型中是否有这个属性或非法。使用方法:

  alert("sayColors" in ob1);//true
  alert("sayColors" in createObject);//false,直接对原型检查没意义   

for in 的另一种用法,类似于数组的forEach()方法,是一个循环,返回实例或原型中的可枚举的属性


继承的实现: 1.原型链

:把超类型的实例复制给子类型的原型,这样超类型的方法和属性就由子类型继承。

问题:子类型原型会继承超类型实例的属性,如果这个属性值是引用类型,就会导致子类型的所有实例都共享了这个属性,导致不同实例属性的耦合,这是原型模式创建对象的固有问题。并且,在创建子类型的实例时,无法向超类型的构造函数传递参数。因此,实际中很少多带带使用原型链。

2 借用构造函数(经典继承)

在子类型构造函数的内部调用超类型构造函数。这个方法之所以被称为借用构造函数,我觉得就是因为这种方法和前面介绍的通过构造函数创建实例的私有属性是一样的道理,只不过是在子类型构造函数内部调用了超类型构造函数。



而且可以在创建子类型实例时向超类型传递参数,因为这就相当于调用了一个函数,函数当然是可以有参数的。
缺点是同样具有构造函数模式创建对象的固有弊端:构造函数中烦人方法(函数对象)重复创建。并且,只有在超类型构造函数中定义的属性和方法才会被继承,通过超类型原型定义的方法和属性对子类型不可见,这是因为只执行了构造函数内部的语句。因此实际中这个方法也很少多带带使用。



3 组合继承(伪经典模式继承)

将原型链继承和借用构造函数模式组合到一起,使用原型链实现对原型属性和方法的继承(实现了方法复用),但是通过借用构造函数实现对实例属性的继承(实现了实例属性私有)。避免了缺陷,融合了优点,是最常用的继承模式,而且,instanceof操作符和isPrototypeOf()都适用于组合继承创建的对象。


文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。

转载请注明本文地址:https://www.ucloud.cn/yun/80555.html

相关文章

  • 听飞狐聊JavaScript设计模式系列04

    摘要:介一回,偶们来聊一下用中的类,有些盆友可能用过或者的,知道语法糖,可是在中并没有,中需要用到构造函数来模拟类。而且要注意一点,构造函数没有语句,是自动返回。 本回内容介绍 上一回聊到JS的Function类型,做了柯里化,数组去重,排序的题。 介一回,偶们来聊一下用JS中的类,有些盆友可能用过ES6或者TypeScript的,知道Class语法糖,可是在ES5中并没有,ES5中需要用到...

    kgbook 评论0 收藏0
  • JavaScript基础之创建对象、原型、原型对象、原型链

    摘要:在最开始的时候,原型对象的设计主要是为了获取对象的构造函数。同理数组通过调用函数通过调用原型链中描述了原型链的概念,并将原型链作为实现继承的主要方法。 对象的创建 在JavaScript中创建一个对象有三种方式。可以通过对象直接量、关键字new和Object.create()函数来创建对象。 1. 对象直接量 创建对象最直接的方式就是在JavaScript代码中使用对象直接量。在ES5...

    wangbjun 评论0 收藏0
  • JS高程读书笔记--第六章原型继承

    摘要:高程读书笔记第六章理解对象创建自定义对象的方式有创建一个实例,然后为它添加属性和方法。创建了自定义的构造函数之后,其原型对象默认只会取得属性至于其他方法都是从继承而来的。 JS高程读书笔记--第六章 理解对象 创建自定义对象的方式有创建一个Object实例,然后为它添加属性和方法。还可用创建对象字面量的方式 属性类型 ECMAScript在定义只有内部采用的特性时,描述了属性的各种特征...

    EasonTyler 评论0 收藏0
  • 面向对象的小九九

    摘要:由构造函数返回的对象就是表达式的结果。如果构造函数没有显式返回一个对象,则使用步骤创建的对象。运算符返回一个布尔值,表示对象是否为某个构造函数的实例。 面向对象 本人能力有限,有误请斧正 本文旨在复习面向对象(不包含es6) 本文学习思维 创建对象的方式,获取对象属性 构造函数,构造函数的new 做了什么 原型与原型对象 原型链 继承(借用构造继承、原型继承、组合继承、寄生组合继承)...

    时飞 评论0 收藏0
  • 读“js高程”笔记一

    showImg(http://img3.douban.com/lpic/s8958650.jpg); 0x00 javascript组成 ECMAScript(-265)核心语言部分 DOM文档对象模型(DOM1、2、3) BOM浏览器对象模型(提供与浏览器交互的接口和方法) 0x01 async 异步加载 执行顺序不定 charset defer 延迟加载,立即下载脚本但不执行 src ...

    CollinPeng 评论0 收藏0

发表评论

0条评论

最新活动
阅读需要支付1元查看
<