1. 原型模式
JavaScript中我们创建的每个函数都有一个prototype(原型)属性,这个属性是一个对象,它的用途是包含可以由特定类型的所有实例共享的属性和方法。
如果按照字面意思来理解,那么prototye就是通过调用构造函数而创建的那个对象的原型对象。
使用原型对象的好处就是可以让所有对象实例共享它所包含的属性和方法。
或句话说,就是不必在构造函数中定义对象信息,而是可以将这些信息直接添加到原型对象中。
function Person() {}
//将属性和方法添加到Person的prototype属性中,构造函数变成了空函数
Person.prototype.name = "answer";
Person.prototype.age = 22;
Person.prototype.job = "Software Engineer";
Person.prototype.sayName = function() {
alert(this.name);
}
var person1 = new Person();
person1.sayName(); //answer
var person2 = new Person();
person2.sayName(); //answer
//不同的实例同享对象原型中的方法
alert(person1. sayName == person2.sayName); //true
2. 理解原型
无论什么时候,只要创建了一个新函数,就会根据一组特定的规则为该函数生成一个prototype属性。
在默认情况下,所有prototype属性都会自动获得一个constructor(构造函数)属性,这个属性包含一个指向prototype属性所在函数的指针。
如上面Person.prototype.constructor指向Person。而通过这个构造函数,我们还可以继续为原型添加其他属性和方法。
创建了自定义的构造函数后,其原型属性默认只会取得contructor属性;至于其他方法,则都是从Object继承而来。
当调用构造函数创建一个新实例后,该实例的内部将包含一个指针(一般为"_proto_",内部属性),指向构造函数的原型属性。(一些浏览器中,这个属性对脚本可见)
要明确的重要一点就是:就是这个连接存在于实例与构造函数的原型属性之间,而不是存在于实例与构造函数之间。
虽然实例的内部_proto_指针在一些实现中无法访问,但是,所有实现中都可以通过isPrototypeOf()方法来确定对象之间是否存在这种关系。
如果对象的_proto_指向调用isPrototypeOf()方法的对象,那么这个方法就返回true,如下所示:
alert(Person.prototype.isProtorypeOf(person1)); //true
alert(Person.prototype.isProtorypeOf(person2)); //true
3. 查找对象属性的过程
每当代码读取某个对象的某个属性的时候,都会执行一次搜索,目标是具有给定名字的属性。
搜索首先从对象实例本身开始。
如果在实例中找到了具有给定名字的属性,则返回该属性的值;
如果没有找到,则继续搜索指针指向的原型对象,在原型对象中查找到具有给定名字的属性。
如果在原型对象中查找了这个属性,则返回该属性的值。
比如调用person1.sayName()方法,先在实例person1中查找sayName方法,
如果没有,则继续查找person1的原型有sayName属性吗,如果有,则返回,调用sayName方法。
可以通过对象实例访问保存在原型中的值,却不能通过对象实例重写原型中的值。
如果我们在实例中添加了一个属性,而该属性与实例原型中的一个属性同名,
那么我们就在实例中创建了该属性,该属性将会屏蔽原型中的那个属性(注意是在访问时屏蔽,而不是重写)。
//以“1”中的Person对象为例
person1.name = "xhc"; //为person1添加name属性
alert(person1.name); //xhc 来自person1实例
alert(person2.name); //answer 来自person2的原型
当为一个对象实例添加一个属性时,这个属性就会屏蔽原型对象中保存的同名的属性;如上所示。
添加的属性会阻止我们访问原型中的值,但不会修改原型中的那个值;如上面所示访问person2.name.
但是可以通过delete操作符完全删除实例属性,从而让我们能够重新访问原型中的属性。
//以”1“中的Person对象为例
person1.name = "xhc"; //为person1添加name属性
alert(person1.name); //xhc 来自person1实例
alert(person2.name); //answer 来自原型
delete person1.name; //删除person1的实例属性
alert(person1.name); //answer 来自原型
4. 实例属性检查
使用hasOwnProperty()方法可以检测一个属性是存在于实例中,还是存在于原型中,只有属性存在于对象实例中时,才返回true。(该方法从Object继承而来)
//以“1”中"的Person对象为例
alert(person1.hasOwnProperty("name")); //false
person1.name = "xhc"; //为实例添加属性
alert(person1.hasOwnProperty("name")); //true
delete person1.name; //删除实例属性
alert(person1.hasOwnProperty("name")); //false
5. 原型与in操作符
有两种方式使用in操作符:单独使用和在for-in循环中使用。
1) 单独使用时,in操作符会在通过对象能够访问给定属性时返回true,无论该属性存在于实例还是原型中。
因此同时使用in操作符和hasOwnProperty()方法,就可以确定一个属性到底是存在于对象实例中,还是对象原型中。
//判断一个属性是存在于原型中
function hasPrototypeProperty(obj, name) {
return !(obj.hasOwnProperty(name")) && (name in obj);
}
2) 使用for-in循环时,返回的是所有能够通过对象访问的,可枚举的(enumerated)属性,其中即包括存在于实例中的属性,也包括存在于原型中的属性。
屏蔽了原型中不可枚举属性(即设置了[[DontEnum]]标记的属性)的实例属性也会在for-in循环中返回,因为根据规定,所有开发人员定义的属性都是可枚举的——只有IE除外。
IE的JScript实现中存在一个bug,即屏蔽了不可枚举属性的实例属性不会出现在for-in循环中,这导致在IE中,默认不可枚举的属性如:hasOwnProperty(),propertyIsEnumerable(),toLodalString(), toString()和valueOf()。有的浏览器也为constructor和prototype属性打上了[[DonEnum]]标记,但这并不是所有浏览器共同的做法。
var obj = {
toString : function() { //屏蔽Object中的不可枚举属性toString方法
return "My Object";
}
}
for(var prop in obj) {
if(prop == "toString") {
alert("Found toString"); //在IE中不会显示
}
}
6. 更简单的原型语法
上面的Person例子中,没添加一个属性和方法都需要敲一遍Person.prototype。为了减少不必要的输入,也为了从视觉上更好地封装原型的功能,
更常见的做法是用一个包含所有属性和方法的对象字面量来重写整个原型对象。
function Person() {}
//将Person的prototype属性设置为一个用对象字面量形式创建的新对象。
Person.prototype = {
name : "answer",
age : 22
job :"SoftWare Engineer",
sayName : function() {
alert(this.name);
}
}
var person = new Person();
alert(person instanceof Person); //true
alert(person instanceof Objcet); //true
alert(person.constructor == Person); //false
alert(person.constructor == Object); //true
这种方式和”1“中使用Person的最终结果相同,但有一个例外:constructor属性不再指向Person了。
因为,每创建一个函数,就会同时创建它的prototype对象,这个对象也会自动获得constructor属性。
而现在,我们本质上完全重写了prototype对象,因此constructor属性也就变成了新对象的constructor属性(指向Object构造函数),不再指向Person函数。
因此,尽管instanceof操作符还能返回正确的结果,但通过constructor已经无法确认对象的类型了。
function Person() {}
//将Person的prototype属性设置为一个用对象字面量形式创建的新对象。
Person.prototype = {
constructor : Person; //将constructor属性设置回为Person构造函数
name : "answer",
age : 22
job :"SoftWare Engineer",
sayName : function() {
alert(this.name);
}
}
7. 原型的动态性
1) 由于在原型中查找值的过程是一次搜索,因此我们对原型对象所作的任何修改都能够立即从实例上反映出来——及时是先创建了实例后修改了原型也照样如此。
因为原型和实例之间是松散链接关系,实例和原型之间的连接不过是一个指针,而不是一个副本。
//以”1“中Person为例
Person person = new Person();
Person.prototype.sayHi() {
alert("hi");
}
person.sayHi(); //没有问题,即使先创建对象,后为原型添加方法
2) 尽管可以随时为原型添加新的属性和方法,可以在创建的实例中反映出来,可是如果重写整个原型对象,就不一样了。
因为调用构造函数时会为实例添加一个指向最初原型的_proto_指针,而把原型对象修改为另一个对象就等于切断了构造函数与最初原型之间的联系。
记住:实例中的指针仅仅指向原型,而不指向构造函数。
8. 原生对象的原型
原型模式的重要性不仅体现在自定义类型方面,就连所有原生的引用类型,都是采用这种模式创建的。
所有原生引用类型(Object, Array, String 等)都在其构造函数的原型上定义了方法。
alert(typeof Array.prototype.sort); //function
通过原生对象的原型,不仅可以取得所有默认方法的引用,而且可以定义新方法。(但不推荐修改原生对象的原型)
分享到:
相关推荐
由于JS不是纯的面向对象的语言,所以对象的继承是以原型函数的形式继承的,很多人刚开始接触的时候不太理解,但是JS这种以原型函数的形式实现面向对象技术,不仅是可行的,而且还为面向对象技术提供了动态继承的功能...
JavaScript是一种广泛...通过深入学习这本《JavaScript面向对象编程指南(第2版)》,开发者不仅能掌握JavaScript的面向对象编程基础,还能了解到实际项目中如何有效地运用这些知识,提升编程技巧和解决问题的能力。
JavaScript是一种广泛应用于Web开发的动态编程语言,尤其以其强大的...以上就是JavaScript中面向对象程序设计的一些关键知识点,通过理解并熟练运用这些概念,开发者可以构建出更加健壮和易于维护的JavaScript应用。
《JavaScript面向对象编程指南》内容包括:JavaScript作为一门浏览器语言的..., 《JavaScript面向对象编程指南》着重介绍JavaScript在面向对象方面的特性,展示如何构建强健的、可维护的、功能强大的应用程序及程序库。
JavaScript作为一门浏览器语言的核心思想;面向对象编程的基础知识及其在... 《JavaScript面向对象编程指南》着重介绍JavaScript在面向对象方面的特性,展示如何构建强健的、可维护的、功能强大的应用程序及程序库
JavaScript是一种广泛应用于Web开发的脚本语言,尤其在构建交互式网页和...这份"JavaScript面向对象程序程序设计PPT与代码"资源将详细解释这些概念,并可能包含实际示例,帮助你深入理解和应用这些面向对象编程技术。
JavaScript,作为一种广泛应用...JavaScript 面向对象程序设计——继承与多态.pdf 和 JavaScript 面向对象程序设计——封装.pdf 这两个文档可能深入探讨了这些主题,帮助读者深入理解并掌握JavaScript的面向对象开发。
标题“JavaScript经典面向对象设计”指出了本书的主要内容是关于如何使用面向对象编程(OOP)原则和技术来编写高质量、可扩展且可重用的JavaScript应用程序及库。描述中提到本书相比其他中文资料更为清晰,深入到...
总而言之,学习现代JavaScript面向对象编程,有助于开发者在认识这门语言演化的基础上,运用面向对象的设计和编程模式来构建更加健壮和可维护的JavaScript应用程序。同时,测试和调试是保证代码质量不可或缺的环节,...
下面将详细探讨JavaScript面向对象编程的基本原理、特性以及实际应用。 1. **类与对象** - 在JavaScript中,对象是键值对的集合,可以通过字面量语法或构造函数创建。例如: ```javascript const person = { ...
### JavaScript面向对象精要 #### 一、概述 《JavaScript面向对象精要》是一本深入讲解JavaScript面向对象编程原理的专业书籍。本书由知名的前端开发者Nicholas C. Zakas撰写,全面介绍了JavaScript作为一种动态...
JavaScript是一种广泛应用于Web开发的动态、弱类型、基于原型的脚本语言,它不仅支持函数式编程,还具有强大的面向对象编程能力...通过阅读《javascript面向对象编程.pdf》这样的资料,你可以深入理解并掌握这些概念。
面向对象程序设计的基本原则包括封装(Encapsulation)、继承(Inheritance)和多态(Polymorphism),这被称为面向对象的三大特性。封装是隐藏对象内部状态和行为的过程,只暴露出操作接口。继承是子对象继承父对象...
, 内容提要, JavaScript语言是一种具有高度表达能力的、基于原型特性的、非常灵活的面向对象程序设计语言。本书着重于介绍JavaScript在面向对象方面的特性,以为您展示如何去构建强健的、可维护的、功能强大的应用...
《JavaScript高级程序设计》(第3版)是一本深入探讨该语言精髓的权威书籍,它为读者提供了全面且深入的JavaScript知识,包括语言核心、DOM操作、BOM处理、事件处理以及面向对象编程等多个方面。 在书中,作者详细...