function Father(){
console.log('father');
}
function Sub(){
console.log('sub');
}
Sub.prototype = Object.create(Father.prototype);
Sub.prototype.constructor = Sub; //为什么实现继承必须要有这句?没有这句会产生什么后果?
我知道最简单的后果就是Sub.prototype.constructor === Father(){...}
,然而知道这个貌似也没什么用。
你认为
Sub.prototype === Father(){...}
,是不对的,关于JavaScript的面向对象,具体来说是原型这种设计,有几个点需要注意。先说结论:那一句只是为了指明constructor,如果你不用constructor的话,这确实没什么用。
不过你说的
是不对的。
你可以亲自试验一下
题主需要重点了解一下Object.create方法,以及JavaScript的原型链。
特别是区分对象的原型以及构造函数的prototype属性。
另外建议题主把《JavaScript高级程序设计》的第六章再仔细读一读, 尤其注意分析其中的配图。
下面详细说一说:
JavaScript中对象的构建方式
使用字面量构建的对象其原型是Object.prototype,使用Object.create(proto[, propObj])所构建的对象的原型是你所提供的proto,而是用构造函数创建的对象其原型即为构造函数的prototype属性指向的对象。下面提供几段代码以供实验:
我看你在另一个答案下的评论提到了
__proto__
是非标准实现,确实是这样的,但是这是目前各个浏览器都采用了的方案,Node.js内部也是使用这种实现的。而且__proto__
确实是窥探对象原型(标准中指定的[[Prototype]])的一种方式。这说明Object.create(proto[, propObj])为新对象添加了propObj中指定的属性,还指定了proto为其原型。
这说明在使用new创建对象时,新对象作为this传入构造函数中,并且新对象的
__proto__
属性(标准中的[[Prototype]]内部属性)被置为Fruit.prototype,也就是构造函数的prototype属性。这里需要注意区别构造函数的prototype属性,以及构造函数本身作为一种对象(函数也是一种对象)它自己的原型[[Prototype]]。构造函数的prototype属性就是为了指定新对象的原型而存在的,而与函数本身没有太大关系。函数本身的[[Prototype]]可以通过
Fruit.__proto__
这种形式窥探到,它实际上是Function.prototype。JavaScript中instanceof的工作方式
instanceof运算符(obj instanceof Constructor)会以obj为参数调用Constructor的[[HasInstance]]内部方法,这个方法的操作就是循着左侧操作符的原型链检查是否有与Constructor.prototype相同的原型,如果有就返回true,否则返回false。
例如:
JavaScript的原型继承
JavaScript的继承采用了原型链这种方式,但又加入了模仿Java、C++中“类”这种概念的成分,因此出现了一些比较令人纠结的概念。
在1.中提到了要区别构造函数的prototype属性以及函数的原型[[Prototype]]、对象的原型这些概念,一旦理清这几个概念,JavaScript的原型继承也就非常清楚了。
把上面的实验操作一遍, 仔细分析结果, 应该就可以理清JavaScript的原型链与构造函数, 构造函数的prototype之间的关系了。