这是一个非常简单的基于原型的对象模型,在解释过程中将其视为示例,暂无评论:
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有三种不同的方法来实现这一点:
a.第一种方法是将函数作为常规函数调用:
Person("George");
getName();//would print the "George" in the console
在本例中,这将是当前上下文对象,通常是浏览器中的全局窗口对象或Node.js中的global。这意味着我们将具有浏览器中的window.name或Node.js中的global.name,其值为“George”。
b.我们可以将它们附加到对象上,作为其财产
-最简单的方法是修改空的person对象,例如:
person.Person = Person;
person.getName = getName;
这样我们可以这样称呼他们:
person.Person("George");
person.getName();// -->"George"
现在person对象是这样的:
Object {Person: function, getName: function, name: "George"}
-另一种将属性附加到对象的方法是使用该对象的原型,该原型可以在任何名为__proto__的JavaScript对象中找到,我已经尝试在总结部分对此进行了解释。因此,我们可以通过以下操作获得类似的结果:
person.__proto__.Person = Person;
person.__proto__.getName = getName;
但这样我们实际上要做的是修改Object.prototype,因为每当我们使用文字({…})创建JavaScript对象时,它都是基于Object.prototype创建的,这意味着它会作为名为__proto__的属性附加到新创建的对象上,因此如果我们像前面的代码片段那样更改它,所有JavaScript对象都会更改,这不是一个好的做法。那么,现在最好的做法是:
person.__proto__ = {
Person: Person,
getName: getName
};
现在其他物体都处于和平状态,但这似乎仍然不是一个好的做法。所以我们还有一个解决方案,但要使用这个解决方案,我们应该回到创建person对象的代码行(varperson={};),然后将其更改为:
var propertiesObject = {
Person: Person,
getName: getName
};
var person = Object.create(propertiesObject);
它所做的是创建一个新的JavaScript对象,并将propertiesObject附加到__proto__属性。为了确保您能够做到:
console.log(person.__proto__===propertiesObject); //true
但这里需要注意的是,您可以在person对象的第一层访问__proto__中定义的所有财产(有关详细信息,请阅读摘要部分)。
正如您所看到的,使用这两种方法中的任何一种,这都将精确地指向person对象。
c.JavaScript还有另一种方法来为函数提供此功能,即使用call或apply来调用函数。
apply()方法调用具有给定this值的函数作为数组(或类似数组的对象)提供的参数。
and
call()方法调用具有给定this值的函数单独提供的参数。
这种方式是我最喜欢的,我们可以很容易地调用我们的函数,比如:
Person.call(person, "George");
or
//apply is more useful when params count is not fixed
Person.apply(person, ["George"]);
getName.call(person);
getName.apply(person);
这三种方法是确定原型功能的重要初始步骤。
2-新关键字是如何工作的?
这是理解原型功能的第二步。这是我用来模拟过程的步骤:
function Person(name){ this.name = name; }
my_person_prototype = { getName: function(){ console.log(this.name); } };
在这一部分中,我将尝试采取JavaScript所采取的所有步骤,当您使用新关键字时,不使用新关键字和原型。所以,当我们创建新的Person(“George”)时,Person函数充当构造函数,这是JavaScript所做的,一个接一个:
首先,它生成一个空对象,基本上是一个空散列,如:
var newObject = {};
b.JavaScript的下一步是将所有原型对象附加到新创建的对象
这里的mypersontotype类似于原型对象。
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"
现在我们可以在my_person_totype中调用getName函数:
newObject.getName();
c.然后将该对象交给构造函数,
我们可以用我们的样本来做这件事,比如:
Person.call(newObject, "George");
or
Person.apply(newObject, ["George"]);
那么构造函数可以做它想做的任何事情,因为构造函数的内部就是刚刚创建的对象。
现在是模拟其他步骤之前的最终结果:对象{name:“George”}
摘要:
基本上,当您在函数上使用new关键字时,您正在调用该关键字,而该函数充当构造函数,因此当您说:
new FunctionName()
JavaScript在内部生成一个对象,一个空散列,然后它将该对象提供给构造函数,然后构造函数可以做它想要做的任何事情,因为该构造函数的内部是刚刚创建的对象,然后它会给你该对象,当然,如果你没有在函数中使用return语句,或者你没有定义return;在功能体的末端。
因此,当JavaScript在对象上查找属性时,它首先要做的就是在该对象上查找它。然后有一个秘密属性[[prototype]],我们通常将其命名为__proto__,这个属性就是JavaScript接下来要看的。当它查看__proto__时,只要它再次是另一个JavaScript对象,它就有自己的__proto__属性,它会不断上升,直到下一个__proto__为空。点是JavaScript中唯一一个__proto__属性为空的对象。它是object.prototype对象:
console.log(Object.prototype.__proto__===null);//true
这就是继承在JavaScript中的工作原理。
换句话说,当你在一个函数上有一个prototype属性,并且你在该属性上调用了一个new,在JavaScript完成对新创建的财产对象的查看后,它会去查看函数的.prototype,而且这个对象可能有自己的内部原型。等等