我不太喜欢动态编程语言,但我已经编写了相当多的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继承的“原型”性质。甚至更多。请阅读整篇文章。

JavaScript基本上有两种数据类型

非对象物体

非对象

以下是非对象数据类型

一串数字(包括NaN和Infinity)布尔值(true,false)未定义

使用typeof运算符时,这些数据类型返回以下内容

typeof“string literal”(或包含字符串文本的变量)==“string”

typeof 5(或任何数字文本或包含数字文本或NaN或Infynity的变量)==“number”

typeof true(或false或包含true或false的变量)==“boolean”

typeof undefined(或未定义变量或包含未定义的变量)==“undefineed”

字符串、数字和布尔数据类型既可以表示为对象,也可以表示为非对象。当它们表示为对象时,其类型总是==“object”。一旦我们了解了对象数据类型,我们将回到这一点。

物体

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

函数类型对象非函数类型对象

Function类型对象是返回带有typeof运算符的字符串“Function”的对象。所有用户定义的函数和所有可以使用new运算符创建新对象的JavaScript内置对象都属于这一类。例如。

对象一串数字布尔型大堆类型化数组RegExp(RegExp)作用所有其他可以使用new运算符创建新对象的内置对象函数UserDefinedFunction(){/*用户定义的代码*/}

所以typeof(对象)==typeof(字符串)==typeof(数字)==typeof(布尔)==typeof(数组)==type-of(RegExp)==typeoft(函数)==typesof(UserDefinedFunction)==“函数”

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

var Object= new Function ([native code for object Object])
var String= new Function ([native code for object String])
var Number= new Function ([native code for object Number])
var Boolean= new Function ([native code for object Boolean])
var Array= new Function ([native code for object Array])
var RegExp= new Function ([native code for object RegExp])
var Function= new Function ([native code  for object Function])
var UserDefinedFunction= new Function ("user defined code")

如上所述,Function类型对象可以使用new运算符进一步创建新对象。例如,object、String、Number、Boolean、Array、RegExp或UserDefinedFunction类型的对象可以使用

var a=new Object() or var a=Object() or var a={} //Create object of type Object
var a=new String() //Create object of type String
var a=new Number() //Create object of type Number
var a=new Boolean() //Create object of type Boolean
var a=new Array() or var a=Array() or var a=[]  //Create object of type Array
var a=new RegExp() or var a=RegExp() //Create object of type RegExp
var a=new UserDefinedFunction() 

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

var b=new a() //error. a is not typeof==='function'

内置对象Math的类型为==“object”。因此,新运算符不能创建Math类型的新对象。

var b=new Math() //error. Math is not typeof==='function'

还要注意,Object、Array和RegExp函数可以创建一个新对象,而无需使用运算符new。然而,以下这些不适用。

var a=String() // Create a new Non Object string. returns a typeof==='string' 
var a=Number() // Create a new Non Object Number. returns a typeof==='number'
var a=Boolean() //Create a new Non Object Boolean. returns a typeof==='boolean'

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

var a=UserDefinedFunction() //may or may not create an object of type UserDefinedFunction() based on how it is defined.

由于函数类型对象可以创建新对象,因此它们也被称为构造函数。

每个构造函数/函数(无论是内置的还是用户定义的)在定义时都会自动具有一个名为“prototype”的属性,其值默认设置为对象。此对象本身有一个名为“constructor”的属性,默认情况下该属性引用构造函数/函数。

例如,当我们定义一个函数

function UserDefinedFunction()
{
}

自动发生以下情况

UserDefinedFunction.prototype={constructor:UserDefinedFunction}

此“prototype”属性仅存在于Function类型对象中(并且从不在非函数类型对象中)。

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

在对象中创建的用于引用继承的财产的“内部引用”称为对象的原型(它引用由Constructor的“prototype”属性引用的对象,但与之不同)。对于任何对象(函数或非函数),都可以使用object.getPrototypeOf()方法进行检索。使用此方法可以跟踪对象的原型链。

