深刻理解js构造函数

JavaScript对象的建立方式

在JavaScript中,建立对象的方式包括两种:对象字面量和使用new表达式。对象字面量是一种灵活方便的书写方式,例如:html

 

?
1
2
3
4
5
6
var o1 = {
     p:”I’m in Object literal”,
     alertP:function(){
         alert( this .p);
     }
}

 

这样,就用对象字面量建立了一个对象o1,它具备一个成员变量p以及一个成员方法alertP。这种写法不须要定义构造函数,所以不在本文的讨论范围以内。这种写法的缺点是,每建立一个新的对象都须要写出完整的定义语句,不便于建立大量相同类型的对象,不利于使用继承等高级特性。java

new表达式是配合构造函数使用的,例如new String(“a string”),调用内置的String函数构造了一个字符串对象。下面咱们用构造函数的方式来从新建立一个实现一样功能的对象,首先是定义构造函数,而后是调用new表达式:编程

 

?
1
2
3
4
5
6
7
function CO(){
     this .p = “I’m in constructed object”;
     this .alertP = function(){
         alert( this .p);
     }
}
var o2 = newCO();

 

那么,在使用new操做符来调用一个构造函数的时候,发生了什么呢?其实很简单,就发生了四件事:浏览器

?
1
2
3
4
var obj  ={};
obj.__proto__ = CO.prototype;
CO.call(obj);
return obj;

 

第一行,建立一个空对象obj。编程语言

第二行,将这个空对象的__proto__成员指向了构造函数对象的prototype成员对象,这是最关键的一步,具体细节将在下文描述。函数

第三行,将构造函数的做用域赋给新对象,所以CA函数中的this指向新对象obj,而后再调用CO函数。因而咱们就给obj对象赋值了一个成员变量p,这个成员变量的值是” I’min constructed object”。测试

第四行,返回新对象obj。当构造函数里包含返回语句时状况比较特殊,这种状况会在下文中说到。this

 

正肯定义JavaScript构造函数

不一样于其它的主流编程语言,JavaScript的构造函数并非做为类的一个特定方法存在的;当任意一个普通函数用于建立一类对象时,它就被称做构造函数,或构造器。一个函数要做为一个真正意义上的构造函数,须要知足下列条件:spa

一、 在函数内部对新对象(this)的属性进行设置,一般是添加属性和方法。prototype

二、 构造函数能够包含返回语句(不推荐),但返回值必须是this,或者其它非对象类型的值。

上文定义的构造函数CO就是一个标准的、简单的构造函数。下面例子定义的函数C1返回了一个对象,咱们可使用new表达式来调用它,该表达式能够正确返回一个对象:

 

?
1
2
3
4
5
6
7
8
function C1(){
     var o = {
         p:”I’m p in C1”
     }
     return o;
}
var o1 = new C1();
alert(o1.p); //I’m p in C1

 

但这种方式并非值得推荐的方式,由于对象o1的原型是函数C1内部定义的对象o的原型,也就是Object.prototype。这种方式至关于执行了正常new表达式的前三步,而在第四步的时候返回了C1函数的返回值。该方式一样不便于建立大量相同类型的对象,不利于使用继承等高级特性,而且容易形成混乱,应该摒弃。

一个构造函数在某些状况下彻底能够做为普通的功能函数来使用,这是JavaScript灵活性的一个体现。下例定义的C2就是一个“多用途”函数:

 

?
1
2
3
4
5
6
7
8
9
10
function C2(a, b){
     this .p = a + b;
     this .alertP = function(){
         alert( this .p);
     }
     return this .p; //此返回语句在C2做为构造函数时没有意义
}
var c2 = new C2( 2 , 3 );
c2.alertP(); //结果为5
alert(C2( 2 , 3 )); //结果为5

 

该函数既能够用做构造函数来构造一个对象,也能够做为普通的函数来使用。用做普通函数时,它接收两个参数,并返回二者的相加的结果。为了代码的可读性和可维护性,建议做为构造函数的函数不要掺杂除构造做用之外的代码;一样的,通常的功能函数也不要用做构造对象。

 

