翻看了之前的文章,有说到,如果有一个对象的访问链是这样的a.b.c,那么其实它可以用数组来表示,写为[a, b, c]。要达到a.b.c转为*[a, b, c]是比较简单的,只需要遍历a*的所有可枚举的属性,再push到一个数组里面。
好了,现在考虑这样一个场景,在一颗树中,我搜索出了c的路径为*[a, b, c],如何把它转化为一个对象a.b.c*?
为了解决这个问题,去复习了下对象的创建方式,这篇文章更像是笔记。
说了那么多的废话,现在开始吧。下面都是参考《JavaScript高级程序设计》。
1、工厂模式
1 2 3 4 5 6 7 8 9 10
| function createPerson(name) { var o = new Object(); o.name = name; o.getName = function () { console.log(this.name); }; return o; }
var person1 = createPerson('kevin');
|
这种方式所有的对象都是从一个函数创建的。所有的实例都指向一个原型,对象无法识别。
2、构造函数模式
1 2 3 4 5 6 7 8
| function Person(name) { this.name = name; this.getName = function () { console.log(this.name); }; }
var person1 = new Person('kevin');
|
这种方式的实例可以识别为一个特定的类型,但是每次创建实例时,每个方法都要被创建一次。
2.1、构造函数模式优化
1 2 3 4 5 6 7 8 9 10
| function Person(name) { this.name = name; this.getName = getName; }
function getName() { console.log(this.name); }
var person1 = new Person('kevin');
|
这种方式解决了创建实例时,每个方法都要被创建一次的问题,但是,这代码。。
3、原型模式
1 2 3 4 5 6 7 8 9 10
| function Person(name) {
}
Person.prototype.name = 'keivn'; Person.prototype.getName = function () { console.log(this.name); };
var person1 = new Person();
|
这样的话,原型方法不会被重新创建,但是所有的属性和方法都是被共享的,很多自定义参数也不能初始化。
3.1、原型模式优化1
1 2 3 4 5 6 7 8 9 10 11 12
| function Person(name) {
}
Person.prototype = { name: 'kevin', getName: function () { console.log(this.name); } };
var person1 = new Person();
|
封装性好了一点,但是重写了原型,丢失了constructor属性。
3.2、原型模式优化2
1 2 3 4 5 6 7 8 9 10 11 12 13
| function Person(name) {
}
Person.prototype = { constructor: Person, name: 'kevin', getName: function () { console.log(this.name); } };
var person1 = new Person();
|
保留了构造函数,但是所有的属性和参数还是被共享。
4、组合模式
1 2 3 4 5 6 7 8 9 10 11 12
| function Person(name) { this.name = name; }
Person.prototype = { constructor: Person, getName: function () { console.log(this.name); } };
var person1 = new Person();
|
构造函数模式与原型模式的组合,该共享的共享,该私有的私有,使用最广泛的方式。但是有人就喜欢有更好的封装,所有的东西写在一块。
4.1、动态原型模式
1 2 3 4 5 6 7 8 9 10
| function Person(name) { this.name = name; if (typeof this.getName != "function") { Person.prototype.getName = function () { console.log(this.name); } } }
var person1 = new Person();
|
使用这种方式时,不能用对象字面量重写原型。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
| function Person(name) { this.name = name; if (typeof this.getName != "function") { Person.prototype = { constructor: Person, getName: function () { console.log(this.name); } } } }
var person1 = new Person('kevin'); var person2 = new Person('daisy');
person1.getName();
person2.getName();
|
使用字面量方式直接覆盖 Person.prototype,并不会更改原来实例的原型的值,person1 依然是指向了以前的原型,而不是 Person.prototype。而之前的原型是没有 getName 方法的,所以就报错了!
5.1、寄生构造函数模式
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
| function Person(name) {
var o = new Object(); o.name = name; o.getName = function () { console.log(this.name); };
return o;
}
var person1 = new Person('kevin'); console.log(person1 instanceof Person) console.log(person1 instanceof Object)
|
就是寄生在构造函数中的一种模式,应用场景在于比如我们想创建一个具有额外方法的特殊数组,但是又不想直接修改Array构造函数。
1 2 3 4 5 6 7
| function SpecialArray() { var array = new Array();
array.selfFn = function () { }; return array; }
|
5.2、稳妥构造函数模式
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
| function person(name){ var o = new Object(); o.sayName = function(){ console.log(name); }; return o; }
var person1 = person('kevin');
person1.sayName();
person1.name = "daisy";
person1.sayName();
console.log(person1.name);
|
所谓稳妥,指的是没有公共属性,而且其方法也不引用 this 的对象。适合在一些安全的环境中。
每一种模式都对应着不同的应用场景,我们使用的最多的应该是组合模式。