此外,创建的每个对象(函数类型或非函数类型)都有一个“构造函数”属性,该属性继承自构造函数函数的原型属性引用的对象。默认情况下,此“构造函数”属性引用创建它的构造函数(如果构造函数的默认“原型”未更改)。

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

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

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

function UserDefinedFunction()
{ 

} 

/* creating the above function automatically does the following as mentioned earlier

UserDefinedFunction.prototype={constructor:UserDefinedFunction}

*/


var newObj_1=new UserDefinedFunction()

alert(Object.getPrototypeOf(newObj_1)===UserDefinedFunction.prototype)  //Displays true

alert(newObj_1.constructor) //Displays function UserDefinedFunction

//Create a new property in UserDefinedFunction.prototype object

UserDefinedFunction.prototype.TestProperty="test"

alert(newObj_1.TestProperty) //Displays "test"

alert(Object.getPrototypeOf(newObj_1).TestProperty)// Displays "test"

//Create a new Object

var objA = {
        property1 : "Property1",
        constructor:Array

}


//assign a new object to UserDefinedFunction.prototype
UserDefinedFunction.prototype=objA

alert(Object.getPrototypeOf(newObj_1)===UserDefinedFunction.prototype)  //Displays false. The object referenced by UserDefinedFunction.prototype has changed

//The internal reference does not change
alert(newObj_1.constructor) // This shall still Display function UserDefinedFunction

alert(newObj_1.TestProperty) //This shall still Display "test" 

alert(Object.getPrototypeOf(newObj_1).TestProperty) //This shall still Display "test"


//Create another object of type UserDefinedFunction
var newObj_2= new UserDefinedFunction();

alert(Object.getPrototypeOf(newObj_2)===objA) //Displays true.

alert(newObj_2.constructor) //Displays function Array()

alert(newObj_2.property1) //Displays "Property1"

alert(Object.getPrototypeOf(newObj_2).property1) //Displays "Property1"

//Create a new property in objA
objA.property2="property2"

alert(objA.property2) //Displays "Property2"

alert(UserDefinedFunction.prototype.property2) //Displays "Property2"

alert(newObj_2.property2) // Displays Property2

alert(Object.getPrototypeOf(newObj_2).property2) //Displays  "Property2"

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

var o=Starting object;

do {
    alert(o + "\n" + Object.getOwnPropertyNames(o))

}while(o=Object.getPrototypeOf(o))

各种对象的原型链工作如下。

每个Function对象(包括内置Function对象)->Function.prototype->Object.prototype->null简单对象(由新的Object()或{}创建,包括内置的Math对象)->Object.prototype->null使用新的或Object.create创建的对象->一个或多个原型链->Object.prototype->null

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

var o=Object.create(null)
alert(Object.getPrototypeOf(o)) //Displays null

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

function UserDefinedFunction(){}
UserDefinedFunction.prototype=null// Can be set to any non object value (number,string,undefined etc.)

var o=new UserDefinedFunction()
alert(Object.getPrototypeOf(o)==Object.prototype)   //Displays true
alert(o.constructor)    //Displays Function Object

本文摘要如下

有两种类型的对象功能类型和非功能类型只有函数类型的对象才能使用运算符new创建新对象。这样创建的对象是非函数类型的对象。非函数类型对象无法使用运算符new进一步创建对象。默认情况下,所有函数类型对象都具有“prototype”属性。此“prototype”属性引用了一个对象,该对象具有默认引用Function类型对象本身的“构造函数”属性。所有对象(函数类型和非函数类型)都有一个“构造函数”属性,默认情况下该属性引用创建它的函数类型对象/构造函数。内部创建的每个对象都引用创建该对象的构造函数的“prototype”属性。该对象称为创建对象的prototype(与它引用的函数类型对象“prototype“属性不同)。这样,创建的对象可以直接访问Constructor的“prototype”属性(在创建对象时)引用的对象中定义的方法和财产。可以使用object.getPrototypeOf()方法检索对象的原型(以及继承的属性名称)。事实上,这种方法可以用于导航对象的整个原型链。每个对象的原型链最终都会追溯到object.prototype(除非对象是使用object.create(null)创建的,在这种情况下,对象没有原型)。typeof(new Array())==“object”是由语言设计的,而不是Douglas Crockford指出的错误将构造函数的原型属性设置为null(或undefined、number、true、false、string)不会创建具有null原型的对象。在这种情况下,新创建的对象的原型设置为object.prototype,其构造函数设置为函数object。

希望这有帮助。

其他回答

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

如果我们在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属性,该属性在下面概括了整个大型图。

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

考虑以下keyValueStore对象:

var keyValueStore = (function() {
    var count = 0;
    var kvs = function() {
        count++;
        this.data = {};
        this.get = function(key) { return this.data[key]; };
        this.set = function(key, value) { this.data[key] = value; };
        this.delete = function(key) { delete this.data[key]; };
        this.getLength = function() {
            var l = 0;
            for (p in this.data) l++;
            return l;
        }
    };

    return  { // Singleton public properties
        'create' : function() { return new kvs(); },
        'count' : function() { return count; }
    };
})();

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

kvs = keyValueStore.create();

此对象的每个实例都将具有以下公共财产:

数据收到设置删去获取长度

现在,假设我们创建了这个keyValueStore对象的100个实例。尽管get、set、delete、getLength将对这100个实例中的每一个执行完全相同的操作,但每个实例都有自己的函数副本。

现在,想象一下,如果您只有一个get、set、delete和getLength副本,并且每个实例都引用相同的函数。这将更好地提高性能,并且需要更少的内存。

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

现在,再次考虑keyValueStore对象。我可以这样重写:

var keyValueStore = (function() {
    var count = 0;
    var kvs = function() {
        count++;
        this.data = {};
    };

    kvs.prototype = {
        'get' : function(key) { return this.data[key]; },
        'set' : function(key, value) { this.data[key] = value; },
        'delete' : function(key) { delete this.data[key]; },
        'getLength' : function() {
            var l = 0;
            for (p in this.data) l++;
            return l;
        }
    };

    return  {
        'create' : function() { return new kvs(); },
        'count' : function() { return count; }
    };
})();

这与keyValueStore对象的上一版本完全相同,只是它的所有方法现在都放在一个原型中。这意味着,所有100个实例现在都共享这四个方法,而不是每个实例都有自己的副本。

面向对象JavaScript的最终指南-一个非常简洁和清晰的30分钟视频解释(原型继承主题从5:45开始,尽管我更愿意听整个视频)。这段视频的作者还制作了JavaScript对象可视化工具网站http://www.objectplayground.com/.

重要的是要理解对象的原型(可通过object.getPrototypeOf(obj)或通过不推荐的__proto__属性获得)和构造函数的原型属性之间存在区别。前者是每个实例上的属性,后者是构造函数上的属性。也就是说,Object.getPrototypeOf(newFoobar())引用与Foobar.prototype相同的对象。

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

摘要:

函数是javascript中的对象,因此可以具有财产(构造函数)函数始终具有原型属性当函数用作带有new关键字的构造函数时,对象将获得原型。可以在新创建的对象的__proto__属性中找到对该原型的引用。此__proto__属性引用构造函数的原型属性。

例子:

职能人员(姓名){this.name=名称;}let me=新人('willem');console.log(Person.prototype)//Person具有prototype属性console.log(Person.prototype==me.__proto__)//实例的__proto__属性引用函数的原型属性。

为什么这很有用:

Javascript在查找Objects上的财产时有一种机制,称为“原型继承”,其基本功能如下:

如果属性位于Object本身,则选中First。如果是,则返回此属性。如果属性不位于对象本身,它将“爬上协议链”。它基本上看proto属性所指的对象。在那里,它检查proto引用的对象上的属性是否可用如果属性不位于原型对象上,它将沿着原型链一直爬到对象对象。如果在对象及其原型链上找不到属性,则返回undefined。

例如:

职能人员(姓名){this.name=名称;}let mySelf=新人('Wilem');console.log(mySelf.__proto__==Person.prototype);console.log(mySelf.__proto__.__proto__==对象.原型);

更新:

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

对象.getPrototypeOf()