---恢复内容开始---
1:工厂模式
function creatPerson(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 =creatPerson("xiaoming",29,"it"); var person2 =creatPerson("lili",27,"ittoo"); person1.sayName()//xiaoming 没有解决对象识别问题、 person2.sayName()//lili 没有解决对象识别问题,既怎么知道一个对象的类型
2:构造函数模式
function Person(name,age,job){//构造函数始终以大写字母开头 this.name = name; this.age = age; this.job = job; this.sayName =function(){ alert(this.name) } } //作为构造函数使用 var person1 =new Person("xiaoming",29,"it"); var person2 =new Person("lili",27,"ittoo"); //都有一个constructor(构造函数)属性,该属性指向Person alert(person1.constructor == Person)//true alert(person1 instanceof Object)//true alert(person1 instanceof Person)//true //作为普通函数调用 Person("Gay",28,"itthree"); window.sayName()//gay var o = Object(); Person.call(o,"blue",28,"itthree"); o.sayName()
//缺点 创建了两个同样任务的Function实例
alert(person1.sayName()==person2.sayName())//false
不同之处:
一、没有显示地创建对象;
二、直接将属性和方法赋给了this对象`
三、没有return语句
3:原型模式
每个函数都有一个prototype(原型)属性,这个属性是一个指针,指向一个对象,使用对象的好处就是所有实例共享它所包含的属性和方法。
function Person(){ } Person.prototype.name = "Nicholas"; Person.prototype.age = 29; Person.prototype.job = "it"; Person.prototype.sayName =function(){ alert(this.name) } var person1 =new Person(); person1.sayName();//"Nicholas" var person2 =new Person(); person2.sayName();//"Nicholas" alert(person1.sayName()==person2.sayName())//true
Person.prototype.isPrototypeOf(person1) //true
alert(Object.getPrototypeOf(person1) ==Person.prototype )//true
alert(Object.getPrototypeOf(person1).name )//"Nicholas"
person1.name = "Gay";
alert(person1.name)//"Gay"
alert(person2.name)//"Nicholas"
//实例的优先级高于原型 使用delete操作符可以完全删除实例属性,从而让我们能够重新访问原型中的属性
delete person1.name;
alert(person1.name)//"Nicholas"
用hasOwnProperty()用于检测一个属性是否存在于实例中,还是存在于原型中 实例中为true 原型中为false
person1.name = "Gay"; alert(person1.name)//"Gay" alert(person1.hasOwnProperty("name"))//true 来自实例 alert(person2.name)//"Nicholas" alert(person1.hasOwnProperty("name"))//false 来自原型 delete person1.name alert(person1.name)//"Nicholas alert(person1.hasOwnProperty("name"))//false 来自原型
2:原型与in操作符
//由于in操作符只要通过对象能够访问到属性就返回true,hasOwnProperty()只在属性存在于实例中才返回true,因此只要in操作符返回true而hasOwnProperty()返回false,就可以确定属性是原型中的属性 function hasPrototypeProperty(Object,name){ return !Object.hasOwnProperty(name)&&(name in object) }
for in,返回的是所有能够通过对象访问的,可枚举的(enumerated)属性
var o = { toString : function(){ return "My Object"; } } for (var prop in o){ if (prop == "toString") { alert("Found toString") } }
要取得对象上所有可枚举的实例属性,可以通过ES5的Object.keys()方法。这个方法接收一个对象作为参数,返回一个包含所有可枚举属性的字符串数组
function Person(){ } Person.prototype.name = "Nicholas"; Person.prototype.age = 29; Person.prototype.job = "it"; Person.prototype.sayName =function(){ alert(this.name) } var keys = Object.keys(Person.prototype); alert(keys)//name age job sayName var p1 = new Person(); p1.name = "gay"; p1.age = 31; var p1keys = Object.keys(p1) alert(p1keys)//name age //如果你想知道所有实例属性,不管是否有枚举,都可以使用Object.getOwnPropertyNames() var keys = Object.getOwnPropertyNames(Person.prototype); alert(keys)//constructor,name,age,job,sayName
3:更简单的原型语法
function Person(){ } Person.prototype ={ constructor:Person,//少了这一行,,constructor将不再指向Person name : "Nicholas", age : 29, sayName : function(){ alert(this.name) } } //但是会导致他的[[Enumerable]]特性被设置为true,默认情况下,原生的constructor属性是不可枚举的 //重设构造函数, Object.defineProperty(Person.prototype,'constructor',{ enumerable:false,//设置为不可枚举 value:Person })
function Person(){ } //var friend = new Person();//报错 Person.prototype ={ constructor:Person,//少了这一行,,constructor将不再指向Person name : "Nicholas", age : 29, sayName : function(){ alert(this.name) } } var friend = new Person();//不报错
原型的缺点:所有的属性是被很多实例共享的,这种共享对于函数非常适合。然而对于包含引用类型值得属性来说,问题就比较突出
function Person(){ } //var friend = new Person();//报错 Person.prototype ={ constructor:Person,//少了这一行,,constructor将不再指向Person name : "Nicholas", age : 29, friends:["1","2"] sayName : function(){ alert(this.name) } } var person1 = new Person();//不报错 var person2 = new Person(); person1.friends.push("3"); alert(person1.friends); alert(person2.friends); alert(person1.friends===person2.friends)//true
ES中使用最广泛、认同度最高的一种创建方法,组合使用构造函数模式和原型模式,每个实例都有一份实例属性的副本,但同时又共享着对方的引用,最大限度地节省了内存。
function Person(name,age,job){ this.name = name; this.age = age; this.job = job; this.friends = ["1","2"] } Person.prototype ={ constructor:Person,//少了这一行,,constructor将不再指向Person sayName : function(){ alert(this.name) } } var person1 = new Person("gay",29,"it");//不报错 var person2 = new Person("blue",27,"meinv"); person1.friends.push("3"); alert(person1.friends);//"1","2","3" alert(person2.friends);//"1","2" alert(person1.friends===person2.friends)//false alert(person1.sayName===person2.sayName)//true
动态原型模式
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); }; } }
Person
是一个构造函数,通过new Person(...)
来生成实例对象。每当一个Person的对象生成时,Person内部的代码都会被调用一次。
如果去掉if
的话,你每new一次(即每当一个实例对象生产时),都会重新定义一个新的函数,然后挂到Person.prototype.sayName
属性上。而实际上,你只需要定义一次就够了,因为所有实例都会共享此属性的。所以如果去掉if
的话,会造成没必要的时间和空间浪费;而加上if
后,只在new第一个实例时才会定义sayName
方法,之后就不会了。
至于第二个问题,是这样的:假设除了sayName
方法外,你还定义了很多其他方法,比如sayBye
、cry
、smile
等等。此时你只需要把它们都放到对sayName判断的if块里面就可以了。
寄生构造函数
在JS里面,有一种类似工厂模式的定义对象方法——寄生构造函数模式,如下所示:
其实,除了使用new操作符来定义新的对象,以及将其称之为构造函数之外,其他和工厂模式定义一模一样。那么这个寄生构造函数到底是为了什么呢?
我们看一个例子:
我们定义一个Array的引用类型,并且初始化
var colors=new Array("red","blue","yellow");
alert("colors"); //red,blue,yellow
有时候我们并不想数组输出元素之间用“,”分割,于是我们采用join()方法;
但是每定义一个引用类型都使用一次join()方法有点麻烦,那么解决这个问题的办法就是直接改变Array构造函数默认定义的输出方式,
类似Object,Array,Date等等的拥有原生构造函数的引用类型并不能直接修改其原生构造函数,那么此时寄生构造函数就派上用场了。
于是我们可以定义如下一个特殊的Array引用类型:
只要每次利用SpecialArray()来定义新的引用类型即可实现改变Array的输出方式。
综上所述,其实寄生构造函数就是在原生构造函数上的一个扩展,也就是你可以利用寄生构造函数来自定义一种引用类型,实现自己想要达到的效果。
function Person(name, age, job) { var o = new Object(); o.sayName = function() { console.log(name); }; o.setName = function(value) { name = value; }; return o; }; var friend = Person("Nicholas", 29, "Soft Engineer");
//除了sayName方法之外,没有其他办法访问name的值