2182

我不太喜欢动态编程语言,但我已经写了相当一部分JavaScript代码。我从来没有真正了解过这种基于原型的编程,有人知道它是如何工作的吗?

var obj=新对象();obj.prototype.test=function(){alert('Hello?');};var obj2=新obj();obj2.test();

我记得前一段时间我和人们进行了很多讨论(我不太确定自己在做什么),但据我所知,我没有课堂的概念。它只是一个对象,这些对象的实例是原始对象的克隆,对吗?

但是JavaScript中这个“.prototype”属性的确切用途是什么?它与实例化对象有什么关系?

更新:正确方式

var obj=新对象();//不是功能对象obj.prototype.test=function(){alert('Hello?');};//这是错误的!函数MyObject(){}//一类函数对象MyObject.prototype.test=function(){alert('OK');}//确定

还有这些幻灯片真的帮了大忙。

9
  • 83
    John Resig有几张关于函数原型的幻灯片,在研究这个主题时对我很有帮助(你也可以对代码进行更改,看看会发生什么……)http://ejohn.org/apps/learn/#64 评论 2009年2月21日12:56
  • 5
    很好的参考资料,为了保持这个问题的信息性,也许可以在你的答案上放置一些来自John网站的评论,以防他的网站发生变化,导致你的链接不再可用。不管怎样+1,帮了我。
    – 克里斯
    评论 2011年5月27日23:14
  • 96
    +1代表您的链接John Resig的JavaScript忍者幻灯片#64从那里开始真的很有帮助,我觉得我对原型的理解是正确的。 评论 2012年6月24日21:02
  • 4
    我们真的需要一个功能对象来应用原型吗?如果是,为什么?
    – 安舒尔
    评论 2013年2月19日15:40
  • 6
    这可能有助于您:webdeveasy.com/javascript-prototype
    – 纳尔
    评论 2013年4月10日11:35

26个答案26

重置为默认值
1834

在Java、C#或C++等实现经典继承的语言中,首先要创建一个类——对象的蓝图——然后可以从该类创建新对象,也可以扩展该类,定义一个新类来扩充原始类。

在JavaScript中,你首先创建一个对象(没有类的概念),然后你可以增加自己的对象或从中创建新对象。这并不难,但对于习惯于经典方式的人来说,这有点陌生,很难新陈代谢。

例子:

//在JavaScript中定义用于容纳人员的函数对象var Person=函数(名称){this.name=名称;};//向已定义的对象动态添加新的getterPerson.prototype.getName=函数(){返回this.name;};//创建类型为Person的新对象var john=新人(“john”);//试试吸气剂警报(john.getName());//如果现在我修改person,John也会得到更新Person.prototype.sayMyName=函数(){alert(“你好,我的名字是”+this.getName());};//对约翰调用新方法john.sayMyName();

到目前为止,我一直在扩展基础对象,现在我创建了另一个对象,然后从Person继承。

//通过定义其构造函数创建Customer类型的新对象。不是这样的//目前与Person相关。var客户=功能(名称){this.name=名称;};//现在我将对象链接起来,为此,我们将Customer的原型链接到//Person的新实例。原型是用于//构造所有新实例,并且将动态修改所有已创建的实例//构造对象,因为在JavaScript对象中保留指向//原型Customer.prototype=新人物();//现在我可以调用Person对Customer的方法,让我们先试试//我需要创建一个客户。var myCustomer=新客户(“Dream Inc.”);myCustomer.sayMyName();//如果我向Person添加新方法,它们将被添加到Customer,但如果我//将新方法添加到Customer,它们不会添加到Person。例子:Customer.prototype.setAmountDue=函数(amountDue){this.amountDue=到期金额;};Customer.prototype.getAmountDue=函数(){return this.amountDue;};//让我们试试:myCustomer.setAmountDue(2000年);警报(myCustomer.getAmountDue());

var Person=函数(名称){this.name=名称;};Person.prototype.getName=函数(){返回this.name;};var john=新人(“john”);警报(john.getName());Person.prototype.sayMyName=函数(){alert('你好,我的名字是'+this.getName());};john.sayMyName();var客户=功能(名称){this.name=名称;};Customer.prototype=新人物();var myCustomer=新客户(“Dream Inc.”);myCustomer.sayMyName();Customer.prototype.setAmountDue=函数(amountDue){this.amountDue=到期金额;};Customer.prototype.getAmountDue=函数(){return this.amountDue;};myCustomer.setAmountDue(2000);警报(myCustomer.getAmountDue());

正如前面所说,我不能对Person调用setAmountDue()和getAmountDue()。

//以下语句生成错误。john.setAmountDue(1000);
4
  • 358
    我认为stackoverflow上的答案不仅对原来的海报很有趣,而且对一个由潜伏或来自搜索的其他人组成的大社区来说也很有趣。我也是其中之一,我从以前的帖子中受益匪浅。我想我可以为其他答案添加一些代码示例。关于你的问题:如果你忽略了新的,它就不起作用。当我调用myCustomer.sayMyName()时,它返回“myCustoser.sayMyName不是函数”。最简单的方法是用firebug进行实验,看看会发生什么。 评论 2011年1月24日9:52
  • 7
    据我所知,var Person=函数(名称){…};正在定义能够构建Person对象的构造函数。因此还没有Object,只有匿名构造函数被分配给Person。这是一个很好的解释:helephant.com/2008/08/how-javascript-objects-work 评论 2011年1月25日3:30
  • 20
    警告:这个答案忽略了这样一个事实,即父类构造函数不是基于每个实例调用的。它工作的唯一原因是,他在子构造函数和父构造函数中做了完全相同的事情(设置名称)。有关在JavaScript中尝试继承时常见错误的更深入解释(以及最终解决方案),请参阅:此堆栈溢出帖子 评论 2013年11月13日6:26
  • 14
    关于Customer.prototype=新人物();行,MDN显示了一个使用Customer.protype=Object.create(Person.prototype),并声明'这里常见的错误是使用“new Person()”'.来源 评论 2014年9月1日20:24
1066

每个JavaScript对象有一个内部“插槽”(即隐藏属性)调用[[原型]](方括号是经过深思熟虑的)其值为无效的或一个对象该值通俗地称为“该对象的原型”

这些原型将对象连接到原型链用于查找属性。当您尝试通过以下任一方式从对象访问属性时对象.propNameobj['propName'],而对象没有拥有(对象hasOwnProperty('propName'))属性,运行时将通过递归引用[[原型]]插槽,直到无效的已到达。

对象的[[原型]]最初在对象创建期间设置,现代JavaScript允许对[[原型]]以以下方式:

  1. 这个新Ctor()语法,用于设置[[原型]]功能原型用于新创建的对象。
  2. 这个延伸关键字,用于配置类语法的原型链。
  3. 对象.创建,它将提供的参数设置为[[原型]]结果对象的。
  4. 对象.get原型对象的设置原型(获取/设置[[原型]] 之后对象创建)
  5. 名为的标准化访问器属性__原型__.(但它有异常行为当对象具有的原型时无效的.)

请注意.原型是真正的财产,而不是内部插槽。因此,所有类,以及可以与新的运算符的属性名为.原型除了他们自己[[原型]]内部插槽。“原型”一词的这种双重用法是该语言初学者无尽困惑的根源。

在JavaScript引入类语法之前,人们使用语法新Ctor()通过原型继承模拟经典继承:

  • 共享成员,例如方法,已添加到构造函数的.原型属性。
  • 然而,实例字段是在构造期间添加到对象本身的。

例如,有一种方法:

函数Child(){}函数Parent(){}Parent.prototype.inheritedMethod=函数(){return“这是继承的”}函数inherit(子级,父级){child.prototype=对象.create(parent.prototype)child.prototype.constructor=子返回儿童;}Child=继承(Child,Parent)const o=新子项console.log(o.inheritedMethod())//“这是继承的”

……还有另一种方法:

函数Child(){}函数Parent(){}Parent.prototype.inheritedMethod=函数(){return“这是继承的”}函数inherit(child、parent){函数tmp(){}tmp.prototype=父母原型const proto=新tmp()proto.constructor=子级child.prototype=原型返回儿童}Child=继承(Child,Parent)const o=新子项console.log(o.inheritedMethod())//“这是继承的”

但如果你小心的话,你可能会发现起源在这两种情况下,不考虑的实例字段。幸运的是,在ES2015中,这些细节是隐藏的,我们可以只写一个线性版本,这要归功于延伸语法:

类Parent{inheritedMethod(){return“this is inherited”}}类Child扩展父{}const o=新子项console.log(o.inheritedMethod())//“这是继承的”

…结果对象的[[原型]]将设置为的实例起源,谁的[[原型]]反过来家长.家长类型.

最后,如果您通过创建新对象对象.create(foo),结果对象的[[原型]]将设置为foo公司.

0
197

这是一个非常简单的基于原型的对象模型,在解释过程中将被视为示例,目前尚未发表评论:

职能人员(姓名){this.name=名称;}Person.prototype.getName=函数(){console.log(this.name);}var person=新人(“George”);

在讨论原型概念之前,我们必须考虑一些关键点。

1-JavaScript功能的实际工作方式:

为了迈出第一步,我们必须弄清楚JavaScript函数实际上是如何工作的,作为一个类函数,使用关键字,或者作为带有参数的常规函数,它做什么以及返回什么。

假设我们想创建一个对象模型。但在这一步我会努力不使用原型新的关键字.

所以在这个步骤中功能,物体关键字,是我们所有的。

第一个问题是怎样关键字可以不使用新的关键字.

所以为了回答这个问题,假设我们有一个空对象和两个函数,比如:

var人={};函数Person(name){this.name=name;}函数getName(){console.log(this.name);}

现在不使用新的关键字我们如何使用这些函数。因此JavaScript有三种不同的方法:

a.第一种方法是将函数作为常规函数调用:

人(“乔治”);获取名称()//会在控制台上打印“乔治”

在这种情况下,这将是当前上下文对象,它通常是全局的窗口浏览器中的对象或全球的在里面节点.js。这意味着我们在浏览器中有window.name或在Node.js中有GLOBAL.name,其值为“George”。

b.我们可以贴上作为对象的属性

-最简单的方法为此,需要修改空对象,如:

人。人=人;person.getName=获取名称;

这样我们可以这样称呼他们:

人。人(“乔治”);person.getName();//-->“乔治”

现在对象类似于:

对象{Person:函数,getName:函数,name:“George”}

-附加属性的另一种方法对象使用原型可以在任何名为的JavaScript对象中找到的对象__原型__,我已经尝试在总结部分对其进行了解释。因此,我们可以通过以下操作获得类似的结果:

人__原型__。人=人;人__proto__.getName=获取名称;

但是这样我们实际上正在做的是修改对象.原型,因为每当我们使用文本创建JavaScript对象时({ ... }),它是基于对象.原型,这意味着它将作为名为__原型__,所以如果我们像在前面的代码片段中那样更改它,所有JavaScript对象都会更改,这不是一个好的实践。那么,现在有什么更好的做法呢

人__原型={人员:人员,获取名称:获取名称};

现在其他物体都安然无恙了,但这似乎并不是一个好习惯。所以我们还有一个解决方案,但要使用这个解决方案,我们应该回到代码行,其中已创建对象(var人={};)然后将其更改为:

var属性对象={人员:人员,获取名称:获取名称};var person=Object.create(propertiesObject);

它所做的是创建一个新的JavaScript对象并将属性对象__原型__属性。因此,为了确保您可以:

console.log(person.__proto__===属性对象)//真的

但这里需要注意的是,您可以访问中定义的所有属性__原型__在第一层对象(有关更多详细信息,请阅读摘要部分)。


正如您看到的,使用这两种方法中的任何一种正好指向对象。

c.JavaScript有另一种提供函数的方法,正在使用呼叫应用调用函数。

apply()方法调用具有给定此值的函数,并且作为数组(或类似数组的对象)提供的参数。

call()方法使用给定的此值调用函数,并且单独提供的参数。

这种方法是我最喜欢的,我们可以很容易地调用我们的函数,例如:

个人电话(个人,“George”);

//当参数计数不固定时,apply更有用个人申请(个人,[“乔治”]);getName.call(个人);getName.apply(个人);

这三种方法是计算原型功能的重要初始步骤。


2-如何新的关键词工作?

这是理解.原型功能。这是我用来模拟流程的工具:

函数Person(name){this.name=name;}my_person_prototype={getName:function(){console.log(this.name);}};

在这一部分中,我将尝试采取JavaScript所采取的所有步骤,而不使用新的关键字和原型,当您使用新的关键字。所以当我们这样做的时候新人(“乔治”),函数充当构造函数,JavaScript的功能如下:

a.首先,它生成一个空对象,基本上是一个空散列,如:

var newObject={};

b.JavaScript的下一步是贴上新创建对象的所有原型对象

我们有我的个人原型这里类似于prototype对象。

for(my_person_prototype中的var键){newObject[key]=my_person_protype[key];}

JavaScript实际上并不是以这种方式附加原型中定义的属性。实际方式与原型链概念有关。


a.和b.不用这两个步骤,您可以通过执行以下操作获得完全相同的结果:

var newObject=Object.create(my_person_prototype);//在这里您可以查看proto_属性console.log(newObject.__proto__===my_person_prototype)//真的//并检查您是否可以访问所需的属性console.log(newObject.getName的类型)//“功能”

现在我们可以打电话给获取名称在我们的我的个人原型:

newObject.getName();

c.然后将该对象提供给构造函数,

我们可以用我们的样品这样做:

Person.call(newObject,“George”);

Person.apply(newObject,[“George”]);

那么构造函数可以做它想做的任何事情,因为该构造函数的内部是刚刚创建的对象。

现在是模拟其他步骤之前的最终结果:对象{name:“George”}


总结:

基本上,当您使用新的关键字,您正在调用该函数,该函数充当构造函数,因此当您说:

新建FunctionName()

JavaScript在内部生成一个对象,一个空散列,然后将该对象交给构造函数,然后构造函数可以做它想做的任何事情,因为该构造函数的内部是刚刚创建的对象,当然,如果您没有在函数中使用return语句,或者如果您放置了返回未定义;在函数体的末尾。

因此,当JavaScript在对象上查找属性时,它做的第一件事就是在该对象上查找它。还有一个秘密财产[[原型]]我们通常都是这样的__原型__JavaScript接下来会看到这个属性。当它看到__原型__,就另一个JavaScript对象而言,它有自己的__原型__属性,它会不断向上移动,直到到达下一个__原型__为null。点是JavaScript中唯一的对象__原型__属性为null为对象.原型对象:

console.log(Object.prototype.__proto__===空)//真的

这就是JavaScript中继承的工作原理。

原型链

换言之,当你在一个函数上有一个原型属性并在其上调用一个new时,在JavaScript完成查看新创建的对象的属性后,它将查看函数的.原型而且这个对象可能有自己的内部原型。等等。

0
82

原型允许您创建类。如果你不使用原型然后它就变成了静态。

这里有一个简短的例子。

var obj=新对象();obj.test=函数(){alert('你好?');};

在上面的例子中,您有一个静态函数调用测试。此函数只能由obj.test访问,您可以将obj想象为一个类。

其中如下代码所示

函数obj(){}obj.prototype.test=function(){alert('Hello?');};var obj2=新obj();obj2.test();

obj已经成为一个类,现在可以进行实例化。obj的多个实例可以存在,并且它们都具有测试功能。

以上是我的理解。我正在把它变成一个社区维基,所以如果我错了,人们可以纠正我。

1
  • 14
    -1:原型是构造函数的属性,而不是实例,即您的代码是错误的!也许你指的是非标准房地产__原型__物体,但那是一个完全不同的野兽。。。 评论 2009年2月21日13:25
80

原型的七个Koans

西罗·桑经过深思熟虑后登上火狐山,他的头脑清醒而平静。

然而,他的手却坐立不安,抓起一把刷子,草草记下了下面的笔记。


0)两种不同的东西可以称为“原型”:

  • prototype属性,如对象.原型

  • 原型内部属性,表示为[[原型]] 在ES5中.

    可以通过ES5检索Object.getPrototypeOf().

    Firefox通过__原型__属性作为扩展。ES6现在提到一些可选要求__原型__.


1)这些概念的存在是为了回答这个问题:

当我这样做的时候对象属性,JS在哪里寻找.属性?

直观地说,经典继承应该会影响属性查找。


2)

  • __原型__用于点.特性查找,如中所示对象属性.
  • .原型用于直接查找,仅间接查找__原型__使用创建对象时新的.

查找顺序为:

  • 对象添加了属性obj.p=。。。Object.defineProperty(对象,…)
  • 的属性目标_原型__
  • 的属性目标_原型___原型__,依此类推
  • 如果有的话__原型__无效的,返回未定义.

这就是所谓的原型链.

你可以避免.使用查找对象hasOwnProperty('key')对象.getOwnPropertyNames(f)


3)有两种主要的设置方法目标_原型__:

  • 新的:

    var F=函数(){}var f=新f()

    然后新的已设置:

    f.__proto__===f.原型

    这个是在哪里.原型得到使用。

  • 对象.创建:

    f=Object.create(原型)

    套:

    f.__proto__===原型

4)代码:

var F=函数(i){this.i=i}var f=新f(1)

对应下图(部分编号省略了内容):

(函数)(F)(F)----->(1)|^||^|i||  |             | | |                                        |        ||  |             | | +-------------------------+              |        |||构造函数||||||  |             | +--------------+            |              |        ||  |             |                |            |              |        ||  |             |                |            |              |        ||[[原型]]|[[原型]]|Prototype|constructor|[[样机]]|  |             |                |            |              |        ||  |             |                |            |              |        ||  |             |                | +----------+              |        ||  |             |                | |                         |        ||  |             |                | | +-----------------------+        ||  |             |                | | |                                |v|v v|v|(功能原型)(F.prototype)||                                 |                                    ||                                 |                                    ||[[原型]]|[[原型]||                                 |                                    ||                                 |                                    || +-------------------------------+                                    || |                                                                    |v v v(对象原型)(数字原型)| | ^| | || | +---------------------------+| |                             || +--------------+              ||                |              ||                |              ||[[原型]]|constructor|Prototype|                |              ||                |              ||                | -------------+|                | |v v|(null)(对象)

此图显示了许多语言预定义的对象节点:

  • 无效的
  • 对象
  • 对象.原型
  • 功能
  • 功能.原型
  • 1
  • 编号.原型(可以在以下位置找到(1).__原型__,必须使用括号才能满足语法要求)

我们的两行代码只创建了以下新对象:

  • (f)
  • F类
  • F.原型

现在是的属性(f)因为当你这样做时:

var f=新f(1)

它评估F类具有价值观新的将返回,然后分配给(f).


5) .构造函数通常来自F.原型通过.查找:

f.constructor===f!f.hasOwnProperty(“建筑商”)Object.getPrototypeOf(f)===f.prototypeF.prototype.hasOwnProperty(“承包商”)F.原型结构===F.结构

当我们写作时f.结构,JavaScript执行.查找为:

  • (f)没有.构造函数
  • f.__proto__===f.原型.构造函数===F,所以接受它

结果f.constructor==f直觉上是正确的,因为F类用于构造(f)例如,集合字段,非常类似于经典OOP语言。


6)经典继承语法可以通过操纵原型链来实现。

ES6添加了延伸关键字,它们大多是之前可能的原型操作疯狂的语法糖。

C类{建造师(i){这个i=i}inc(){返回这个.i+1}}D类扩展了C类{建造师(i){超级(i)}inc2(){返回这个.i+2}}
//继承语法按预期工作。c=新c(1)c.增量()===2(新D(1)).inc()===2(新D(1)).inc2()===3
//“类”只是函数对象。C.constructor===函数C.__proto__===功能原型D.constructor===函数//D是通过链“间接”的函数。D.__proto__===CD.__协议____proto__===功能原型
//“extends”设置原型链,以便基类//查找将按预期工作var d=新d(1)d__proto__===d原型D.原型__原型__===C原型//这就是“d.inc”的实际功能。d.原型__proto__.inc===C.prototype.inc
//类变量//显然没有ES6语法:// http://stackoverflow.com/questions/22528967/es6-class-variable-alternativesC.C=1C.C===1//因为`D.__proto__===C`。D.c===1//没有什么能让它起作用。d.c==未定义

不包含所有预定义对象的简化图:

(c) ----->(1)|我|||[[原型]]||v _原型__(C) <--------------(D)(D)| |                |           || |                |           |||原型|原型|[[原型]]| |                |           || |                |           || |                | +---------+| |                | || |                | ||| v v|[[原型]](D.Prototype)-------->(inc2函数对象)|||inc2公司| |                ||||[[原型]]| |                || |                || | +--------------+| | || | ||v v|(C.prototype)-------->(包含函数对象)|公司v(v)功能.原型

让我们花点时间研究一下以下工作原理:

c=新c(1)c.增量()===2

第一行集合c.i公司1如“4)”所述。

在第二行,当我们这样做时:

c.增加()
  • 公司通过[[原型]]链条:c(c)->C类->C.原型->股份有限公司
  • 当我们将Javascript中的函数调用为X.Y(),JavaScript自动设置等于X(X)在内部Y()函数调用!

