function Father(){
console.log('father');
}
function Sub(){
console.log('sub');
}
Sub.prototype = Object.create(Father.prototype);
Sub.prototype.constructor = Sub; //为什么实现继承必须要有这句?没有这句会产生什么后果?
我知道最简单的后果就是Sub.prototype.constructor === Father(){...}
,然而知道这个貌似也没什么用。
请参考这篇 :http://m.blog.csdn.net/blog/adwu73/7224356
////
标准里面prototype只有一个属性就是constructor,你这里没有写Object.create()的实现,但是从函数名猜下来Sub.prototype = Object.create(Father.prototype);你这句是在把函数的prototype属性的prototype对象替换成Father,也就是要继承的那个,可是问题随之而来了,你替换了之后,prototype的constructor也是一并被换了,而constructor是指向自身的,Sub的prototype的constructor指向sub自己,Father的也指向自己,也就是没有下面那句的话,Sub的constructor是指向了Father,这样你用new之后,完蛋了,类的实例们突然发现,不知道(爸爸)类去哪了,prototype里放了要继承的Father的属性和方法,可是constructor也指向了要继承的Father,原来的Sub没他什么事情了。注意只有Function有prototype,而new之后的实例是没有prototype,它只有一个东西指向类的prototype。总之就是,类中只有一个属性prototype,prototype属性有一个prototype对象,我们继承就是通过在prototype中放要继承的Father的属性和方法来实现继承的。但是即便我们什么东西也没有放,prototype中也有一个constructor属性,此属性指向自身。我们替换prototype不能把constructor也换掉。如果你还不是很清楚,我会晚些时候写一些例子。
//
var Father=function(){//需继承的父类
this.gate ="ok";
}
var One=function(){
this.person="no";
}
One.prototype=new Father();//替换prototype属性的prototype对象 就差不多是你
//Object.creat()的实现
one=new One();//one的实例
console.log(one.gate);//输出ok 实现继承
console.log(One.prototype.constructor);//打印被替换的prototype.constructor,本应该是One
//的函数实现的,这里变成了Father的函数实现,就是因为没有写One.prototype.constructor=One
//由此带来的一个问题就是One的实例one的constructor被错误的更改了,你觉得可以啊,ok的,因为
//console.log(one.preson);//打印出来也ok的,好像有它没它无所谓
//那么问题是,一会造成代码上的混乱,本来是One的实例,构造函数却变成了Father
//二是如果你想构造更多的One的实例,当然你可以由One这个构造函数来产生,可是如果你拿到的只是One的
//一个实例 one呢,你用如下方式产生更多的和one一样的实例
var two=new (one.constructor)//error 你产生的实际上是Father的实例
console.log(two.person);//undefined
你认为Sub.prototype === Father(){...}
,是不对的,关于JavaScript的面向对象,具体来说是原型这种设计,有几个点需要注意。
先说结论:那一句只是为了指明constructor,如果你不用constructor的话,这确实没什么用。
不过你说的
我知道最简单的后果就是Sub.prototype === Father(){...},然而知道这个貌似也没什么用。
是不对的。
你可以亲自试验一下
function Father(){
console.log('father');
}
function Sub(){
console.log('sub');
}
Sub.prototype = Object.create(Father.prototype);
//并没有修改Sub.prototype.constructor
//Sub.prototype.constructor = Sub;
var sub = new Sub();
console.log(sub.constructor); //Father
console.log(Sub.prototype); //Object { }
console.log(Sub.prototype === Father); //false
console.log(Sub.prototype === Father.prototype); //false
console.log(Sub.prototype.__proto__ === Father.prototype); //true
//下面两行说明JavaScript对对象所属的“类”的判断并不依赖于constructor属性
console.log(sub instanceof Sub); //true
console.log(sub instanceof Father); //true
console.log(sub instanceof Object); //true
题主需要重点了解一下Object.create方法,以及JavaScript的原型链。
特别是区分对象的原型以及构造函数的prototype属性。
另外建议题主把《JavaScript高级程序设计》的第六章再仔细读一读, 尤其注意分析其中的配图。
下面详细说一说:
-
JavaScript中对象的构建方式
使用字面量构建的对象其原型是Object.prototype,使用Object.create(proto[, propObj])所构建的对象的原型是你所提供的proto,而是用构造函数创建的对象其原型即为构造函数的prototype属性指向的对象。下面提供几段代码以供实验:- 使用对象字面量
var s = {}; console.log(s instaceof Object); //true console.log(s.constructor === Object); //true console.log(s.__proto__ === Object.prototype); //true
我看你在另一个答案下的评论提到了
__proto__
是非标准实现,确实是这样的,但是这是目前各个浏览器都采用了的方案,Node.js内部也是使用这种实现的。而且__proto__
确实是窥探对象原型(标准中指定的[[Prototype]])的一种方式。- 使用Object.create()
var apple = { color: 'red' }; var fuji = Object.create(apple, { price: {value: 15, configurable: true, enumerable: true, writable: true} }); console.log(fuji.hasOwnProperty('price')); //true console.log(fuji.__proto__ === apple); //true
这说明Object.create(proto[, propObj])为新对象添加了propObj中指定的属性,还指定了proto为其原型。
- 使用构造函数
function Fruit(name, price) { this.name = name; this.price = price; } Fruit.prototype.sale = function () { console.log(name + ' is $' + price); }; var fruit = new Fruit('banana', 20); fruit.sale(); //banana is $20 console.log(fruit instanceof Fruit); //true console.log(fruit.__proto__ === Fruit.prototype); //true
这说明在使用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。
例如:function C1() { } C1.prototype = { old: 1 }; var c1 = new C1(); console.log(c1 instaceof C1); //true console.log(c1.__proto__ === C1.prototype); //true //下面改变函数C1的prototype的指向 C1.prototype = { new: 2 }; console.log(c1 instanceof C1); //false console.log(c1.__proto__ === C1.prototype); //false console.log(c1.__proto__); //{ old: 1 } console.log(C1.prototype); //{ new: 2 }
-
JavaScript的原型继承
JavaScript的继承采用了原型链这种方式,但又加入了模仿Java、C++中“类”这种概念的成分,因此出现了一些比较令人纠结的概念。
在1.中提到了要区别构造函数的prototype属性以及函数的原型[[Prototype]]、对象的原型这些概念,一旦理清这几个概念,JavaScript的原型继承也就非常清楚了。function Fruit(name, price) { this.name = name; this.price = price; } //函数的默认prototype属性中是有constructor属性的(值为函数本身),这是由JavaScript解析器做好的 console.log(Fruit.prototype.constructor); //Fruit Fruit.prototype.sale = function () { console.log(name + ' is $' + price); }; function Apple(name, price, color) { Fruit.call(this, name, price); this.color = color; } Apple.prototype = Object.create(Fruit.prototype); console.log(Apple.prototype === Fruit.prototype); //false, 前面已经说了,Object.create(proto)函数是创建一个以proto为的对象 //前面修改了Apple.prototype的指向,默认的prototype对象已经丢失了, 新建立的对象并没有指定constructor console.log(Apple.prototype.hasOwnProperty('constructor')); //false console.log(Apple.prototype.__proto__.hasOwnProperty('constructor')); //true, Apple.prototype.__proto__就是Fruit.prototype //暂时不指明Apple的构造函数, 直接创建新对象 var fuji = new Apple('fuji', 20); console.log(fuji.constructor); //Fruit //来看一看constructor到底是谁的属性 console.log(fuji.hasOwnProperty('constructor')); //false console.log(fuji.__proto__.hasOwnProperty('constructor')); //false fuji.__proto__就是Apple.prototype, 我们并没有给Apple.prototype指定constructor属性,因此这里也是false console.log(fuji.__proto__.__proto__.hasOwnProperty('constructor')) //true fuji.__proto__.__proto__就是Apple.prototype.__proto__,也就是Fruit.prototype //现在指定Apple的构造函数 Apple.prototype.constructor = Apple; console.log(fuji.constructor); //Apple console.log(fuji.hasOwnProperty('constructor')); //false console.log(fuji.__proto__.hasOwnProperty('constructor')); //true
把上面的实验操作一遍, 仔细分析结果, 应该就可以理清JavaScript的原型链与构造函数, 构造函数的prototype之间的关系了。