Javascript -为什么要循环?(原型属性->构造函数属性->函数对象->构造函数属性)

Javascript - why this loop ? instance->(prototype property->constructor property->function object->constructor property)

本文关键字:属性 构造函数 函数 对象 原型 为什么 循环 Javascript      更新时间:2023-09-26

我无法理解javascript的这个循环行为。

谁能告诉我为什么要这样设计?这种行为有真正的用例吗?

为什么要循环?{新创建的实例继承构造函数对象的prototype属性。构造函数的Prototype属性是一个保留构造函数属性的对象。构造函数属性等于构造函数对象。再次构造函数对象保持原型属性。}

instance1——inhertis(keeps)——>func()的原型属性——>keep构造函数属性——>函数对象func——>keep原型属性

var func = function(){};
var construct = func.prototype.constructor;
console.log(construct === func); //true
var instance1 = new func();

更新:即使在i赋值之间,instanceof也总是返回true。

var func1 = function(){};
func1.prototype.constructor = 1;
var instance1 = new func1();
console.log(instance1 instanceof func1); //true

var func2 = function(){};
func2.prototype.constructor = 0;
var instance2 = new func2();
console.log(instance2 instanceof func2); //true

很抱歉在一个问题中问两个问题,但这两个问题可能是相关的。

当然它保留了实例。为什么不呢?如果你在做一只鸭子,它就是一只鸭子——它的DNA说它是一只鸭子,不管你把它涂成黑色还是教它做一只鹅。

同样,在您的例子中,设置构造函数没有效果。当你做new func(或new func(),它们是相同的)时,你去抓函数的内部属性([[Construct]]属性),而不是func.prototype.constructor

obj.constructor定义在每个对象上,因为它定义在每个"构造函数"上:也就是说,Object Number Function Date Boolean String等等。每个原型都有一个constructor属性:

Object.prototype.constructor === Object;
String.prototype.constructor === String;
//etc

每一个都有自己的prototype.constructor指向自己。

由于函数也可以像构造函数一样工作,因此它们的.prototype.constructor属性也指向它们自己。据我所知,这在语言本身中是未使用的。

简洁、专业的答案?http://es5.github.com/x11.8.6

//foo instanceof bar
Return the result of calling the [[HasInstance]] internal method of bar with argument foo.

(稍微转述)

基本上,你是在问鸭妈妈:"对不起,女士,这是你的孩子吗?"这孩子在这件事上几乎没有发言权。

编辑:正如评论中提到的,改变原型确实会影响instanceof的结果。和上面一样,有直观的答案和技术的答案。

直观的答案很简单:原型定义对象。因此,改变原型会改变DNA——你把鸭子变成一只鹅,不是通过教它成为一只鹅,而是通过进入它的DNA,把它变成一只鹅。

技术性是看[[HasInstance]]做什么。(另一个[[HasIntsance]]调用这个)规范是非常干燥和简洁的,所以这里是用伪javascript编写的算法:

//assume Func is the function we're talking about
function HasInstance ( value ) {
    if ( IsntAnObject(value) ) {
        return false;
    }
    var proto = Func.prototype;
    if ( Type(proto) !== "Object" ) {
        return false;
    }
    while ( value !== null ) {
        value = value.prototype;
        if ( value === proto ) {
            return true;
        }
    }
    return false;
}

可以看到,通过改变原型,我们改变了行为- value将是不同的值

非常好的问题!

由于原型是Object实例(JavaScript中的所有东西都是),它们必须共享Object的原型。正好有constructor属性

实例共享其构造函数原型的所有属性,包括constructor属性。

指定函数原型的constructor属性来反映函数本身。例如:

Object.prototype.constructor === Object
Function.prototype.constructor === Function
/* the same with every other function */

当你重写构造函数的原型时,像这样:

Func.prototype = 1

原型本身和Func的所有实例都有一个不同的constructor属性,在上面的例子中是Number


对于instanceof,它与constructor属性本身无关。这与prototype有关。

function Func() {}
Func.prototype = { a: 1 } // assign a prototype
var fn = new Func() // create an instance
fn instanceof Func // true (reflects connexion to the original prototype) 
Func.prototype = { b: 2 } // assign a completely different prototype
fn instanceof Func // false (the connexion is broken)

下面的一小段代码可能会消除您的困惑。正如您所看到的,新实例实际上没有自己的属性"constructor"。因此,当您要求instance.constructor时,由于原型链接,您实际上获得了instance.prototype.constructor值。为了保持一致,您希望将instance.constructor设置为与创建它的函数相同的值。这就是为什么JS解释器将prototype.constructor设置为函数本身的值。

function Rabbit() { }
var rabbit = new Rabbit()
alert( rabbit.hasOwnProperty('constructor') ) // false
alert( Rabbit.prototype.hasOwnProperty('constructor') ) // true

对于非函数对象也是如此。它们没有自己的。constructor属性,因此调用最终在Object.prototype.constructor中结束,因此您得到所有非函数对象的相同答案。

在这一点上要问的一个问题是,为什么JS设计者选择函数对象而不是显式地将实例的constructor属性设置为函数本身。我不知道答案,但我猜它给语言增加了更多的"活力"。实例。即使您没有创建实例的原始函数,也可以使用构造函数以通用方式创建新实例。
function makeUnicorn(animal) {
    var unicorn = new animal.constructor();
    unicorn.HornOnNose = true;
    return unicorn;
}
var unicornRabbit = makeUnicorn(rabbit);

请注意,上面的代码对任何对象运行都没有错误,即使不是函数的实例。你可以使用这个JavaScript特性来定制上述"泛型"函数的行为,如果构造函数是实例的一个自己的属性,你就不能这样做。

Horse.prototype.constructor = function() {
   var newHorse = new Horse();
   newHorse.Color = "White";       
   return newHorse;
}