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

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

446a76b749873f38c0050c6094c06407.png

介绍

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

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/365861.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

将.py文件装成这执行文件.exe

目前已知 cx_freeze支持python3生成可执行文件 cx_freeze的操作 参考http://keliang.blog.51cto.com/3359430/661884 转载于:https://www.cnblogs.com/qingsheng/p/9599885.html

python测试脚本截图_selenium + python实现截图并且保存图片

webdriver的截图功能十分强悍,无论页面多长,webdriver都能比较完美的截到完整的页面。 python代码: # -*- coding: utf-8 -*- from selenium import webdriver import unittest import os, sys, time from pathlib import Path # 初始化实例 …

骁龙660是32位还是64位_高通发布骁龙 7c/8c 芯片,以后你可能会在电脑上看到它...

高通的芯片生意早已不局限于移动设备领域,而是进一步深入至 PC 市场。相比强调性能的 X86 芯片,以高通骁龙为代表的 ARM 系芯片则希望突出自己的优势,即更长的电池续航、无风扇设计和全天候的蜂窝网络连接。在骁龙技术峰会的第三天&#xff0…

Java泛型中的子类型化

泛型类型为Java程序引入了新的类型安全范围。 在同一类型上,泛型类型可以表现得很好,尤其是在使用通配符时 。 在本文中,我想解释子类型如何与Java泛型一起工作。 关于泛型类型子类型化的一般思考 不同泛型类型相同的类或接口的不定义亚型层…

css3之盒模型

什么是盒模型? css中的每个元素都是一个盒模型, 包括html body元素, 浏览器解析css的时候也会把每个元素看成一个盒子来解析。 盒模型具备的属性有: content 、padding 、margin、border 、background等 盒模型的分类&#xff1…

小朋友排队|2014年蓝桥杯B组题解析第十题-fishers

小朋友排队 n 个小朋友站成一排。现在要把他们按身高从低到高的顺序排列,但是每次只能交换位置相邻的两个小朋友。 每个小朋友都有一个不高兴的程度。开始的时候,所有小朋友的不高兴程度都是0。 如果某个小朋友第一次被要求交换,则他的不高兴…

学计算机的误解,让人误解的六大专业

原标题:让人误解的六大专业隔行如隔山,很多人喜欢看名字猜专业,所以导致很多大学专业被人误解。其实了解一个专业不能仅仅凭借它的名字,也不能断章取义,只取片面意思。接下来就让我们来了解一下有哪些被人误解的专业吧…

杂项:轮询

ylbtech-杂项:轮询1.返回顶部 1、轮询(Polling)是一种CPU决策如何提供周边设备服务的方式,又称“程控输出入”(Programmed I/O)。轮询法的概念是,由CPU定时发出询问,依序询问每一个周…

python类属性的调用方法_问一个关于PYTHON类属性调用方法的问题

def baidu(self): self.APP_ID2180368 self.API_KEYEYAvb2oTaGa9oSNs5S2yx6v self.SECRET_KEYdmgvBELGq9cMvk2uSPqLUaLUpEng02D self.aipAipOcr(self.APP_ID,self.API_KEY,self.SECRET_KEY) #1.这里定义的self.aip 3. 如果 我把这个self.aip定义在__init__(self)里面就不会报错…

js和css实现手机横竖屏预览思路整理

实现效果,如上图。 首先,实现手机页面在PC端预览, 则先在网上找到一个手机的背景图片,算好大概内间距,用来放预览的页面,我这里是给手机预览页面的尺寸按iphone5的尺寸来的; 一个手机页面在这里…

Lambda,会序列化吗?

因此,我一直在思考Tyrus项目所需的增强功能,该功能允许用户广播到跨机器集群连接到URL的客户端子集。 有多种方法可以做到这一点; 但是自从我使用JDK 8以来,这个问题肯定看起来像钉子。 为此,我创建了一个简单的单元测…

thinkphp日志泄漏漏洞_【Windows高危漏洞预警】CVE20200601,影响关键加密功能

一、事件报告2020年伊始,NSA发现了一个影响Microsoft Windows加密功能的严重漏洞(CVE-2020-0601)。证书验证漏洞允许攻击者破坏Windows验证加密信任的方式,并且可以启用远程代码执行。该漏洞会影响Windows 10和Windows Server 2016/2019以及依赖Windows的…

第一章计算机网络概述答案,第一章 计算机网络概述[3]

1-07 试在下列条件下比较电路交换和分组交换。要传送的报文共x(bit)。从源站到目的站共经过k段链路,每段链路的传播时延为d(s),数据率为b(b/s)。在电路交换时电路的建立时间为S(s)。在分组交换时分组长度为p(bit),且各结点的排队等待时间可忽…

meta标签的用处详解

meta标签的用处: 用来描述html文档的一个属性。列如作者。日期和时间,网页描述,关键字,页面刷新等。 是文档最基本的元数据 元数据(metadata): 用来概括描述数据的一些基本数据 meta 标签的使用…

最小路径算法(Dijkstra算法和Floyd算法)

1.单源点的最短路径问题:给定带权有向图G和源点v,求从v到G中其余各顶点的最短路径。 我们用一个例子来具体说明迪杰斯特拉算法的流程。 定义源点为 0,dist[i]为源点 0 到顶点 i 的最短路径。其过程描述如下: 步骤dist[1]dist[2]di…

react如何监听路由url变化

"componentWillReceiveProps" "shouldComponentUpdate" "componentWillUpdate" "render" "componentDidUpdate" 使用这些生命周期钩子可以监听到路由相同,参数不同的变化,但是监听不到完全不相同的ur…

python 购物车程序_python_购物车程序

#需求1.启动程序后,让用户输入工资,然后打印商品列表 2.允许用户根据商品编号购买商品 3.用户选择商品后,检测余额是否够,够就直接扣款,不够就提醒 4.可随时退出,退出时,打印已购买商品和余额 #…

matlab tsai手眼标定程序代码_标定系列一 | 机器人手眼标定的基础理论分析

旷视MegMaster机器人系列是旷视自主研发的一系列AI智能机器人硬件设备,基于旷视全球领先的人工智能算法及机器人技术,可实现搬运、分拣、托举、存储等功能,被广泛应用于物流仓储、工厂制造等场景。旷视SLAM组主要负责多传感器建图、定位、标定…

Java8中的外观(JavaFX8)

JavaFX8在外观方面进行了一些更改 ,其中最相关的是新CSS API ,它允许您为控件以及已公开的Skin类创建新CSS属性和伪类。 使用CSS可以改变控件的很多外观,但是CSS只能实现很多功能,而这正是Skin类的用处。从“ UI控件体系结构”快…

600分理科选计算机专业,天津600分左右,计算机或电子信息专业,怎么选院校?...

原标题:天津600分左右,计算机或电子信息专业,怎么选院校?想学计算机或电子信息,一定是偏理选科对吧!所谓的换算,是指的高考后出了全天津市不分文理的总排名后,如何换算成相当于天津市…