同样的逻辑也解释了d.inc公司直流电2.

这篇文章https://javascript.info/class#不-只是一种语法糖提到的进一步影响值得了解。如果没有关键字(TODO检查哪个):

0
70

阅读完这个帖子后,我对JavaScript Prototype Chain感到困惑,然后我找到了这些图表

http://iwiki.readthedocs.org/en/latest/javascript/js_core.html#继承 *函数对象的[[protytype]]*和原型属性

通过原型链显示JavaScript继承是一个清晰的图表

http://www.javascriptbank.com/javascript/article/javascript_Classical_Inheritance/

这一个包含一个带有代码的示例和几个漂亮的图表。

原型链最终回到Object.prototype。

原型链可以在技术上扩展,只要你想,每次都可以将子类的原型设置为父类的对象。

希望这也有助于您理解JavaScript原型链。

0
42

每个对象都有一个内部属性,[[原型]],将其链接到另一个对象:

对象[[原型]]→另一个对象

在传统的javascript中,链接对象是原型函数的属性:

对象[[原型]]→功能原型

某些环境会暴露[[原型]]作为__原型__:

anObject(对象)__proto__===另一个对象

您可以创建[[原型]]创建对象时链接。

//(1)对象创建:var object=object.create(另一个对象)//对象__proto__=另一个对象//(2)ES6对象初始值设定项:var对象={__proto_:另一个对象};//对象__proto__=另一个对象//(3)传统JavaScript:var对象=新aFunction;//对象__原型__=功能原型

因此,这些陈述是等价的:

var object=object.create(object.prototype);var对象={__proto__:object.prototype};//仅ES6var对象=新对象;

你实际上看不到链接目标(对象.原型)在中新的声明;相反,目标是由构造函数隐含的(对象).

记得:

  • 每个对象都有一个链接,[[原型]],有时暴露为__原型__.
  • 每个函数都有一个原型属性,最初保存一个空对象。
  • 使用创建的对象新的链接到原型它们的构造函数的属性。
  • 如果函数从未用作构造函数,则其原型财产将被闲置。
  • 如果不需要构造函数,请使用对象.创建而不是新的.
4
  • 1
    修订版5删除了一些有用的信息,包括Object.create()上的信息。请参阅第4次修订.
    – 帕莱茨
    评论 2015年7月26日20:21
  • @苍白我该加什么?
    – 山姆
    评论 2015年7月27日18:50
  • 2
    IMO至少与对象.create()文档,@sam.链接至__原型__对象.原型将是很好的增强。我喜欢你关于原型如何与构造函数和对象.create(),但它们可能是你想摆脱的冗长且不太相关的部分。
    – 帕莱茨
    评论 2015年7月29日9:55
  • 从所有的讨论中,我得到了什么(来自经典继承)如果我创建构造函数并尝试使用new操作符创建它的实例,我只会得到附加到proto对象的方法和属性,因此如果我们想继承,有必要将所有方法和属性附加到protoobject,对吗?
    – 黑鹰
    评论 2016年11月20日5:33
31

Javascript没有通常意义上的继承,但它有原型链。

原型链

如果在对象中找不到对象的成员,则会在原型链中查找它。链由其他对象组成。可以使用__原型__变量。每个对象都有一个,因为javascript中的类和实例没有区别。

向原型中添加函数/变量的优点是,它只能在内存中存在一次,而不是每个实例都存在。

它对继承也很有用,因为原型链可以由许多其他对象组成。

  • 1
    FF和Chrome支持原型但不是IE或Opera。
    – 一些
    评论 2009年2月21日12:44
  • Georg,请澄清一下noob——“javascript中的类和实例没有区别。”——你能详细说明一下吗?这是怎么工作的? 评论 2012年11月7日23:03
  • 从所有的讨论中,我得到了什么(来自经典继承)如果我创建构造函数并尝试使用新的运算符创建它的实例,我只会得到附加到proto对象的方法和属性,因此如果我们想继承,有必要将所有方法和属性附加到proto对象,对吗?
    – 黑鹰
    评论 2016年11月20日5:36
28

这篇文章很长。但我相信它会消除你的大部分疑问关于JavaScript继承的“原型”性质。甚至更多。请阅读完整的文章。

JavaScript基本上有两种数据类型

  • 非对象
  • 物体

非对象

以下是非对象数据类型

  • 一串
  • number(包括NaN和Infinity)
  • 布尔值(true、false)
  • 未定义

当您使用类型操作人员

类型 “字符串文字”(或包含字符串文字的变量)===“字符串”

类型 5(或任何数字文字或包含数字文字或NaN或Infynity) ===“数字”

类型 真的(或或包含以下内容的变量真的) ===“布尔值”

类型 未定义(或未定义的变量或包含未定义) ==='未定义'

这个一串,布尔值数据类型可以表示为物体非对象。当它们被表示为对象时,它们的类型总是==='对象'。一旦我们了解了对象数据类型,我们将回到这一点上。

物体

对象数据类型可以进一步分为两种类型

  1. 函数类型对象
  2. 非函数类型对象

这个函数类型对象是那些返回字符串的'功能'具有类型操作员。所有用户定义的函数和所有可以使用new操作符创建新对象的JavaScript内置对象都属于这一类。例如。

  • 对象
  • 字符串
  • 编号
  • 布尔值
  • 阵列
  • 类型化数组
  • 注册Exp
  • 功能
  • 可以使用new运算符创建新对象的所有其他内置对象
  • 功能 用户定义的函数(){/*用户定义代码*/}

所以,typeof(对象)===typeof(字符串)===类型(数字)===typeof(布尔)===typeof(数组)===类型(RegExp)===typeof(函数)===typeof(用户定义函数)==='功能'

所有的函数类型对象实际上是内置JavaScript对象的实例功能(包括功能对象,即它是递归定义的)。就好像这些对象是按以下方式定义的

var Object=新函数([native code for Object Object])var String=新函数([native code for object String])var Number=新函数([native code for object Number])var Boolean=新函数([native code for object Boolean])var数组=新函数([对象数组的本机代码])var RegExp=新函数([对象RegExp的本机代码])var函数=新函数([native code for object Function])var UserDefinedFunction=新函数(“用户定义代码”)

如前所述函数类型对象可以使用新操作员.例如,类型为的对象对象,字符串,编号,布尔值,阵列,注册Exp或者用户定义的函数可以使用创建

