理解JavaScript中原型继承

1,解决上篇的问题先

前面一篇文章,我发了一堆的牢骚,想来也是很愚蠢的,只是被JavaScript搞的头疼,不爽而已。像许多东西你不懂的时候以为他是屎,当你懂了时候才知道他是宝。

书也是读第二遍的时候才能懂,08年能就读了爱民的《JavaScript语言精髓与编程实践》,而且也通读了语言精髓的部分,而且还和他通邮件聊了一点。不过当前我也没有读懂,我在书上有所记录。

image

现在看来Crockford的《JavaScript:Good Parts》中说的也对JavaScript确实有比较狗屎的地方,当然瑕不掩玉,JavaScript确定有着另人吃惊的能力。先来解决上篇的问题,我们再看看上篇的代码:

function Shape(){
  this.area = function(){};
}
  
function Point(){
  this.x = 0;
  this.y = 0;
}
  
var p = new Point;
 
//为啥是undefine,因为instace没有prototype属性,prototype对于p来说只是一个普通地跟p.val一样是一个undefined的属性。
console.log(p.prototype);  
   
//为啥又是指向Point的function,因为p没有constructor,只有Point.prototype有,通过原型查找,即p.constructor == Point.prototype.constructor == Point;
console.log(p.constructor);
console.log(p.constructor == Point.prototype.constructor);  
console.log(p.constructor == Point);
  
console.log("-----------------------------");
  
p.prototype = new Shape();
console.log(p.area);          //为啥又是undefine,不是设置了原型对象嘛,我操。
//var p = new Point;做了下面三步:p={}; p.__proto__=Point.prototype=new Object(); Point.apply(p);
//也就是说p的原型已经确定是new Object(),即一个空对象。 
  
Point.prototype = new Shape();  //设置原型
console.log(p.area);          //TMD还是不行,我设置了类型的原型对象啊。p中没有area属性,p的原型new Object()中也没有area属性,当然就是undefined,无比的正确
  
Point.prototype.area = function(){};
console.log(p.area);          //你妈,为什么啊。同上,p的原型在定义时已经确定就他妈的是一个空对象
  
var p2 = new Point;
console.log(p2.area);         //我操,这时又可以了@#¥%……&*(
//var p2 = new Point;的工作过程:p2={},p2.__proto__=Point.prototype=new Shape();Ponit.apply(p2);
//关键是第27行我们已经设置了Point.prototype这个原型为new Shape();
 
Point.prototype.val = 10;
console.log(p2.val);         //这下好理解了,p2中未定义val,这时去原型Point.prototype中找,找到了为10.

Shape.prototype.val2 = 20;
console.log(p2.val2);       //p2中没有val2,去Point.prototype(即new Shape())中找,new Shape()的__proto__为Shape.prototype,终于找到了。

2,一些JavaScript的总结

先谢谢博客园的汤姆的一张图

image

 

2.1 JavaScript中函数也是对象

如果感觉这不好理解,看下面的代码

//函数第一种写法,更能看出函数也是obj
var f1 = function Point(){
    this.x = 0;
    this.y = 0;
}

//函数第二种写法,与上面等效
function f1(){
    this.x = 0;
    this.y = 0;
}

除此之外,函数的原型是f1.__proto__ == Function.prototype; Function.__proto__ == Object.prototype; Object.__proto__ = null;

 

2.2 利用new 构造函数()来创建对象做了些什么

比如 var p = new Point();这句话做下以下工作:

var p = {};

p.__proto__ == Point.prototype;    //很关键的一步,原型继承来自于这里

Point.apply(p);

刚才看Crockford的视频,看到了new的JavaScript实现:

function new(func,arguments){
    var that = Object.create(func.prototype);   //创建一个对象,并将期__proto__设置为func.prototype指向的object instance.
    result = func.apply(that, arguments);
    return (typeof result === 'object' && result) ||  that;
}

 

2.3 一些注意点

a,只有构造器有prototype属性,即Point.prototype。prototype是(指向)一个实例(instance)

b,实例中有__proto__属性,即p.__proto__;原型回溯时就通过些属性。

c,p.prototype只是p的一个普通属性。系统没有对此有约定和特殊照顾。

d,构造器.prototype.constructor指向构造器自身,即Point.prototype.constructor == Point;

e, p.constructor == Point.prototype.constructor == Point;

 

2.4原型链的维护

如果你不想通过公开属性比如constructor来回溯整个原型链,你不用考虑太多。

但若你想回溯,就必须留心了。比如

function MyObject(){};
function MyObjectEx(){};

MyObjectEx.prototype = new MyObject(); //这句话会使得下面成立

var o = new MyObjectEx();
console.log(o.constructor == MyObject);   //我们在建立原型链时,将constructor指向打乱了。

//解法1
MyObjectEx.prototype = new MyObject();
MyObjectEx.prototype.constructor = MyObjectEx;  //手动修改constructor指向

//上面解法的问题是MyObject.prototype.constructor其实应该指向MyObject这样才能完成原型回溯,这便有了解法2

//解法2
function MyObjectEx(){
    this.constructor = arguments.callee;
   //Or, this.constructor = MyObjectEx;
}
MyObjectEx.prototype = new MyObejct();

//这样MyObjectEx.Instance.constructor == MyObjectEx
//并且MyObjectEx.prototype.constructor = MyObject

 

2.5 学JavaScript不能太纠结于实现的细节

你可以纠结与语言的细节,但不要纠结于实现的细节(SpideMonkey,JScript)。有些问题就是很奇怪。

比如:

var b = {};

console.log(b.__proto__ instanceof Object)  //此处是false即使我用debugger看到他是Object

 

如果你还想看一些诡异的地方,移步至《Wat

posted @ 2012-02-25 11:47  Jerry Chou  阅读(572)  评论(0编辑  收藏  举报