把prototype属性整明白(之一)
长久以来,看到js代码中的prototype属性都有一种神经错乱的感觉,时而明白,时而又糊涂了,甚至我都没搞明白自己到底糊涂在哪个地方上,经过这些天的死磕,终于把这事儿整明白了。
下面写一些我的心得,关于原型的基本概念就不讲了,请先参考相关书籍,这里只讲容易陷入误区的地方:
首先看这段代码:
function O(){}
O.foo =
function(){
     console.log('O.foo');        
}
O.prototype.foo =
function(){
     console.log('O.prototype.foo');        
}
var o1 =
new O();
o1.foo();
var o2 = Object.create(O);
o2.foo();
先不看下面答案,你能看出代码的输出结果吗?
===============================================
下面是答案
===============================================
输出:
O.prototype.foo
O.foo
你答对了吗?答对了是明白人,就不用继续看了,像我一样糊涂的继续看:
function O(){}  创建了一个函数
当使用 var o1 = new O(); 时,O就不是一个普通的函数了,而是一个构造函数
凡是用new +构造函数创建的对象,它的原型对象就是构造函数的prototype属性,在这里
o1的原型对象就是 O.prototype
我以前有个误区,以为o1的原型对象是O,这是不对的,应该是O.prototype
当执行 o1.foo()时,先查询o1自己有没有foo属性(或者说方法也行,在js中数据和代码分的没那么清的),如果没有,就到它的原型对象中去找,前面说了,o1的原型对象是 O.prototype,原型对象里面有foo,所以输出 ‘O.prototype.foo’。
这个其实很好理解,一般人光看这行代码也不会有什么糊涂的,如果和其他一些代码放在一起,就会糊涂了,那些代码我以后会讲到。
再看 var o2 = Object.create(O);
Object.create()方法是ECMAScript5中定义的方法(不是所有浏览器都支持,但很容易模拟),它实现了基于原型的继承!它返回一个新对象,传入的第一个参数是这个新对象的原型。
在这里,传入的参数是O,返回的对象是o2,也就是说:
o2的原型是O。
看出和o1的不同了吗?o1的原型是O.prototype,而o2的原型是O。
所以执行 o2.foo(),执行的是 O.foo()
Object.create()是如何实现继承的呢?这需要深入到Ojbect.create内部,抛去其他代码,核心代码其实只有三行:
Object.create =
function(o){
    
function f(){};
     f.prototype = o;
    
return
new f()
}
为了方便理解,我们不如把 var o2 = Object.create(O) 替换成下面三行代码:
function F(){};
F.prototype = O;
o2 =
new F()
o2.foo();
F是一个构造函数,F的prototype属性是O,前面已经讲过,通过new+构造函数创建的对象,它的原型对象是构造函数的prototype属性,也就是 F.prototype,也就是 O,
所以 o2的原型对象是O
开始看可能有点绕,看一会儿就明白了。