揭开js之constructor属性的神秘面纱

转载自 https://blog.csdn.net/zengyonglan/article/details/53465505

揭开 constructor

在 Javascript 语言中,constructor 属性是专门为 function 而设计的,它存在于每一个 function 的prototype 属性中。这个 constructor 保存了指向 function 的一个引用。
在定义一个函数(代码如下所示)时,

function F() {
// some code
}

 JavaScript 内部会执行如下几个动作:

1.为该函数添加一个原形(即 prototype)属性
2. 为 prototype 对象额外添加一个 constructor 属性,并且该属性保存指向函数F 的一个引用

这样当我们把函数 F 作为自定义构造函数来创建对象的时候,对象实例内部会自动保存一个指向其构造函数(这里就是我们的自定义构造函数 F)的 prototype 对象的一个属性proto

所以我们在每一个对象实例中就可以访问构造函数的 prototype 所有拥有的全部属性和方法,就好像它们是实例自己的一样。当然该实例也有一个 constructor属性了(从 prototype 那里获得的),每一个对象实例都可以通过 constrcutor 对象访问它的构造函数,请看下面代码:

var f = new F();
alert(f.constructor === F);// output true
alert(f.constructor === F.prototype.constructor);// output true

我们可以利用这个特性来完成下面的事情:
对象类型判断,如

if(f.constructor === F) {
// do sth with F
}

其实 constructor 的出现原本就是用来进行对象类型判断的,但是 constructor 属性易变,不可信赖。我们有一种更加安全可靠的判定方法:instanceof 操作符。下面代码
仍然返回 true

if(f instanceof F) {
// do sth with F
}

原型链继承,由于 constructor 存在于 prototype 对象上,因此我们可以结合
constructor 沿着原型链找到最原始的构造函数,如下面代码:

function Base() {}

// Sub1 inherited from Base through prototype chain
function Sub1(){}
Sub1.prototype = new Base();
Sub1.prototype.constructor = Sub1;

Sub1.superclass = Base.prototype;

// Sub2 inherited from Sub1 through prototype chain
function Sub2(){}
Sub2.prototype = new Sub1();
Sub2.prototype.constructor = Sub2;

Sub2.superclass = Sub1.prototype;

// Test prototype chain
alert(Sub2.prototype.constructor);// function Sub2(){}
alert(Sub2.superclass.constructor);// function Sub1(){}
alert(Sub2.superclass.constructor.superclass.constructor);// function Base(){}

上面的例子只是为了说明 constructor 在原型链中的作用,更实际一点的意义在于:一个子类对象可以获得其父类的所有属性和方法,称之为继承。

之前提到 constructor 易变,那是因为函数的 prototype 属性容易被更改,我们用时下很流行的编码方式来说明问题,请看下面的示例代码:

function F() {}
    F.prototype = {
    _name: 'Eric',
    getName: function() {
    return this._name;
    }
};

初看这种方式并无问题,但是你会发现下面的代码失效了:
var f = new F();
alert(f.constructor === F); // output false

怎么回事?F 不是实例对象 f 的构造函数了吗?当然是!只不过构造函数 F 的原型被开发者重写了,这种方式将原有的 prototype 对象用一个对象的字面量{}来代替。而新建的对象{}只是 Object 的一个实例,系统(或者说浏览器)在解析的时候并不会在{}上自动添加一个 constructor 属性,因为这是 function 创建时的专属操作,仅当你声明函数的时候解析器才会做此动作。然而你会发现 constructor 并不是不存在的,下面代码可以
测试它的存在性:

alert(typeof f.constructor == 'undefined');// output false

既然存在,那这个 constructor 是从哪儿冒出来的呢?我们要回头分析这个对象字面量
{}。因为{}是创建对象的一种简写,所以{}相当于是 new Object()。那既然{}是 Object
的实例,自然而然他获得一个指向构造函数 Object()的 prototype 属性的一个引用proto,又因为 Object.prototype 上有一个指向 Object 本身的 constructor属性。所以可以看出这个constructor其实就是Object.prototype的constructor,
下面代码可以验证其结论:

alert(f.constructor === Object.prototype.constructor);//output true
alert(f.constructor === Object);// also output true

一个解决办法就是手动恢复他的 constructor,下面代码非常好地解决了这个问题:

function F() {}
F.prototype = {
    constructor: F, /* reset constructor */
    _name: 'Eric',
    getName: function() {
    return this._name;
    }
};

之后一切恢复正常,constructor 重新获得的构造函数的引用,我们可以再一次测试上面的代码,这次返回 true

var f = new F();
alert(f.constructor === F); // output true this time ^^
  
  
  • 1
  • 2

解惑:构造函数上怎么还有一个 constructor ?它又是哪儿来的?

细心的会发现,像 JavaScript 内建的构造函数,如 Array, RegExp, String,Number, Object, Function 等等居然自己也有一个 constructor:
alert(typeof Array.constructor != ‘undefined’);// output true
经过测试发现,此物非彼物它和 prototype 上 constructor 不是同一个对象,他们是共存的:

alert(typeof Array.constructor != 'undefined');// output true
alert(typeof Array.prototype.constructor === Array); // output true
  
  
  • 1
  • 2

不过这件事情也是好理解的,因为 构造函数也是函数。是函数说明它就是 Function 构造函数的实例对象,自然他内部也有一个指向 Function.prototype 的内部引用proto啦。因此我们很容易得出结论,这个 constructor(构造函数上的constructor 不是 prototype 上的)其实就是 Function 构造函数的引用

alert(Array.constructor === Function);// output true
alert(Function.constructor === Function); // output true
  
  
  • 1
  • 2
					<link href="https://csdnimg.cn/release/phoenix/mdeditor/markdown_views-a47e74522c.css" rel="stylesheet">
            </div>
								<div class="hide-article-box text-center">
					<a class="btn" id="btn-readmore" data-track-view="{&quot;mod&quot;:&quot;popu_376&quot;,&quot;con&quot;:&quot;,https://blog.csdn.net/zengyonglan/article/details/53465505,&quot;}" data-track-click="{&quot;mod&quot;:&quot;popu_376&quot;,&quot;con&quot;:&quot;,https://blog.csdn.net/zengyonglan/article/details/53465505,&quot;}">阅读更多</a>
				</div>
				<script>
					(function(){
						function setArticleH(btnReadmore,posi){
							var winH = $(window).height();
							var articleBox = $("div.article_content");
							var artH = articleBox.height();
							if(artH > winH*posi){
								articleBox.css({
									'height':winH*posi+'px',
									'overflow':'hidden'
								})
								btnReadmore.click(function(){
									if(typeof window.localStorage === "object" && typeof window.csdn.anonymousUserLimit === "object"){
										if(!window.csdn.anonymousUserLimit.judgment()){
											window.csdn.anonymousUserLimit.Jumplogin();
											return false;
										}else if(!currentUserName){
											window.csdn.anonymousUserLimit.updata();
										}
									}
									
									articleBox.removeAttr("style");
									$(this).parent().remove();
								})
							}else{
								btnReadmore.parent().remove();
							}
						}
						var btnReadmore = $("#btn-readmore");
						if(btnReadmore.length>0){
							if(currentUserName){
								setArticleH(btnReadmore,3);
							}else{
								setArticleH(btnReadmore,1.2);
							}
						}
					})()
				</script>
				</article>

猜你喜欢

转载自blog.csdn.net/ddwddw4/article/details/85008812