繁体   English   中英

试图理解 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. 原型是 JS 实现继承的一种方式,因为Emp(name)是基函数,原型被引用到同一个函数本身。 是这样的吗?

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

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

  4. 我怎样才能清楚地预测(无需在控制台中输入)原型或构造函数或prototype.constructor ......会产生什么?

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

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

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

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项目和 setter 和 getter。

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

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

此时Title函数如下所示:

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

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

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

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

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

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

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

在Javascript中我们要修改Title类的构造函数来调用父对象的构造函数。

所以Title类的构造函数会变成

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

通过使用Function对象属性Foo继承,我们可以在Titl对象中初始化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 //给出 Function()

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

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

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

employee.prototype // 给 Emp {}

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

jack.__proto__==employee.prototype

employee.prototype.constructor //给出 Emp(name)

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

jack.constructor //给出 Emp(name)

如上一点所述,这个对象原型是由函数 Emp(name) 在您使用 new Emp() 创建对象时创建的,

jack.prototype //给出未定义的

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

jack.__proto__

构造函数:

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

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

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

“对象是通过在 new 表达式中使用构造函数创建的;例如,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);

原型:

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

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

构造函数的prototype属性有一个非常强大的特性,它在类中不可用。 如果它有用,那就另当别论了。 构造函数的prototype属性可以更改在原型链中链接到该原型的每个实例的属性。

长话短说:

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

如果您使用带有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");

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

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

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

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

使用 2 种创建对象的方法来创建对象,即(1)对象字面量,或(2)使用 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 对象的原型。 使用大写首字母命名构造函数被认为是一种很好的做法。

向原型添加属性

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

示例: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() 函数。 不要将它与构造函数混淆,因为它不一样。

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

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

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

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM