试图理解 JavaScript 中原型和构造函数之间的区别

Trying to understand the difference between prototype and constructor in JavaScript

我是 JavaScript 的新手,为了理解这个概念,我阅读了很多关于原型和构造函数的文章,但无论走到哪里,我都感到困惑。

当人们同时谈论构造函数和原型时,就会产生混淆。

在下面的例子中

var employee = function Emp(name) {
    this.name = name;
}
var jack = new employee("Jack Dwain");

employee.constructor //gives Function()

employee.prototype // gives  Emp {}

employee.prototype.constructor //gives Emp(name)

jack.constructor //gives Emp(name)

jack.prototype //gives undefined
  1. prototype 是 JS 实现继承的一种方式,因为 Emp(name) 是基函数 prototype 是引用同一个函数本身。是这样吗?

  2. employee.constructoremployee.prototype.constructor有什么不同?

  3. 为什么 jack.prototypeundefined 即如果它继承自函数 Emp(name) 为什么它没有引用该函数?

  4. 如何在不在控制台中输入原型或构造函数或prototype.constructor ......产量

    [=37=的情况下如何清楚地预测自己]

如果您习惯于在其他 OOP 语言中轻松扩展 objects,那么很难让您全神贯注,但我会尽力解释这些的用途以及什么是什么。我假设您熟悉其他 OOP 语言。如果我错了请纠正我。

所有函数都有原型 Function()。它们继承了 Function 的所有基本功能,例如 toString() 和 valueOf()。

然后是构造函数。这就是你用来初始化 object 的东西。

p = new Foo();

所以在这种情况下我们有两件事。

  • A function FooFunction 作为原型(Foo)
  • A Function object 以 Foo() 作为构造函数(p)

(关注我了吗?)

Foo() 构造函数可以覆盖 Function 构造函数的一些基本功能,但也可以保持原样并充分利用它。

如果您熟悉 OOP 原则,原型是基础 class,构造函数是当前 class。在 OOP 中,上面的内容是 class Foo extends Function

您还可以从原型和构造函数的整个设置开始继承,在共享功能的同时制作更复杂的 objects。

例如这个:

// make a object initialiser extending Function. in oop `class Foo extends Function`

function Foo(bar) {
    this.baz = bar;
}
Foo.prototype.append = function(what) {
    this.baz += " " + what;
};
Foo.prototype.get() {
    return this.baz
}

现在假设我们想要用不同的方法把巴兹从那里弄出来。一种用于控制台日志记录,另一种用于将其放在标题栏上。 我们可以对我们的 class Foo 做一些大事,但我们没有那样做,因为我们需要用新的 classes 做完全不同的事情,但它们是为不同的实现而设计的。他们唯一需要共享的是 baz 项目以及 setter 和 getter。

所以我们需要扩展它以使用 OOP 术语。在 OOp 中,这将是期望的最终结果 class Title extends Foo(){}。那么让我们来看看如何到达那里。

function Title(what) {
    this.message = what;
}

此时 Title 函数如下所示:

  • 原型函数
  • 构造函数标题

因此,要使其扩展 Foo,我们需要更改原型。

Title.prototype = new Foo();
  • 原型 Foo
  • 构造函数 Foo

这是通过针对原型初始化一个新的 Foo() object 来完成的。 现在它基本上是一个名为 Title 的 Foo object。这不是我们想要的,因为现在我们无法访问标题中的消息部分。 我们可以通过将构造函数重置为 Title

来正确扩展 Foo()
Title.prototype.constructor = Title;
  • 原型 Foo
  • 构造函数名称

现在我们又面临一个问题。 Foo 的构造函数没有被初始化,所以我们最终得到一个未定义的 this.baz

要解决这个问题,我们需要调用 parent。在 java 中,您可以使用 super(vars),在 php $parent->__construct($vars).

在java脚本中我们必须修改Titleclass构造函数来调用parentobject.

的构造函数

因此 Title class 构造函数将变为

function Title(what) {
    Foo.call(this,what);
    this.message = what;
}

通过使用函数 object 属性 Foo 继承我们可以初始化标题中的 Foo object object.

现在你有一个正确继承的object。

因此,它没有像其他 OOP 语言那样使用 extend 这样的关键字,而是使用 prototypeconstructor.

如果你想创建一个 javascript object 你可以简单地声明一个新对象并赋予它属性(我选择自己对象化):

var myself= {
    name:"Niddro",
    age:32
};

此方法允许您制作一个对象。如果你想要的是一个 prototype 描述一般人,你可以在其中声明几个具有相同设置的人。要创建原型,您可以使用 构造函数 ,如下所示:

//Constructor
function generalNameForObject(param1, param2,...) {
    //Give the object some properties...
}

我心里有一个原型(食谱),我想调用 person,它应该包含属性名称和年龄,我将使用构造函数来实现它:

function person(name,age) {
    this.name=name;
    this.age=age;
}

上面的构造函数描述了我的 person 对象的原型。

通过调用构造函数创建一个新人:

var myself = new person("Niddro",31);
var OP = new person("rajashekar thirumala",23);

一段时间过去了,我意识到我已经过生日了,所以我需要更改原型的 属性:

myself.age=32;

如果要向构造添加属性,需要手动将其添加到构造函数中:

function person(name,age,rep) {
    this.name=name;
    this.age=age;
    this.reputation=rep;
}

相反,您可以通过执行以下操作向原型添加属性(这里 "prototype" 是一个实际的命令,而不仅仅是一个名称):

function person(name,age,rep) {
    this.name=name;
    this.age=age;
}
person.prototype.reputation=105;

请注意,这将为创建的所有对象增加 105 的声誉。

我希望这能让您对构造函数和原型之间的关系有更多的了解。

employee.constructor //gives Function()

在 JavaScript 中,函数也是对象,可以使用自己的构造函数 Function 来构造。因此,您可以编写以下代码来获取 Function 的实例。

var employee2 = new Function('a', 'b', 'return a+b');

当您像您的情况一样使用函数文字创建函数时,也会发生同样的情况。而这个对象的构造函数属性也引用了同一个native Functionobject/class.

employee.prototype // gives Emp {}

JavaScript 中的每个对象都有一个与之关联的原型。虽然只有函数对象原型可以使用 .prototype 直接访问。当您使用 new 关键字创建对象时,会在其对象原型上复制相同的原型。此复制主要负责 inheritance/extension。虽然原型被复制了,但它不像 Function 对象那样可以直接关联。它可以通过 .__proto__ 以非标准方式使用。以下代码将 return 为真。

jack.__proto__==employee.prototype

employee.prototype.constructor //gives Emp(name)

Object.prototype.constructor 的文档所述。 return 是对创建实例原型的 Object 函数的引用。这里被引用的对象是 employee.prototype 和 not employee。这有点复杂,但是对象 employee.prototype 的原型是由函数 Emp(name)

创建的

jack.constructor //gives Emp(name)

如前所述,当您使用 new Emp() 创建对象时,此对象原型是由函数 Emp(name) 创建的,

jack.prototype //gives undefined

jack 不是一个函数对象,所以你不能那样访问它的原型。您可以访问(不是标准方式)jack 的原型,如下所示。

jack.__proto__

构造函数:

function Foo(x) {
    this.x =x;
}

Foo 是构造函数。构造函数是一个函数。

有两种方法可以使用此构造函数Foo

"Objects are created by using constructors in new expressions; for example, new Date(2009,11) creates a new Date object. Invoking a constructor without using new has consequences that depend on the constructor. For example, Date() produces a string representation of the current date and time rather than an object."

来源ECMA-262

这意味着如果 Foo return 是某种东西(通过 return "somevalue";),那么 typeof Foo() 就是 return 值的类型。

另一方面,当您调用

var o = new Foo();

JavaScript 实际上只是

var o = new Object();
o.[[Prototype]] = Foo.prototype;
Foo.call(o);

原型:

当您调用 o.a 时,javascript 首先检查 a 是否是对象 o 自己的 属性。如果不是 javascript 将查找 属性 链以找到 a

有关 属性 链的更多信息,请查看 mdn

构造函数的prototype属性有一个非常强大的特性,在类中是没有的。如果它有用是另一场辩论。构造函数的 prototype 属性可以改变每个实例的属性 link 到其原型链中的那个原型。

TL,DR:

注意:这不是一个确切的定义,总结的目的只是让你对构造函数和原型有一个感觉。

