relationship of constructor and prototype in javascript

javascript构造方法与原型关系。

var animal = function(status) {    this.status = status;    this.breathes = "yes";    this.action = function() {        console.log('flying...')    };},human = function() {    this.name = 'human';},cat = function() {    this.type = 'cat';};// javascript支持原型继承,这种方式比类继承更强大,类继承中一个对象可以继承结构和行为,而原型继承可以继承结构和行为之外,并可以继承一个对象的状态// new一个animal的实例对象作为cat.prototype的原型,这个animal实例对象就成为cat的实例对象原型链上的一员// __proto__这个魔法属性在这些浏览器不能工作: ie 6/7/8, safari < 5, opera < 10.50// 当在cat的某个实例上检索一个属性时,如果在其本身中没有找到,则会延着原型链向上检索,如下例子中的c.__proto__即为一个animal对象// 如果检索c.breathes,如果在c对象本身没有找到此属性,则会检索t.__proto__.breathes、t.__proto__.__proto__.breathes等原型链上的对象,直到找到为止,没找到返回undefinedcat.prototype = new animal("live");// cat继承的原型对象是具有特定状态的animal对象var c = new cat();console.log(cat.prototype);console.log(cat.prototype.constructor.tostring());console.log(c.constructor.tostring());console.log("cat breathes:" + c.breathes);console.log("c.__proto__:", c.__proto__);// ie不支持此属性// 你可以利用object.__proto__这个魔法属性修改当前对象的原型,下面将一只猫猫化为人形var d = new cat();d.__proto__ = new human();console.log("d.__proto__:", d.__proto__);// 从上面结果可以看到cat的实例c.constructor不是指向cat这个构造函数,而是animal构造函数// 需要修改对象的constructor为其构造函数本身// 当一个函数对象被创建时,function构造器产生的函数对象会运行类似这样的一些代码:this.prototype = {constructor: this},参考javascript: the good parts 5.1节说明// 新函数对象被赋予一个prototype属性,其值是包含一个constuctor属性,并且其属性值为此新函数对象本身// 但是通过原型方式继承时,会给prototype重新赋予一个新对象,此prototype对象中的constructor是指向其自身的构造函数,而不是新函数的,所以需要重置其fn.prototype.constructor = this// 参考javascript权威指南第五版example 9-3. subclassing a javascript classcat.prototype.constructor = cat;console.log(cat.prototype.constructor.tostring());console.log(c.constructor.tostring());console.log(c.__proto__);var tostring = object.prototype.tostring;language = function() {    this.type = "programming";    return {        "locale": "en",        "class-free": function() {            return false        },        "tostring": function() {            return tostring.apply(this, arguments)        }        // 如果tostring方法被重写成非function对象,则后面console中无法输出对象j    }},javascript = function() {    this.value = "javascript";    this["class-free"] = function() {        return true    };};language.prototype = {    a: 1,    b: 2};javascript.prototype = new language();var j = new javascript();console.log(j);console.log(j.__proto__);// locale: en,此处因为language构造函数返回不是this,而是另一个object直接量,而object直接的构造方法为object(),因此language的原型被丢失了console.log(language.prototype);console.log(javascript.prototype);console.log(j.constructor.tostring());// function object() { [native code] }

构造函数与其返回值

构造函数会返回一个对象,如果没有直接return语句,构造函数会自动返回当前对象:return this;,也可以返回一个对象直接量,而不返回this,这样会中断正常的原型链。

prototype.js中class对象定义是封装在一个匿名函数里的,从而使得其内部变量和方法与外界隔离,其中有二句代码为:

function subclass() {};subclass.prototype = parent.prototype;

因为parent的构造可能返回语句不是返回this对象,而是返回了一个其他的对象,如{tostring: true},如果不用subclass.prototype = parent.prototype这样写,可能这样会丢失原型链上的方法和属性,通过subclass这个空构造将parent.prototype引用到自身的prototype上,从而保持住部分原型链。这其实也已经不是原型继承了,因为它不是通过new parent()来获取原型对象,丢失了new parent所得对象中的属性和方法。

prototype中的class其实放弃了原型对象,只是简单的继承了parent.prototype对象,已经失去原型继承可以继承对象状态的功能,这样操作其实是很好的模似了类继承方式。

var class = (function() {    function subclass() {};    function create() {        var parent = null,            properties = $a(arguments);        if (object.isfunction(properties[0])) parent = properties.shift();        function klass() {            this.initialize.apply(this, arguments);        }        object.extend(klass, class.methods);        klass.superclass = parent;        klass.subclasses = [];        if (parent) {            // 因为parent的构造可能返回对象直接量,而不是返回this,如{tostring:true}            subclass.prototype = parent.prototype;            klass.prototype = new subclass;            parent.subclasses.push(klass);        }        for (var i = 0; i < properties.length; i++) klass.addmethods(properties[i]);        if (!klass.prototype.initialize) klass.prototype.initialize = prototype.emptyfunction;        klass.prototype.constructor = klass;        return klass;    }    function addmethods(source) {        var ancestor = this.superclass && this.superclass.prototype;        var properties = object.keys(source);        if (!object.keys({            tostring: true        }).length) {            if (source.tostring != object.prototype.tostring) properties.push("tostring");            if (source.valueof != object.prototype.valueof) properties.push("valueof");        }        for (var i = 0, length = properties.length; i < length; i++) {            var property = properties[i],                value = source[property];            if (ancestor && object.isfunction(value) && value.argumentnames().first() == "$super") {                var method = value;                value = (function(m) {                    return function() {                        return ancestor[m].apply(this, arguments);                    };                })(property).wrap(method);                value.valueof = method.valueof.bind(method);                value.tostring = method.tostring.bind(method);            }            this.prototype[property] = value;        }        return this;    }    return {        create: create,        methods: {            addmethods: addmethods        }    };})();