var a=new Object()或var a=Object(var a=new String()//创建String类型的对象var a=new Number()//创建Number类型的对象var a=new Boolean()//创建布尔类型的对象var a=new Array()或var a=Array(var a=new RegExp()或var a=RegExp)//创建RegExp类型的对象var a=新的UserDefinedFunction()

这样创建的对象都是非函数类型对象并返回他们的类型==='对象'。在所有这些情况下,对象“a”都无法进一步创建对象使用运算符new。所以以下是错误的

var b=新a()//错误。a不是typeof===“函数”

内置对象数学类型==='对象'。因此,new运算符无法创建Math类型的新对象。

var b=new Math()//错误。数学不是typeof===“函数”

还请注意对象,阵列注册Exp函数甚至可以不使用操作员新。然而,以下这些选项没有。

var a=String()//创建新的非对象字符串。返回类型==='String'var a=Number()//创建新的非对象编号。返回类型===“数字”var a=Boolean()//创建一个新的非对象布尔值。返回一个类型==='boolean'

用户定义的函数是特殊情况。

var a=UserDefinedFunction()//可以或不可以根据定义方式创建UserDefined函数()类型的对象。

自从函数类型对象可以创建新对象建造师.

构造函数/函数(无论是内置的还是用户定义的)在自动定义时具有一个名为“原型”其值默认设置为对象。此对象本身具有一个名为“构造函数”默认情况下引用构造函数/函数.

例如,当我们定义函数时

函数UserDefinedFunction(){}

以下情况会自动发生

UserDefinedFunction.prototype={constructor:UserDefined_Function}

这个“原型”属性仅存在于函数类型对象(而且从未进入非函数类型对象).

这是因为创建新对象时(使用new操作符),它继承Constructor函数当前原型对象的所有属性和方法,即 内部参考 在新创建的对象中创建,该对象引用Constructor函数的当前原型对象引用的对象。

这个“内部参考”在对象中创建用于引用继承属性的称为对象的原型(它引用构造函数引用的对象“原型”属性,但与之不同)。对于任何对象(函数或非函数),可以使用Object.getPrototypeOf()方法。使用此方法可以跟踪对象的原型链。

也,创建的每个对象(函数类型非函数类型)有一个“构造函数”属性,该属性继承自Constructor函数的原型属性所引用的对象。默认情况下“构造函数”属性引用构造函数函数创造了它(如果构造函数函数默认的“原型”不变)。

对于所有人函数类型对象构造函数总是函数function(){}

对于非函数类型对象(例如Javascript内置数学对象)构造函数是创建它的函数。对于数学对象是函数Object(){}.

在没有任何支持代码的情况下,要理解上面解释的所有概念可能有点困难。请逐行阅读下面的代码以理解这个概念。尝试执行它,以便更好地理解。

函数UserDefinedFunction(){ } /*创建上述函数会自动执行前面提到的以下操作UserDefinedFunction.prototype={constructor:UserDefined_Function}*/var newObj_1=新用户定义函数()alert(Object.getPrototypeOf(newObj_1)===UserDefinedFunction.protype)//显示truealert(newObj_1.constructor)//显示函数UserDefinedFunction//在UserDefinedFunction.prototype对象中创建新属性用户定义功能。原型。TestProperty=“测试”alert(newObj_1.TestProperty)//显示“test”alert(Object.getPrototypeOf(newObj_1))。TestProperty)//显示“test”//创建新对象var对象A={property1:“property1”,构造函数:数组}//将新对象分配给UserDefinedFunction.prototypeUserDefinedFunction.prototype=objAalert(Object.getPrototypeOf(newObj_1)===UserDefinedFunction.prototype)//显示false。UserDefinedFunction.protype引用的对象已更改//内部引用不变alert(newObj_1.constructor)//这仍将显示函数UserDefinedFunctionalert(newObj_1.TestProperty)//这仍将显示“test”alert(Object.getPrototypeOf(newObj_1))。TestProperty)//这仍将显示“test”//创建另一个UserDefinedFunction类型的对象var newObj_2=新的UserDefinedFunction();alert(Object.getPrototypeOf(newObj_2)===objA)//显示true。alert(newObj_2.constructor)//显示函数Array()alert(newObj_2.property1)//显示“property1”alert(Object.getPrototypeOf(newObj_2).property1)//显示“property1”//在objA中创建新属性objA.property2=“属性2”alert(objA.property2)//显示“property2”alert(UserDefinedFunction.protype.property2)//显示“property2”alert(newObj_2.property2)//显示属性2alert(Object.getPrototypeOf(newObj_2).property2)//显示“property2”

每个对象的原型链最终都可以追溯到object.prototype(它本身没有任何原型对象)。以下代码可用于跟踪对象的原型链

var o=启动对象;做{警报(o+“\n”+Object.getOwnPropertyNames(o))}while(o=Object.getPrototypeOf(o))

各种对象的原型链如下所示。

  • 每个函数对象(包括内置函数对象)->功能原型->对象原型->空
  • 简单对象(由new Object()或{}创建,包括内置的Math对象)->Object.prototype->null
  • 使用new或Object.create创建的对象->一个或多个原型链->Object.prototype->null

要创建没有任何原型的对象,请使用以下命令:

var o=Object.create(空)alert(Object.getPrototypeOf(o))//显示null

人们可能会认为,将Constructor的prototype属性设置为null将创建一个具有null原型的对象。然而,在这种情况下,新创建的对象的原型被设置为object.prototype,其构造函数被设置为函数object。下面的代码演示了这一点

函数UserDefinedFunction(){}UserDefinedFunction.prototype=null//可以设置为任何非对象值(数字、字符串、未定义等)var o=新的UserDefinedFunction()alert(Object.getPrototypeOf(o)==Object.prototype)//显示truealert(o.constructor)//显示函数对象

本文摘要如下

  • 有两种类型的对象函数类型非函数类型
  • 仅限函数类型对象可以使用操作员新。由此创建的对象为非函数类型物体。这个非函数类型对象无法使用进一步创建对象操作员新.

  • 全部函数类型对象默认情况下具有“原型”属性。这个“原型”属性引用的对象具有“构造函数”默认情况下引用函数类型对象自身。

  • 所有对象(函数类型非函数类型)具有一个“构造函数”属性,该属性默认情况下引用函数类型对象/施工单位创造了它。

  • 每个内部创建的对象都引用“原型”创建它的构造函数的属性。此对象称为创建的对象的原型(这与它引用的Function类型对象“prototype”属性不同)。这样,创建的对象可以直接访问构造函数的“原型”属性(在创建对象时)引用的对象中定义的方法和属性。

  • 对象的原型可以使用Object.getPrototypeOf()方法。事实上,这种方法可用于导航对象的整个原型链。

  • 每个对象的原型链最终追溯到object.prototype(除非对象是使用object.create(null)创建的,在这种情况下,对象没有原型)。

  • typeof(new Array())===“对象”是通过语言设计的,而不是道格拉斯·克罗克福德

  • 将Constructor的prototype属性设置为null(或undefined、number、true、false、string)不会创建具有null原型的对象。在这种情况下,新创建的对象的原型被设置为object.prototype,其构造函数被设置为函数object。

希望这能有所帮助。

0
27

将原型链分为两类可能会有所帮助。

考虑构造函数:

函数Person(){}

的价值Object.getPrototypeOf(个人)是一个函数。事实上,确实如此功能.原型.自作为函数创建,它共享所有函数都具有的相同原型函数对象。它与人员__原型__,但不应使用该属性。无论如何,与Object.getPrototypeOf(个人)你有效地走上了所谓的原型链的阶梯。

向上的链条如下所示:

    功能.原型对象.原型(终点)

重要的是,这个原型链与可以建造。这些构造的对象有自己的原型链,而该链可能与上述对象没有共同的近亲。

以这个对象为例:

var p=新角色();

第页与没有直接的原型关联他们的关系不同。对象第页有自己的原型链。使用对象.get原型,您会发现链如下所示:

    第页人员.原型对象.原型(终点)

此链中没有函数对象(尽管可能是)。

所以似乎与两种链条有关,它们各自生活。要从一条链条“跳”到另一条链条,可以使用:

  1. .原型:从构造函数的链跳到created-object的链。因此,此属性仅为函数对象定义(作为新的只能用于函数)。

  2. .构造函数:从created-object的链跳到构造函数的链。

以下是所涉及的两个原型链的可视化演示,以列表示:

在此处输入图像描述

总结如下:

这个原型属性不提供主题的原型链,但属于对象由创建主题。

毫不奇怪,物业的名称原型可能会导致混淆。如果这个财产被命名的话,可能会更清楚构造实例的原型或者类似的东西。

您可以在两个原型链之间来回跳跃:

Person.prototype.constructor===个人

通过将不同对象显式指定给原型属性(稍后详细介绍)。

创建一个函数,获取两个对象

人员.原型是与函数同时创建的对象已创建。它有作为构造函数,即使该构造函数实际上还没有执行。因此,同时创建两个对象:

  1. 功能它本身
  2. 将函数作为构造函数调用时用作原型的对象

两者都是对象,但它们具有不同的角色:函数对象构件,而另一个对象表示函数将构造的任何对象的原型。原型对象将成为其原型链中构造对象的父对象。

因为函数也是一个对象,所以它在自己的原型链中也有自己的父级,但回想一下,这两个链是关于不同的事情的。

以下是一些有助于把握问题的等式——所有这些印刷品真的:

函数Person(){};//这是构造函数(函数对象)的原型链信息:console.log(Object.getPrototypeOf(Person)===函数原型);//在同一层次结构中更上一步:console.log(Object.getPrototypeOf(Function.prototype)===对象原型);console.log(Object.getPrototypeOf(Object.prototype)===null);console.log(Person.__proto__===函数原型);//这里我们交换车道,并查看构造函数的构造函数console.log(Person.constructor===函数);console.log(函数的个人实例);//Person.prototype是由Person创建的(在创建时)//在这里,我们来回交换车道:console.log(Person.prototype.constructor===个人);//尽管它不是它的一个实例:console.log(!(Person.prototype-instanceof Person));//实例是由构造函数创建的对象:var p=新角色();//与为构造函数显示的内容类似,这里我们有//由构造函数创建的对象也是如此:console.log(Object.getPrototypeOf(p)===个人原型);console.log(p.__proto__===个人原型);//这里我们交换车道,并查看构造函数console.log(p.constructor===个人);console.log(p instanceof Person);

向原型链添加标高

尽管原型对象是在创建构造函数时创建的,但您可以忽略该对象,并为该构造函数创建的任何后续实例指定另一个应用作原型的对象。

例如:

函数Thief(){}var p=新角色();Thief原型=p;//这决定了任何新的Thief对象的原型:var t=新盗贼();

现在的原型链比…长一步第页:

    第页人员.原型对象.原型(终点)

另一个原型链并不更长:小偷兄弟姐妹是否在其原型链中共享相同的父项:

    }
    小偷  } →功能.原型对象.原型(终点)

