因为最近一直在读《JavaScript高级程序设计》,深感从前对JavaScript的了解实在过于肤浅,所以打算顺手做些摘抄笔记,总结一下,给自己做个备忘。
在JavaScript里创建对象总共有以下几种实现方式:
下面对以上诸方式举例说明:
最简单的模式,直接看例子即可:
1: function createPerson(name, age, job){
2: var o = {};
3: o.name = name;
4: o.age = age;
5: o.job = job;
6: return o;
7: }
8: var p = createPerson("Bob", 22, "spy");
缺点:该方法的缺憾是类似对象的识别问题, 无法准确判断两个object实例是否是同一类型。
1: function Person(name age, job){
2: this.name = name;
3: this.age = age;
4: this.job = job;
5: this.sayName = function(){
6: alert(this.name);
7: }
8: }
9: var p1 = new Person("Bob", 22, "Spy");
10: var p2 = new Person("Mike", 25, "Engineer")
在使用这种模式创建对象时,其实主要有以下几个步骤:
优点:可以用 instanceof 标识对象的类型, 如上面的 p1,p2 在 instanceof Person时都可以返回true值
缺点:每个实例上都定义了sayName方法,不够环保,而且不同实例上的同名函数(这里是sayName)是不相等的。
1: function Person(){
2: }
3: Person.prototype.name = "Bob";
4: Person.prototype.age = 22;
5: Person.prototype.job = "Spy";
6: Person.prototype.sayName = function(){
7: alert(this.name)
8: };
9:
10: var p1 = new Person();
11: p1.sayName();// "Bob"
12: var p2 = new Person();
13: p2.sayName();//"Bob"
1: function Person(name, age, job){
2: this.name = name;
3: this.age = age;
4: this.job = job;
5: }
6: Person.prototype.sayName = function(){
7: alert(this.name);
8: }
9:
10: var p1 = new Person("Bob", 22, "Spy");
11: var p2 = new Person("Mike", 24, "Engineer");
把实例属性定义在构造函数中,把共享的方法定义在prototype中,是ECMAScript中使用最广泛,认同度最高的一种创建自定义类型的方法。
习惯了其他语言面向对象的编程人员,在看到JavaScript里采用上面的模式定义对象是可能会感到困惑,为什么要在两个地方去定义一个类型呢?于是就诞生了动态原型模式,如下
1: function Person(name, age, job){
2: this.name = name;
3: this.age = age;
4: this.job = job;
5: if(typeof this.sayName != "function"){
6: Person.prototype.sayName = function(){
7: alert(this.name);
8: }
9: }
10: }
11:
12: var p1 = new Person("Bob", 22, "Spy");
所以说,动态原型模式 跟 原型和构造函数的混合模式 并没有本质上的不同,仅仅是动态原型模式会在第一次调用构造函数是才会在原型上定义共享的方法。
1: function Person(name, age, job){
2: var o = new Object();
3: o.name = name;
4: o.age = age;
5: o.job = job;
6: o.sayName = function(){
7: alert(this.name);
8: }
9: }
10: var p = new Person("Bob", 22, "Spy");
除了使用了new操作符,这个方式跟工厂模式并无很大差别。在上面的构造函数模式里曾经说过,当采用 new Person() 时,会先创建一个新的对象实例,再去绑定调用构造函数云云,这里应该把返回值这部分补全一下,当构造函数没有返回值时,就默认返回了第一步创建的那个新对象实例,但是如果构造函数里有return语句,并且返回了一个object,那么最终的返回结果就是这个在构造函数里创建的object,而不是最初创建的那个新对象实例。(这里必须要保证构造函数内部返回值是object,如果是数字,字符串等基本型,是无法覆盖在第一步创建的对象实例的,这个是我自己试验的 )。
这里还有一点需要注意的就是由构造函数内部返回出来的这个object,它的原型和Person的prototype是没有关系的,其实在Chrome里不妨试一试,当使用构造函数模式创建了一个对象实例后,输出这个实例所得结果如下
Person {name: "Bob", age: 22, job: "Spy", … }
但是如果用寄生构造函数创建一个对象实例,输出该实例所得结果如下
Object {name: "Bob", age: 22, job: "Spy", …}
所以,使用该模式创建的实例与使用工厂模式创建的实例一样,没法使用 instanceof 来判断类型。
关于这个寄生构造函数模式使用的场合,一般是想在某种已有类型的基础上添加方法等,但是又不方便直接去修改已有类型的构造函数,这时候,寄生构造函数模式就比较适合了,下面是个例子
1: function SpecialArray(){
2: var values = new Array();
3: values.push.apply(values, argumeents);
4: values.toPipedString = function(){
5: return this.join("|");
6: }
7: return values;
8: }
9: var arr = new SpecialArray("Tiger", "Lion", "Dog");
这里就在Array的基础上定义了SpecialArray,初始化数组,并添加了新的方法toPipedString。
先来看看实例,再来说说这种模式有什么特点吧
1: function Person(name){
2: var o = new Object();
3: o.sayName = function(){
4: alert(name);
5: };
6: return o;
7: }
8:
9: var p = Person("Bob");
其实满足了以上条件的对象就叫做稳妥对象,稳妥对象适合在一些安全环境中使用,这些环境中,会禁止使用 new 和 this , 或者防止数据被其他应用改动,从示例中可以看到,除了sayName, 外界是无法访问 name 的,(这里用了一个闭包)。
哦,暂时就到这里吧,实在有些困了,如果示例代码里有小毛病,还请多包涵。