如果您使用带有 new 关键字的构造函数,那么构造函数和原型具有相似的用途,尽管它们完全不同。构造函数初始化对象的属性,因此它提供属性。原型还通过 属性 链(基于原型的继承)提供属性。

但事实是,这种方法在很多情况下可能是错误的。在 Javascript 中,当您将一个方法绑定到 this 关键字时,您只是将该方法提供给该特定实例,并且它实际上与该构造函数的对象实例没有任何关系,这与静态方法非常相似。请记住,函数是 Javascript 中的第一个 class 公民,我们可以像处理对象一样处理它们,在这种情况下,我们只向函数实例添加一个 属性目的。这只是故事的一部分,您还必须知道通过它附加的任何方法都会为我们创建的每个新实例重新声明,如果我们希望创建这么多实例,这可能会对应用程序的内存使用产生负面影响。

原型只是一个对象,构造函数是指向创建对象的函数的指针.

构造函数是一个指针。它指向创建点的 Function(),您从中检索构造函数。 (即构造函数只是对 Function() 的引用,我们可以根据需要多次调用它。)

构造函数的用途之一是帮助您创建对象的复制副本。由于构造函数属性是对创建对象的函数的引用,所以只要你有对象的副本,它就会始终指向原始构造函数。https://coderwall.com/p/qjzbig/understanding-constructor-and-prototype

使用对象构造函数: 通常,单独创建的对象在许多情况下是有限的。它只创建一个对象。

有时我们喜欢有一个“对象类型”,可以用来创建许多一种类型的对象。

创建“对象类型”的标准方法是使用对象构造函数:

function person(first, last, email ) {
  this.first_name = first;
  this.last_name = last;
  this.e_mail = email;
}
var myFather = new person("Ibm", "Muh", "ibm@gmail.com");

上面的函数(person)是一个对象构造函数。一旦有了对象构造器,就可以创建相同类型的新对象:

var myFather = new person("Sul", "Ahm", "sul@gmail.com");

每个 JavaScript 对象都有一个原型。 原型也是对象。

所有 JavaScript 对象从它们的 原型.

继承它们的属性和方法

使用 2 种创建对象的方法创建对象,即 (1) 对象字面量,或 (2) with new Object( ), 继承自名为 Object.prototype 的原型。使用 new Date() 创建的对象继承 Date.prototype.

Object.prototype在原型链的顶端。

所有 JavaScript 对象(日期、数组、正则表达式、函数......)继承自 Object.prototype。https://www.w3schools.com/js/js_object_prototypes.asp

关键字原型是 属性 个 Function() 对象。

原型的值 是创建该特定对象的对象构造函数。让我们看几个原型:

Boolean.prototype // returns Object Boolean
String.prototype // returns Object String with methods such as "toUpperCase"
Function.prototype // returns function() {} or function Empty() {}

创建原型:

创建对象原型的标准方法是使用对象构造函数:

function Person(first, last, age, eyecolor) {
  this.firstName = first;
  this.lastName = last;
  this.age = age;
}
var myFather = new Person("John", "Doe", 50);

使用构造函数,您可以使用 new 关键字从如上所示的相同原型创建新对象:

构造函数是 Person 对象的原型。 用 upper-case 第一个字母命名构造函数被认为是一种很好的做法。

向原型添加属性

不能像向现有对象添加新 属性 那样向原型添加新 属性,因为原型不是现有对象。

示例: Person.nationality = "英语";

要向原型添加新的属性,必须将其添加到构造函数中:

function Person(first, last, age, eyecolor) {
  this.firstName = first;
  this.lastName = last;
  this.age = age;
  this.eyeColor = eyecolor;
  this.nationality = "English";
}

所有本机和复杂对象都会检索到它们的原始构造函数,在本例中是它们自己。唯一的例外是 Function 原型,returns 创建它的 Function() 函数。不要将它与构造函数混淆,因为它们不一样。

Function.prototype === Function.constructor // returns false, Function.constructor is function Function(){}

多了一个属性,__proto__,指的是实例对象的内部[[proto]] 属性。与 Function() 对象不同,每个对象都有一个 __proto__。 不建议更新实例对象的原型,因为原型不应该在运行时更改(你应该能够看到谁是谁的原型,否则你需要花费额外的计算来确保没有循环引用)。