Javascript之创建对象(原型模式)
合集下载
相关主题
- 1、下载文档前请自行甄别文档内容的完整性,平台不提供额外的编辑、内容补充、找答案等附加服务。
- 2、"仅部分预览"的文档,不可在线预览部分如存在完整性等问题,可反馈申请退款(可完整预览的文档不适用该条件!)。
- 3、如文档侵犯您的权益,请联系客服反馈,我们会尽快为您处理(人工客服工作时间:9:00-18:30)。
1、理解原型对象
无论什么时候,只要创建了一个新函数,ECMAScript就会根据一组特定的规则为该函数创 建一个prototype属性,这个属性指向函数的原型对象。在默认情况下,所有原型对象都会自 动获得一个constructor(构造函数)属性,这个属性包含一个指向prototype属性所在函数的 指针。就拿前面的例子,Person.prototype.constructor指向Person。而通过这个构造函数, 我们还可以继续为原型对象添加其他属性和方法。 创建了自定义指针之后,其原型对象默认只会取得constructor属性;至于其它方法,都会 从Object对象继承而来。当调用构造函数创建一个新实例之后,该实例的内部将包括一个指针 (内部属性),指向构造函数的原型对象。ECMA-262第5版中管这个叫[[Prototype]]。 要明确一点的就是,这个连接存在于实例和构造函数的原型对象之间,而不是存在于实例和 构造函数之间。 以前面使用的Person构造函数和Person.prototype创建实例的代码为例,如下图:
function Person() { } Person.prototype.name = "zxj";
Person.prototype.age = 29; Person.prototype.job = "Software Engineer"; Person.sayName = function () { alert(this.name); } var person1 = new Person(); var person2 = new Person(); person1.name = "Greg"; alert(person1.name); //Greg 来自实例 alert(person2.name); //zxj 来自原型 delete person1.name; //删除实例中的name属性 alert(person1.name); //zxj 来自原型 使用hasOwnPeoperty()方法可以检测一个属性是否存在于实例中, 还是存在原型中,这个方法(它是从Object继承来的)只在给定属性存在 域对象实例中时,才返回true。 function Person() { } Person.prototype.name = "zxj"; Person.prototype.age = 29; Person.prototype.job = "Software Engineer"; Person.sayName = function () { alert(this.name); } var person1 = new Person(); var person2 = new Person(); alert(person1.hasOwnProperty("name")); //false person1.name = "Greg"; alert(person1.name); //Greg 来自实例 alert(person1.hasOwnProperty("name")); //true alert(person2.name); //zxj 来自原型 alert(person2.hasOwnProperty("name")); //false delete person1.name; alert(person1.name); //zxj 来自原型 alert(person1.hasOwnProperty("name")); //false
上图展示了Person构造函数、Person的原型属性以及Person现有的两个实例之间的关系。 在此,Person.prototype指向了原型对象,而Person.prototype.constructor又指回了 Person。原型对象中除了包含constructor属性之外,还包括后来添加的其他属性。Person的 每一个实例——person1和person2都包含一个内部属性,该属性仅仅指向 Person.prototype。换句话说,它们与构造函数没有直接的联系。此外,要格外注意的是,虽 然这两个实例都不包含属性和方法,但我们却可以调用person1.sayName()。这是通过查找对 象属性的过程来实现的。 虽然我们无法访问到[[Prototype]],但可以通过isPrototypeOf()方法来确定对象之间是否 存在这种关系。从本质上讲,如果[[Prototype]]指向调用isPrototypeOf()方法的对象 (Person.prototype),那么这个方法就会返回true。
2、原型与in操作符
有两种方式使用in操作符:一、单独使用;二、for-in中使用。 功能:会在通过对象能够访问给定属性时返回true,无论是在对象实例中或是原型中。
function Person() { } Person.prototype.name = "zxj"; Person.prototype.age = 29; Person.prototype.job = "Software Engineer"; Person.sayName = function () { alert(this.name); } var person1 = new Person(); var person2 = new Person(); alert(person1.hasOwnProperty("name")); alert("name" in person1); //true person1.name = "Greg"; alert(person1.name); //Greg 来自实例 alert(person1.hasOwnProperty("name")); //true alert("name" in person1); //true alert(person2.name); //zxj 来自原型 alert(person2.hasOwnProperty("name")); //false alert("name" in person2); //true delete person1.name; alert(person1.name); //zxj 来自原型 alert(person1.hasOwnProperty("name")); //false alert("name" in person1); //true alert(person1.hasOwnProperty("qqqq")); //false alert("qqqq" in person1); //false
结果是与先前的相同,但有一个是不同的:contrcutor属性不再指向Person了。我们曾经介 绍过,没创建一个函数,就会同时创建它的prototype对象,这个对象也会自动获得 constructor属性。而我们这样写,本质上是完全重写了默认的prototype对象,因此 constructor属性也就变成了新对象的constructor属性(指向Object构造函数),不再指向 Person函数。
function Person() { } Person.prototype = { constructor: Person, name: "zxj", age: 29, sayName: function () { alert(this.name); } }; Object.defineProperty(Person.prototype,"constructor",{ enumerable:false, value:Person });
同时使用hasOwnProperty()和in操作符可以判断出该属性到底是存在对象实例中还是存在
//false
与原型中。 使用for-in循环时,返回的是所有能够通过对象访问的,可枚举(enumerated)属性,其中即 包括存在与实例中的属性,也包括存在与原型中的属性。根据规定,开发人员定义的属性都是 可枚举的——IE8及更早版本除。
ຫໍສະໝຸດ Baidu
3、更简单的原型语法
我们可以用一个包含属性和方法的对象字面量重写整个原型对象。
function Person(){ } Person.peototype={ name:"zxj", age:29, job:"Software Engineer", sayName:function(){ alert(this.name); } };
4、原型的动态性
由于在原型中查找值的过程是一次搜索,因此我们对原型对象所做的任何修改都能够立即从 实例上反映出来——即使是先创建了实例后修改原型也照样可以,如下所示:
function Person() { } var friend = new Person(); Person.prototype.sayHi = function () { alert("hi"); } friend.sayHi(); //"hi"
当然我可以将它特意设置成适当的值:
function Person() { } Person.prototype = { constructor: Person, name: "zxj", age: 29, sayName: function () { alert(this.name); } };
以上代码特意包含了一个constructor属性,并将它的值设置为Person,从而确保了通过该 属性能够访问到适当的值。 注意,以这种方式重设constructor属性会导致它的[[Enumerable]]特性被设置为true。默 认情况下,constructor属性是不可枚举的。因此如果你使用兼容ECMASCript5的JavaScript 引擎,可以试一试Object.definePropety()。
Javascript之创建对象(原型模式) 我们创建的每个函数都有一个prototype(原型)属性,这个属性是一个指针,指向一个对 象,它的用途是包含可以有特定类型的所有实例共享的属性和方法。 prototype就是通过构造函数而创建的那个对象的原型对象。使用原型的好处就是可以让所 有对象实例共享它所包含的属性和方法 。
alert(Person.prorotype.isPrototypeOf(person1)); //true alert(Person.prorotype.isPrototypeOf(person2)); //true
每当代码要读取某个对象的属性时,都会进行一次搜索,搜索目标是具有给定名称的属性。 搜索当然先从对象实例的本身开始,如果找到了,就可以返回该值了;如果找不到,则会去指 针所指向的原型对象中去查找,在原型对象中找到了,就可以顺利返回该值。而这正是多个对 象实例共享原型所保存的属性和方法的基本原理。 虽然可以通过对象实例访问到保存在原型中的值,但不能通过对象实例重写原型中的值。根 据查找原理,如果找到了实例中的值,就不会再去查找原型对象中的值。,代码如下所示:
var o = { toString: function () { return "My Object"; } } for (var prop in o) { if (prop == "toString") { alert("Found toString"); //在IE中不会显示(IE9(未测试) 和IE10(已测试)可用) } }
function Person() { } Person.prototype.name = "zxj"; Person.prototype.age = 29; Person.prototype.job = "Software Engineer"; Person.prototype.sayName = function () { alert(this.name); } var person1 = new Person(); person1.sayName(); //zxj var person2 = new Person(); person2.sayName(); //zxj