面向对象与原型(二)

楔子

问题

在 java 中,首先定义一个 class,然后 new 出该 class 的实例,那么无论 new 多少次,新出现的类永远都具有 class 中定义的属性。

但 js 中不一样,js 中为某个对象新添加的属性,不会出现在新对象中。如下:

var o1 = {}
o1.name = 'o1.name'

var o2 = {}
alert(o2.name)

首先为 o1 创建 name 属性,但这个属性并没有被带到 o2 中,如果 o2 也想具有与 o1 一样的属性,就需要将 o1 的属性定义全部重写一遍。这就导致了一个问题 想要创建一个类似的对象,就需要写很多重复代码

假设有对象 A,现在想创建一个跟 A 只有一个属性不同的对象 B。按上述思路,我们需要重新创建一个 B 对象,然后将 A 、B 对象共用的属性与方法赋值给 B,代码繁琐。

工厂方法模式

为解决上述问题,可以使用工厂方法模式。

function createObj(){
    var d = new Object();
    d.h='h';
    return d;
}

通过上述方法产生的对象都会具有 h 属性。但由于工厂方法具有封装性,外界使用者根本不知道产生的对象是哪一个类的实例,也无法修改返回对象对应的类,因此,使用者可能没办法调用某些类特有的方法。

例如使用 createObj 方法产生对象后, 想调用 getFullYear() 方法是不可能的。因此 Object 对象没有 getFullYear() 方法。如果想使用 getFullYear() 就需要改成如下代码:

function creatObj2(){
    var d = new Date();
    d.h='h';
    return d;
}

这会有几个问题:1,使用者不能修改工厂方法里的代码;2. 即使修改了,也没有办法保证工厂方法能兼容所有的类,类的个数无限,不可能为每一个类创建一个工厂方法。

构造函数

  1. 构造函数也是函数,也可以直接调用;每一个函数都可以转成构造函数,从而可以创建实例

  2. 与 new 关键字结合时,普通函数会转为构造函数,进而创建出一个对象,该类的类名就是构造函数的方法名

    var o = function(){
        this.age = arguments[0]; // this 指代的是新创建出来的对象
    }
    var oo = new o('age'); // 与 new 关键字结合,转为构造函数,进而创建一个实例
    alert(oo.age)
    alert(oo instanceof o) // true
    
    o('直接调用'); //直接调用时,this 指代的是 window
    alert(window.age+",from window") // 所以 window.age 输出的是 ‘直接调用’
    
  3. 由于 this 指代的是当前对象,所以在构造函数中的 this 指的就是新创建的对象

    function Demo(){
            this.m = function(){
            alert('this is m ')
        }
    }
    
    var o = new Object();
    Demo.call(o)
    o.m()
    

    如果不调用 Demo.call() 就没办法直接调用 o.m() ,因为 Object 类中没有方法名为 m 的方法。
    调用 call 方法后,相当于在 o 对象中运行了一次 Demo 方法,而其中的 this 指的就是 o 对象,所以 o 对象中已经具有了 m 方法。

  4. 与工厂方法相比,构造函数有如下优点:

    • 没有 new Object()

    • 直接将属性赋值给 this ,而不是赋值给一个 Object 对象

    • 不需要直接返回一个对象。

    • 返回的对象不但 instanceof Object ,而且还 instanceof 构造函数对应的类。

  5. 构造函数实质上与工厂方法一样,只不过 new Object() 与 return 语句是在后台进行。构造函数算是一个改良后的工厂方法

  6. 一般来说,构造函数首字母大写,而普通函数不需要。

你可能感兴趣的:(面向对象与原型(二))