理解javascript中的原型

一.概念

在了解概念之前,我们需要明白这几个概念是不是一回事儿:

  • 原型
  • prototype属性
  • __proto__属性

JavaScript中有两个特殊的对象:Object与Function。Object.prototype是所有对象的原型,处于原型链的最底层。Function.prototype是所有函数对象的原型,包括构造函数。

我们看一个demo及图示:

代码:

// a constructor function
function Foo(y) {
  this.y = y;
}// inherited property "x"
Foo.prototype.x = 10;

// and inherited method "calculate"
Foo.prototype.calculate = function (z) {
  return this.x + this.y + z;
};

// now create our "b" and "c"
// objects using "pattern" Foo
var b = new Foo(20);
var c = new Foo(30);

// call the inherited method
b.calculate(30); // 60
c.calculate(40); // 80

// let's show that we reference
// properties we expect

console.log(

  b.__proto__ === Foo.prototype, // true
  c.__proto__ === Foo.prototype, // true
  b.constructor === Foo, // true
  c.constructor === Foo, // true
  Foo.prototype.constructor === Foo // true

  b.calculate === b.__proto__.calculate, // true
  b.__proto__.calculate === Foo.prototype.calculate // true

);

图示:

一个对象的真正原型是被对象内部的[[Prototype]]属性(property)所持有。ECMA引入了标准对象原型访问器Object.getPrototypeOf(object),到目前为止只有Firefox和chrome实现了此访问器。除了IE,其他的浏览器支持非标准的访问器__proto__,如果这两者都不起作用的,我们需要从对象的构造函数中找到的它原型属性。

上面一句话引自:http://blog.jobbole.com/9648/

无论是哪篇文章也好,一个对象的原型我们其实还是有办法去访问的,IE除外。

  • 通过非标准实现__proto__去访问
  • 通过标准实现Object.getPrototypeOf(object)去访问

好,根据代码和图,我们来给出一实例对象的一些结论,你可以假定存在__proto__这么一个东西

  • 实例对象没有prototype属性
  • 实例对象都有一个__proto__属性,指向其原型(原型对象)
  • 实例对象的__proto__属性类型是个对象 (js基础类型的对象除外)
  • 原型是一个对象,所以原型对象也有__proto__属性
  • 函数也是对象,它同时存在__proto__和prototype属性

再给出构造函数(函数)的有关原型的一些结论:

  • 构造函数有__proto__属性和prototype属性
  • 构造函数的__proto__指向Function.prototype,若该构造函数是非内置的,则其prototype属性为Object类型, 而Number,String,Object的prototype属性同其类型。
  • 基本类型的实例的__proto__其实并不是object,但是typeof会对此进行转换

code:

/*
*自定义的构造函数
*/
    var F=function(){};
    var f1=new F();
    console.log(f1.__proto===F.prototype);//true
    console.log(Function.prototype);//empty function
    console.log(F.__proto__===Function.prototype);//true
    console.log(Object.__proto__===Function.__proto__);//true
    console.log(f.constructor===F);//true
/*
*内置包装类,原理其实还是构造函数
*/
    var str='lalalala',str2=new String('llalalalla');
    console.log(str.__proto__);//string类型
    console.log(typeof str.__proto__);//运算转换成了object类型
    console.log(str.__proto__===String.prototype);//true
    console.log(str2.__proto__===String.prototype);//true

Object和Function:

  • Function 是Object的实例,Object又是Function的实例
  • Function是函数的构造函数,而Object也是函数,Function自身也是函数
    console.log(Function instanceof Object);//true
    console.log(Object instanceof Function);//true
    console.log(Object.__proto__===Function.prototype);//true
    console.log(Function.__proto__===Object.prototype);//false,这个我就无法理解了,既然Function也是Object的实例,那么为何这个就错了呢
    console.log(Function.__proto__===Object.__proto__);//true
    console.log(typeof Function);//function
    console.log(typeof Object);//function 

二.构造函数prototype属性的修改

给出两个代码示例:

demo1:

var F=function() {
    // body...
}
F.prototype={
    show:function(){

    },
    hide:function(){

    }
};
console.log(F.constructor===Function);//true
console.log(F.prototype.constructor===Object);//true

demo2:

var Parent=function(){};
var Son=function(){};
Son.prototype=new Parent();
var parent1=new Parent();
var son1=new Son();
console.log(parent1.constructor===Parent);//true
console.log(son1.constructor===Parent);//true

从以上代码可以看出,一旦构造函数的prototype属性发生了更改,那么该构造函数的原型对象的constructor也就发生了改变(这不是废话么)。

通常情况下,构造函数实例化的对象的constructor属性指向其构造函数(最好说成指向其原型对象的constructor)。比如:

var A=function(){};
var a=new A();
console.log(a.constructor===A);//true

但是,构造函数的prototype属性发生了更改(构造函数的原型对象也就发生了改变),于是,构造函数实例化的对象的constructor属性也就变了。

三.构造函数prototype的constructor属性更改

鉴于以上出现的问题,通常有两种做法:

  • 重置constructor到原来的构造函数,F.prototype.constructor=F
  • 实例化时,将实例的constructor属性重置。当然这个可以在构造函数中处理。如:var Son=function(){ this.constructor=Son; }

两种方法,各有利弊。如果要正确访问原型链,则使用第二种方法,虽然效率差了点;否则,请使用第一种方法。

---------------------------------------------------------------

后续:

其实,写这篇文章初期是为了证明:javascript中的原型是一个对象。但是后来发现函数(js中没有构造函数之说法)的__proto__属性指向的是Function.prototype,而这个只是个空函数。并非对象类型。

后来提了问题去segmentfault(详见:http://segmentfault.com/q/1010000000209832),看了作者的翻译,我才明白,javascript中往大了说,就是两种值类型:原始值和对象。所以函数也是对象,这一点也可以从new Function()这种创建函数的形式可以看出。

posted @ 2013-06-07 15:20  xmlovecss  阅读(1293)  评论(2编辑  收藏  举报