创建对象的7种方式

JavaScript 创建对象的 7 种方法

工厂模式

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
function Person() {
var o = new Object();
o.name = 'hanmeimei';
o.say = function() {
alert(this.name);
}
return o;
}
var person1 = Person();
```
> 缺点:
* 无法通过constructor识别对象,以为都是来自Object,无法得知来自Person
* 每次通过Person创建对象的时候,所有的say方法都是一样的,但是却存储了多次,浪费资源。

<!-- more -->

### 构造函数模式
```javascript
function Person(){
this.name='abc'
this.say = function (){
alert(this.name)
}
}
var person1 = new Person()

优点:

  • 通过constructor或者instanceof可以识别对象实例的类别
  • 可以通过new 关键字来创建对象实例,更像OO语言中创建对象实例

缺点:

  • 多个实例的say方法都是实现一样的效果,但是却存储了很多次(两个对象实例的say方法是不同的,因为存放的地址不同)

注意:

  • 构造函数模式隐试的在最后返回return this 所以在缺少new的情况下,会将属性和方法添加给全局对象,浏览器端就会添加给window对象。
  • 也可以根据return this 的特性调用call或者apply指定this。这一点在后面的继承有很大帮助。

原型模式

1
2
3
4
5
6
7
function Person(){}
Person.prototype.name = 'abc'
Person.prototype.say = function (){
alert(this.name)
}
Person.prototype.friends = ['lilei'];
var person1 = new Person();

优点:

  • say方法是共享的了,所有的实例的say方法都指向同一个。
  • 可以动态的添加原型对象的方法和属性,并直接反映在对象实例上。
    1
    2
    3
    4
    5
    var person1 = new Person()
    Person.prototype.showFriends = function() {
    console.log(this.friends)
    }
    person1.showFriends() //['lilei']

缺点:

  • 出现引用的情况下会出现问题具体见下面代码:
    1
    2
    3
    4
    var person1 = new Person();
    var person2 = new Person();
    person1.friends.push('xiaoming');
    console.log(person2.friends) //['lilei', 'xiaoming']

因为js对引用类型的赋值都是将地址存储在变量中,所以person1和person2的friends属性指向的是同一块存储区域。

  • 第一次调用say方法或者name属性的时候会搜索两次,第一次是在实例上寻找say方法,没有找到就去原型对象(Person.prototype)上找say方法,找到后就会在实力上添加这些方法or属性。
  • 所有的方法都是共享的,没有办法创建实例自己的属性和方法,也没有办法像构造函数那样传递参数。

注意:

  • 优点②中存在一个问题就是直接通过对象字面量给Person.prototype进行赋值的时候会导致constructor改变,所以需要手动设置,其次就是通过对象字面量给Person.prototype进行赋值,会无法作用在之前创建的对象实例上
    1
    2
    3
    4
    5
    6
    7
    8
    var person1 = new Person()
    Person.prototype = {
    name: 'hanmeimei2',
    setName: function(name){
    this.name = name
    }
    }
    person1.setName() //Uncaught TypeError: person1.set is not a function(…)

这是因为对象实例和对象原型直接是通过一个指针链接的,这个指针是一个内部属性[[Prototype]],可以通过proto访问。我们通过对象字面量修改了Person.prototype指向的地址,然而对象实例的proto,并没有跟着一起更新,所以这就导致,实例还访问着原来的Person.prototype,所以建议不要通过这种方式去改变Person.prototype属性

构造函数和原型组合模式

1
2
3
4
5
6
7
8
9
function Person(name) {
this.name = name
this.friends = ['lilei']
}
Person.prototype.say = function() {
console.log(this.name)
}
var person1 = new Person('hanmeimei')
person1.say() //hanmeimei

优点:

  • 解决了原型模式对于引用对象的缺点
  • 解决了原型模式没有办法传递参数的缺点
  • 解决了构造函数模式不能共享方法的缺点

动态原型模式

寄生构造模式

稳妥构造模式

参考 [‘https://juejin.im/entry/58291447128fe1005cd41c52']