我不太喜欢动态编程语言,但我已经编写了相当多的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]]
null
object
[[Prototype]]
对象所指向的值通俗地称为“该对象的原型”。[[Prototype]]
如果通过点 () 或方括号 () 表示法访问属性,并且该对象没有直接具有此类属性(即 own 属性,可通过 检查),则运行时会在 引用的对象上查找具有该名称的属性。如果 也没有这样的属性,则依次检查其,依此类推。通过这种方式,原始对象的原型链被遍历,直到找到匹配项或到达其终点。原型链的顶端是价值。obj.propName
obj['propName']
obj.hasOwnProperty('propName')
[[Prototype]]
[[Prototype]]
[[Prototype]]
null
现代 JavaScript 实现允许通过以下方式读取和/或写入访问:[[Prototype]]
new
extends
Object.create
将提供的参数设置为结果对象的参数,[[Prototype]]
Object.getPrototypeOf
和(获取/设置对象创建后),以及Object.setPrototypeOf
[[Prototype]]
__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");
在完成原型概念之前,我们必须考虑一些关键点。
要迈出第一步,我们必须弄清楚, JavaScript 函数如何实际工作 ,作为在其中使用此关键字的类函数,或者只是作为带有其参数的常规函数,它做什么以及它返回什么。
假设我们要创建一个对象模型。但在此步骤中,我将尝试在不使用原型
和新
关键字的情况下做同样的事情。Person
所以在此步骤中,函数
、对象
和这个
关键字就是我们所拥有的。
第一个问题是,如果不使用新
关键字,这个
关键字如何有用。
所以要回答这个问题,假设我们有一个空对象,以及两个函数,例如:
var person = {};
function Person(name){ this.name = name; }
function getName(){
console.log(this.name);
}
现在不使用新
关键字,我们如何使用这些函数。所以 JavaScript 有 3 种不同的方法来做到这一点:
Person("George");
getName();//would print the "George" in the console
在这种情况下,这将是当前上下文对象,通常是浏览器中或 中的全局对象。这意味着我们将 window.name 浏览器中或在 Node.js 中 GLOBAL.name,以“George”作为其值。window
GLOBAL
Node.js
–最简单的方法是修改空对象,例如: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
};
现在其他物体处于和平状态,但这似乎仍然不是一个好的做法。所以我们还有一个解决方案,但是要使用此解决方案,我们应该回到创建对象的那行代码(),然后像这样更改它:person
var person = {};
var propertiesObject = {
Person: Person,
getName: getName
};
var person = Object.create(propertiesObject);
它所做的是创建一个新的 JavaScript 并将 附加到属性。因此,为了确保您可以执行以下操作:Object
propertiesObject
__proto__
console.log(person.__proto__===propertiesObject); //true
但这里的棘手之处在于,您可以访问对象第一级上定义的所有属性(有关更多详细信息,请阅读摘要部分)。__proto__
person
如您所见,使用这两种方式中的任何一种都会精确地指向对象。this
person
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 功能的重要初始步骤。
new
这是了解功能的第二步。这是我用来模拟该过程的:.prototype
function Person(name){ this.name = name; }
my_person_prototype = { getName: function(){ console.log(this.name); } };
在这一部分中,我将尝试采取JavaScript采取的所有步骤,不使用关键字和,当你使用关键字时。所以当我们这样做时,函数充当构造函数,这些是JavaScript所做的,一个接一个:new
prototype
new
new Person("George")
Person
var newObject = {};
我们这里有类似于原型对象。my_person_prototype
for(var key in my_person_prototype){
newObject[key] = my_person_prototype[key];
}
这不是JavaScript实际附加原型中定义的属性的方式。实际方式与原型链概念有关。
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"
现在我们可以调用我们的函数:getName
my_person_prototype
newObject.getName();
我们可以像这样使用我们的示例来做到这一点:
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】栏目查找您需要的精美模板。