然后,可以将先前显示的图形扩展到此(原始图形硫.原型省略):

在此处输入图像描述

蓝色线条代表原型链,其他彩色线条代表其他关系:

  • 在对象及其构造函数之间
  • 在构造函数和将用于构造对象的原型对象之间
25

概念原型继承对于许多开发人员来说是最复杂的问题之一。让我们试着去理解问题的根源原型继承更好。让我们从一个平原功能。

在此处输入图像描述

如果我们使用新的上的运算符Tree函数,我们称之为建造师功能。

在此处输入图像描述

JavaScript脚本函数具有原型。当您记录树原型,你得到。。。

在此处输入图像描述

如果你看上面控制台.log()输出时,您可以看到上的构造函数属性树原型和a__原型__属性。这个__原型__代表原型这个功能是基于,因为这只是一个普通的JavaScript函数没有继承它指的是对象原型这是JavaScript中内置的东西。。。

https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/prototype

这有如下情况.toString、.toValue、.hasOwnProperty等。。。

__原型__我的mozilla被弃用了,取而代之的是对象.get原型方法来获取对象的原型.

在此处输入图像描述

Object.getPrototypeOf(Tree.protype);//对象{}

让我们为我们的 原型.

在此处输入图像描述

我们已经修改了并添加了功能分支到它。

在此处输入图像描述

这意味着当您创建实例属于,你可以称之为分支方法。

在此处输入图像描述

我们还可以添加基本体物体到我们的原型.

在此处输入图像描述

让我们添加一个儿童树到我们的.

在此处输入图像描述

这里是儿童继承其原型来自Tree,我们在这里所做的是使用对象.create()方法根据传递的内容创建新对象,如下所示树原型。在本例中,我们所做的是将Child的原型设置为与原型。接下来,我们将设置Child到Child的构造函数,如果我们不这样做,它将指向树().

在此处输入图像描述

儿童现在有了自己的原型,其__原型__指向树的原型指向底座的点对象.

儿童|\\树原型-分支||\\对象.原型-toString(字符串)-的值-等。,等。

现在您可以创建一个实例属于儿童然后打电话分支最初在中提供。我们还没有真正定义我们的分支子原型但是,在根原型子项从中继承。

在此处输入图像描述

在JS中,一切都不是对象,一切都可以像对象一样运行。

Java脚本具有如下基本体字符串,数字,布尔值,未定义,null。他们不是对象(即引用类型),但当然可以像对象。让我们看一个例子。

在此处输入图像描述

在清单的第一行中原始的字符串值被分配给名称。第二行将名称视为对象和电话字符位于(0)使用点表示法。

这就是幕后发生的事情://什么JavaScript脚本发动机有

在此处输入图像描述

这个String对象在销毁之前只存在一条语句(一个名为自动包装). 让我们再次回到我们的原型 继承.

  • Java脚本支持通过继承代表团基于原型.
  • 每个功能有一个原型属性,它引用另一个对象。
  • 属性/功能对象自身或通过原型链(如果不存在)

A类原型在JS中是一个对象产量你是另一个孩子的父母对象.[即授权] 授权意思是如果你不能做某事,你会告诉别人帮你做。

在此处输入图像描述

https://jsfiddle.net/say0tzpL/1/

如果你查一下上面的提琴,狗就可以toString(字符串)方法,但它在其中不可用,但可以通过委托给的原型链使用对象.原型

在此处输入图像描述

如果您查看以下内容,我们正在尝试访问呼叫方法,每个功能.

在此处输入图像描述

https://jsfiddle.net/rknffckc/

如果你查一下上面的小提琴,简况功能可以访问呼叫方法,但它在其中不可用,但可以通过委托给的原型链使用功能.原型

在此处输入图像描述

注: 原型是函数构造函数的属性,而__原型__是从函数构造函数构造的对象的属性。每个函数都带有原型值为空的属性对象。当我们创建函数的实例时,我们得到一个内部属性[[原型]]__原型__其引用是函数的原型建造师.

