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

var obj = new Object();
obj.prototype.test = function() { alert('Hello?'); };
var obj2 = new obj();
obj2.test();

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

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

更新:正确方式

var obj = new Object(); // not a functional object
obj.prototype.test = function() { alert('Hello?'); }; // this is wrong!

function MyObject() {} // a first class functional object
MyObject.prototype.test = function() { alert('OK'); } // OK

这些幻灯片也帮了大忙。


当前回答

看完这篇文章后,我对JavaScript原型链感到困惑,然后我找到了这些图表

http://iwiki.readthedocs.org/en/latest/javascript/js_core.html#inheritance

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

and

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

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

原型链最终返回到Object.prototype。原型链可以根据您的需要进行技术上的扩展,每次都将子类的原型设置为父类的对象。

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

其他回答

原型的七个Koan

当西罗·桑在深度冥想后下了火狐山时,他的头脑清晰而平静。

然而,他的手却坐立不安,自己抓起画笔,记下了下面的笔记。


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

prototype属性,如obj.prototype原型内部属性,在ES5中表示为[[prototype]]。它可以通过ES5 Object.getPrototypeOf()进行检索。Firefox可以通过__proto__属性作为扩展访问它。ES6现在提到了__proto__的一些可选要求。


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

当我执行obj.properties时,JS在哪里查找.properties?

直觉上,经典继承应该会影响属性查找。


2)

__proto__用于点。在obj.properties中查找属性。.prototype不直接用于查找,而是间接用于查找,因为它在使用new创建对象时确定__proto__。

查找顺序为:

obj财产添加了obj.p=。。。或Object.defineProperty(obj,…)obj.__proto的财产__对象proto_.proto__的财产,依此类推如果某些__proto__为空,则返回undefined。

这就是所谓的原型链。

你可以避免。使用obj.hasOwnProperty('key')和Object.getOwnPropertyNames(f)查找


3) 设置对象__proto__的主要方法有两种:

新增:var F=函数(){}var f=新f()则new已设置:f.__proto__==f原型这就是使用原型的地方。对象创建:f=对象.create(proto)集合:f.__proto__==原型


4) 代码:

var F = function(i) { this.i = i }
var f = new F(1)

对应于下图(省略了一些数字内容):

(Function)       (  F  )                                      (f)----->(1)
 |  ^             | | ^                                        |   i    |
 |  |             | | |                                        |        |
 |  |             | | +-------------------------+              |        |
 |  |constructor  | |                           |              |        |
 |  |             | +--------------+            |              |        |
 |  |             |                |            |              |        |
 |  |             |                |            |              |        |
 |[[Prototype]]   |[[Prototype]]   |prototype   |constructor   |[[Prototype]]
 |  |             |                |            |              |        |
 |  |             |                |            |              |        |
 |  |             |                | +----------+              |        |
 |  |             |                | |                         |        |
 |  |             |                | | +-----------------------+        |
 |  |             |                | | |                                |
 v  |             v                v | v                                |
(Function.prototype)              (F.prototype)                         |
 |                                 |                                    |
 |                                 |                                    |
 |[[Prototype]]                    |[[Prototype]]          [[Prototype]]|
 |                                 |                                    |
 |                                 |                                    |
 | +-------------------------------+                                    |
 | |                                                                    |
 v v                                                                    v
(Object.prototype)                                       (Number.prototype)
 | | ^
 | | |
 | | +---------------------------+
 | |                             |
 | +--------------+              |
 |                |              |
 |                |              |
 |[[Prototype]]   |constructor   |prototype
 |                |              |
 |                |              |
 |                | -------------+
 |                | |
 v                v |
(null)           (Object)

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

无效的对象对象.原型作用功能.原型1.Number.prototype(可以用(1)找到__proto__,括号必须满足语法)

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

fFF.原型

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

var f = new F(1)

它计算F,这是new将返回的值,然后将其分配给F。


5) 构造函数通常通过。查找:

f.constructor === F
!f.hasOwnProperty('constructor')
Object.getPrototypeOf(f) === F.prototype
F.prototype.hasOwnProperty('constructor')
F.prototype.constructor === f.constructor

当我们编写f.constructor时,JavaScript会执行。查找为:

f没有.构造函数f.__proto__==f.prototype具有构造函数==f,因此接受它

结果f.constructor==f直观上是正确的,因为f用于构造f,例如集合字段,与经典OOP语言中的情况非常相似。


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

ES6添加了类并扩展了关键字,这些关键字主要是语法糖,用于之前可能的原型操作疯狂。

class C {
    constructor(i) {
        this.i = i
    }
    inc() {
        return this.i + 1
    }
}

class D extends C {
    constructor(i) {
        super(i)
    }
    inc2() {
        return this.i + 2
    }
}
// Inheritance syntax works as expected.
c = new C(1)
c.inc() === 2
(new D(1)).inc() === 2
(new D(1)).inc2() === 3
// "Classes" are just function objects.
C.constructor === Function
C.__proto__ === Function.prototype
D.constructor === Function
// D is a function "indirectly" through the chain.
D.__proto__ === C
D.__proto__.__proto__ === Function.prototype
// "extends" sets up the prototype chain so that base class
// lookups will work as expected
var d = new D(1)
d.__proto__ === D.prototype
D.prototype.__proto__ === C.prototype
// This is what `d.inc` actually does.
d.__proto__.__proto__.inc === C.prototype.inc
// Class variables
// No ES6 syntax sugar apparently:
// http://stackoverflow.com/questions/22528967/es6-class-variable-alternatives
C.c = 1
C.c === 1
// Because `D.__proto__ === C`.
D.c === 1
// Nothing makes this work.
d.c === undefined

没有所有预定义对象的简化图:

(c)----->(1)
 |   i
 |
 |
 |[[Prototype]]
 |
 |
 v    __proto__
(C)<--------------(D)         (d)
| |                |           |
| |                |           |
| |prototype       |prototype  |[[Prototype]] 
| |                |           |
| |                |           |
| |                | +---------+
| |                | |
| |                | |
| |                v v
|[[Prototype]]    (D.prototype)--------> (inc2 function object)
| |                |             inc2
| |                |
| |                |[[Prototype]]
| |                |
| |                |
| | +--------------+
| | |
| | |
| v v
| (C.prototype)------->(inc function object)
|                inc
v
Function.prototype

让我们花一点时间研究一下以下功能是如何工作的:

c = new C(1)
c.inc() === 2

第一行将c.i设置为1,如“4)”所述。

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

c.inc()

.inc是通过[[原型]]链找到的:c->c->c.Prototype->inc当我们将Javascript中的函数调用为X.Y()时,Javascript会在Y()函数调用中将其自动设置为等于X!

同样的逻辑也解释了d.inc和d.inc2。

这篇文章https://javascript.info/class#not-只是一个语法糖提到了值得了解的类的进一步影响。如果没有class关键字(TODO check which),其中一些可能无法实现:

[[FunctionKind]]:“classConstructor”,强制使用new调用构造函数:ES6类构造函数不能作为普通函数调用的原因是什么?类方法是不可枚举的。可以使用Object.defineProperty完成。类总是使用strict。可以通过显式使用strict for each函数来完成,这无疑是乏味的。

看完这篇文章后,我对JavaScript原型链感到困惑,然后我找到了这些图表

http://iwiki.readthedocs.org/en/latest/javascript/js_core.html#inheritance

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

and

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

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

原型链最终返回到Object.prototype。原型链可以根据您的需要进行技术上的扩展,每次都将子类的原型设置为父类的对象。

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

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

原型链

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

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

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

如果您想从基础上理解原型和基于原型的继承的概念,请查看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

原型继承的概念对于许多开发人员来说是最复杂的概念之一。让我们试着理解问题的根源,以便更好地理解原型继承。让我们从一个简单的函数开始。

如果我们在Tree函数上使用一个新运算符,我们将其作为构造函数调用。

