创建对象的方法

1. 通过 Object 构造器

var obj = new Object();
obj.name = 'xxx'
obj...

问题:需要写大量的重复代码

2. 字面量创建

var obj = {};
obj.name = 'xxx';

问题:需要写大量的重复代码

3. 工厂模式

function createObj(name, age) {
    var obj = new Object();
    obj.name = name;
    obj.age = age;
    obj.running = function () {
        console.log('我会跑...');
    }
    return obj;
}
var person1 = createObj('黑黑', 18);

好处:通过在工厂中加工出一个对象,来解决重复代码的问题。

坏处: 我们通过工厂加工出来的对象,我们不知道具体是个什么对象。所有的实例都指向一个原型。

4. 通过构造函数

function Person(name) {
    this.name = name;
    this.age = 18;
    this.running = function () {
        console.log('我会跑...');
    }
}
var person1 = new Person('黑黑', 18);

好处:可以自定义对象, 可以识别对象.

坏处: 每一个方法都会在实例中创建一次,意味着共同拥有的行为将创建多次,大量重复的代码。同样的工作,造成内存的浪费(每创建一个对象都会申请内存空间)。

5. 原型模式

function Person() {
    
}
Person.prototype.name = '黑黑';
Person.prototype.age = 18;
Person.prototype.running = function () {
    console.log('我会跑...');
}
var person1 = new Person('黑黑', 18);

好处:解决了上面的对同一个行为创建多次,实例只需要去原型上取那个唯一的行为。节约了内存。

坏处:1. 将所有的属性都放置原型上,失去了对象独有的属性,造成了不方便,所有的属性和方法都会共享。 2. 如果属性是一个引用类型的对象,那么代表共有一块内存区域,那么只要有一个实例修改,就会全部修改, 不能初始化参数.

原型模式优化

function Person() {
    
}
Person.prototype.name = {
    constructor: Person,
    name: '黑黑',
    age: 18,
    running: function () {
        console.log('我会跑...');
    }
}
var person1 = new Person('黑黑', 18);

6. 组合模式

function Person(name, age) {
    this.name = name;
    this.age = age;
}
Person.prototype = {
    constructor: Person,
    running: function () {
        console.log('我会跑...');
    }
}
var person1 = new Person('黑黑', 18);

好处:混用两种模式可以解决上面的问题,可以自定义对象独有的属性,并且还能够共用行为。

坏处:使用了两种模式,不利于封装。

7. 动态原型

function Person(name, age) {
    this.name = name;
    this.age = age;
    if (typeof this.running !== 'function') {
        Person.prototype.running = function () {
            console.log('我会跑...');
        }
    }
}
var person1 = new Person('黑黑', 18);

通过判断我们来决定 running 行为是否是需要挂到原型上的。这样灵活度更高,创建对象初始化一次就.

这样写主要就是为了解决 原型 + 构造函数环境中的不利于封装问题。

缺点:我们不能够使用对象字面量来重写原型。

function Person(name, age) {
    this.name = name;
    this.age = age;
    if (typeof this.running !== 'function') {
        Person.prototype = {
            constructor: Person,
            running: function () {
                console.log('我会跑...');
            }
        }
    }
}
var person1 = new Person('黑黑', 18);
person1.running();  // 会报错
var person2 = new Person('哈哈', 19);
person.running();  // 我会跑...

我们结合 new 的原理,new 第二部会先使 对象的原型绑定到了 Person.prototype 。然后才调用的 Person.apply(对象); 这时候再去重写原型。所以第一次访问 running 方法的时候是不存在于原型上的。

但是如果经过第一次 new ,进行了一次性的初始化,再去调用 running ,就可以正常运行了。

可以这样修改

function Person(name, age) {
    this.name = name;
    this.age = age;
    if (typeof this.running !== 'function') {
        Person.prototype = {
            constructor: Person,
            running: function () {
                console.log('我会跑...');
            }
        }
        return new Person(name);
    }
}

重新 new 一次,代表已经初始化过了。就可以正常执行。

8. 寄生构造函数模式

function createObj(name, age) {
    var obj = new Object();
    obj.name = name;
    obj.age = age;
    obj.running = function () {
        console.log('我会跑...');
    }
    return obj;
}
var person1 = new createObj('黑黑', 18);

我们可以看到跟工厂模式比较起来,只是多了一个 new。 我们从 new 实现原理过程来看。二者的区别在与最终的对象是由 new 操作符给返回的,而不是 createObj 函数返回的。

使用 new 的函数才能被看作是真正意义上的构造函数。 所以说其实意义上这两个模式是差不多的。

我觉得这个模式是基于一个已经存在的对象,进行一个扩展。(具体还是要研究一下设计模式).