gpt4 book ai didi

javascript - 如果 JavaScript 不支持经典继承,为什么我能够创建构造函数并使用 new 关键字?

转载 作者:塔克拉玛干 更新时间:2023-11-02 20:40:25 26 4
gpt4 key购买 nike

根据 MDN javascript 仅支持原型(prototype)继承。然而我可以做到以下几点:

function Human() {

this.eyes = 2;
this.feet = 2;

}

Var Mark = new Human();

更令人困惑的是,我可以使用 .prototype 向构造函数添加一个方法关键词:
Human.prototype.walk = function(distance) {
//code
}

然而,有一种使用 Object.Create 创建对象的正确方法,这显然是正确的基于原型(prototype)的对象创建:
var Human = {
eyes: 2,
feets: 2

}

var Mark = Object.create(Human);

有人可以帮我解决这个问题吗?谢谢

最佳答案

您应该了解的第一件事是,您作为示例提供的代码段仍然是原型(prototype)继承,原因如下:

  • Human是一个包含 prototype 的函数目的。 Human 的实例使用在 Human 中初始化的自己的数据扩展该原型(prototype)对象构造函数。
  • prototype对象可以在运行时修改。即使在您创建了类的实例之后,您仍然可以通过在 prototype 上添加或更改属性来修改它们的继承行为。目的。对于经典继承,这一切都不可能。
  • 在经典继承中,类和对象之间有明显的区别。在原型(prototype)继承中,类只是一个可构造函数的对象,这意味着它可以通过 new 调用。关键字,但除此之外,可以像任何其他对象一样对待。

  • 鉴于此信息,让我们演示 Object.create() 之间的一些关键相似点和不同点。和 new :

    function Human() {
    this.eyes = 2;
    this.feet = 2;
    }

    Human.prototype.walk = function () { };

    var josh = new Human();

    console.log(josh);



    var human = {
    constructor: function Human() {
    this.eyes = 2;
    this.feet = 2;
    },
    walk: function () { }
    };

    // create josh with prototype of human
    var josh = Object.create(human);

    // initialize own properties by calling constructor
    human.constructor.call(josh); // or josh.constructor();

    console.log(josh);


    乍一看可能不会,但这两个片段实际上是在创建一个实例 josh具有完全相同的布局:
    {
    eyes: 2,
    feet: 2,
    __proto__: {
    walk: f (),
    constructor: f Human(),
    __proto__: Object.prototype
    }
    }

    也就是说:
    var proto = Object.getPrototypeOf(josh);
    var protoProto = Object.getPrototypeOf(proto);

    console.log(proto === Human.prototype); // or proto === human
    console.log(protoProto === Object.prototype);
    <- true
    <- true

    这演示了 josh 的原型(prototype)链.决定对象行为的是继承路径,并表明 josh继承自 Human ,继承自 Object .

    上面两个 Stack Snippet 控制台之间的差异是因为第一个代码段的 constructorHuman.prototype 的不可枚举属性,而第二个片段的 constructorhuman 的可枚举属性.

    如果您想拆分第二个片段,我强烈建议您仔细查看 Object.create() 的文档。在 MDN 上,如果你能理解密集的语言,甚至可能看一下它的规范。

    以下是如何使用 Object.create()根据我们对 Human 的定义反而:

    function Human() {
    this.eyes = 2;
    this.feet = 2;
    }

    Human.prototype.walk = function () { };

    // create prototypal inheritance
    var josh = Object.create(Human.prototype);

    // initialize own properties
    Human.call(josh); // or josh.constructor();

    console.log(josh);


    这将初始化实例的实例属性 josh通过 calling带有 josh 的 ES5 构造函数作为上下文( this 关键字)。

    最后,由于它在评论中被提及,所有这些都可以使用 ES6 class 进行抽象。关键字,它仍然使用原型(prototype)继承:

    class Human {
    constructor() {
    this.eyes = 2;
    this.feet = 2;
    }

    walk() { }
    }

    var josh = new Human();

    console.log(josh);


    输出可能看起来不同,但如果你在真正的开发者控制台中检查,你会发现 josh 的布局的唯一区别是是因为 ES6 类声明了像 walk() 这样的成员方法。作为 Human.prototype 的不可枚举属性,这就是它没有出现在 Stack Snippet 控制台中的原因。

    您不能使用 Object.create()与 ES5 中演示的方式相同,因为 ES6 class只能构造(可调用 new )并且不可调用(可调用不 new ):

    class Human {
    constructor() {
    this.eyes = 2;
    this.feet = 2;
    }

    walk() { }
    }

    var josh = Object.create(Human.prototype); // still works

    // no own properties yet because the constructor has not been invoked
    console.log(josh);

    // cannot call constructor to initialize own properties
    Human.call(josh); // josh.constructor(); would not work either


    附录

    我试图想出一种方法来更容易地在 Stack Snippet 控制台中查看对象的原型(prototype)链,所以我编写了这个函数 layout() .它递归到对象的原型(prototype)链中,并使所有属性都可枚举。由于原型(prototype)链不能有循环,因此永远不会陷入无限递归:

    // makes all properties in an object's prototype chain enumerable
    // don't worry about understanding this implementation
    function layout (o) {
    if (typeof o !== 'object' || !o || o === Object.prototype) return o;
    return [...Object.getOwnPropertyNames(o), '__proto__'].reduce(
    (a, p) => Object.assign(a, { [p]: layout(o[p]) }),
    Object.create(null)
    );
    }

    // this is intentionally one line in order to
    // make Stack Snippet Console output readable
    function HumanES5() { this.eyes = 2; this.feet = 2; }
    HumanES5.prototype.walk = function () { };

    var josh = new HumanES5();
    console.log(layout(josh));

    var josh = Object.create(HumanES5.prototype);
    HumanES5.call(josh); // or josh.constructor();
    console.log(layout(josh));

    class HumanES6 {
    constructor () { this.eyes = 2; this.feet = 2; }
    walk () { }
    }

    var josh = new HumanES6();
    console.log(layout(josh));

    var josh = Object.create(HumanES6.prototype);
    // HumanES6.call(josh); will fail, remember?
    console.log(layout(josh));
    .as-console-wrapper{min-height:100%!important}


    这里有两点需要注意。
  • 在最后两个输出中,class HumanES6 { ... }实际上是指constructor类声明中的函数。在原型(prototype)继承中,类及其构造函数是同义词。
  • 最后一个输出没有自己的属性 eyesfeet自从constructor从未被调用来初始化 josh 的实例.
  • 关于javascript - 如果 JavaScript 不支持经典继承,为什么我能够创建构造函数并使用 new 关键字?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/53511864/

    26 4 0
    Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
    广告合作:1813099741@qq.com 6ren.com