在此处输入图像描述

上面的图表看起来有点复杂,但展示了如何原型链接作品。让我们慢慢来:

有两个实例b1号机组b2型,其构造函数是酒吧父级为Foo,具有来自原型链的两个方法识别说话通过酒吧

在此处输入图像描述

https://jsfiddle.net/kbp7jr7n网址/

如果你查一下上面的代码,我们有拥有方法的构造函数标识()酒吧构造函数具有说话方法。我们创造了两个酒吧实例b1号机组b2型其父类型为。正在呼叫说话方法酒吧,我们能够通过识别呼叫通话的人原型链条。

在此处输入图像描述

酒吧现在有了所有的方法其定义见原型。让我们进一步深入了解对象.原型功能.原型以及它们之间的关系。如果您查找的构造函数,酒吧对象函数构造函数.

在此处输入图像描述

这个原型属于酒吧,原型属于对象如果你仔细看原型属于与相关对象.原型.

在此处输入图像描述

在我们结束之前,让我们在这里用一小段代码来结束总结以上内容。我们正在使用运算符运算符来检查对象在其中有原型原型的属性建造师下面总结了整个大图。

在此处输入图像描述

我希望这是一些信息,我知道这可能是一个大的把握。。。简单地说就是它只是链接到对象的对象!!!!

1
  • Child现在有了自己的原型,它的__proto__指向Tree-似乎错了。__原型__指向功能.原型而不是为了. 评论 2020年8月26日17:51
22

这个“原型”属性的确切目的是什么?

标准类的接口变得可扩展。例如,您正在使用阵列类,还需要为所有数组对象添加自定义序列化程序。你会花时间编码一个子类,还是使用composition或。。。prototype属性通过让用户控制类可用的确切成员/方法集来解决这个问题。

把原型想象成一个额外的vtable-pointer。当原始类中缺少一些成员时,将在运行时查找原型。

20

面向对象JavaScript最终指南-对所提问题进行了30分钟的视频解释(原型继承主题从以下开始5:45,尽管我宁愿听整个视频)。该视频的作者还制作了JavaScript对象可视化程序网站http://www.objectplayground.com/.在此处输入图像描述 在此处输入图像描述

1
  • 1
    很棒的视频参考
    – 卢克
    评论 2016年8月10日8:55
15

对象_属性_ X正在引用:

如果对象_n.prop_X不存在,请检查obj_n+1.prop_X哪里obj_n+1=对象_n.[[原型]]

如果属性X(_X)最终在第k个原型对象中找到

obj_1.prop_X=obj_1.[[原型]]。[[原型]]。。(k次)。。[[原型]].prop_X

您可以在此处通过Javascript对象的属性找到它们之间的关系图:

js对象图

http://jsobjects.org

1
  • 但图中这些圆圈的布局毫无意义。 评论 5月17日4:10
14

当构造函数创建对象时,该对象隐式引用构造函数的“原型”属性以解析属性引用。构造函数的“prototype”属性可以由程序表达式constructor.prototype引用,添加到对象原型中的属性通过继承由共享原型的所有对象共享。

0
12

这里有两个不同但相关的实体需要解释:

  • 这个.原型函数的属性。
  • [[原型]][1]所有对象的内部插槽[2].

这是两件不同的事情。

[[Prototype]]内部插槽:

这是一个内部插槽,存在于所有[2]物体。

这里存储的是另一个对象,作为一个对象本身,它有一个自己的[[原型]]指向另一对象。另一个对象有自己的[[原型]]。这个故事一直持续到您到达提供可在所有对象上访问的方法的原型对象(例如.to字符串).

[[原型]]槽是原型链的组成部分。例如,在对象上执行[[Get]]或[[Set]]操作时,会检查[[Prototype]]对象链:

变量对象={}obj.a//[[Get]]参考原型链obj.b=20///[[Set]]参考原型链

这个.原型属性:

这是一个仅在函数中找到的属性。使用一个非常简单的函数:

函数Bar(){};

这个.原型财产持有一个对象将分配给b当你这样做的时候var b=新棒。您可以轻松检查:

函数Bar(){}//两者都将Bar.prototype指定为b1/b2[[原型]]var b=新Bar;//Object.getPrototypeOf抓取对象[[原型]]console.log(Object.getPrototypeOf(b)===Bar.prototype)//true

其中最重要的.原型s是那个对象功能。此原型包含所有原型链包含的原型对象。在此基础上,定义了新对象的所有可用方法:

//获取在此对象上定义的属性console.log(Object.getOwnPropertyDescriptors(Object.prototype))

现在,因为.原型是一个对象,它有一个[[原型]]内部插槽。当你不给功能.原型,的.原型的[[原型]]指向原型对象(对象.原型). 这是在您创建新功能时自动执行的。

这样,任何时候都可以新酒吧;原型链已经为您设置好了,您可以在上面定义所有内容条形图原型以及定义在对象.原型:

函数Bar(){}var b=新Bar;//获取所有Bar.protype属性控制台日志(Object.getPrototypeOf(b)===Bar.prototype);//获取所有Object.protype属性控制台日志(Object.getPrototypeOf(Object.getPrototype Of(b))===对象原型);

当你分配给功能.原型您所做的只是扩展原型链以包含另一个对象。这就像在一个单链表中插入一样。

这基本上改变了原型链,允许在指定给的对象上定义属性功能.原型被函数创建的任何对象看到。


[1]这不会让任何人感到困惑;通过提供这个__原型__财产在许多实现中。

[2]无效的.

10

让我告诉你我对原型的理解。我不打算将这里的继承与其他语言进行比较。我希望人们停止比较语言,只理解语言本身。理解原型和原型继承非常简单,我将在下面向您展示。

原型就像一个模型,您可以根据它创建产品。要理解的关键点是,当您使用另一个对象作为原型创建对象时,原型和产品之间的联系是永恒的。例如:

var模型={x:2};var product=Object.create(model);模型y=5;产品.y=>5

每个对象都包含一个名为[[prototype]]的内部属性Object.getPrototypeOf()功能。Object.create(模型)创建一个新对象并将其[[prototype]]属性设置为该对象模型因此,当你这样做时Object.getPrototypeOf(产品),您将获得对象模型.

中的属性产品按以下方式处理:

  • 当访问属性以读取其值时,它会在范围链中进行查找。搜索变量从产品直到它的原型。如果在搜索中找到这样的变量,搜索将立即停止,并返回值。如果在范围链中找不到这样的变量,则返回undefined。
  • 写入(更改)属性时,该属性始终写入产品对象。如果产品已经没有这样的属性,它是隐式创建和写入的。

使用prototype属性的这种对象链接称为prototypeal继承。好了,很简单,同意吗?

