javascript prototype 剖析

时间:2023-03-08 16:35:53

学过javascript的一定对prototype不陌生,但是这个究竟是个什么东西,就不一定很清楚。

我们先对prototype进行一个定义:每个函数都有一个prototype属性,这个属性是指向一个对象的引用(通俗点说就是这个prototype属性的值是这个对象的引用),这个对象称为原型对象,原型对象包含函数实例共享的方法和属性,也就是说将函数用作构造函数调用(使用new操作符调用)的时候,新创建的对象会从原型对象上继承属性和方法。

为了更好的理解prototype的设计意图,我们先来说几个概念。

私有的变量和函数:

    在函数内定义的变量和函数,如果不对外提供接口,那么外部将无法访问到,也就是变为私有变量和私有函数。

function Obj() {
  var a = 0; //私有变量
  var fn =function(){ //私有函数
     //code
  }
}

这样在函数对象Obj外部无法访问变量a和函数fn,它们就变成私有的,只能在Obj内部使用,即使是函数Obj的实例仍然无法访问到这些变量和函数:

代码如下:
var o = new Obj();
console.log(o.a); //undefined
console.log(o.fn); //undefined
 

静态变量和函数

当定义一个函数后,通过"."为其添加属性和函数,通过函数对象本身可以访问到,但是其实例却访问不到,这样的变量和函数分别被称为静态变量和静态函数。

代码如下:
function Obj(){ } Obj.a=0; //静态变量 Obj.fn=function(){
//静态函数 } console.log(Obj.a);
//0
console.log(typeof Obj.fn); //function var
o=new Obj();
console.log(o.a); //undefined
console.log(typeof o.fn); //undefined

实例变量和函数

    在面向对象编程中,我们希望在构造函数定义的时候,能够定义一些属性和方法,实例化后可以访问。

function Obj(){
  this.a=[]; //实例变量
  this.fn=function(){ //实例方法    }
} console.log(typeof Obj.a); //undefined
console.log(typeof Obj.fn); //undefined var o=new
Obj();
console.log(typeof o.a); //object
console.log(typeof o.fn); //function

再来看一下代码:

function Obj(){
  this.a=[]; //实例变量
  this.fn=function(){ //实例方法   }
} var o1=new Obj();
o1.a.push(1);
o1.fn={};
console.log(o1.a);
//[1]
console.log(typeof o1.fn); //object
var
o2=new Obj();
console.log(o2.a); //[]
console.log(typeof o2.fn); //function

上面的代码运行结果完全符合预期,说明一个问题,在o1中修改了a和fn,o2中没有改变,由于数组和函数都是引用类型,这就说明o1中的属性及方法和o2中的属性及方法虽然同名但却不是同一个引用,而是对Obj对象定义的属性和方法的一个复制。

这种机制对属性来说没有什么问题,但是对方法来说问题就很大,因为方法都是在做完全一样的功能,但是却有两份复制,如果一个对象有上千的实例方法,那么每个实例都要保存上千个复制,这显然是不科学的,如何解决?prototype就这样诞生了。

prototype

"prototype"字面翻译是"原型",是JavaScript实现继承的主要手段。prototype是JavaScript中函数(function)的一个保留属性,它的值是一个对象(我们称为"prototype对象或原型对象")。

通过函数作为构造函数构造出来的对象都自动拥有构造函数的prototype对象中的成员属性和方法。

其中要点有:

  1. prototype是函数(function)的一个必备属性(书面一点的说法是"保留属性")(只要是function,就一定有一个prototype属性)
  2. prototype的值是一个对象
  3. 可以任意修改函数的prototype属性的值。
  4. 一个对象会自动拥有这个对象的构造函数的prototype的成员属性和方法
//定义一个函数(构造函数),并定义一些属性和方法用来给另外一个构造函数构造出来的对象继承
var function1=function(){
  this.name="function1";
  this.saySomething=function(){alert("This's a method of "+this.name);}//定义一个方法
}
 //定义另外一个构造函数
var function2=function(){
}
//将构造函数function2的prototype属性设置为一个由function1构造出来的对象,以便使由function2构造出来的对象(并且原本是没有任何属性和方法的对象)拥有function1的属性和方法
function2.prototype=new function1();
var obj1=new function1();
//obj1本来什么成员也没有,多得prototype机制,是它坐享其成地拥有了function1对象的属性和方法。
obj1.saySomething();
 

那么prototype背后是怎么工作的呢?这里不得不提一个跟prototype一样是系统保留的属性:__proto__

__proto___是一个对象自动拥有的内置属性,指向这个对象的构造函数的prototype对象,才使这个对象认识了它的构造函数的prototype对象,并且拥有了这个prototype对象的属性和方法,比如Person构造函数:

javascript prototype 剖析