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

Trying to understand the difference between prototype and constructor in JavaScript

本文关键字:构造函数 之间 区别 原型 JavaScript      更新时间:2024-01-24

我是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)基函数原型被引用到同一个函数本身。是这样吗?

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

  3. 为什么jack.prototype undefined?即如果它继承自函数Emp(name)为什么它不引用该函数?

  4. 我怎样才能清楚地预测(无需在控制台中输入(原型或构造函数或原型......收益 率?

如果您习惯于在其他 OOP 语言中轻松扩展对象,那么很难围绕这个概念进行思考,但我会尽力解释这些用途以及是什么。我假设您熟悉其他 OOP 语言。如果我错了,请纠正我。

所有功能都有原型Function()。他们继承了toString()valueOf()Function的所有基本功能。

然后有一个构造函数。这就是你用来初始化对象的东西。

p = new Foo();

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

  • Function为原型的function Foo(Foo(
  • Foo() 作为构造函数的 Function 对象(p(

(跟着我吗?

Foo()构造函数可以重写Function构造函数的某些基本功能,或者保持原样并充分利用它。

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

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

例如:

// Make an 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
}

现在让我们说我们想要不同的方法来baz离开那里。一个用于控制台日志记录,另一个用于将其放在标题栏上。我们可以对类Foo做一件大事,但我们不这样做,因为我们需要对为不同实现所做的新类做完全不同的事情。他们唯一需要分享的是baz物品以及二传手和获取者。

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

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

此时,Title函数如下所示:

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

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

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

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

Title.prototype.constructor = Title;
  • 原型
  • 构造函数标题

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

要解决这个问题,我们需要打电话给父母。在Java中,你可以用super(vars),在PHP $parent->__construct($vars)中做到这一点。

在 Javascript 中,我们必须修改 Title 类构造函数以调用父对象的构造函数。

所以Title类构造函数将变为

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

通过使用继承Function对象属性Foo我们可以初始化Titl e 对象中的Foo对象。

现在,您有一个正确继承的对象。

因此,它不像其他OOP语言那样使用extend关键字,而是使用prototypeconstructor

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

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

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

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

我想到一个原型(配方(,我想打电话给 person,它应该包含属性名称和年龄,我将使用构造函数来制作它:

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

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

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

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;
}

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

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

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

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

employee.constructor//gives Function((

在JavaScript中,函数

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

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

当您使用函数文字创建函数时也会发生同样的情况,就像您的情况一样。并且此对象的构造函数属性也引用相同的本机函数对象/类。

employee.prototype//给出 Emp {}

JavaScript 中的每个对象都有一个与之关联的原型。虽然只有函数对象原型可以直接通过.prototype访问。当您使用关键字创建对象时,同一原型将复制到其对象原型new。此复制主要负责继承/扩展。尽管原型是复制的,但它不像函数对象那样直接可用。它以非标准方式提供,.__proto__.以下代码将返回 true。

jack.__proto__==employee.prototype

employee.prototype.constructor//give Emp(name(

正如在 Object.prototype.constructor 的文档中所述。这将返回对创建实例原型的 Object 函数的引用。这里引用的对象是 employee.prototype 和 not employee 。这有点复杂,但对象 employee.prototype 的原型是由函数 Emp(name( 创建的

jack.constructor//give Emp(name(

如上一点所述,当您使用新的 Emp(( 创建对象时,此对象原型是由函数 Emp(name( 创建的,

jack.prototype//给出未定义

Jack 不是一个函数对象,所以你不能像那样访问它的原型。您可以像下面这样访问(不是标准方式(插孔的原型。

jack.__proto__

构造函数:

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

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

有两种

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

"对象是通过在新表达式中使用构造函数创建的;为 例如,new Date(2009,11( 创建一个新的 Date 对象。调用 不使用 new 的构造函数会产生取决于 构造 函数。例如,Date(( 生成一个字符串表示形式 当前日期和时间,而不是对象。

来源 ECMA-262

这意味着如果Foo返回某些内容(通过return "somevalue";(,则返回值的类型typeof Foo()

另一方面,当您致电时

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属性可以更改链接到其原型链中该原型的每个实例的属性。

TL,DR:

注意:这不是一个确切的定义,摘要的目的只是让您了解构造函数和原型。

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

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

构造函数是一个指针。它指向创建从中检索构造函数的点的 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");

上面的函数(人(是一个对象构造函数。拥有对象构造函数后,可以创建相同类型的新对象:

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

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

所有 JavaScript 对象都从其原型继承其属性和方法。

对象是使用2种

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

Object.prototype 位于原型链的顶端。

所有 JavaScript 对象(Date、Array、RegExp、Function等(都继承自 Object.prototype.https://www.w3schools.com/js/js_object_prototypes.asp

关键字 prototype 是 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 对象的原型。使用大写首字母命名构造函数被认为是一种很好的做法。

向原型添加属性

不能像向现有对象添加新

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

例:Person.nationality = "English";

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

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

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

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

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

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