JavaScript 继承和构造函数属性

标签 javascript inheritance constructor instanceof

考虑以下代码。

function a() {}
function b() {}
function c() {}

b.prototype = new a();
c.prototype = new b();

console.log((new a()).constructor); //a()
console.log((new b()).constructor); //a()
console.log((new c()).constructor); //a()
  • 为什么不为 b 和 c 更新构造函数?
  • 我做错了继承吗?
  • 更新构造函数的最佳方法是什么?

  • 此外,请考虑以下事项。
    console.log(new a() instanceof a); //true
    console.log(new b() instanceof b); //true
    console.log(new c() instanceof c); //true
    
  • 鉴于 (new c()).constructor等于 a()Object.getPrototypeOf(new c())a{ } , instanceof 怎么可能要知道new c()c 的一个实例?

  • http://jsfiddle.net/ezZr5/

    最佳答案

    好吧,让我们来玩一个小游戏:



    从上图我们可以看出:

  • 当我们创建像 function Foo() {} 这样的函数时, JavaScript 创建一个 Function实例。
  • Function实例(构造函数)有一个属性 prototype这是一个指针。
  • prototype构造函数的属性指向它的原型(prototype)对象。
  • 原型(prototype)对象有一个属性 constructor这也是一个指针。
  • constructor原型(prototype)对象的属性指向其构造函数。
  • 当我们创建 Foo 的新实例时喜欢 new Foo() , JavaScript 创建一个新对象。
  • 内部[[proto]]实例的属性指向构造函数的原型(prototype)。

  • 现在,问题出现了,为什么 JavaScript 不附加 constructor属性到实例对象而不是原型(prototype)。考虑:
    function defclass(prototype) {
        var constructor = prototype.constructor;
        constructor.prototype = prototype;
        return constructor;
    }
    
    var Square = defclass({
        constructor: function (side) {
            this.side = side;
        },
        area: function () {
            return this.side * this.side;
        }
    });
    
    var square = new Square(10);
    
    alert(square.area()); // 100
    

    如您所见 constructor property 只是原型(prototype)的另一种方法,如 area在上面的例子中。是什么让 constructor属性的特殊之处在于它用于初始化原型(prototype)的一个实例。否则它与原型(prototype)的任何其他方法完全相同。

    定义 constructor由于以下原因,原型(prototype)上的属性是有利的:
  • 这在逻辑上是正确的。例如考虑 Object.prototype . constructor Object.prototype 的属性(property)指向 Object .如果 constructor属性是在实例上定义的,然后 Object.prototype.constructor将是 undefined因为Object.prototypenull 的一个实例.
  • 它的处理方式与其他原型(prototype)方法没有区别。这使得 new 的工作更容易,因为它不需要定义 constructor每个实例的属性。
  • 每个实例共享相同的 constructor属性(property)。因此它是有效的。

  • 现在当我们谈论继承时,我们有以下场景:



    从上图我们可以看出:
  • 派生构造函数的 prototype属性设置为基本构造函数的实例。
  • 因此内部 [[proto]]派生构造函数实例的属性也指向它。
  • 因此 constructor派生构造函数实例的属性现在指向基构造函数。

  • 至于instanceof运算符,与流行的看法相反,它不依赖于 constructor实例的属性。从上面我们可以看到,这会导致错误的结果。
    instanceof运算符是二元运算符(它有两个操作数)。它对实例对象和构造函数进行操作。正如 Mozilla Developer Network 上的解释,它只是执行以下操作:
    function instanceOf(object, constructor) {
        while (object != null) {
            if (object == constructor.prototype) { //object is instanceof constructor
                return true;
            } else if (typeof object == 'xml') { //workaround for XML objects
                return constructor.prototype == XML.prototype;
            }
            object = object.__proto__; //traverse the prototype chain
        }
        return false; //object is not instanceof constructor
    }
    

    简单地说,如果Foo继承自 Bar ,然后是 Foo 实例的原型(prototype)链将会:
  • foo.__proto__ === Foo.prototype
  • foo.__proto__.__proto__ === Bar.prototype
  • foo.__proto__.__proto__.__proto__ === Object.prototype
  • foo.__proto__.__proto__.__proto__.__proto__ === null

  • 如您所见,每个对象都继承自 Object构造函数。原型(prototype)链在内部 [[proto]] 结束时结束。属性指向 null .
    instanceof函数简单地遍历实例对象的原型(prototype)链(第一个操作数)并比较内部[[proto]]每个对象的属性到 prototype构造函数的属性(第二个操作数)。如果它们匹配,则返回 true ;否则,如果原型(prototype)链结束,则返回 false .

    关于JavaScript 继承和构造函数属性,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/8093057/

    相关文章:

    c++ - cpp 多重继承意外构造函数调用

    c++ - "MyClass a(anotherInstance);"比 "MyClass a = anotherInstance;"快吗?

    javascript - 如何让桌面通知保持不变?

    javascript - 将元素向前放置以使它们可点击

    c++ - 如何 "inherit"来自 STL 类的迭代器?

    python - 如何在 Flask 中设置 Inherited MethodView 对 sqlalchemy 模型进行 CRUD 操作

    java - 编译后不显示GUI

    javascript - 使用 Gulp + Karma + PhantomJS 构建 ES6 应用程序

    javascript - 'DiscordAPI错误: Unknown Message' when deleting message upon a reaction

    java - 如何加入 Hibernate/JPA 2.0 CriteriaQuery 中的 JOINED 子类?