JS创造对象主要有:工厂模式、构造函数模式、原型模式、组合模式(构造函数+原型)、动态原型模式、寄生构造函数模式、稳妥构造函数模式。现主要区分概念以及各方式的优缺点。
1.工厂模式
function createPerson(name, age, job){
var o = new Object();
o.name = name;
o.age = age;
o.job = job;
o.sayName = function(){
alert(this.name);
}
return o;
}
var person = createPerson("Nico",15,"Engineer");
person instanceof createPerson; //false
优点:解决了用Obeject构造函数或对象字面量方式带来大量重复代码的问题。
缺点:没有解决对象识别问题(即怎样知道一个对象的类型)
2.构造函数模式
function Person(name, age, job){
this.name = name;
this.age = age;
this.job = job;
this.sayName = sayName;
}
//把函数定义移出构造函数,避免每个方法都要在每个实例上重新创建一遍
function sayName(){
alert(this.name);
}
var person1 = new Person("Nico",15,"Engineer");
var person2 = new Person("Greo",19,"Engineer");
person1 instanceof Person; //true
优点:解决了对象识别问题(优于工厂模式)
缺点:可能要定义多个全局函数,但有只能被某个对象调用,无封装性
3.原型模式
function Person(){}
Person.prototype = {
constructor: Person, //会导致变成可枚举,而原生的construtor不可枚举;不设置该属性,则construtor会指向Object
name: "Nico",
age: 15,
job: "Engineer",
friend: ["Gero","Hannah"],
sayName: function(){
alert(this.name);
}
}
//重设构造函数,只适用于ES5兼容的浏览器
Object.defineProperty(Person.prototype, "constuctor",{
enumerable: false,
value: Person
})
var person1 = new Person();
var person2 = new Person();
person1.friend.push("Maro");
console.log(person2 .friend); // ["Gero", "Hannah", "Maro"]
优点:所有对象实例共享原型的属性和方法
缺点:引用类型值的属性(例如数组),也被共享了
4.组合模式(构造函数+原型)
function Person(name, age, job){
this.name = name;
this.age = age;
this.job = job;
this.friend =["Gero","Hannah"]
}
Person.prototype = {
constructor: Person,
sayName: function(){
alert(this.name);
}
}
var person1 = new Person("Nico",15,"Engineer");
var person2 = new Person("Gero",19,"Engineer");
person1.friend.push("Maro");
console.log(person1.friend); //["Gero", "Hannah", "Maro"]
console.log(person2.friend); //["Gero", "Hannah"]
console.log(person1.sayName === person2.sayName); //true
优点:每个实例都有自己的一份实例属性的副本,但又可以同时共享方法的引用
5.动态原型模式
function Person(name, age, job){
this.name = name;
this.age = age;
this.job = job;
if(typeof this.sayName!="function"){ //只在初次调用构造函数时才会执行
Person.prototype.sayName = function(){
alert(this.name);
}
}
}
var person1 = new Person("Nico",15,"Engineer");
console.log(person1.sayName()); //Nico
person1 instanceof Person; //true
优点:几乎完美
缺点:若在已经创建实例的情况下,使用对象字面量重写原型,会切断现有实例和新原型之间的联系
6.寄生构造函数模式
function Person(name, age, job){
var o = new Object();
o.name = name;
o.age = age;
o.job = job;
o.sayName = function(){
alert(this.name);
}
return o;
}
var person = new Person("Nico",15,"Engineer");
person instanceof Person; //false
与工厂模式基本一样,除了用了new操作符
例如:
function SpecialArray(){
var value = new Array();
value.push.apply(value, arguments);
value.toPipeString = function(){
return this.join("|");
}
return value;
}
var array = new SpecialArray("A","B","C");
console.log(array.toPipeString()); // “A|B|C”
array instanceof SpecialArray; //false
优点:可用在不能直接修改构造函数,又想创建一个具有额外方法的对象
缺点:返回的对象与构造函数或者与构造函数的原型属性之间并没有关系,不能依赖instanceof来确定对象类型,可使用其他模式时,不推荐该模式
7.稳妥构造函数模式
function Person(name, age, job){
var o = new Object();
o.name = name;
o.age = age;
o.job = job;
o.sayName = function(){
alert(name);
}
return o;
}
var person = Person("Nico",15,"Engineer");
person instanceof Person; //false
除了使用sayName()方法之外,没有其他方法访问name的值
优点:适合安全环境中,禁止使用this,new的环境中,或者防止数据被其他应用程序改动;
缺点:同寄生构造函数
------------------------------------------------------------------------------
in : 是否能够通过对象访问给定属性,无论该属性存在于实例中还是原型中
hasOwnProperty() : 只在属性存在于实例中时,才返回true
判断属性存在对象实例中,还是原型中:
//是否在原型上
function isHasPrototypeProperty(object, name){
return !object.hasOwnProperty(name) && (name in object);
}
------------------------------------------------------------------------------
Object.keys() :得到对象的实例属性(不包括不可枚举)
Object.getOwnPropertyName() :得到对象的实例属性,包括不可枚举的constructor
Object.keys(Person.prototype); //"name,age,job,sayName"
Object.getOwnPropertyName(Person.prototype);//"constructor,name,age,job,sayName"