6
  • 不总是写在分配的产品上。您没有明确表示必须初始化特定于实例的成员,共享成员可以在原型上运行。特别是当您有实例特定的可变成员时:stackoverflow.com/questions/16063394/…
    – HMR公司
    评论 2014年11月9日23:43
  • HMR:在你回答的例子中,ben.food.push(“汉堡”);行更改了原型对象的属性,原因如下:1.)首先查找ben.food,任何查找操作都只会查找范围链。2.)执行ben.food对象的push函数。在我的回答中,我指的是当你显式地为它设置一个值时,比如:ben.food=['Idly'];这将始终在产品对象上创建一个新属性(如果还没有),然后为其赋值。
    – 阿拉文
    评论 2014年11月10日3:57
  • HMR:谢谢你的评论,这让我思考并测试了我的理解力。
    – 阿拉文
    评论 2014年11月10日4:37
  • 当重新分配ben.food时,它将对food成员进行阴影处理,除非使用Object.defineProperty、Object.define Properties或Object.create和第二个参数创建了food(因此并不总是这样)。甚至可以在创建getter-setter时通过重新赋值来更改原型。当谈到继承模式时,我知道构造函数很难理解,并且有一些主要问题,但如果你能理解它,那就好了。JavaScript中的继承并不是以设置原型开始和结束的,初始化(构造函数)也要(重新)使用。
    – HMR公司
    评论 2014年11月10日10:11
  • 您的答案很好地解释了原型,但可能会因过度简化JavaScript和实例特定成员中的继承而被误解。人们提出了很多问题,为什么在实例上更改原型成员会影响其他实例。
    – HMR公司
    评论 2014年11月10日10:14
10

考虑以下内容keyValueStore(密钥值存储)对象:

var keyValueStore=(函数(){var计数=0;var kvs=函数(){计数++;this.data={};this.get=函数(键){return this.data[key];};this.set=函数(键,值){this.data[key]=值;};this.delete=函数(键){delete this.data[key];};this.getLength=函数(){var l=0;对于(本数据中的p)l++;返回l;}};return{//Singleton公共属性“创建”:function(){return new kvs();},“count”:function(){返回count;}};})();

通过执行以下操作,我可以创建此对象的新实例:

kvs=keyValueStore.create();

此对象的每个实例都具有以下公共属性:

  • 数据
  • 得到
  • 设置
  • 删除
  • 获取长度

现在,假设我们创建100个这样的实例keyValueStore(密钥值存储)对象。尽管得到,设置,删除,获取长度将对这100个实例中的每个实例执行完全相同的操作,每个实例都有自己的函数副本。

现在,想象一下如果你能有一个单身得到,设置,删除获取长度复制,每个实例都会引用相同的函数。这将有助于提高性能,并且需要更少的内存。

原型就是这样产生的。原型是继承但不被实例复制的属性的“蓝图”。因此,这意味着对于对象的所有实例,它在内存中只存在一次,并且由所有这些实例共享。

现在,考虑一下keyValueStore(密钥值存储)对象。我可以这样改写:

var keyValueStore=(函数(){var计数=0;var kvs=函数(){计数++;this.data={};};kvs.原型={“get”:函数(键){return this.data[key];},“set”:函数(key,value){this.data[key]=value;},“delete”:函数(键){delete this.data[key];},“getLength”:函数(){var l=0;对于(this.data中的p)l++;返回l;}};返回{“创建”:function(){return new kvs();},“count”:function(){返回count;}};})();

这与以前版本的keyValueStore(密钥值存储)对象,除了它的所有方法现在都放在一个原型中。这意味着,所有100个实例现在都共享这四种方法,而不是每个实例都有自己的副本。

9

再次尝试解释基于JavaScript原型的继承有更好的图片

简单对象继承

9

总结:

  • 函数是javascript中的对象,因此可以具有属性
  • (构造函数)函数总是具有原型属性
  • 当函数用作构造函数时新的关键字对象获取原型。可在__原型__新创建对象的属性。
  • 这个__原型__属性是指原型构造函数的属性。

例子:

职能人员(姓名){this.name=名称;}让我=新人('willem');console.log(Person.prototype)//个人有一个prototype属性log(Person.prototype===me.__proto__)//实例的__proto属性是指函数的prototype属性。

为什么这很有用:

Javascript有一种在对象上查找属性的机制,称为'原型继承',以下是它的基本功能:

  • 首先检查属性是否位于对象本身上。如果是这样,则返回此属性。
  • 如果属性不位于对象本身上,它将“向上攀登到原链”。它主要查看由原型属性。在那里,它检查属性在所引用的对象上是否可用原型
  • 如果属性不位于原型它将爬上原型一直链接到Object对象。
  • 如果它在对象及其原型链上找不到属性,它将返回未定义的。

例如:

职能人员(姓名){this.name=名称;}让mySelf=新人('Willem');console.log(mySelf.__proto__===个人原型);console.log(mySelf.__proto__.__prodot__===对象.prototype);

更新:

这个__原型__属性已被弃用,尽管它在大多数现代浏览器中实现,但获取原型对象引用的更好方法是:

Object.getPrototypeOf()

6

我总是喜欢用类比来理解这类东西。”在我看来,“原型继承”与类bass继承相比相当令人困惑,尽管原型是更简单的范式。事实上,对于原型来说,实际上没有继承,因此名称本身就有误导性,它更多的是一种“委托”。

想象一下。。。。

你在上高中,你在上课,有一个今天到期的测验,但你没有笔来填写你的答案。多!

你坐在你的朋友芬纽斯旁边,他可能有一支笔。你问他,他环顾了一下办公桌,但没有说“我没有钢笔”,而是一个好朋友,他和他的另一个朋友德普核实他是否有钢笔。Derp确实有一支备用钢笔,并将其交还给Finnius,Finnius将其交给您完成测试。Derp已将笔委托给Finnius,Finnius已将笔授权给您使用。

这里重要的是,德普不会把笔给你,因为你没有直接的关系和他在一起。

这是一个原型工作原理的简化示例,其中搜索数据树以查找您要查找的内容。

2

另一个方案显示__原型__,原型建造师关系:在此处输入图像描述

1
  • 如果您为每个功能. 评论 5月17日4:07
1

只是你已经有了一个对象对象新建但在使用构造函数语法时仍然没有对象。

1

重要的是要理解对象的原型(可通过以下途径获得)之间的区别Object.getPrototypeOf(对象),或通过已弃用的__原型__属性)和原型构造函数的属性。前者是每个实例上的属性,后者是构造函数上的属性。那就是,Object.getPrototypeOf(new Foobar())引用与Foobar.原型.

参考:https://developer.mozilla.org/en-US/docs/Learn/JavaScript/Objects/Object_prototypes

0

如果您想从基础上理解原型和基于原型的继承的概念,请查看官方MDN公司医生们,他们解释得很好。

https://developer.mozilla.org/en-US/docs/Web/JavaScript/Inheritance_and_the_prototype_chain

在继承方面,JavaScript只有一个构造:物体。每个对象都有一个私有属性,该属性包含指向另一个对象称为原型。该原型对象具有它自己的原型,依此类推,直到用null到达对象作为其原型。根据定义,null没有原型,其作用是这个原型链中的最后一个环节。

此外,这里还有另一个很好的资源,可以通过简单的例子进行解释-https://developer.mozilla.org/en-US/docs/Learn/JavaScript/Objects/Object_prototypes

-1

这个原型创建新建对象通过克隆现有对象所以当我们考虑原型时,我们可以克隆或制作复制某物而不是编造它。

不是你想要的答案吗?浏览已标记的其他问题问你自己的问题.