1.工厂模式
工厂模式是一种常用的创建对象的模式,可以使用以下函数封装创建对象的细节:
function CreatePerson(name,age){ var p=new Object(); p.name=name; p.age=age; p.speak=function(){ console.log("my name is "+p.name); } return p; } var p1=CreatePerson('Wang',15); p1.speak(); var p2=CreatePerson("Li",18); p2.speak();
特点:工厂模式实现了创建相似对象的功能,但是缺点是无法确定所创建出来对象的类型。
2.构造函数
①使用构造函数可以创建对象:
function Person(name,age){ this.name=name; this.age=age; this.speak=function(){ console.log("my name is "+this.name); } }
构造函数和工厂模式区别的地方:没有在函数内显式的创建对象,而是直接将属性赋值给this;没有使用return语句返回。
②调用:使用new操作符调用构造函数创建对象。
//作为构造函数调用 var p1=new Person('Wang',15); p1.speak();// my name is Wang var p2=new Person("Li",18); p2.speak();// my name is Li
调用过程经历了几个步骤:
- 创建一个新对象;
- 将构造函数的作用域赋给该对象,此时this也就指向了该对象;
- 执行构造函数中的代码;
- 返回该对象。
另外,构造函数也可以作为普通函数调用,此时this指向的是window对象:
Person('chen',15); window.speak();//my name is chen
③调用构造函数创建的对象,既是Person的实例,也是Object的实例。这一点上比工厂模式略胜一筹。
console.log(p1 instanceof Object);//true console.log(p1 instanceof Person);//true
④缺点:方法speak要在每个实例上都创建一遍,不能进行共享。
p1.speak==p2.speak;//返回false
3.原型模式
使用原型对象prototype的好处是可以让所有对象实例共享它所包含的属性和方法。
function Person(){ } //原型 Person.protype={ constructor:Person, name:'Tom', age:18, speak:function(){ console.log('my name is '+this.name+',I am '+this.age); } } //调用 var p1=new Person(); var p2=new Person(); console.log(p1.speak==p2.speak);//true console.log(p1.speak());//my name is Tom,I am 18
可以看出Person的实例对象共享了属性和方法。
isPrototypeOf()方法用来检测实例和原型对象之间的关系。
console.log(Person.prototype.isPrototypeOf(p1));//true
当给实例设置与原型中同名的属性时,会给该实例对象新增一个属性,它会屏蔽访问原型中的同名属性值但不会修改原型中的属性值。
p1.name='Cathy'; console.log(p1.name);//Cathy console.log(p2.name);//Tom
如果想重新访问原型中的属性值,可以使用delete操作符删除实例属性即可。
delete p1.name; console.log(p1.name);//Tom console.log(p2.name);//Tom
hasOwnProperty()方法:用来检测某属性是存在于实例中还是存在于原型中。仅当属性存在于实例中时,才返回true。
in 操作符:只要对象存在某属性就返回true,无论是实例属性还是原型属性。
p1.hasOwnProperty('name')//false "name" in p1;//true p1.name='Cathy'; p1.hasOwnProperty('name')//true "name" in p1;//true p2.hasOwnProperty('name')//false "name" in p2;//true delete p1.name; p1.hasOwnProperty('name')//false "name" in p1;//true
结合使用hasOwnProperty和in,可以判断属性是不是存在于原型中。
function hasPrototypeProperty(object,name){ return (!object.hasOwnProperty(name))&&(name in object); } var p1=new Person(); console.log(hasPrototypeProperty(p1,'name'));//true p1.name='Peter'; console.log(hasPrototypeProperty(p1,'name'));//false
原型的动态性:对原型所做的修改能够立即从实例中体现出来。
function Person(){ } var p=new Person(); Person.prototype.speak=function(){ console.log("hello world"); } p.speak();//hello world
原型模式缺点:实例之间共享所有的原型属性和方法,有些个性化的属性无法体现。
4. * 组合使用构造函数模式和原型模式
这种组合是最常见的一种方式,用构造函数模式创建实例属性,原型模式定义公用的属性和方法。
function Person(name,age) { this.name=name; this.age=age; this.friends=['lucy','kate'] } Person.prototype={ constructor:Person, sayName:function(){ console.log("my name is "+this.name); } } var p1=new Person('Ken',16); p1.sayName();//my name is Ken var p2=new Person('Peter',20); p2.sayName();//my name is Peter p1.friends.push('Zhang'); console.log(p1.friends);// ["lucy", "kate", "Zhang"] console.log(p2.friends);// ["lucy", "kate"]
5.动态原型模式
function Person(name,age) { this.name=name; this.age=age; if(typeof this.sayName!='function'){ Person.prototype.sayName=function(){ console.log("my name is "+this.name); } } } var p1=new Person('Ken',16); p1.sayName();//my name is Ken var p2=new Person('Peter',20); p2.sayName();//my name is Peter
说明:不必检查原型中的每个属性和方法,只要检查其中一个即可;
不能使用对象字面量方法重写原型。