Email:longsu2010 at yeah dot net
很久以前有人发了我如下的代码, 问我有什么问题。今天旧话重提,我索性就写一篇博客分享给大家。事先声明,本博只分析问题,并不提供解决方案(我并不清楚写这段代码的人的真正意图)。
代码咋一看很头疼,仔细看更头疼,反正我不会这么写代码。代码如下:
function aaa(sColor){
this.color = sColor;
if(typeof aaa._is_ == "undefined"){
aaa.prototype.sayColor = function(){
alert(this.color);
};
}
aaa._is_ = true;
}
function bbb(sColor,name){
this.name = name;
if(typeof bbb._iss_ == "undefined"){
bbb.prototype = new aaa();
bbb.prototype.sayName = function(){
alert(this.name);
};
}
bbb._iss_ = true;
}
看完代码之后发现这是两个构造函数。先说第一个,在使用该函数创建第一个实例或者直接调用该函数(会污染全局对象)时向其原型中添加一个操作color属性的方法。代码正确性上没啥问题,用如下代码测试:
var a1 = new aaa("red");
var a2 = new aaa("green");
console.dir(a1);
console.dir(a2);
console.log(a1.__proto__ === a2.__proto__); // 输出true
现在说说第二个构造函数,在使用该函数创建第一个实例或者直接调用该函数(会污染全局对象)时会将该函数的原型重新赋值并在新原型中增加一个操作那么属性的方法,有继承的意思。但是问题来了,只有在创建第一个实例并且执行到if语句的时候才开始改变原型,这时已经来不及了,第一个实例使用的将会是默认的原型。测试代码如下:
var bbb_proto = bbb.prototype; // 先记录bbb的原型
var b1 = new bbb("red", "liuchen1");
var b2 = new bbb("green", "liuchen2");
console.dir(b1);
console.dir(b2);
console.log(b1.__proto__ === b2.__proto__); // 输出false
bbb.prototype = bbb_proto;
var b3 = new bbb("green", "liuchen3");
console.log(b1.__proto__ === b3.__proto__); // 输出true
写到这里问题就分析完了,那总结一下吧。
总结:改变构造函数原型的时机很重要,动态改变原型要慎重,特别是已经创建了一些实例以后。已经创建的对象不会使用新原型对象。
多说几句,做如下一个有趣的实验(实验在chrome中进行),请大家仔细反复阅读代码。
function a(){}
function b(){}
// Object.prototype.xxx = '--00--'; // IE这样写
new a().__proto__.__proto__.xxx = "--00--"; // 非IE这样写
console.log(new a().__proto__ === new b().__proto__); //false // IE会打印true,因为 undefined === undefined
console.log(new a().__proto__.__proto__ === new b().__proto__.__proto__); //true // IE会报错,测试请删除
console.log(Object.prototype === new a().__proto__.__proto__); //true // IE会报错,测试请删除
console.log(a.prototype instanceof Object); //true
console.log(new a().__proto__ instanceof Object); //true // 同样注意IE的问题
console.log(new a() instanceof Object) //true
console.log(Object.prototype instanceof Object) //false
console.log(a.prototype === b.prototype); //false
console.log(new a().__proto__ === a.prototype); //true
console.log(typeof(a.prototype)); //object
console.log(typeof(Object.prototype)); //object
console.log(new a().xxx); //--00--
console.log(new b().xxx); //--00--
console.log(Object.xxx); //--00--
console.log(new Object().xxx); //--00--
console.log(window.xxx); //--00--
console.log(document.xxx); //--00--
console.log(document.head.xxx); //--00--
console.log(document.getElementById("div").xxx); //--00-- //请确保dom节点已经存在
console.log("global", xxx); //global --00--
yyy = "0000";
delete window.yyy;
console.log(window.yyy); //undefined
写了一些乱其乱七八糟的代码,下面做写说明:
1、所有js对象原型链的尽头是同一对象,即Object的prototype属性,这是为什么都会输出--00--的原因。
2、构造函数默认prototype是Object的一个实例,所有该函数创建的实例共享同一个对象。
3、修改Object的prototype功能很强大,后果很严重,慎重。
4、之所以输出global --00--是因为在查找变量的时候在window的原型链上找到了xxx,详情请研究js变量查找规则。
5、每个函数都有prototype属性,同时函数也是对象,所以也拥有原型链。如果将函数做为对象使用那么属性查找在原型链上进行,如果将函数作为构造函数使用(即new 函数名()这种形式)则会新创建一个对象,并且该对象使用函数的prototype作为原型链。
6、以上实验使用chrome25版本,IE浏览器会有一定出入,特别是可爱的IE6、7、8。好消息是win xp要退出历史舞台了,IE6、7、8应该也一起走了吧。
longsu2010 at yeah dot net 我的邮箱,有事儿别发邮件给我,:)。
关于原型还有很多内容可写,但与本文关系不那么大了,不能再写了。
分享到:
相关推荐
构造函数、函数原型、函数实例三者之间的关系!详细的后续补上
主要介绍了js构造函数constructor和原型prototype原理与用法,结合实例形式分析js构造函数constructor和原型prototype基本原理、功能、使用方法及操作注意事项,需要的朋友可以参考下
下面小编就为大家带来一篇JS构造函数与原型prototype的区别介绍。小编觉得挺不错的,现在就分享给大家,也给大家做个参考。一起跟随小编过来看看吧
下面小编就为大家带来一篇浅谈js构造函数的方法与原型prototype。小编觉得挺不错的,现在就分享给大家,也给大家做个参考。一起跟随小编过来看看吧
1. 从存储空间角度,虚函数对应一个指向vtable虚函数表的指针,这大家都知道,可是这个指向vtable的指针其实是存储在对象的内存空间的。问题出来了,如果构造函数是虚的,就需要通过 vtable来调用,可是对象还没有...
JavaScript构造函数和原型对象介绍,对于构造函数的创建以及一些简单地介绍,还有就是原型对象对于构造函数的一些补充。
构造函数 原型对象 实例、图解
深入浅出的讲解JavaScript中最难理解的Js构造函数、原型链、Ajax三大部分
详细介绍了构造函数、复制构造函数、拷贝构造函数之间的区别
在Javascript中不存在class的概念,它的class概念是通过构造函数(constructor)与原型链(prototype)来实现。 1.构造函数(constructor):创建对象时的初始化对象,总是与new 关键是一同出现。 构造函数存在以下...
本程序包含构造函数和析构函数,可以把构造函数和析构函数的作用区分开
构建一个类Point,它提供两个公有的构造函数,一个没有参数的Point构造函数和一个有两个double参数的构造函数。另外在该类中提供一个静态方法计算两个点的直线距离,传入参数为两个Point类实例。然后设计一个测试类...
构造函数和原型构造函数构造函数通过原型分配的函数是所有对象所共享的JS规定,每一个构造函数都有一个 prototype 属性,指向另一个对象,注意这个proto
析构函数也是以类名作为函数名,与构造函数不同的是在函数名前添加一个“~”符号,标识该函数是析构函数。析构函数没有返回值,甚至void类型也不可以,析构函数也没有参数,因此析构函数是不能够重载的。这是析构...
构造函数与默认构造函数的声明、定义、应用、比较
子类如果有多个构造函数的时候,父类要么没有构造函数, 让编译器自动产生,那么在执行子类构造函数之前先执行编 译器自动产生的父类的缺省构造函数;要么至少要有一个显 式的缺省构造函数可以让子类的构造函数调用...
构造函数(对象冒充)的主要问题是必须使用构造函数方式,且无法继承通过原型定义的方法,这不是最好的选择。不过如果使用原型链,就无法使用带参数的构造函数了。开发者如何选择呢?答案很简单,两者都用。 构造...
c#析构构造函数c#析构构造函数c#析构构造函数c#析构构造函数