js面向对象程序设计 (创建对象)
创建对象
工厂模式
function creatPerson(name, age, job){ var o = new Object(); o.name = name; o.age = age; o.job = job; o.sayName = function(){ alert(this.name); } } var person1 = creatPerson("Nicy", 23, "teacher"); var person2 = creatPerson("Grey", 30, "doctor"); *缺点: 没有解决对象识别问题。(对象的类型是什么?)*
构造函数模式
function Person(name, age, job){ this.name = name; this.age = age; this.job = job; this.sayName = function(){ alert(this.name); } } var person1 = new Person("Nicy", 23, "teacher"); var person2 = new Person("Grey", 30, "doctor"); a.要创建Person的新实例,必须使用new操作符。 b.以这种方式调用构造函数会经过一下四个步骤: 1. 创建一个新对象。 2. 将构造函数的作用域赋给新对象(this 指向新对象) 3. 执行构造函数中的代码(为对象添加属性和方法) 4. 返回新对象 c.创建自定义的构造函数,将来可以将它的实例标示为一种特定的类型。 alert( person1 instanceof Person); // true d.person1和person2分别保存着Person的一个不同实例。 这两个实例都有一个constructor(构造函数)属性,该属性指向Person alert( person1.constructor == Person); // true e.构造函数的问题 每个方法都要在实例上重新创建一遍。不同实例中的相名方法不是同一个Function实例,创建多个完全相同任务的Function实例是没必要的。
原型模式
原型的写法
function Person(){ } Person.prototype.name = "Nic"; Person.prototype.age = 12; Person.prototype.job = "doctor"; Person.prototype.sayName = function (){ alert(this.name); } var person1 = new Person(); var person2 = new Person(); a.与构造函数的不同,新对象的属性和方法是由所有实例共享的。 b.理解原型对象 1)只要创建一个新函数,就会为该函数创建一个prototype属性,这个属性指向函数的原型对象。 Person.prototype是Person的原型 2)默认情况下,所有的原型都会自动获得一个constructor(构造函数)属性,这个属性指向一个prototype属性所在函数得指针。 Person.prototype.constructor 指向 Person. 3)当调用构造函数创建一个实例,该实例内部报验一个指针(内部属性),指向构造函数的原型对象。 person1 的内部属性[[prototype]]//person1.__proto__ person1.__proto__ 指向 Person.prototype 4)原型的isPrototyoeOf()方法 测试原型与实例之间是否存在关系 alert(Person.prototype.isPrototypeOf(person1));//true 5)Object.getPrototypeOf(实例名),返回[[prototype]]值 alert( Object.getPrototypeOf(person1) === Person.prototype ); //true alert( Object.getPrototypeOf(person1).name);//Nic 6)当读取某个对象的某个属性时,都会执行一次搜索。 先从对象实例本身开始,如果找到,则返回。如果没找到,继续搜索置身指向的原型对象。 7)虽然可以通过实例访问保存在原型中的值,但不能通过对象实例重写原型中的值 如果在实例中添加一个属性,该属性与实例原型中的一个属性同名,在实例中创建该属性, 会屏蔽原型中的那个属性,不会修改原型中的属性值。 person1.name = "xiaoniu"; alert(person1.name); // "xiaoniu" alert(person2.name); // "Nic" 8)实例.hasOwnPrototype("属性名") 检测一个属性存在于实例中还是原型中。true:实例中,false: 原型中或者没有该属性 9)原型与in操作符: in 操作符会通过对象能够访问给定属性时返回true,无论属性存在原型还是实例中。 alert("name" in person1); // true alert("name" in Person.prototype);// true 10)in 和hasOwnproperty() 连用,确认属性在原型中还是实例中 function hasPrototypeProperty(object, name){ return !object.hasOwnProperty(name) &&(name in object); } 11)Object.keys()方法,取得对象上的可枚举属性 Object.keys(Person.prototype); Object.keys(person1); //问题:实例上的可枚举属性包括原型上的属性吗?测试之后填写
原型的重写
function Person(){ } Person.prototype = { name: "Nic", age : 23, sayName : function(){ alert(this.name); } } 1.用包含所有属性和方法的字面量对象重写原型。 2.问题:constructor 属性不再指向Person。 3.当创建一个函数,同时会创建它的protptype对象,这个对象会自动获得constructot属性。 4.而这里重写了函数的原型,constructor属性变成了新对象的constructor属性(指向Object构造函数),不再指向Person函数。 5.尽管instaceof操作符还能返回城阙结果,但是通过constructor已经无法确认对象的类型了。 var friend = new Person(); alert(friend instanceof Object); // true alert(friend instanceof Person); // true alert(friend.constructor == Object); // true alert(friend.constructor == Person); // false 6. 如果constructor的值真的很重要,可以特意将它设置回适当的值。 function Person(){ } Person.prototype = { constructor: Person, name: "Nic", age : 23, sayName : function(){ alert(this.name); } }
原型的动态性
1. 由于在原型中查找属性时一个检索的过程,对原型对象所做的任何修改都能立即从实例上反应出来。 (即使先创建了实例后修改原型) var friend = new Person(); Person.prototype.sayHi = function(){ alert("Hi"); } friend.sayHi(); // "Hi" 2. 重写原型之后,原型属性不能立即生效。 调用构造函数时,会为实例添加一个指向最初原型的[[prototype]]指针,而把原型修改为另一个对象, 就等于切断了构造函数与最初原型之间的联系。 var friend = new Person(); Person.prototype = { constructor: Person, name : "Nic", sayHi : function(){ alert("Hi"); } } friend.sayHi(); // error // 实例中的指针仅指向原型,而不是指向构造函数。 // 重写原型对象切断了现有原型与任何之前已经存在的对象实例之间的联系,它们引用的仍然是最初的原型。
原型对象的问题
1.原型模式忽略了为构造函数传递初始化参数这一环节。 2.原型属性共享:函数共享、属性共享。当属性的值为引用类型,问题出现 function Person(){ } Person.prototype = { constructor: Person, friends: ["ming","liang"] } var person1 = new Person(); var person2 = new Person(); person1.friends.push("xiao"); console.log(person2.friends);// 'ming','liang','xiao'
组合使用构造函数模式和原型模式 (经典模式)
1.构造函数模式用于定义实例属性,原型用于定义方法和共享属性。最大限度节省内存空间。 function Person(name,age){ this.name = name; this.age = age; this.friends = ["Shel","Curs"]; } Person.prototype = { constructor: Person, sayName : function(){ alert(this.name); } } var person1 = new Person("ming",22); var person1 = new Person("liang",40); person1.friends.push("nick"); alert(person1.friends); //"Shel","Curs","nick" alert(person1.friends); //"Shel","Curs" alert(person1.friends === person2.friends); // false alert(person1.sayName === person2.sayName); // true
动态原型模式 (经典模式)
//把所有的信息都封装在构造函数中。 function Person(name,age){ this.name = name; this.age = age; if(typeof this.sayName != "function"){ //这段代码只会在初次调用的时候执行一次 person.prototype.sayName = function(){ alert(this.name); } } } // 不必一大堆的if语句检查每个属性和方法,只检查一个即可。
寄生构造函数模式
function Person(name,age){ var o = new Object(); o.name = name; o.age = age; o.sayName = function(){ alert(this.name); } return o; } var friend = new Person("liang",20); friend.sayName(); 1. 基本思想: 创建一个函数,该函数的作用仅仅是封装创建对象的代码。然后再返回新创建的对象。 2. 除了使用new操作符并把使用的包装函数叫做构造函数,这个模式和工厂模式一摸一样。 3.构造函数在不返回值的情况下,默认会返回新对象实例。而通过在构造函数的末尾添加一个return语句, 可以重写调用构造函数时返回的值。 4.这个模式可以在特殊的情况下用来为对象创建构造函数。 假设像创建一个具有额外方法的特殊数组,可以用这个模式 function SpecialArray(){ var value = new Array(); value.push.apply(values, arguments); value.toPipedString = function(){ return this.join("|"); } return value; } var colors = new SpecialArray("red","blue","green"); alert(colors.toPipedString());// "red|blue|green" // 寄生构造函数模式: 返回的对象与构造函数以及构造函数的原型属性之间没有关系。 //不能依赖instanceof操作符来确定对象类型。 //建议在可以使用其他模式的情况下,不要使用这种模式
稳妥构造函数模式
1.稳妥对象: 没有公共属性,方法也不用引用this的对象。 2.稳妥对象最适合在一些安全环境中(这些环境禁止使用this和new), 或者方式数据被其他应用程序改动时使用。 3.新创建对象的实例方法不引用this,不使用new操作符调用构造函数。 function Person(name,age){ var o = new Object(); o.sayName = function(){ alert(name); } return o; } var friend = Person("ming",20); friend.sayName(); // "ming"