首页 > JavaScript > JavaScript .prototype 是如何工作的?

JavaScript .prototype 是如何工作的?

上一篇 下一篇

我不太喜欢动态编程语言,但我已经编写了相当多的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

这些幻灯片也确实有很大帮助。

分割线

网友回答:

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

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

例:

//Define a functional object to hold persons in JavaScript
var Person = function(name) {
  this.name = name;
};

//Add dynamically to the already defined object a new getter
Person.prototype.getName = function() {
  return this.name;
};

//Create a new object of type Person
var john = new Person("John");

//Try the getter
alert(john.getName());

//If now I modify person, also John gets the updates
Person.prototype.sayMyName = function() {
  alert('Hello, my name is ' + this.getName());
};

//Call the new method on john
john.sayMyName();

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

//Create a new object of type Customer by defining its constructor. It's not 
//related to Person for now.
var Customer = function(name) {
    this.name = name;
};

//Now I link the objects and to do so, we link the prototype of Customer to 
//a new instance of Person. The prototype is the base that will be used to 
//construct all new instances and also, will modify dynamically all already 
//constructed objects because in JavaScript objects retain a pointer to the 
//prototype
Customer.prototype = new Person();     

//Now I can call the methods of Person on the Customer, let's try, first 
//I need to create a Customer.
var myCustomer = new Customer('Dream Inc.');
myCustomer.sayMyName();

//If I add new methods to Person, they will be added to Customer, but if I
//add new methods to Customer they won't be added to Person. Example:
Customer.prototype.setAmountDue = function(amountDue) {
    this.amountDue = amountDue;
};
Customer.prototype.getAmountDue = function() {
    return this.amountDue;
};

//Let's try:       
myCustomer.setAmountDue(2000);
alert(myCustomer.getAmountDue());
var Person = function (name) {
    this.name = name;
};
Person.prototype.getName = function () {
    return this.name;
};
var john = new Person("John");
alert(john.getName());
Person.prototype.sayMyName = function () {
    alert('Hello, my name is ' + this.getName());
};
john.sayMyName();
var Customer = function (name) {
    this.name = name;
};
Customer.prototype = new Person();

var myCustomer = new Customer('Dream Inc.');
myCustomer.sayMyName();
Customer.prototype.setAmountDue = function (amountDue) {
    this.amountDue = amountDue;
};
Customer.prototype.getAmountDue = function () {
    return this.amountDue;
};
myCustomer.setAmountDue(2000);
alert(myCustomer.getAmountDue());

虽然如前所述,我不能在一个人身上调用setAmountDue(),getAmountDue()。

//The following statement generates an error.
john.setAmountDue(1000);

分割线

网友回答:

每个 JavaScript 对象都有一个内部“槽”,称为其值为 .您可以将插槽视为对象上的属性,该属性位于 JavaScript 引擎内部,隐藏在您编写的代码中。周围的方括号是故意的,是表示内部插槽的 ECMAScript 规范约定。[[Prototype]]nullobject[[Prototype]]

对象所指向的值通俗地称为“该对象的原型”。[[Prototype]]

如果通过点 () 或方括号 () 表示法访问属性,并且该对象没有直接具有此类属性(即 own 属性,可通过 检查),则运行时会在 引用的对象上查找具有该名称的属性。如果 也没有这样的属性,则依次检查其,依此类推。通过这种方式,原始对象的原型链被遍历,直到找到匹配项或到达其终点。原型链的顶端是价值。obj.propNameobj['propName']obj.hasOwnProperty('propName')[[Prototype]][[Prototype]][[Prototype]]null