为何要使用构造函数

根据上文的定义,在表面上看来,构造函数彷佛只是对一个新建立的对象进行初始化,增长一些成员变量和方法;然而构造函数的做用远不止这些。为了说明使用构造函数的意义,咱们先来回顾一下前文提到的例子。执行var o2 = new CO();建立对象的时候,发生了四件事情:

 

?
1
2
3
4
var obj  ={};
obj.__proto__ = CO.prototype;
CO.call(obj);
return obj;

 

咱们说最重要的是第二步,将新生成的对象的__prop__属性赋值为构造函数的prototype属性,使得经过构造函数建立的全部对象能够共享相同的原型。这意味着同一个构造函数建立的全部对象都继承自一个相同的对象,所以它们都是同一个类的对象。关于原型(prototype)和继承的细节,笔者会再另外一篇文章中深刻说明。

在JavaScript标准中,并无__prop__这个属性,不过它如今已是一些主流的JavaScript执行环境默认的一个标准属性,用于指向构造函数的原型。该属性是默认不可见的,并且在各执行环境中实现的细节不尽相同,例如IE浏览器中不存在该属性。咱们只要知道JavaScript对象内部存在指向构造函数原型的指针就能够了,这个指针是在调用new表达式的时候自动赋值的,而且咱们不该该去修改它。

在构造对象的四个步骤中,咱们能够看到,除第二步之外,别的步骤咱们无须借助new表达式去实现,所以new表达式不只仅是对这四个步骤的简化,也是要实现继承的必经之路。

 

容易混淆的地方

关于JavaScript的构造函数,有一个容易混淆的地方,那就是原型的constructor属性。在JavaScript中,每个函数都有默认的原型对象属性prototype,该对象默认包含了两个成员属性:constructor和__proto__。关于原型的细节就不在本文赘述了,咱们如今关心的是这个constructor属性。

按照面向对象的习惯性思惟,咱们说构造函数至关于“类”的定义,从而可能会认为constructor属性就是该类实际意义上的构造函数,在new表达式建立一个对象的时候,会直接调用constructor来初始化对象,那就大错特错了。new表达式执行的实际过程已经在上文中介绍过了(四个步骤),其中用于初始化对象的是第三步,调用的初始化函数正是“类函数”自己,而不是constructor。若是没有考虑过这个问题,这一点可能不太好理解,那就让咱们举个例子来讲明一下吧:

 

?
1
2
3
4
5
6
7
8
9
10
11
12
13
function C3(a, b){
     this .p = a + b;
     this .alertP = function(){
         alert( this .p);
     }
}
//咱们定义一个函数来覆盖C3原型中的constructor,试图改变属性p的值
function fake(){
     this .p = 100 ;
}
C3.prototype.constructor = fake; //覆盖C3原型中的constructor
var c3 = new C3( 2 , 3 );
c3.alertP(); //结果仍然为5

 

上述代码手动改变了C3原型中的constructor函数,然而却没有对c3对象的建立产生实质的影响,可见在new表达式中,起初始化对象做用的只能是构造函数自己。那么constructor属性的做用是什么呢?通常来讲,咱们可使用constructor属性来测试对象的类型:

 

?
1
2
var myArray = [ 1 , 2 , 3 ];
(myArray.constructor == Array); // true

 

这招对于简单的对象是管用的,涉及到继承或者跨窗口等复杂状况时,可能就没那么灵光了:

 

?
1
2
3
4
5
6
7
function f() { this .foo = 1 ;}
function s() { this .bar = 2 ; }
s.prototype = new f(); // s继承自f
  
var son = new s(); // 用构造函数s建立一个子类对象
(son.constructor == s); // false
(son.constructor == f); // true

 

这样的结果可能跟你的预期不相一致,因此使用constructor属性的时候必定要当心,或者干脆不要用它。

 

引用地址:http://www.2cto.com/kf/201402/281841.html

相关文章
相关标签/搜索