把prototype属性整明白(之一) 长久以来,看到js代码中的prototype属性都有一种神经错乱的感觉,时而明白,时而又糊涂了,甚至我都没搞明白自己到底糊涂在哪个地方上,经过这些天的死磕,终于把这事儿整明白了。 下面写一些我的心得,关于原型的基本概念就不讲了,请先参考相关书籍,这里只讲容易陷入误区的地方: 首先看这段代码: 先不看下面答案,你能看出代码的输出结果吗? =============================================== 下面是答案 =============================================== 输出: 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内部,抛去其他代码,核心代码其实只有三行: 为了方便理解,我们不如把 var o2 = Object.create(O) 替换成下面三行代码: F是一个构造函数,F的prototype属性是O,前面已经讲过,通过new+构造函数创建的对象,它的原型对象是构造函数的prototype属性,也就是 F.prototype,也就是 O, 所以 o2的原型对象是O 开始看可能有点绕,看一会儿就明白了。
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();
Object.create = function(o){ function f(){}; f.prototype = o; return new f() }
function F(){}; F.prototype = O; o2 = new F() o2.foo();