每个JavaScript函数都有一个原型。当你记录Tree.prototype时,你会。。。

如果查看上面的console.log()输出,您可以看到Tree.prototype上的构造函数属性和__proto__属性。__proto__表示这个函数所基于的原型,由于这只是一个普通的JavaScript函数,还没有设置继承,所以它引用的是Object原型,它是JavaScript中刚刚内置的东西。。。

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

这包括.toString、.toValue、.hasOwnProperty等。。。

__我的mozilla带来的proto__已被弃用,取而代之的是Object.getPrototypeOf方法来获取对象的原型。

Object.getPrototypeOf(Tree.prototype); // Object {} 

让我们向Tree原型添加一个方法。

我们修改了Root并向其添加了一个函数分支。

这意味着当您创建Tree实例时,可以调用它的分支方法。

我们还可以向原型中添加原语或对象。

让我们在树中添加一个子树。

在这里,Child从Tree继承了它的原型,我们在这里做的是使用Object.create()方法根据您传递的内容创建一个新对象,这里是Tree.prototype。在这种情况下,我们正在做的是将Child的原型设置为一个看起来与Tree原型相同的新对象。接下来,我们将Child的构造函数设置为Child,如果不设置,它将指向Tree()。

孩子现在有了自己的原型,它的__proto__指向Tree,Tree的原型指向base Object。

Child  
|
 \
  \
   Tree.prototype
   - branch
   |
   |
    \
     \
      Object.prototype
      -toString
      -valueOf
      -etc., etc.

现在您创建了一个最初在Tree中可用的Child和call分支的实例。我们还没有在Child原型上定义分支。但是,在Child继承的Root原型中。

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

Javascript有字符串、数字、布尔值、未定义、null等原语。它们不是对象(即引用类型),但肯定可以充当对象。让我们看一个例子。

在该列表的第一行中,为name指定了一个基本字符串值。第二行将名称视为对象,并使用点表示法调用charAt(0)。

这是幕后发生的事情://JavaScript引擎的作用

String对象在被销毁之前只存在一个语句(一个称为自动装箱的过程)。让我们再次回到我们的原型继承。

Javascript支持基于原型。每个函数都有一个原型属性,该属性引用另一个对象财产/函数是从对象本身或通过原型链(如果不存在)

JS中的原型是一个对象,它使您成为另一个对象的父对象。授权意味着如果你不能做某事,你会告诉别人帮你做。

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

如果您查找上面的fiddle,dog可以访问toString方法,但它在其中不可用,但可以通过委托给Object.prototype的原型链使用

如果您查看下面的一个,我们正在尝试访问每个函数中可用的调用方法。

https://jsfiddle.net/rknffckc/

如果您查找上面的fiddle,Profile Function可以访问调用方法,但它在其中不可用,但可以通过委托给Function.prototype的原型链使用

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

上图看起来有点复杂,但展示了原型链接工作原理的全貌。让我们慢慢来:

有两个实例b1和b2,其构造函数是Bar,父实例是Foo,并且有两个来自原型链的方法通过Bar和Foo识别和说话

https://jsfiddle.net/kbp7jr7n/

如果你查阅上面的代码,我们有Foo构造函数,它有方法identify(),Bar构造函数有speak方法。我们创建了两个Bar实例b1和b2,它们的父类型是Foo。现在,在调用Bar的speak方法时,我们能够通过原型链识别谁在调用speak。

Bar现在拥有Foo的所有方法,这些方法都在其原型中定义。让我们进一步了解Object.prototype和Function.prototype以及它们之间的关系。如果您查找Foo的构造函数,Bar和Object是Function构造函数。

Bar的原型是Foo,Foo的原型是Object,如果仔细观察,Foo原型与Object.prototype相关。

在结束之前,让我们在这里用一小段代码来总结上面的内容。我们在这里使用instanceof运算符来检查对象的原型链中是否包含构造函数的prototype属性,该属性在下面概括了整个大型图。

我希望这是一些信息,我知道这类信息可能很难掌握。。。简单地说,它只是链接到对象的对象!!!!