JavaScript中的对象原型是什么?怎么用?针对这些问题,今天小编总结了这篇文章,希望能帮助更多想解决这些问题的朋友找到更加简单易行的办法。
十年专注成都网站制作,成都定制网页设计,个人网站制作服务,为大家分享网站制作知识、方案,网站设计流程、步骤,成功服务上千家企业。为您提供网站建设,网站制作,网页设计及定制高端网站建设服务,专注于成都定制网页设计,高端网页制作,对成都报废汽车回收等多个方面,拥有多年的网站设计经验。
介绍
javascript中一切皆对象,而每个对象都有一个原型(Object除外),这个原型,大概就像Java中的父类,所以,基本上你可以认为原型就是这个对象的父对象,即每一个对象(Object除外)内部都保存了它自己的父对象,这个父对象就是原型。一般创建的对象如果没有特别指定原型,那么它的原型就是Object(这就很类似Java中所有的类默认继承自Object类)。
JavaScript原型
JavaScript中的每个对象都有一个称为[[Prototype]]的内部属性。我们可以通过创建一个新的空对象来演示这一点。
let x = {};
这是我们通常创建对象的方法,但是请注意,另一种实现方法是使用对象构造函数:
let x = new object()
包围[[Prototype]]的双方括号表示它是一个内部属性,不能在代码中直接访问。
要找到这个新创建对象的[[Prototype]],我们将使用getPrototypeOf()方法。
Object.getPrototypeOf(x);
输出将由几个内置属性和方法组成。
输出:
{constructor: ƒ, __defineGetter__: ƒ, __defineSetter__: ƒ, …}
找到的另一种方法[[Prototype]]是通过__proto__财产。__proto__是一个公开[[Prototype]]对象内部的属性。
需要注意的是,. _proto__是一个遗留特性,不应该在生产代码中使用,而且它也不是在每个现代浏览器中都存在。但是,我们可以在本文中使用它来进行演示。
x.__proto__;
输出将与使用getPrototypeOf()相同。
输出
{constructor: ƒ, __defineGetter__: ƒ, __defineSetter__: ƒ, …}
重要的是JavaScript中的每个对象都有一个[[Prototype]],因为它为任何两个或多个对象创建了链接的方法。
您创建的对象和内置对象(如Date和Array)一样具有[[Prototype]]。可以通过prototype属性将这个内部属性从一个对象引用到另一个对象,我们将在本教程的后面看到这一点。
原型继承
当您试图访问对象的属性或方法时,JavaScript将首先搜索对象本身,如果没有找到,它将搜索对象的[[Prototype]]。如果在查询对象及其[[Prototype]]后仍然没有找到匹配项,JavaScript将检查被链接对象的原型,并继续搜索,直到到达原型链的末端。
原型链的末尾是Object.prototype。所有对象都继承对象的属性和方法。任何超出链末端的搜索都会导致null。
在我们的示例中,x是一个从object继承而来的空对象。x可以使用对象具有的任何属性或方法,比如toString()。
x.toString();
输出
[object Object]
这个原型链只有一个链长。x - > Object。我们知道这一点,因为如果我们试图将两个[[Prototype]]属性链接在一起,它将为null。
x.__proto__.__proto__;
输出
null
让我们看看另一种类型的对象。如果您有使用JavaScript处理数组的经验,就会知道它们有许多内置方法,比如pop()和push()。创建新数组时可以访问这些方法的原因是,创建的任何数组都可以访问array .prototype上的属性和方法。
我们可以通过创建一个新的数组来测试它。
let y = [];
请记住,我们也可以把它写成数组构造函数,让y = new array()。
如果我们查看新y数组的[[Prototype]],我们将看到它比x对象具有更多的属性和方法。它继承了Array.prototype中的所有内容。
y.__proto__;
[constructor: ƒ, concat: ƒ, pop: ƒ, push: ƒ, …]
您将注意到原型上的构造函数属性被设置为Array()。构造函数属性返回对象的构造函数,这是一种用于从函数构造对象的机制。
我们现在可以将两个原型链接在一起,因为在这种情况下,我们的原型链更长。它看起来像y -> Array -> Object。
y.__proto__.__proto__;
{constructor: ƒ, __defineGetter__: ƒ, __defineSetter__: ƒ, …}
这个链现在引用Object.prototype。我们可以根据构造函数的Prototype属性测试内部的[[Prototype]],以确定它们引用的是相同的东西。
y.__proto__ === Array.prototype; // true y.__proto__.__proto__ === Object.prototype; // true
我们还可以使用isPrototypeOf()方法来实现这一点。
Array.prototype.isPrototypeOf(y); // true Object.prototype.isPrototypeOf(Array); // true
我们可以使用instanceof操作符来测试构造函数的prototype属性是否出现在对象原型链中的任何位置。
y instanceof Array; // true
总而言之,所有JavaScript对象都具有隐藏的内部[[Prototype]]属性(可能__proto__在某些浏览器中公开)。对象可以扩展,并将继承[[Prototype]]其构造函数的属性和方法。
这些原型可以被链接,并且每个额外的对象将继承整个链中的所有内容。链以Object.prototype结束。
构造器函数
构造函数是用来构造新对象的函数。new操作符用于基于构造函数创建新实例。我们已经看到了一些内置的JavaScript构造函数,比如new Array()和new Date(),但是我们也可以创建自己的自定义模板来构建新对象。
例如,我们正在创建一个非常简单的基于文本的角色扮演游戏。用户可以选择一个角色,然后选择他们将拥有的角色类别,例如战士、治疗者、小偷等等。
由于每个字符将共享许多特征,例如具有名称、级别和生命值,因此创建构造函数作为模板是有意义的。然而,由于每个角色类可能有非常不同的能力,我们希望确保每个角色只能访问自己的能力。让我们看看如何使用原型继承和构造函数来实现这一点。
首先,构造函数只是一个普通函数。当使用new关键字的实例调用它时,它将成为一个构造函数。在JavaScript中,我们按照惯例将构造函数的第一个字母大写。
// Initialize a constructor function for a new Hero function Hero(name, level) { this.name = name; this.level = level; }
我们创建了一个名为Hero的构造函数,它有两个参数:name和level。因为每个字符都有一个名称和一个级别,所以每个新字符都有这些属性是有意义的。this关键字将引用创建的新实例,因此将this.name设置为name参数将确保新对象具有name属性集。
现在我们可以用new创建一个新的实例。
let hero1 = new Hero('Bjorn', 1);
如果我们在控制台输出hero1,我们将看到已经创建了一个新对象,其中新属性按预期设置。
输出
Hero {name: "Bjorn", level: 1}
现在,如果我们得到hero1的[[Prototype]],我们将能够看到构造函数Hero()。(记住,它的输入与hero1相同。,但这是正确的方法。)
Object.getPrototypeOf(hero1);
输出
constructor: ƒ Hero(name, level)
您可能注意到,我们只在构造函数中定义了属性,而没有定义方法。在JavaScript中,为了提高效率和代码可读性,通常在原型上定义方法。
我们可以使用prototype向Hero添加一个方法。我们将创建一个greet()方法。
// Add greet method to the Hero prototype Hero.prototype.greet = function () { return `${this.name} says hello.`; }
因为greet()在Hero的原型中,而hero1是Hero的一个实例,所以这个方法对hero1是可用的。
hero1.greet();
输出
"Bjorn says hello."
如果检查Hero的[[Prototype]],您将看到greet()现在是一个可用选项。
这很好,但是现在我们想要为英雄创建角色类。将每个类的所有功能都放到Hero构造函数中是没有意义的,因为不同的类具有不同的功能。我们希望创建新的构造函数,但也希望它们连接到原始的Hero。
我们可以使用call()方法将属性从一个构造函数复制到另一个构造函数。让我们创建一个战士和一个治疗构造器。
// Initialize Warrior constructor function Warrior(name, level, weapon) { // Chain constructor with call Hero.call(this, name, level); // Add a new property this.weapon = weapon; }// Initialize Healer constructor function Healer(name, level, spell) { Hero.call(this, name, level); this.spell = spell; }
两个新的构造函数现在都具有Hero和unqiue的属性。我们将把attack()方法添加到Warrior中,而heal()方法添加到Healer中。
Warrior.prototype.attack = function () { return `${this.name} attacks with the ${this.weapon}.`; } Healer.prototype.heal = function () { return `${this.name} casts ${this.spell}.`; }
此时,我们将使用两个可用的新字符类创建字符。
const hero1 = new Warrior('Bjorn', 1, 'axe'); const hero2 = new Healer('Kanin', 1, 'cure');
hero1现在被认为是拥有新属性的战士。
输出
Warrior {name: "Bjorn", level: 1, weapon: "axe"}
我们可以使用我们在战士原型上设置的新方法。
hero1.attack();
Console "Bjorn attacks with the axe."
但是如果我们尝试使用原型链下面的方法会发生什么呢?
hero1.greet();
输出
Uncaught TypeError: hero1.greet is not a function
使用call()链接构造函数时,原型属性和方法不会自动链接。我们将使用Object.create()来链接原型,确保在创建并添加到原型的任何其他方法之前将其放置。
Warrior.prototype = Object.create(Hero.prototype); Healer.prototype = Object.create(Hero.prototype); // All other prototype methods added below…
现在我们可以在一个战士或治疗者的实例上成功地使用Hero的原型方法。
hero1.greet();
输出
"Bjorn says hello."
这里是我们的角色创建页面的完整代码。
// Initialize constructor functions function Hero(name, level) { this.name = name; this.level = level; } function Warrior(name, level, weapon) { Hero.call(this, name, level); this.weapon = weapon; } function Healer(name, level, spell) { Hero.call(this, name, level); this.spell = spell; } // Link prototypes and add prototype methods Warrior.prototype = Object.create(Hero.prototype); Healer.prototype = Object.create(Hero.prototype); Hero.prototype.greet = function () { return `${this.name} says hello.`; } Warrior.prototype.attack = function () { return `${this.name} attacks with the ${this.weapon}.`; } Healer.prototype.heal = function () { return `${this.name} casts ${this.spell}.`; } // Initialize individual character instances const hero1 = new Warrior('Bjorn', 1, 'axe'); const hero2 = new Healer('Kanin', 1, 'cure');
使用这段代码,我们已经用基本属性创建了Hero类,从原始构造函数创建了两个名为Warrior和Healer的字符类,向原型添加了方法,并创建了单独的字符实例。
关于JavaScript中的对象原型就分享到这里了,希望以上内容可以对大家有一定的帮助,可以学到更多知识。如果觉得文章不错,可以把它分享出去让更多的人看到。
分享文章:JavaScript中的对象原型是什么?怎么用
URL地址:http://scyingshan.cn/article/gecjci.html