工厂模式
该模值抽象了创建具体对象de过程。用函数来封装噫特定接口创建对象的细节。
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 person1=createPerson("Chiaki",21,"Software Engineer"); person2=createPerson("Wu",20,"Student");
特点:可以无数次调用,解决了创建多个相似对象的问题,但没有解决对象识别的问题(即怎样知道一个对象的类型)。
构造函数模式
创建自定义的构造函数,从而定义自定义对象类型的属性方法。
function Person(name,age,job){ this.name=name; this.age=age; this.job=job; this.sayName=function(){ alert(this.name); }; } var person1=new Person("Chiaki",21,"Software Engineer");//person1的constructor(构造函数)属性指向Person var person2=new Person("Wu",20,"Student");//person2的constructor(构造函数)属性指向Person
person1和person2都有一个constructor(构造函数)属性,该属性指向person.
alert(person1.constructor==Person);//true alert(person2.constructor==Person);//true
构造函数模式与工厂模式的区别:
1、没有显示的创建对象;
2、直接将属性和方法赋给this对象;
3、没有return语句;
4、构造函数函数名以大写字母开头,非构造函数函数名以小写字母开头;
5、创建构造函数的新实例必须使用new操作符,例如var person1=new Person(“Chiaki”,21,”Web Engineer”);
6、将来可以将他的实例标识为一种特定的类型。
构造函数与其他函数的区别与联系:两者的调用方式不同;任何函数只要用new操作符调用,就可以把它当作构造函数,不通过new操作符调用,则和普通函数没什么区别。
//将上例当作构造函数使用 var person=new Person("Chiaki",21,"Software Engineer"); person.sayName();//Chiaki //作为普通函数调用 Person("Wu",20,"Student"); window.sayName();//Wu//在全局作用域中调用一个函数,this对象总是指向Global对象(即浏览器的window对象) //在另一个对象作用域中调用 var o=new Object(); Person.call(o,"lola",21,"designer");//使用call()在对象o的作用域中调用Person()函数,调用后o就拥有了Person()函数的所有属性和sayName()方法 o.sayName();//lola
缺点:每个方法都要在每个实例上重新创建一遍。
alert(person1.sayName==person2.sayName);//false
解决方法:把函数定义转到构造函数外,但是此方法可能需要定义多个全局函数,将导致该自定义引用类型毫无封装性可言。
function Person(name,ago,job){ this.name=name; this.age=age; this.job; this.sayName=sayName; } function sayName(){ alert(this.name); } var person1=new Person("Chiaki",21,"Software Engineer"); var person2=new Person("Wu",20,"Student"); //person1和person2对象共享在全局作用域中定义的同一个sayName函数
原型模式
我们创建的每一个函数都有一个property(原型)属性,这个属性是一个指针,指向一个对象(该对象包含可以由特定类型的所有实例共享的属性和方法)。
function Person(){ } Person.prototype.name="Chiaki"; Person.prototype.age=21; Person.prototype.job="Software Engineer"; Person.prototype.sayName=function(){ alert(this.name); }; var person1=new Person(); person1.sayName();//Chiaki var person2=new Person(); person2.sayName();//Chiaki
这里写图片描述
创建一个新函数就会根据特定的规则为其生成一个prototype属性,该属性指向函数的原型对象,原型对象自动获得一个constructor属性,这个属性包含一个指向prototype属性所在函数的指针。当调用构造函数创建一个新实例后,该实例的内部将包含一个指针(内部属性[[Prototype]]),指向构造函数的原型对象。
可以通过对象实例访问保存在原型中的值,却不能通过对象实例重写原型中的值(当为对象实例添加一个属性时,这个属性就会屏蔽原型对象中保存的同名属性)。
function Person(){ } Person.prototype.name="Chiaki"; Person.prototype.age=21; Person.prototype.job="Software Engineer"; Person.prototype.sayName=function(){ alert(this.name); }; var person1=new Person(); var person2=new Person(); person1.name="Wu"; alert(person1.name);//Wu——来自实例//当alert()访问person1.name时,在该实例上搜索到一个名为name的属性,则不必搜索原型 alert(person2.name);//Chiaki——来自原型//当alert()访问person2.name时,在该实例上搜索不到一个名为name的属性,就继续搜索原型,结果在那里找到了name属性。
使用delete操作符可以完全删除实例属性:。
var person1=new Person(); var person2=new Person(); person1.name="Wu"; alert(person1.name);//Wu alert(person2.name);//Chiaki delete person1.name; alert(person1.name);//Chiaki//delete操作符删除了person1.name,从而恢复了对原型中name属性的连接
可使用hasOwnProperty()方法检测一个属性是否存在于实例中,还是存在原型中。
alert(person1.hasOwnProperty("name"));//false//person1实例中没有name属性 person1.name="Wu";//person1重写name属性,即person1实例中有name属性 alert(person1.hasOwnProperty("name"));//true//原型与in操作符:单独使用、在for-in循环中使用。
单独使用时,in操作符会在通过对象能够访问给定属性时返回true,无论该属性存在实例中还是原型中。
alert("属性" in 实例);如果该实例中有该属性则返回true,否则返回false。
function Person(){ } Person.prototype.name="Chiaki"; Person.prototype.age=21; Person.prototype.job="Software Engineer"; Person.prototype.sayName=function(){ alert(this.name); }; var person1=new Person(); var person2=new Person(); alert(person1.hasOwnProperty("name"));//false//name属性在原型中 alert("name" in person1);//true person1.name="Wu";alert(person1.hasOwnProperty("name"));//true//重新name属性后,person1实例中有name属性 alert("name" in person1);//true
hasPrototypeProperty() 方法判断访问到的属性是否存在于原型中。 var person =new Person(); alert(hasPrototypeProperty(perso,"name"));//true person.name="Wu";//重写person.name,原型中的name属性被屏蔽,则访问到的name属性来自实例person alert(hasPrototypeProperty(person,"name");//false
in操作符和hasOwnProperty()方法同时使用可以判断属性是处于对象中还是处于原型中。
在for-in循环中使用,返回所有能够通过对象访问的可枚举(enumerated)属性,包括实例中的属性和原型中的属性。屏蔽了原型中不可枚举属性(即[[enumerable]]设置为false)的实例属性也会在for-in循环中返回(IE 8 及更早的版本除外).
var o={ name:"Chiaki" }; for (var prop in o){ alert(prop);//name }
IE早期版有个bug会导致屏蔽不可枚举属性的实例属性不会出现在for-in循环中。
默认不可枚举的属性和方法有:hasOwnProperty()、propertyIsEnumerable()、toLocaleString(),toString()和valueOf();ECMAScript5也将constructor和prototype属性的[[Enumerable]]设置为false。
Object.keys()方法接收一个对象作为参数,返回一个包含所有可枚举属性的字符串数组。
var keys=Object.keys(Person.prototype); alert(keys);//"name,age,job,sayName" var p1=new Person(); p1.name="Wu"; p1.age=20; var p1Keys=Object,keys(p1); alert(p1Keys);//"name,age"
Object.getOwnPropertyNames()返回所有实例属性 var keys=Object.getOwnPropertyNames(Person.prototype); alert(keys);//“constructor,name,age,job,sayName"
更简单的原型语法:
Person.prototype={} function Person(){} Person.prototype={ name:"Chiaki", age:21, job:"Software Engineer", sayName:function(){ alert(this.name); } };
上例中,我们将Person.prototype设置为等于一个以对象字面量形式创建的新对象,此时constructor属性不再指向Person.(原因:Person.prototype={}本质上完全重写了创建构造函数Person时默认的prototype对象,因此constructor属性就变成了新对象的constructor属性【指向Object构造函数】)。
此时如果constructor的值很重要时可特意将它设置回适当的值,如下:
funtion Person(){ } Person.prototype={ constructor:Person,//重设constructor属性将导致它的[[enumerable]]特性被设置为true,可以通过Object.defineProperty()修改成不可枚举的 name:"Chiaki", age:21, job:"Software Engineer", sayName:function(){ alert(this.name); } };
原型的动态性:由于在原型中查找值的过程是一次搜索,因此我们对原型对象所做的任何修改都能够立即从实例上反应出——即使先创建了实例后修改原型也照样如此。
var friend=new Person(); Person.prototype.sayHi=function(){ alert("hi"); } friend.sayHi();//"hi"
实例和原型时间的连接是一个指针而非一个副本,因此可以随时为原型添加属性和方法,并且修改能够立即在所有对象实例中反应出来。但是如果是重写整个原型对象,将会切断构造函数和最初原型之间的联系。实例中的指针仅指向原型而非构造函数。
原型对象的问题:1、省略了为构造函数传递初始化参数的环节,结果所有实例在默认情况下都将取得相同的属性值;2、原型对象具有共享的本性,对于包含引用类型值的属性,如果一个原型对象创建的两个实例其中一个修改了引用类型的属性值,将导致另一个实例的该引用类型的属性值也被改变。
function Person(){} Person.prototype={ constructor=Person, friends:["lola","cherry"], } var person1=new Person(); var person2=new Person(); person1.friends.push("van"); alert(person1.friends);//lala,cherry,van alert(person2.friends);//lola,cherry,van alert(person1.friends==person2.friends)//true
组合使用构造函数模式和原型模式
构造函数模式用于定义实例属性,原型模式用于定义方法和共享属性。
function Person(name,age,job){ this.name=name; this.age=age; this.job=job; this.friends=["lola","cherry"]; } Person.prototype={ constructor:Person, sayName:function(){ alert(this.name); } } var person1=new Person("Chiaki",21,"Software Engineer"); var person2=new Person("Wu",20,"Student"); person1.friends.push("Van"); alert(person1.friends);//lola,cherry,Van alert(person2.friends);//lola,cherry alert(person1.friends==person2.friends);//false//friends是构造函数中定义的实例属性 alert(person1.sayName==person2.sayName);//trues//sayName()是原型中定义的,具有共享性
动态原型模式
把所有信息封装在构造函数中,而通过在构造函数中初始化原型又保持了同时使用构造函数和原型的优点。即通过检查某个应该村咋的方法是否有效来决定是否需要初始化原型。
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 friend=new Person("Chiaki",21,"Software Engineer"); friend.sayName();
寄生式构造函数模式
创建一个函数以封装创建对象的代码,然后返回新创建的对象。
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 friend=new Person("Chiaki",21,"Software Engineer"); friend.sayName();//Chiaki
构造函数在不返回值的情况下默认会返回新对象实例,而通过在构造函数的末尾添加一个return语句,可以重写调用构造函数时返回的值。构造函数返回的对象与构造函数或者构造函数的原型属性之间没有关系。
稳妥构造函数模式
稳妥对象:即没有公共属性,且方法也不用this的对象。适用于安全的环境中(该环境下禁用this和new)或者在在防止数据被其他应用程序改动时使用。
function Person(name,age,job){ //创建要返回的对象 var o=new Object(); //可以在这里定义私有变量和函数 //添加方法 o.sayName=function(){ alert(name); } //返回对象 return o; }
这种模式下创建的对象在,除使用sayName方法外,没办法访问name值。