如何更好修改(增加)原生js面向对象 原型继承的原型

这个对象实例的constructor指针同构造它;总结:从上面的这个例子可以看出,虽然新创建的实例;这样,constructor指针就指回了Pers;值得注意的是:这种方式重设constructor;F.原生对象的原型;原型的重要性不仅体现在自定义类型方面,就连所有原;于是,实际上我们是可以通过原生对象的原型来修改它;这是,便在窗口中弹出了“Thisisas
这个对象实例的constructor指针同构造它的构造函数一样指向新的原型对象。
总结:从上面的这个例子可以看出,虽然新创建的实例对象仍可以共享添加在原型对象里面的属性,但是这个新的原型对象却不再指向Person构造函数而指向Object构造函数,如果constructor的值真的非常重要的时候,我们可以像下面的代码这样重新设置会适当的值:
这样,constructor指针就指回了Person构造函数。即如下图所示:
值得注意的是:这种方式重设constructor属性会导致它的[[Enumerable]]特性设置位true,而默认情况下,原生的constructor属性是不可枚举的。但是我们可以试用Object.defineProperty()将之修改为不可枚举的(这一部分可以参见我的另一篇博文:《深入理解JavaScript中的属性和特性》)。
F.原生对象的原型
原型的重要性不仅体现在自定义类型方面,就连所有原生的引用类型,都是使用这种模式创建的。所有原生引用类型(Object、Array、String,等等)都在其构造函数的原型上定义了方法。例如在Array.prototype中可以找到sort()方法,而在String.prototype中就可以找到substring()方法。
于是,实际上我们是可以通过原生对象的原型来修改它。比如:
这是,便在窗口中弹出了“This is a string”。尽管可以这样做,但是我们不推荐在产品化的程序中修改原生对象的原型。这样做有可能导致命名冲突等问题。 G.原型模式存在的问题
实际上,从上面对原型的讲解来看,原型模式还是有很多问题的,它并没有很好地解决我在第四部分初提出的若干问题:“其一:可以直接识别创建的对象的类型。其二:解决工厂模式解决的创建大量相似对象时产生的代码重复的问题。其三:解决构造函数产生的封装性不好的问题。”其中第一个问题解决的不错,通过构造函数便可以直接看出来类型。第二个问题却解决的不好,因为它省略了为构造函数传递初始化参数这一环节,结果所有的实例在默认情况下都将取得相同的默认值,我们只能通过在实例上添加同名属性来屏蔽原型中的属性,这无疑也会造成代码重复的问题。第三个问题,封装性也还说的过去。因此原型模式算是勉强解决了上述问题。
但是这种方法还由于本身产生了额外的问题。看下面的例子: 1
function Person(){}
2 Person.prototype={
constructor:Person,
name:&zzw&,
school:&xjtu&,
friends:[&pengnian&,&zhangqi&],
sayName:function (){
console.log(this.name);
12 var person1=new Person();
13 var person2=new Person();
14 person1.friends.push(&feilong&);
15 console.log(person1.friends);//[&pengnian&,&zhangqi&,&feilong&]
16 console.log(person2.friends);//[&pengnian&,&zhangqi&,&feilong&]
这里我在新建的原型对象中增加了一个数组,于是这个数组会被后面创建的实例所共享,但是person1.friends.push(&feilong&);这句代码我的意思是添加为person1的朋友而不是person2的朋友,但是在结果中我们可以看到person2的朋友也有了feilong,这就不是我们所希望的了。这也是对于包含引用类型的属性的最大问题。
也正是这个问题和刚刚提到的第二个问题(即它省略了为构造函数传递初始化参数这一环节,结果所有的实例在默认情况下都将取得相同的默认值,我们只能通过在实例上添加同名属性来屏蔽原型中的属性,这无疑也会造成代码重复的问题),很少有人会单单使用原型模式。
第五部分:组合使用自定义构造函数模式和原型模式
刚刚我们说到的原型模式存在的两个最大的问题。问题一:由于没有在为构造函数创建对象实例时传递初始化参数,所有的实例在默认情况下获取了相同的默认值。问题二:对于原型对象中包含引用类型的属性,在某一个实例中修改引用类型的值,会牵涉到其他的实例,这不是我们所希望的。而组合使用自定义构造函数模式和原型模式即使构造函数应用于定义实例属性,而原型模式用于定义方法和共享的属性。它能否解决问题呢?下面我们来一探究竟! 1 function Person(name,age,school){
this.name=
this.school=
this.friends=[&pengnian&,&zhangqi&];
7 Person.prototype={
constructor:Person,
sayName:function(){
console.log(this.name);
13 var person1=new Person(&zzw&,21,&xjtu&);
14 var person2=new Person(&ht&,18,&tjut&);
15 person1.friends.push(&feilong&);
16 console.log(person1.friends);//[&pengnian&,
17 &zhangqi&, &feilong&]
18 console.log(person2.friends);//[&pengnian&,
&zhangqi&]
console.log(person1.sayName==person2.sayName);//true
OK!我们来看看组合使用构造函数模式和原型模式解决的问题:
4. 解决了Object构造函数和对象字面量方法在创建大量对象时造成的代码重复问题(因为只要在创建对象时向构造函数传递参数即可)。 解决了工厂模式产生的无法识别对象类型的问题(因为这里通过构造函数即可获知对象类型)。 解决了自定义构造函数模式封装性较差的问题(这里全部都被封装)。 解决了原型模式的两个问题:所有实例共享相同的属性以及包含引用类型的数组在实例中修改时会影响原型对象中的数组。
综上所述,组合使用构造函数模式和原型模式可以说是非常完美了。
第六部分:动态原型模式、寄生构造函数模式、稳妥构造函数模式
实际上,组合使用构造函数模式和原型模式确实已经非常完美了,这里将要讲的几种模式都是在特定的情况下使用的,所以我认为第六部分相对于第五部分并没有进一步的提高。仅仅是多学习几种模式可以解决更多的问题。
A 动态原型模式
这里的动态原型模式相对于第五部分的组合使用自定义构造函数模式和原型模式本质上是没有什么差别的,只是因为对于有其他OO(Object Oriented,面向对象)语言经验的开发人员看到这种模式会觉得奇怪,因此我们可以将所有信息都封装在构造函数中。本质上是通过检测某个应该存在的方法是否存在或有效,来决定是否要初始化原型。如下例所示:
1 function Person(name,age,school){
this.name=
this.school=
if(typeof this.sayName != &function&){
Person.prototype.sayName=function(){
console.log(this.name);
var person=new Person(&zzw&,21,&xjtu&);//
12 使用new调用构造函数并创建一个实例对象
person.sayName(); //zzw
console.log(person.school);//xjtu
这里先声明了一个构造函数,然后当使用new操作符调用构造函数创建实例对象时进入了构造函数的函数执行环境,开始检测对象的sayName是否存在或是否是一个函数,如果不是,就
使用原型修改的方式向原型中添加sayName函数。且由于原型的动态性,这里所做的修改可以在所有实例中立即得到反映。值得注意的是,在使用动态原型模式时,不能使用对象字面量重写原型,否则,在建立了实例的情况下重写原型会导致切断实例和新原型的联系。
B 寄生构造函数模式
寄生构造函数模式是在前面几种模式都不适用的情况下使用的。看以下例子,再做出说明:
包含各类专业文献、外语学习资料、专业论文、高等教育、应用写作文书、文学作品欣赏、生活休闲娱乐、各类资格考试、幼儿教育、小学教育、行业资料、35深入理解JavaScript中创建对象模式的演变(原型)等内容。 
 深入理解JavaScript系列(42) 本系列教程为自己从网络上整理的资料,是javascript的...正文对于原型模式,我们可以利用 JavaScript 特有的原型继承特性去创建对象的方式,...  深入理解 JavaScript 系列(46):代码复用模式(推荐篇)
08:38 by ...(null)创建的对象的原型为 undefined,也就是没有 toString 和 valueOf 方法,...  深入理解JavaScript系列(10)_计算机软件及应用_IT/计算机...原型对象存放于 ConstructorFunction.prototype 属性中...构造函数将会以特定模式创建对象:被创建的对象都会有...  深入理解JavaScript系列(5):强大的原型和原型链_...所以 理解两种继承模式的差异是需要一定时间的, 今天...一次性设置了原型对象, 我们再来说一下如何分来设...  深入理解JavaScript系列_计算机软件及应用_IT/计算机_...的状态发生变化时就会通知 所有的观察者对象,使得...我们也可以利用原型的特性实现一个观察者模式,代码如...  深入理解 JavaScript 系列(17):面向对象编程之概论 ...也是从从原型 z 中获取 z.q = 200 // 添加新...依然可能得到结果,或者该对象经过一系列变化以 后...  深入理解javascript中的new操作符_计算机软件及应用_IT/计算机_专业资料。new 操作...{ // 创建一个新对象,它继承字构造函数的原型对象 var that =Object.create(...  深入理解javascript原型和闭包――从【自由变量】到【作用域链】_计算机软件及...――要 到创建 fn 函数的那个作用域中取――无论 fn 函数将在哪里调用。 ...  深入理解 JavaScript 系列(29):设计模式之装饰者模式 介绍 装饰者提供比继承更...将 Angel 对象赋给 tree,这时候父原型的父原型里的 getDecorator 依然可用 ...& & & & 文章简介:我们创建的每个函数都有一个prototype(原型)属性,这个属性是一个指针,指向一个对象,而这个对象的用途是包含可以由特定类型的所有实例共享的属性和方法。如果按照字面量的意思来理解,那么prototype就是通过调用构造函数而创建的那个对象实例的原型对象。使用原型对象& & 我们创建的每个函数都有一个prototype(原型)属性,这个属性是一个指针,指向一个对象,而这个对象的用途是包含可以由特定类型的所有实例共享的属性和方法。如果按照字面量的意思来理解,那么prototype就是通过调用构造函数而创建的那个对象实例的原型对象。使用原型对象的好处是可以让所有对象实例共享它所包含的属性和方法。换句话说,不必在构造函数中定义对象实例的信息,而是可以将信息直接添加到原型对象中,如下面的例子所示:function Person() {}Person.prototype.name = "Nicholas";Person.prototype.age = "29";Person.prototype.job = "Software Engineer";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& & 在此,我们将sayName()方法和所有属性直接添加了Person的prototype属性中,构造函数变成了空函数。即使如此,也仍然可以通过调用构造函数来创建新对象,而且新对象还会具有相同的属性和方法。但与构造函数模式不同的是,新对象的这些属性和方& & 法是由所有实例共享的。换句话说,person1和person2访问的都是同一组属性和同一个sayName()函数。要理解原型模式的工作原理,必须理解ECMAScript中原型对象的性质。理解原型对象& & 无论什么时候,只要创建了一个新函数,就会根据一组特定的规则为该函数创建一个prototype属性,这个属性指向函数的原型对象。在默认情况下,所有原型对象都会获得一个constructor(构造函数)属性,这个属性包含一个指向prototype属性所在函数的指针。就拿前面的例子来说,Person.prototype.constructor指向Person。而通过这个构造函数,我们还可以继续为原型对象添加其它属性和方法。& & 创建了自定义的构造函数之后,其原型对象默认只会取得constructor属性;至于其它方法,则都是从Object继承而来的。当调用构造函数创建一个新实例后,该实例的内部将包含一个指针(内部属性),指向构造函数的原型对象。在很多实现中,这个内部属性的名字是_proto_,而且通过脚本可以访问到(在firefox、Safari、Chrome和Flash的ActionScript中,都可以通过脚本访问_proto_);而在其它实现中,这个属性对脚本则是完全不可见的。不过,要明确的真正重要的一点,就是这个连接存在与实例与构造函数的原型对象之间,而不是存在于实例于构造函数之间。& & 虽然在某些实现中无法访问到内部的_proto_属性,但在所有实现中都可以通过isPrototypeOf()方法来确定对象之间是否存在这种关系。从本质上来讲,如果对象的_proto_指向调用isPrototypeOf()方法的对象(Person.prototype),那么这个方法就返回true,如下所示:alert(Person.prototype.isPrototypeOf(person1)); //alert(Person.prototype.isPrototypeOf(person2)); //& & 这里,我们用原型对象的isPrototypeOf()方法测试了person1和person2。因为它们内部都有一个指向Person.prototype的指针,因此都返回了true。& & 每当代码读取某个对象的某个属性时,都会执行一次搜索,目标是具有给定名字的属性。搜索首先从对象实例本身开始。如果在实例中找到了具有给定名字的属性,则返回该属性的值;如果没有找到,则继续搜索指针指向的原型对象,在原型对象中查找具有给定名字的属性。如果在原型对象中找到了这个属性,则返回该属性的值。也就是说,在我们调用person1.sayName()的时候,会先后执行两次搜索。首先,解析器会问:&实例person1有sayName属性吗?&答:&没有。&然后,它继续搜索,再问:&person1的原型有sayName属性吗?&答:&有。&于是,它就读取那个保存在原型对象中的函数。当我们调用person2.sayName()时,将会重现相同的的搜索过程,得到相同的结果。而这正是多个对象共享原型所保存的属性和方法的基本原理。& & 前面提到过,原型最初值包含constructor属性,而该属性也是共享的,因此可以通过对象实例访问。& & 虽然可以通过对象实例访问保存在原型中的值,但却不能通过对象实例重写原型中的值。如果我们在实例中添加了一个属性,而该属性与实例原型中的一个属性同名,那我们就在实例中创建该属性,该属性将会屏蔽原型中的那个属性。来看下面的例子:function Person() {}Person.prototype.name = "Nicholas";Person.prototype.ae = 29;Person.prototype.job = "Software Engineer";Person.prototype.sayName = function () {
alert(this.name);}var person1 = new Person();var person2 = new Person();person1.name = "Greg";alert(person1.name); //"Greg" &&来自实例alert(person2.name); //"Nicholas" &&来自原型& & 在这个例子中,person1的name被一个新值给屏蔽了。但无论访问person1.name还是访问person2.name都能正常地放回值,即分别是&Greg&(来自对象实例)和&Nicholas&(来自原型)。当在alert()中访问person1.name时,需要读取它的值,因此就会在这个实例上搜索一个名为Name的属性。这个属性确实存在,于是就返回它的值而不必再搜索原型了。当以同样的方式访问person2.name时,并没有在实例上发现该属性,因此就会继续搜索原型,结果在那里找到了name属性。& & 当为对象实例添加了一个属性时,这个属性就会屏蔽原型对象汇总保存的同名属性;换句话说,添加这个属性只会组织我们访问原型中的那个属性,但不会修改那个属性。即使将这个属性值设置为null,也只会在实例中设置这个属性。而不会回复其指向原型的连接。不会,使用delete操作符则可以完全删除实例属性,从而让我们能够重新访问原型中的属性,如下所示:function Person() {}Person.prototype.name = "Nicholas";Person.prototype.age = 20;Person.prototype.job = "Software Engineer";Person.prototype.sayName = function () {
alert(this.name);}var person1 = new Person();var person2 = new Person();person1.name = "Greg";alert(person1.name); //&Greg& &&来自实例alert(person2.name); //"Nicholas" &&来自原型delete person1.alert(person1.name); //"Nicholas" &&来自原型& & 在这个修改后的例子中,我们使用delete操作符删除了person1.name,之前它保存的&Greg&值屏蔽了同名的原型属性。把它删除以后,就恢复了原型中name属性的连接。因此,接下来再调用person1.name时,返回的就是原型中的name属性的值了。& & 使用hasOwnProperty()方法可以检测一个属性是存在与实例中,还是存在于原型中。这个方法(不要忘了它是从Object继承来的)只在给定属性存在于对象实例中时,才会返回true。来看下面这个例子:function Person() {}Person.prototype.name = "Nicholas";Person.prototype.jog = "Software Engineer";Person.prototype.sayName = function () {
alert(this.name);};var person1 = new Person();var person2 = new Person();alert(person1.hasOwnProperty("name")); //falseperson1.name = "Greg";alert(person1.name); //"Greg" &&来自实例alert(person1.hasOwnProperty("name")); //truealert(person2.name); //"Nicholas" &&来自原型alert(person2.hasOwnProperty("name")); //falsedelete person1.alert(person1.name); //"Nicholas" &&来自原型alert(person1.hasOwnProperty("name")); //flase& & 通过使用hasOwnProperty()方法,什么时候访问的是实例属性,什么时候访问的是原型属性就一清二楚了。调用person1.hasOwnProperty(&name&)时,只有当person1重写name属性后才会返回true,因为只有这时候name才是一个实例属性,而非原型属性。原型与in操作符& & 有两种方式使用in操作符:单独使用和在for-in循环中使用。在单独使用时,in操作符会在通过对象能够访问给定属性时返回true,无论该属性存在于实例还是原型中。看一看下面的例子:function Person() {}Person.prototype.name = "Nicholas";Person.prototype.age = "29";Person.prototype.job = "Software Engineer";Person.prototype.sayName = function () {
alert(this.name);};var person1 = new Person();var person2 = new Person();alert(person1.hasOwnProperty("name")); //falsealert("name" in person1); //trueperson1.name = "Greg";alert(person1.name); //"Greg" &&来自实例alert(person1.hasOwnProperty("name")); //alert("name" in person1); //truealert(person2.name); //"Nicholas"&&来自原型alert(person2.hasOwnProperty("name")); //falsealert("name" in person1); //truedelete person1.alert(person1.name); //"Nicholas" &&来自原型alert(person1.hasOwnProperty("name")); //falsealert("name" in person1); //& & 在以上代码执行的整个过程中,name属性要么直接在对象上访问到,要么是通过原型访问到的。因此,调用&name& in Person1始终都返回true,无论该属性存在与实例中还是存在与原型中。同时使用hasOwnProperty()方法和in操作符,就可以确定该属性到底是存在于对象中,还是存在于原型中,如下所示:function hasPrototypeProperty(object, name) {
return !object.hasOwnProperty(name) && (name in object);}& & 由于in操作符只要通过对象能够访问到属性就就返回true,hasOwnProperty()只在属性存在于实例中时才返回true,因此只要in操作符返回true而hasOwnProperty()返回false,就可以确定属性是原型中的属性。下面来看一看上面定义的函数hasPrototypeProperty()用法:function Person() {}Person.prototype.name = "Nicholas";Person.prototype.age = "29";Person.prototype.job = "Software Engineer";Person.prototype.sayName = function () {
alert(this.name);};var person = new Person();alert(hasPrototypeProperty(person, "name")); //trueperson.name = "Greg";alert(hasPrototypeProperty(person, "name")); //false& & 在这里,name属性先是存在于原型中,因此hasPrototypeProperty()返回true。当在实例中重写name属性后,该属性就存在于实例中了,因此hasPrototypeProperty()返回false。& & 在使用for-in循环时,返回的是所有能够通过对象访问的、可枚举的(enumerated)属性,其中既包括存在于实例中的属性,也包括存在于原型中的属性。屏蔽了原型中不可枚举属性(即设置了[[DontEnum]]标记的属性)的实例属性也会在for-in循环中返回,因为规定,所有开发人员定义的属性都是可枚举的&&只有IE除外。& & IE的JScript实现中存在一个bug,即屏蔽了不可枚举属性的实例属性不会出现在for-in循环中。例如:var o = {
toString: function () {
return "My Object";
}}for (var prop in o) {
if (prop == "toString") {
alert("Found toString"); //在IE中不会显示
}}& & 当以上代码运行时,应该会显示一个警告框,表明找到了toString()方法。这里的对象o定义了一个名为toString()的方法,该方法屏蔽了原型中(不可枚举)的toString()方法。在IE中,由于其实现认为原型的toString()方法被打上了[[DontEnum]]标记就应该跳过该属性,结果我们就不会看到警告框。该bug会影响默认不可枚举的所有属性和方法,包括:hasOwnProperty()、propertyIsEnumerable()、toLocaleString()、toString()和valueOf()。有的浏览器也为constructor和prototype属性打上了[[DontEnum]]标记,但这并不是所有浏览器共同的做法。更简单的原型语法& & 读者大概注意到了,前面例子中每添加一个属性和方法就要敲一遍Person.prototype。为了减少不必要的输入,也为了从视觉上更好地封装原型的功能,更常见的做法是用一个包含所有属性和方法的对象字面量来重写整个原型对象,如下面的例子所示:function Person() {}Person.prototype = {
name: "Nicholas",
job: "Software Engineer",
sayName: function () {
alert(this.name);
}};& & 在上面的代码中,我们将Person.prototype设置为等于一个以对象字面量形式创建的新对象。最终结果相同,但有一个例外:constructor属性不再指向Person了。前面曾经介绍过,每创建一个函数,就会同时创建它的prototype对象,这个对象也会自动获得constructor属性。而我们在这里使用的语法,本质上完全重写了默认的prototype对象,因此constructor属性也就变成了新对象的constructor属性(指向Object构造函数),不再指向Person函数。此时,尽管instanceof操作符还能返回正确的结果,但通过constructor已经无法确定对象的类型了,如下所示:function Person() {}Person.prototype = {
name: "Nicholas",
job: "Software Engineer",
sayName: function () {
alert(this.name);
}};var person = new Person();alert(person instanceof Object); //truealert(person instanceof Person); //truealert(person.constructor == Person); //falsealert(person.constructor == Object); //true& & 在此,用instanceof操作符测试Object和Person仍然返回true,但constructor属性则等于Object而不等于Person了。如果constructor的值真的很重要,可以像下面这样特意将它设置回适当的值:function Person() {}Person.prototype = {
constructor: Person,
name: "Nicholas",
job: "Software Engineer",
sayName: function () {
alert(this.name);
}};var person = new Person();& & 以上代码特意包含了一个constructor属性,并将它的值设置为Person,从而确保了通过该属性能够访问到适当的值。alert(person instanceof Object); //truealert(person instanceof Person); //truealert(person.constructor == Person); //truealert(person.constructor == Object); //false原型的动态性& & 由于在原型中查找值的过程是一次搜索,因此我们对原型对象所做的任何修改都能够立即从实例上反映出来&&即使是西安创建了实例后修改原型也照样如此。请看下面的例子:var person = new Person();Person.prototype.sayHi = function () {
alert("hi");};person.sayHi(); //"hi" (没有问题!)& & 以上代码先创建了Person的一个实例,并将其保存在person中。然后,下一条语句在Person.prototype中添加了一个方法sayHi()。即使person实例是在添加新方法之前创建的,但它仍然可以访问这个新方法。其原因可以归结为实例与原型之间的松散连接关系。当我们调用Person.sayHi()时,首先会在实例中搜索名为sayHi的属性,在没找到的情况下,会继续搜索原型。因为实例与原型之间的连接只不过是一个指针,而非一个副本,因此就可以在原型中找到新的sayHi属性并返回保存在那里的函数。& & 尽管可以随时为原型添加属性和方法,并且修改能够立即在所有对象实力中反映出来,但如果是重写整个原型对象,那么情况就不一样了。我们知道,调用构造函数时回味实例添加一个指向最初原型的_proto_指针,而把原型修改为另外一个对象就等于切断了构造函数与最初原型之间的联系。请记住:实例中的指针近指向原型,而不指向构造函数。看下面的例子:function Person() {}var person = new Person();Person.prototype = {
constructor: Person,
name: "Nicholas",
job: "Software engineer",
sayName: function () {
alert(this.name);
}};person.sayName(); //error& & 在这个例子中,我们先创建了Person的一个实例,然后又重写了其原型对象。然后在调用person.sayName()时发生了错误,因为person指向的原型中不包含以改名字命名的属性。原生对象的原型& & 圆心模式的重要性不仅体现在创建自定义类型方面,就连所有原生的引用类型,都是采用这种模式创建的。所有原生引用类型(Object、Array、String、等等)都在其构造函数的原型上定义了方法。例如,在Array.prototype中可以找到sort()方法,而在String.prototype中可以找到substring()方法,如下所示:alert(typeof Array.prototype.sort); //"function"alert(typeof String.prototype.substring); //"function"& & 通过原生对象的原则,不仅可以取得所有默认方法的引用,而且也可以定义新方法。可以向修改自定义对象的原型一样修改原生对象的原型,因此尅随时添加方法。下面的代码就给基本包装类型String添加了yield名为startsWith()的方法:String.prototype.startsWith = function (text) {
return this.indexOf(text) == 0;};var msg = "Hello world!";alert(msg.startsWith("Hello")); //true& & 这里定义的startsWith()方法会在传入的文本位于yield字符串开始时返回true。既然方法被添加给了String.pprototype,那么当前环境中的所有字符串就都可以调用它。由于msg是字符串。而且后台会调用String基本保皇函数创建这个字符串,因此通过msg就可以调用startsWith()方法。& & 尽管可以这样做,但我们不推荐在产品化的程序中修改原生对象的原型。如果因某个实现中缺少某个方法,就在原生对象的原型中添加这个方法,那么当在另一个支持该方法的实现中运行代码时,就可能会导致命名冲突。而且,这样做也可能意外地重写原生方法。原型对象的问题& & 原型模式也不是没有缺点。首先,它省略了为构造函数传递初始化参数这一环节,结果所有实例在默认情况下都将取得相同的属性值。虽然这会在某种程度上带来一些不方便,但还不是原型的最大问题。原型模式的最大问题是由其共享的本性所导致的。& & 原型中所有属性是被很多实例共享的,这种共享对于函数非常合适。对于那些包含基本值的属性倒也说的过去,毕竟,通过在实例上添加一个同名属性,可以隐藏原型中的对应属性。然而,对于包含引用类型的属性来说,问题就比较突出了。来看下面的例子:function Person() {}Person.prototype = {
constructor: Person,
name: "Nicholas",
job: "Software Engineer",
friends: ["Shelby", "Court"],
sayName: function () {
alert(this.name);
}};var person1 = new Person();var person2 = new Person();person1.friends.push("Van");alert(person1.friends); //"Shelby,Court,Van"alert(person2.friends); //"Shelby, Court, Van"alert(person1.friends == person2.friends); //ture& & 在此,Person.prototype对象有一个名为friends的属性,该属性包含一个字符串数组。然后,创建了Person的两个实例。接着,修改了person1.friends引用数组,向数组中添加了一个字符串。由于firends数组存在与Person.prototype而非person1中,所以刚刚提到的修改也会通过person2.friends反映出来。假如我们的初衷就是像这样在所有实例中共享一个数组,那么对这个结果我没有话可说。可是,实例一般都是要有属于自己的全部属性的。而这个问题正是我们很少看到有人单独使用原型模式的原因所在。
声明:该文章系网友上传分享,此内容仅代表网友个人经验或观点,不代表本网站立场和观点;若未进行原创声明,则表明该文章系转载自互联网;若该文章内容涉嫌侵权,请及时向
论文写作技巧
上一篇:下一篇:
相关经验教程}

我要回帖

更多关于 js原型链最上层对象 的文章

更多推荐

版权声明:文章内容来源于网络,版权归原作者所有,如有侵权请点击这里与我们联系,我们将及时删除。

点击添加站长微信