现代 JavaScript 实现允许通过以下方式读取和/或写入访问:[[Prototype]]

  1. 运算符(在从构造函数返回的默认对象上配置原型链),new
  2. 关键字(使用类语法时配置原型链),extends
  3. Object.create将提供的参数设置为结果对象的参数,[[Prototype]]
  4. Object.getPrototypeOf和(获取/设置对象创建),以及Object.setPrototypeOf[[Prototype]]
  5. 命名的标准化访问器(即 getter/setter)属性(类似于 4。__proto__

Object.getPrototypeOf并且优先于 ,部分原因是当对象具有 的原型时,的行为是不寻常的。Object.setPrototypeOf__proto__o.__proto__null

对象的最初是在对象创建期间设置的。[[Prototype]]

如果通过 创建新对象,则默认情况下,该对象的将设置为 引用的对象。new Func()[[Prototype]]Func.prototype

因此,请注意,所有类和可与 new 运算符一起使用的所有函数除了具有自己的 [[Prototype]] 内部槽外,还具有一个名为 .prototype 的属性。“原型”一词的这种双重使用是该语言新手无休止的困惑的根源。

与构造函数一起使用允许我们在 JavaScript 中模拟经典继承;尽管 JavaScript 的继承系统——正如我们所看到的——是原型的,而不是基于类的。new

在将类语法引入 JavaScript 之前,构造函数是模拟类的唯一方法。我们可以将构造函数的属性引用的对象的属性视为共享成员;即。每个实例都相同的成员。在基于类的系统中,方法对每个实例的实现方式相同,因此方法在概念上添加到属性中;但是,对象的字段是特定于实例的,因此在构造期间会添加到对象本身。.prototype.prototype

如果没有类语法,开发人员必须手动配置原型链以实现与经典继承类似的功能。这导致了实现这一目标的不同方法的优势。

这是一种方法:

function Child() {}
function Parent() {}
Parent.prototype.inheritedMethod = function () { return 'this is inherited' }

function inherit(child, parent) {
  child.prototype = Object.create(parent.prototype)
  child.prototype.constructor = child
  return child;
}

Child = inherit(Child, Parent)
const o = new Child
console.log(o.inheritedMethod()) // 'this is inherited'

…这是另一种方式:

function Child() {}
function Parent() {}
Parent.prototype.inheritedMethod = function () { return 'this is inherited' }

function inherit(child, parent) {
    function tmp() {}
    tmp.prototype = parent.prototype
    const proto = new tmp()
    proto.constructor = child
    child.prototype = proto
    return child
}

Child = inherit(Child, Parent)
const o = new Child
console.log(o.inheritedMethod()) // 'this is inherited'

ES2015中引入的类语法简化了事情,提供了配置原型链的“一种真实方法”,以模拟JavaScript中的经典继承。extends

因此,与上面的代码类似,如果您使用类语法创建一个新对象,如下所示:

class Parent { inheritedMethod() { return 'this is inherited' } }
class Child extends Parent {}

const o = new Child
console.log(o.inheritedMethod()) // 'this is inherited'

…生成的对象将设置为 的实例,而 的实例又为 。[[Prototype]]Parent[[Prototype]]Parent.prototype

最后,如果通过 创建新对象,则生成的对象将设置为 。Object.create(foo)[[Prototype]]foo

分割线

网友回答:

这是一个非常简单的基于原型的对象模型,在解释过程中将被视为一个示例,尚无注释:

function Person(name){
    this.name = name;
}
Person.prototype.getName = function(){
    console.log(this.name);
}
var person = new Person("George");

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

1-JavaScript函数的实际工作原理:

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

假设我们要创建一个对象模型。但在此步骤中,我将尝试在不使用原型关键字的情况下做同样的事情Person

所以在此步骤中,函数对象这个关键字就是我们所拥有的。

第一个问题是,如果不使用关键字,这个关键字如何有用

所以要回答这个问题,假设我们有一个空对象,以及两个函数,例如:

var person = {};
function Person(name){  this.name = name;  }

function getName(){
    console.log(this.name);
}

现在不使用关键字,我们如何使用这些函数。所以 JavaScript 有 3 种不同的方法来做到这一点:

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

Person("George");
getName();//would print the "George" in the console

在这种情况下,这将是当前上下文对象,通常是浏览器中或 中的全局对象。这意味着我们将 window.name 浏览器中或在 Node.js 中 GLOBAL.name,以“George”作为其值。windowGLOBALNode.js

b.我们可以将它们附加到对象上,作为其属性

最简单的方法是修改空对象,例如:person

person.Person = Person;
person.getName = getName;

这样我们就可以这样称呼它们:

person.Person("George");
person.getName();// -->"George"

现在对象是这样的:person

Object {Person: function, getName: function, name: "George"}

–将属性附加到对象的另一种方法是使用该对象的 ,该对象可以在任何名称为 的 JavaScript 对象中找到,我试图在摘要部分对其进行一些解释。因此,我们可以通过执行以下操作来获得类似的结果:prototype__proto__

person.__proto__.Person = Person;
person.__proto__.getName = getName;

但是这样我们实际要做的是修改 ,因为每当我们使用文字 () 创建 JavaScript 对象时,它都是基于 ,这意味着它会作为名为 __proto__ 的属性附加到新创建的对象,所以如果我们更改它,就像我们在前面的代码片段上所做的那样,所有 JavaScript 对象都会被更改, 不是一个好习惯。那么现在更好的做法是什么:Object.prototype{ ... }Object.prototype

person.__proto__ = {
    Person: Person,
    getName: getName
};

现在其他物体处于和平状态,但这似乎仍然不是一个好的做法。所以我们还有一个解决方案,但是要使用此解决方案,我们应该回到创建对象的那行代码(),然后像这样更改它:personvar person = {};

var propertiesObject = {
    Person: Person,
    getName: getName
};
var person = Object.create(propertiesObject);

它所做的是创建一个新的 JavaScript 并将 附加到属性。因此,为了确保您可以执行以下操作:ObjectpropertiesObject__proto__

console.log(person.__proto__===propertiesObject); //true

但这里的棘手之处在于,您可以访问对象第一级上定义的所有属性(有关更多详细信息,请阅读摘要部分)。__proto__person


如您所见,使用这两种方式中的任何一种都会精确地指向对象。thisperson

JavaScript 还有另一种方式为函数提供 ,即使用 call 或 apply 来调用函数。this

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

call() 方法调用具有给定此值和
单独提供的参数的函数。

这种方式是我最喜欢的,我们可以轻松地调用我们的函数,例如:

Person.call(person, "George");

//apply is more useful when params count is not fixed
Person.apply(person, ["George"]);

getName.call(person);   
getName.apply(person);

这 3 种方法是确定 .prototype 功能的重要初始步骤。


2-关键字如何工作?new

这是了解功能的第二步。这是我用来模拟该过程的:.prototype

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

在这一部分中,我将尝试采取JavaScript采取的所有步骤,不使用关键字和,当你使用关键字时。所以当我们这样做时,函数充当构造函数,这些是JavaScript所做的,一个接一个:newprototypenewnew Person("George")Person

A. 首先,它创建一个空对象,基本上是一个空哈希,如下所示:

var newObject = {};

b. JavaScript 采取的下一步是将所有原型对象附加到新创建的对象

我们这里有类似于原型对象。my_person_prototype

for(var key in my_person_prototype){
    newObject[key] = my_person_prototype[key];
}

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


A. & B.代替这两个步骤,您可以通过执行以下操作获得完全相同的结果:

var newObject = Object.create(my_person_prototype);
//here you can check out the __proto__ attribute
console.log(newObject.__proto__ === my_person_prototype); //true
//and also check if you have access to your desired properties
console.log(typeof newObject.getName);//"function"

现在我们可以调用我们的函数:getNamemy_person_prototype

newObject.getName();

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

我们可以像这样使用我们的示例来做到这一点:

Person.call(newObject, "George");

Person.apply(newObject, ["George"]);

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

现在是模拟其他步骤之前的最终结果:
对象 {名称:“乔治”}


总结:

基本上,当你在一个函数上使用 new 关键字时,你正在调用它,该函数充当构造函数,所以当你说:

new FunctionName()

JavaScript 在内部创建一个对象,一个空哈希,然后将该对象提供给构造函数,然后构造函数可以做任何它想做的事情,因为该构造函数内部是刚刚创建的对象,然后它当然会给你那个对象,如果你没有在你的函数中使用 return 语句,或者如果你在函数体的末尾放了一个。return undefined;

因此,当 JavaScript 去查找对象上的属性时,它做的第一件事就是在该对象上查找它。然后有一个秘密属性[[原型]],我们通常像__proto__一样拥有它,该属性是JavaScript接下来要看的。当它查看__proto__时,只要它又是另一个 JavaScript 对象,它就有自己的 __proto__ 属性,它会上升到下一个__proto__为空的程度。点是 JavaScript 中唯一一个其__proto__属性为 null 的对象是对象:Object.prototype

console.log(Object.prototype.__proto__===null);//true

这就是继承在 JavaScript 中的工作方式。

原型链

换句话说,当你在一个函数上有一个原型属性并调用一个新的属性时,在 JavaScript 完成查看新创建的属性对象后,它将查看函数的,并且该对象也可能有自己的内部原型。等等。.prototype

模板简介:该模板名称为【JavaScript .prototype 是如何工作的?】,大小是暂无信息,文档格式为.编程语言,推荐使用Sublime/Dreamweaver/HBuilder打开,作品中的图片,文字等数据均可修改,图片请在作品中选中图片替换即可,文字修改直接点击文字修改即可,您也可以新增或修改作品中的内容,该模板来自用户分享,如有侵权行为请联系网站客服处理。欢迎来懒人模板【JavaScript】栏目查找您需要的精美模板。

相关搜索
  • 下载密码 lanrenmb
  • 下载次数 218次
  • 使用软件 Sublime/Dreamweaver/HBuilder
  • 文件格式 编程语言
  • 文件大小 暂无信息
  • 上传时间 04-17
  • 作者 网友投稿
  • 肖像权 人物画像及字体仅供参考
栏目分类 更多 >
热门推荐 更多 >
微信公众平台 微信素材 微信模板 html5 自适应 企业网站 微信图片 响应式 单页式简历模板 微信文章
您可能会喜欢的其他模板