首页 > js constructor问题?

js constructor问题?

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高级程序设计》的第六章再仔细读一读, 尤其注意分析其中的配图。


下面详细说一说:

  1. 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。

  2. 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 }
    
  3. 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之间的关系了。

【热门文章】
【热门文章】