学习内容: JavaScript 创建对象的七种方式
七种经典的创建对象的方式
- 工厂模式
- 构造函数模式
- 原型模式
- 组合模式
- 动态原型模式
- 寄生构造函数模式
- 稳妥构造函数模式
我的理解:
这几种模式写法上的核心区别就是*是用新的对象创建还是用this
。如果是创建新的对象new Object()
的方法来创建,对象的属性和方法啊都是加载这个新的对象o
上的,在函数的末尾也需要return o
来返回这个对象值。
也就是说,这种方法是每次创建一个新的对象,而不是创建的实例,即新的对象无法使用instanceof
。
而使用this
来创建实例的方法,它们的区别在于方法和属性分别添加到原型上还是实例上。添加到原型上的优势在于方便共享,缺点在于一变皆变;添加到实例上的优势在于每个实例都拥有这些属性方法,缺点在于每个实例上都要创建一遍方法,浪费资源。
目前最普遍的做法是组合使用构造模式和原型模式,即属性放在实例上,方法放在原型上,或者说我们可以更灵活一些,所有需要实例个性化的内容都放在实例上,所有的公共属性和方法都放在原型上,这样使用起来会更方便。
工厂模式
1 2 3 4 5 6 7 8 9 10 11 12
| function createPerson(name, job) { var o = new Object(); o.name = name; o.job = job; o.sayName = function() { console.log(this.name) } return o; }
var person1 = createPerson("aaa", "student"); var person2 = createPerson("bbb", "doctor");
|
优点:
- 可以无数次调用这个工厂函数
- 每次返回的对象都包含两个属性和一个方法(属性和方法都可以自行往工厂函数里添加)
缺点:
构造函数模式
1 2 3 4 5 6 7 8 9 10
| function Person(name, job) { this.name = name; this.job = job; this.sayName = function() { console.log(this.name); } }
var person1 = new Person("aaa", "student"); var person2 = new Person("bbb", "doctor");
|
使用new
关键字创建对象的步骤:
- 创建一个新的对象
- 这个新对象会被执行
prototype
链接
- 这个新对象会绑定到函数调用的
this
- 返回这个对象
优点:
构造函数创建的对象可以检测对象类型
1 2
| person1 instanceof Object person1 instanceof Person
|
缺点:
原型模式
1 2 3 4 5 6 7 8
| function Person() {} Person.prototype.name = 'Tom'; Person.prototype.job = 'student'; Person.prototype.sayName = function() { console.log(this.name); }
var person1 = new Person();
|
更简单的写法:
1 2 3 4 5 6 7 8 9 10 11
| function Person() {} Person.prototype = { constructor: Person, name: 'Tom', job: 'student', sayName: function() { console.log(this.name) } }
var person1 = new Person();
|
优点:
- 信息直接添加在原型对象上,所有的实例都能共享原型对象包含的属性和方法
- 不用再构造函数中定义实例对象的信息
缺点:
- 无法自定义创建的对象
- 因为实例对象的属性是共享的原型对象的属性,那么一个实例对象修改属性,其他实例对象的属性都会跟着改变
组合使用构造模式和原型模式
这是使用最广泛、认同度最高的一种创建自定义类型的方法
1 2 3 4 5 6 7 8 9 10 11 12 13
| function Person(name) { this.name = name; this.friends = ["Alice", "Bob"]; } Person.prototype.sayName = function() { console.log(this.name); }
var person1 = new Person("Rose"); var person2 = new Person("Jack"); person1.friends.push('Van'); console.log(person1.friends); console.log(person2.friends);
|
优点:
- 每个实例都有自己的一份实例属性副本,同时又共享对原型方法的阴影
- 一个实例属性的修改不会影响其他实例的属性值
动态原型模式
1 2 3 4 5 6 7 8 9 10 11 12 13
| function Person(name, job) { this.name = name; this.job = job; if(typeof this.sayName !== 'function') { Person.prototype.sayName = function() { console.log(this.name); } } }
var person1 = new Person('Tom', 'student'); person1.sayName();
|
只有在sayName
方法不存在时,才会将它添加到原型中。
寄生构造函数模式
1 2 3 4 5 6 7 8 9 10 11 12
| function Person(name, job) { var o = new Object(); o.name = name; o.job = job; o.sayName = function() { console.log(this.name); } return o; }
var person1 = new Person('Tom', 'student'); person1.sayName();
|
稳妥构造函数模式
稳妥对象指的是没有公共属性,方法也不引用this
的对象,适合在一些安全环境中(禁用this
和new
)时使用
1 2 3 4 5 6 7 8 9 10 11 12
| function Person(name, job) { var o = new Object(); o.name = name; o.job = job; o.sayName = function() { console.log(name); } return o; }
var person1 = Person("Tom", "student"); person1.sayName();
|