本文主要讲了原型如何在JavaScript中工作,以及如何通过[Prototype]所有对象共享的隐藏属性链接对象属性和方法;以及如何创建自定义构造函数以及原型继承如何工作以传递属性和方法值。

介绍

JavaScript是一种基于原型的语言,这意味着对象属性和方法可以通过具有克隆和扩展能力的通用对象共享。这被称为原型继承,与类继承不同。在流行的面向对象编程语言中,JavaScript是相对独特的,因为其他著名的语言,如PHP、Python和Java都是基于类的语言,它们将类定义为对象的蓝图。

在文中,我们将学习什么是对象原型,以及如何使用构造函数将原型扩展为新对象。我们还将学习继承和原型链。

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的字符类,向原型添加了方法,并创建了单独的字符实例。

js实现html模板继承,理解JavaScript中的原型和继承相关推荐

  1. 理解JavaScript中的原型与原型链

    理解JavaScript中的原型与原型链 原型链是一种机制,指的是JavaScript中每个内置的对象都有一个内置的__proto__属性指向创建它的构造函数的prototype(原型)属性.原型链的 ...

  2. JavaScript中的原型和继承

    请在此暂时忘记之前学到的面向对象的一切知识.这里只需要考虑赛车的情况.是的,就是赛车. 最近我正在观看 24 Hours of Le Mans ,这是法国流行的一项赛事.最快的车被称为 Le Mans ...

  3. 理解JavaScript中的原型继承(2)

    两年前在我学习JavaScript的时候我就写过两篇关于原型继承的博客: 理解JavaScript中原型继承 JavaScript中的原型继承 这两篇博客讲的都是原型的使用,其中一篇还有我学习时的错误 ...

  4. JavaScript中的原型(prototype)与继承

    在JavaScript中,原型是用来模仿其他「类」语言继承机制的基础.原型并不复杂,原型只是一个对象. 一.原型对象 1.1 什么是原型对象 每当我们创建了一个函数后,这个函数就拥有了一个protot ...

  5. 详解JS中的原型与继承

    每当我们提起原型链时不免会想到原型对象,对象的原型,还有众多的继承方式.于是prototype.[[prototype]].constructor等等难免在头脑中打架. 然而原型其实并不是什么高大上的 ...

  6. JavaScript中的几种继承方式及优缺点分析

    众所周知,继承是面向对象编程思想中的三大特点(封装,继承,多态)之一. 所谓继承,通俗来讲就是子类自动拥有父类的属性和方法, 继承可以提高代码的复用性. 继承也是前端里面的重要的一个知识点,在实际工作 ...

  7. 理解JavaScript中部分设计模式

    理解JavaScript中部分设计模式 什么是设计模式 在软件工程中,设计模式是软件设计中常见问题可重用的方案.设计模式代表着经验丰富的软件开发人员使用的最佳实践.设计模式可以被认为是编程模板. 为什 ...

  8. 理解javascript中的回调函数(callback)【转】

    在JavaScrip中,function是内置的类对象,也就是说它是一种类型的对象,可以和其它String.Array.Number.Object类的对象一样用于内置对象的管理.因为function实 ...

  9. php event loop,理解javascript中的事件循环(Event Loop)

    背景 在研究js的异步的实现方式的时候,发现了JavaScript 中的 macrotask 和 microtask 的概念.在查阅了一番资料之后,对其中的执行机制有所了解,下面整理出来,希望可以帮助 ...

最新文章

  1. Android之本地数据存储(一):SharedPreferences
  2. 使用isolation forest进行dns网络流量异常检测
  3. Oracle 中control_file_record_keep_time参数的解释
  4. Check Point CEO:“我们正在积极寻找收购目标”
  5. php抽象方法db,PHP笔记之抽象方法抽象类
  6. docker集群管理工具_太多选择:如何选择正确的工具来管理Docker集群
  7. 用Markdown格式写一份前端简历
  8. 基于php的高校田径运动会管理系统
  9. 错题集--大一C语言选择题
  10. docker bi工具superset汉化
  11. torch.cat() 和 torch.stack()
  12. Android模拟器横屏和竖屏的切换
  13. 调试心得plc做主站(客户端)
  14. dos 命令——切换盘符与切换工作目录
  15. 网络安全之小程序抓包渗透测试流程
  16. Java8 - Streams flatMap()
  17. Oracle-08:连接查询
  18. 无需端口映射,实现外部网络访问Docker集群内部服务
  19. FZU 1021 飞船赛
  20. matlab中GUI的属性检查器中的XLimMode是什么_OpenBCI_GUI部件指南

热门文章

  1. Tortoise SVN 版本控制常用操作知识
  2. AUTOSAR从入门到精通100讲(四十二)-Autosar架构下的模块详细设计及代码实现--基于配置的编程方法
  3. 基于PyMC的贝叶斯建模实战
  4. 比java好的_Java Spring真的比直接进行Java编程好吗
  5. 日期格式无法识别 oracle,Oracle时间统计 - 不以物喜-不以己悲的个人空间 - OSCHINA - 中文开源技术交流社区...
  6. 【JavaScript高级教程】JavaScript prototype(原型对象)
  7. java线程代码实现_Java 实现线程的2种方法的具体代码实例
  8. 汇智创科机器人_【汇智创科机器人招聘信息】-看准网
  9. React 父组件和子组件中的方法相互调用
  10. 初学servlet之使用web.xml配置