1、(外部属性定义方式:)简单自定义对象的最简单方式就是创建一个
var person = new Object(); person.name="Leon"; person.age = 13; person.show = function(){ alert(this.name+"--"+this.age); }
由于没有类的约束,无法实现对象的重复利用
2、对象字面量(json格式创建):
var p1 = {name:"Leon",age:24}; var p2 = {name:"Ada",age:18}; var ps = [{name:"张三",age:34},{name:"李四",28},{name:"小刘",16}];//一组person对象
3、工厂模式:
function createPerson(name,age){ var o = new Object(); o.name = name; o.age = age; o.say = function(){ alert(this.name+" "+this.age); } return o; } var p1 = createPerson("Joke",22); var p2 = createPerson("John",26);
工厂模式虽然解决了创建多个相似对象的问题,但却没有解决对象识别的问题(即怎样知道一个对象的类型)。
4、构造函数模式:
function Person(name,age){ this.name = name; this.age = age; this.say = function(){ alert(this.name+" "+this.age); } }
var p1 = new Person("Linda",38);
var p2 = new Person("Wuxiao",26);
Person()函数取代了 createPerson()函数,函数名 Person 使用的是大写字母 P,构造函数始终都应该以一个大写字母开头,
要创建 Person 的新实例,必须使用 new 操作符,经过以下4个 步骤
(1) 创建一个新对象;
(2) 将构造函数的作用域赋给新对象(因此 this 就指向了这个新对象);
(3) 执行构造函数中的代码(为这个新对象添加属性);
(4) 返回新对象。
p1 和 p2 分别保存着 Person 的一个不同的实例。这两个对象都有一个 constructor(构造函数)属性,该属性指向 Person :
alert(p1.constructor == Person); //true alert(p2.constructor == Person); //true
对象的 constructor 属性最初是用来标识对象类型的。但是,提到检测对象类型,还是 instanceof 操作符要更可靠一些。我们在这个例子中创建的所有对象既是 Object 的实例,同时也是 Person
的实例,这一点通过 instanceof 操作符可以得到验证。
alert(p1 instanceof Object); //true alert(p2 instanceof Person); //true alert(p2 instanceof Object); //true alert(p2 instanceof Person); //true
创建自定义的构造函数意味着将来可以将它的实例标识为一种特定的类型;而这正是构造函数模式胜过工厂模式的地方。
任何函数,只要通过 new 操作符来调用,那它就可以作为构造函数;而任何函数,如果不通过 new 操作符来调用,那它跟普通函数也不会有什么两样。 // 当作构造函数使用 var person = new Person("Nicholas", 29, "Software Engineer"); person.sayName(); //"Nicholas" // 作为普通函数调用 Person("Greg", 27, "Doctor"); // 添加到 window window.sayName(); //"Greg" // 在另一个对象的作用域中调用 var o = new Object(); Person.call(o, "Kristen", 25, "Nurse"); o.sayName(); //"Kristen
构造函数的问题:主要问题,就是每个方法都要在每个实例上重新创建一遍(相当于拷贝一遍)。 如果 对象有多个方法,那么将占用很多的内存空间,
function Person(name,age){ this.name = name; this.age = age; } function say(){ alert(this.name+" "+this.age); } var p1 = new Person("Linda",38); var p2 = new Person("Wuxiao",26); alert(p1.say == p2.say); //true 就是在局作用域中定义的同一个 say()函数。如果对象需要定义很多方 法,那么就要定义很多个全局函数,于是我们这个自定义的引用类型就丝毫没有封装性可言了
5、原型模式:
当创建函数时,每个函数都有一个 prototype(原型)属性,这个属性是一个指针,指向原型对象,原型对象的好处是可以让所有对象实例共享它所包含的属性和方法 。
function Person(){ } Person.prototype.name = "Linda"; Person.prototype.age = 26; Person.prototype.say = function(){ alert(this.name+" "+this.age); } var p1 = new Person(); alert(p1.name) //Linda var p2 = new Pseron(); alert(p2.name) //Linda alert(p1.say() == p2.say()); //true
新对象的这些属性和方法是由所有实例共享的。换句话说,p1 和 p2 访问的都是同一组属性和同一个 say()函数。
原型模式的问题:1、无法通过构造函数来设置属性值 2、当属性中有引用类型时,会出问题。
下一节,有对原型的 具体 理解。
6、组合使用构造函数模式和原型模式(最常用的方式,)属性放到构造函数中,方法放到原型中去
function Person(name,age){ this.name = name; this.age = age; this.friends = ["Shelby", "Court"]; } Person.prototype.say = function(){ alert(this.name+" "+this.age); } var p1 = new Person("LInda",28); var p2 = new Pseron("John",25);
7、动态原型模式:
为了让整体看着更加的美观,像一个对象,所以把定义原型的方法也 放到了构造函数中去;
function Person(name,age){ this.name = name; this.age = age; this.friends = ["Shelby", "Court"]; if(typeof of this.say !=function){ Pseron.prototype.say = function(){ alert(this.name+" "+this.age); } } }
这段代码只会在初次调用构造函数时才会执行,以后 就不会再执行了。不必用一大堆if 语句检查每个属性和每个方法;只要检查其中一个即可。
使用动态原型模式时,不能使用对象字面量重写原型。前面已经解释过了,如果在已经创建了实例的情况下重写原型,那么就会切断现有实例与新原型之间的联系。
8、寄生构造函数模式:
和工厂模式一模一样
返回的对象与构造函数或者与构造函数的原型属性之间没有关系;也就是说,构造函数返回的对象与在构造函数外部创建的对象没有什么不同。为此,
不能依赖 instanceof 操作符来确定对象类型。