一、概述
虽然对象字面量可以用来创建对象,但在创建多个类似的对象时,不够优雅,不符合DRY原则。
二、创建对象
有以下几种模式:
1.工厂模式
2.构造函数模式
3.原型模式
4.组合构造函数和原型模式(推荐)
5.动态原型模式 (推荐)
6.稳妥构造函数模式
1.工厂模式
function createPerson(name,age,job) { var o={ name:name, age:age, job:job, saySelf:function(){ console.log(this.name+this.age+this.job); } }; return o; } var person=createPerson("我是工厂模式建的",20,"ape"); person.saySelf();
优点:可创建多个相似对象。
缺点:无法识别对象。
2.构造函数模式
function Person(name,age,job) { this.name=name; this.age=age; this.job=job; this.saySelf=function(){ console.log(this.name+this.age+this.job); } } var person=new Person("构造函数模式",20,"ape"); person.saySelf();
特点:
- 没有显式的创建对象,直接将属性和方法给了this;
- 没有return;
- 函数名大写,一般构造函数名大写;
要创建Person的实例,必须使用new操作符,Person的实例都有constructor属性,该属性指向Person();
console.log(person.constructor===Person)//true console.log(person instanceof Person) //true 推荐
自定义构造函数可以标识为一种特定类型。
任何函数,只要通过new调用,就可以当作构造函数,构造函数与普通函数没什么两样。
优点:可识别类型。
缺点:每个方法要在每个实例上重建一遍,工厂模式也有此类问题。
console.log(person.saySelf===person2.saySelf);//false
3.原型模式
(function(){ function Person() { } Person.prototype.name='原型模式'; Person.prototype.age='28'; Person.prototype.job='Ape'; Person.prototype.saySelf=function(count){ if(count) console.log(count+":"+this.name+this.age+this.job); else console.log(this.name+this.age+this.job); } var person=new Person(); var person2=new Person(); person.saySelf(); person2.saySelf(); console.log(1,person.saySelf===person2.saySelf);//true console.log(2,Person.prototype.isPrototypeOf(person));//true console.log(3,Object.prototype.isPrototypeOf(person));//true console.log(4,Object.getPrototypeOf(person)===Person.prototype);//true console.log(8,person.name); person.name="看看变不变"; person.saySelf(9);//9:看看变不变28Ape person2.saySelf(10);//10:原型模式28Ape
delete person.name; person.saySelf(11);//11:原型模式28Ape person.__proto__.name="现在肯定变了"; person2.saySelf();//现在肯定变了28Ape 只在Firefox、Safari、Chrome中有效 })();
当添加一个属性时,这个属性就会屏蔽原型对象的同名属性。但是可以通过delete删除实例属性,让实例重新可以访问这个属性。说话是苍白的,看上面代码。
通过hasOwnProperty()可判断属性是否是实例自己的属性(区别于从原型上继承来的)。
console.log(person.hasOwnProperty("name"));//true delete person.name; console.log(person.hasOwnProperty("name"));//false
那么,怎么检测某个属性是否是实例的属性呢,用in操作符,无论属性在实例中还是在原型中,都返回true
console.log("in","name" in person);//true delete person.name; console.log("in","name" in person);//true
可用Object.keys()方法,返加一个所有可枚举属性的的字符串数组。
如果想得到,全部的属性可用Object.getOwnPropertyNames();
两个方法的共同点:均列举出自有属性,不查找原型链;
不同点:Object.keys()方法返回可枚举属性,Object.getOwnPropertyNames()返回所有属性的keys。
person2.sex="男"; console.log(Object.keys(Person.prototype).join(";"));//name;age;job;saySelf console.log(Object.getOwnPropertyNames(Person.prototype).join(";"));//constructor;name;age;job;saySelf console.log(Object.keys(person2).join(";"));//sex console.log(Object.getOwnPropertyNames(person2).join(";"));//sex
原型的动态性,这个很好理解,原型只是引用用,动态添加,当然可用。
但是,实例化一个对象后,再去重写一个原型,实例仍然指向原来的原型。
//原型的动态性 (function(){ function Person() { } Person.prototype.name='原型模式'; Person.prototype.age='28'; Person.prototype.job='Ape'; Person.prototype.saySelf=function(count){ if(count) console.log(count+":"+this.name+this.age+this.job); else console.log(this.name+this.age+this.job); } var person=new Person(); console.log(Object.getOwnPropertyNames(person.__proto__).join(";")); Person.prototype={ constructor:Person, sex:"汉子" } var person2=new Person(); console.log(Object.getOwnPropertyNames(person.__proto__).join(";")); console.log(Object.getOwnPropertyNames(person2.__proto__).join(";")); })();
优点:共享方法,可用isPrototypeOf检测是否是原型,在ES5中,可用getPrototypeOf得到原型。
缺点:不能拥有自己的属性。这个致命的缺点导致基本没有人用原型模式。
4.组合构造函数和原型模式。
function Person(name,age,job) { this.name=name; this.age=age; this.job=job; } Person.prototype={ constructor:Person, saySelf:function() { console.log(this.name+this.age+this.job); } }; Person.static=function() { console.log("这是一个静态方法"); } var person=new Person(); Person.static();//这是一个静态方法 person.static();//Uncaught TypeError: undefined is not a function
这个模式解决了以上所有的缺点,同时具有以上所有的优点,是在javascript应用最广泛的创建自己定义类型的方法。是用来定义定义引用类型的一种默认模式。
5.动态原型模式
//动态原型模式 (function(){ function Person(name,age,job) { this.name=name; this.age=age; this.job=job; if(typeof this.saySelf!=="function") { Person.prototype.saySelf = function () { console.log(this.name + this.age + this.job); } } } var person=new Person("工厂模式",20,"frontEndpe"); var person2=new Person("工厂模式Ac",20,"frontEndApe"); person.saySelf(); person2.saySelf(); })();
优点:把原型封装构造函数中。
缺点:每个实例化都进行一次判断(这个也不算什么)。
6.稳妥构造函数模式
//稳妥构造函数模式 (function(){ function Person(name) { var o={}; //可以添加私有的方法和变量 o.sayName=function() { console.log(name); } return o; } var person=Person("工厂模式"); var person2=Person("工厂模式Ac"); person.sayName(); person2.sayName(); var say=person2.sayName; say(); })();
稳妥对象:没有公共属性的对象。
name属性只有sayName()闭包访问
三、总结
综合以上,推荐使用 4.组合构造函数和原型模式和5.动态原型模式。
它们都有以下优点:
1.都可以识别对象 。
2.都可以有自己的属性值(constructor) 和共享的属性(prototype)
四、代码
<!DOCTYPE html> <html> <head lang="en"> <meta charset="UTF-8"> <title></title> </head> <body> <script> "use strict"; (function(){ // 工厂模式 (function(){ function createPerson(name,age,job) { var o={ name:name, age:age, job:job, saySelf:function(){ console.log(this.name+this.age+this.job); } }; return o; } var person=createPerson("工厂模式",20,"ape"); var person2=createPerson("工厂模式2",20,"ape"); console.log(person.saySelf===person2.saySelf); person.saySelf(); });//(); // 构造函数模式 (function(){ function Person(name,age,job) { this.name=name; this.age=age; this.job=job; this.saySelf=function(){ console.log(this.name+this.age+this.job); } } var person=new Person("构造函数模式",20,"ape"); var person2=new Person("构造函数模式2",20,"ape"); person.saySelf(); console.log(person.constructor===Person);//true console.log(person instanceof Person); //true 推荐 console.log(person.saySelf===person2.saySelf);//false });//(); //原型模式 (function(){ function Person() { } Person.prototype.name='原型模式'; Person.prototype.age='28'; Person.prototype.job='Ape'; Person.prototype.saySelf=function(count){ if(count) console.log(count+":"+this.name+this.age+this.job); else console.log(this.name+this.age+this.job); } var person=new Person(); var person2=new Person(); person.saySelf(); person2.saySelf(); console.log(1,person.saySelf===person2.saySelf);//true console.log(2,Person.prototype.isPrototypeOf(person));//true console.log(3,Object.prototype.isPrototypeOf(person));//true console.log(4,Object.getPrototypeOf(person)===Person.prototype);//true console.log(5,person.name); person.name="看看变不变"; person.saySelf(9);//9:看看变不变28Ape person2.saySelf(10);//10:原型模式28Ape console.log(person.hasOwnProperty("name"));//true console.log("in","name" in person);//true delete person.name; console.log("in","name" in person);//true console.log(person.hasOwnProperty("name"));//false person.saySelf(11);//11:原型模式28Ape person.__proto__.name="现在肯定变了"; person2.saySelf();//现在肯定变了28Ape 只在Firefox、Safari、Chrome中有效 person2.sex="男"; console.log(Object.keys(Person.prototype).join(";"));//name;age;job;saySelf console.log(Object.getOwnPropertyNames(Person.prototype).join(";"));//constructor;name;age;job;saySelf console.log(Object.keys(person2).join(";"));//sex console.log(Object.getOwnPropertyNames(person2).join(";"));//sex });//(); //原型的动态性 (function(){ function Person() { } Person.prototype.name='原型模式'; Person.prototype.age='28'; Person.prototype.job='Ape'; Person.prototype.saySelf=function(count){ if(count) console.log(count+":"+this.name+this.age+this.job); else console.log(this.name+this.age+this.job); } var person=new Person(); console.log(Object.getOwnPropertyNames(person.__proto__).join(";")); Person.prototype={ constructor:Person, sex:"汉子" } var person2=new Person(); console.log(Object.getOwnPropertyNames(person.__proto__).join(";")); console.log(Object.getOwnPropertyNames(person2.__proto__).join(";")); });//(); //组合构造函数和原型模式。 最常用 (function(){ function Person(name,age,job) { this.name=name; this.age=age; this.job=job; } Person.prototype={ constructor:Person, saySelf:function() { console.log(this.name+this.age+this.job); } }; Person.static=function() { console.log("这是一个静态方法"); } var person=new Person(); Person.static();//这是一个静态方法 person.static();//Uncaught TypeError: undefined is not a function });//(); //动态原型模式 (function(){ function Person(name,age,job) { this.name=name; this.age=age; this.job=job; if(typeof this.saySelf!=="function") { Person.prototype.saySelf = function () { console.log(this.name + this.age + this.job); } } } var person=new Person("工厂模式",20,"frontEndpe"); var person2=new Person("工厂模式Ac",20,"frontEndApe"); person.saySelf(); person2.saySelf(); });//(); //稳妥构造函数模式 (function(){ function Person(name) { var o={}; //可以添加私有的方法和变量 o.sayName=function() { console.log(name); } return o; } var person=Person("工厂模式"); var person2=Person("工厂模式Ac"); person.sayName(); person2.sayName(); var say=person2.sayName; say(); });//(); })(); </script> </body> </html>