第二十一节:JS中的继承

2023-11-17

上节回顾

1、所有 函数 都有一个特殊属性(prototype),prototype指向一个对象,称之为原型对象,原型对象上只有一个属性(constructor),constructor又指向了构造函数,形成了一个闭环。

2、所有 对象 都有一个隐藏的属性proto,大部分浏览器可以使用两个下划线进行访问;

//创建一个构造函数
function Fun(name,age){
	this.name=name;
	this.age=age;
}
Fun.prototype; //{constructor: ƒ}
var peo=new Fun('张三',23); //基于构造函数创建一个对象
peo; //Fun {name: '张三', age: 23}
peo.__proto__;  //{constructor: ƒ}

3、访问 对象的属性 时,查看对象上是否有此属性,有则可直接调用,没有则顺着proto找到原型对象,查看原型对象是否有此属性,如果有则调用原型对象的属性,如果没有则会顺着原型对象隐藏的属性proto向上查找,此查找proto的过程形成的链条称之为原型链,原型链的最后指向一个null。

4、添加和修改则直接对当前对象进行添加和修改。

面向对象 和 基于对象的区别

面向对象:必然有三大特点(封装,继承,多态)
基于对象:是使用对象, 就是无法利用现有的对象产生新的对象类型,继而产生新的对象,也就是说基于对象没有继承的特点!

因为JavaScript没有继承的概念,进而也没有多态的概念,缺少了继承和多态的特性,所以JavaScript就只是个基于对象的语言

JS中的继承

虽然js没有继承,但可以用一些手段模拟出来继承。继承简单来说就是让一个对象拥有另一个对象的属性和方法

1、原型链继承

//父类构造函数
function Afun(){
   this.fname='li';
}
//给父类原型上添加一个getFname的方法
Afun.prototype.getFname = function (){
	return this.fname;
}
//子类构造函数
function Bfun(){
	this.name = 'alvin';
}
//定义子类的prototype指向父类实例
Bfun.prototype = new Afun();
//给B添加一个获取name的方法
Bfun.prototype.getName= function(){
	return this.name;
}
var son = new Bfun();
son;//Bfun {name: 'alvin'}
son.getName();//'alvin'
son.getFname();//'li'
//给父类A的prototype增加属性和方法,son依然可以继承
Afun.prototype.teacher='吴磊';
son.teacher;//'吴磊'

原型链继承的缺点:
①书写先后问题,先给子类指向父类实例,再给子类设置新的方法,否则会被覆盖

function Afun(){
   this.fname='li';
}
Afun.prototype.getFname = function (){
	return this.fname;
}
function Bfun(){
	this.name = 'alvin';
}
Bfun.prototype.getName= function(){
	return this.name;
}
Bfun.prototype = new Afun();
var son=new Bfun();
son.getName();//报错,son.getName is not a function

②无法实现多继承(构造函数A和构造函数B,当创建构造函数C时,C的prototype要不指向A,要不指向B,不可能即指向A又指向B)
③所有属性和方法都是共享的,一旦修改则影响所有基于构造函数创建的对象;
④无法传参

2、借用构造函数方式

在子类构造函数中使用call()或者apply()方法,把父类构造函数在子类重新运行一遍。

function Product(name, price) {
  this.name = name;
  this.price = price;
}
function Father(address) {
  this.address= address;
}
function Food(name, price,address) {
  //重定向构造函数Product的this,Product的this
  //Product.call(this, name, price);
  Product.apply(this, [name, price]);
  Father.apply(this, [address]);
  this.category = 'food';
}
var son = new Food('蛋糕',139,'西三环99号');
son; //Food {name: '蛋糕', price: 139, address: '西三环99号', category: 'food'}

注意: call()方法的语法和作用与 apply() 方法类似,只有一个区别,就是 call() 方法接受的是一个参数列表,而 apply() 方法接受的是一个包含多个参数的数组。

优点:
①可以实现多继承
②解决了共享的问题,子类可以重定向多个父类
③可以传参
缺点:
①创造的实例(例如son)只是子类的实例,不是父类的实例
②只能继承构造函数内的属性和方法,不能继承原型的属性和方法

function Product(name, price) {
  this.name = name;
  this.price = price;
}
Product.prototype.getPrice=function(){
return this.price;
}
function Father(address) {
  this.address= address;
}
function Food(name, price,address) {
  Product.apply(this, [name, price]);
  Father.apply(this, [address]);
  this.category = 'food';
}
var son = new Food('蛋糕',139,'西三环99号');
son.getPrice(); //报错 son.getPrice is not a function

③所有属性都是在构造函数中运行的,无法进行复用

3、组合式继承

function a(name, price) {
  this.name = name;
  this.price = price;
}
a.prototype.getPrice=function(){
	return this.price;
}
function b(name, price) {
  a.apply(this, [name, price]);
  this.category = 'food';
}
b.prototype=new a();
b.prototype.constructor = b;
var food1=new b('蛋糕',139);
food1.getPrice(); //139

缺点:
①父类构造函数被调用了2次;第一次是在使用call()或者apply()方法时,第二次是设置子类prototype指向父类实例时。如果父类构造函数较大,则会比较影响性能。
②基于子类创建的实例对象,对象中的name和price在prototype中也有一个同名属性,存在同名覆盖问题。

4、原型式继承

类似原型链继承,原型链继承继承的是一个构造函数,原型式继承继承的是一个普通的对象
对象直接量创建的对象,怎么让一个对象继承另一个对象的属性(Object对象)

var a={name:'张三'};
var b={age:30};
b.__proto__=a;

注 意:__proto__是一些浏览器定义的特殊属性,不是标准化当中存在的语法,高版本浏览器不会报错,但一些ie浏览器会报错。

var a={name:'张三'};
function F(){};
F.prototype=a;//Object { name: "张三" }
var b=new F();
b.__proto__;//Object { name: "张三" }
b.age=13;
b.age;//13
b.name;//"张三"

ES5创建对象 新增方法Object.create()

Object.create()方法创建一个新对象,使用现有的对象来提供新创建的对象的__proto__

var x={name:'张三'};
x.getName=function(){
	return '你的名字是:'+this.name;
}
var y=Object.create(x);
y.__proto__; //Object { name: "张三" }

注 意: 当创建的新对象需要继承另一个对象的属性和方法时,最简单的就是使用create方法

5、寄生式继承

将创建空的构造函数的过程封装成一个函数,使其看起来更像继承。

function creatFun(obj){
	function F(){};
	F.prototype=obj;
	return new F();
}
var a={name:'张三'};
var b=creatFun(a);
b.name;//'张三'

6、组合寄生式继承

为了解决组合式继承的缺点,所以将寄生式继承和组合式继承结合到一起

function a(name, price) {
  this.name = name;
  this.price = price;
}
a.prototype.getPrice=function(){
	return this.price;
}
function b(name, price) {
  a.apply(this, [name, price]);
  this.category = 'food';
}
//将b的原型对象指向a的原型对象,解决了属性重复的问题,也解决了父类执行两次的问题
//b.prototype.__proto__=a.prototype;//用此行代码替换掉 b.prototype=new a();,因为__proto__是浏览器的特殊定义,所以需要经过下面的改造
//function creatFun(obj){
//	function F(){};
//	F.prototype=obj;
//	return new F();
//}
//var p = creatFun(a.prototype);
//利用ES5 create方法简化写法
var p = Object.create(a.prototype);
p.constructor=b;
b.prototype = p;
var food1=new b('蛋糕',139);
food1.getPrice(); //139

00:40
其他继承介绍文章参考

扩展:深克隆与浅克隆

当对象都是原始数据类型时的拷贝

var a={name:'张三',age:50,sex:'男',id:10524};
var b={};
for(let i in a){
  b[i]=a[i];
}
b;//{name: '张三', age: 50, sex: '男', id: 10524}

这种拷贝被称之为浅克隆;
浅克隆:直接拷贝原始数据类型或者不是原始数据类型的引用;
深克隆:拷贝属性时,属性的值是否为原始数据类型,如果不是原始数据类型,则再进行一个循环进行拷贝,依次类推(也就是常见的递推);

关于赋值和浅拷贝

// 对象赋值
var obj1 = {
   'name' : 'zhangsan',
   'age' :  '18',
   'language' : [1,[2,3],[4,5]],
};
var obj2 = obj1;
obj2.name = "lisi";
obj2.language[1] = ["二","三"];
console.log('obj1',obj1)
console.log('obj2',obj2)

在这里插入图片描述

// 浅拷贝
var obj1 = {
   'name' : 'zhangsan',
   'age' :  '18',
   'language' : [1,[2,3],[4,5]],
};
var obj3 = shallowCopy(obj1);
obj3.name = "lisi";
obj3.language[1] = ["二","三"];
function shallowCopy(src) {
   var dst = {};
   for (var prop in src) {
       if (src.hasOwnProperty(prop)) {
           dst[prop] = src[prop];
       }
   }
   return dst;
}
console.log('obj1',obj1)
console.log('obj3',obj3)

在这里插入图片描述

浅拷贝的实现方式

var obj = {name:'lily', obj: {a: "kobe", b: 39} };
var initalObj = Object.assign({}, obj);
initalObj.obj.a = "wade";
initalObj.name='修改名字';
console.log(initalObj);
console.log(obj);
let arr = [1, 3, {
   username: 'kobe'
}];
let arr2=arr.concat();    
arr2[2].username = 'wade';
console.log(arr);
let arr = [1, 3, {
   username: ' kobe'
}];
let arr3 = arr.slice();
arr3[2].username = 'wade'
console.log(arr);

深拷贝的实现方式

let arr = [1, 3, {
   username: ' kobe'
}];
let arr4 = JSON.parse(JSON.stringify(arr));
arr4[2].username = 'duncan';
console.log(arr, arr4)

原理: 用JSON.stringify将对象转成JSON字符串,再用JSON.parse()把字符串解析成对象,一去一来,新的对象产生了,而且对象会开辟新的栈,实现深拷贝。

这种方法虽然可以实现数组或对象深拷贝,但不能处理函数

深拷贝的实现参考

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

第二十一节:JS中的继承 的相关文章

  • jquery ajax可以调用外部webservice吗?

    jquery ajax代码可以调用吗网络服务来自另一个域名或另一个网站 像这样 ajax type POST url http AnotherWebSite com WebService asmx HelloWorld data name
  • 如何在phonegap应用程序中使用存储在localStorage中的json feed?

    这就是我正在做的 向我的网络服务器发出请求 响应采用 json 格式 使用 jquery 模板在我的应用程序中呈现回调数据 非常简单 就像魅力一样 问题是 我想在本地存储一些数据 这样我的应用程序就不必每次都从服务器获取它 3g 很慢 每笔
  • 在淘汰赛应用程序中使用 setInterval 进行轮询实现?

    我正在尝试使用 setInterval 实现简单的轮询机制 我有一个视图模型如下 define knockout jquery function ko function ViewModel var self this setInterval
  • 单击上一页的按钮路由到该页面后如何刷新页面

    我有 2 个页面 一个主页和一个页面 2 当我单击主页上的按钮时 它会路由到 page2 现在 单击主页上的按钮后 当我到达第 2 页时 它应该刷新 因为我的项目中存在一些缓存问题 我在ngoninit上添加了window location
  • React 不响应按键事件

    我正在尝试实现一些非常基本的按键检测 但我根本无法让它工作 我有一个裸露的组件 应该在onKeyDown事件 但控制台中没有任何内容被注销 class App extends React Component constructor prop
  • 如何在 JS Rails 响应中包含 HTML?

    我有一个响应 HTML 和 JS AJAX 查询的 FooController app controllers foo controller rb class FooController lt ApplicationController l
  • less.js - 在解析器回调中获取变量值

    我正在使用 less js 1 3 0 在客户端将 less 解析为 css 在解析器的回调中 我想获取每个变量的值 我尝试了以下方法但没有成功 var data colour red example background color co
  • 使用 javascript 将 html 文本渲染为位图,无需服务器端代码

    我需要使用 javascript 代码来转换 html 中的文章 帖子 以便最终用户以位图的形式查看 有没有办法在没有服务器端代码的情况下做到这一点 example p testing text here p 您可以使用例如html2can
  • Child_process 处理带有回车符 (\r) 的 STDOUT 流

    我正在编写一个简单的应用程序 它允许工作中的内部系统请求从远程服务器到使用 REST 调用发起的另一个远程服务器的复制过程 使用 rsync 我已经对express框架足够熟悉 并且刚刚开始尝试child process库 并偶然发现了一个
  • 通过 Javascript 将图像切割成碎片

    我正在创建一个简单的拼图游戏 为了做到这一点 我需要将我正在使用的图片切成 20 块 Javascript 有没有办法将一张图片切成 20 个相等的部分 并将它们保存为网页中的 20 个不同的对象 或者我只需要进入 Photoshop 自己
  • 使用 javascript 禁用按钮:FF 与 IE

    我有一排按钮 它们都会创建一个我想在新选项卡中打开的 pdf 文件 这样按钮页面就会保持在顶部 并且 pdf 会打开以进行打印 为了防止单击按钮两次 我禁用该按钮 如下所示 我使用 python
  • 使用 jquery 通配符检查 cookie 名称

    我有一个生成动态 cookie 的表单 例如 webform 62 1234356 62 1234356 可以是任意数字 我需要使用一些通配符检查来检查名称以 webform 开头的 cookie 是否存在 下面不起作用 if cookie
  • 这种 JavaScript 编码风格有什么问题? (闭包与原型)

    我们一直在讨论如何最好地处理我们的 JS 应用程序中的对象 研究 Stoyan Stefanov 的书 阅读无数关于 新 这个 原型 闭包等的 SO 帖子 事实上有很多 而且它们有这么多相互竞争的理论 表明没有完全明显的答案 让我们假设我们
  • Angular 2 runOutsideAngular 仍然改变 UI

    从我的理解来看runOutsideAngular https angular io docs ts latest api core index NgZone class html runOutsideAngular anchor 如果我需要
  • 如何检查 Map 或 Set 是否为空?

    对于 JavaScript 中的传统对象 使用以下命令很容易检查它是否为空 Object keys method const emptyObj console log Object keys emptyObj length 0 true i
  • 水平平滑滚动 100px

    Heyjo problem 一周以来我一直在寻找 javascript 或 jQuery 代码 以便在我的网站上实现滚动按钮 我失败的那一刻是按钮应该多次工作的时候 他的任务不是滚动到专用元素 而是应该向左滚动 例如 100px 此外 滚动
  • 为什么从浏览器上传到 S3 时出现 403 错误?

    因此 我尝试查看此处之前的答案 但似乎没有任何效果 我正在使用 Dropzone 它似乎发出 OPTIONS 请求来获取所有允许的 CORS 相关信息 但它似乎没有正确返回 因此 通过查看 Chrome 开发工具 我有以下请求标头 Host
  • 一个接一个地淡入div

    大家好 我很擅长 HTML 和 CSS 但才刚刚开始接触 jQuery 的皮毛 我希望让 3 个 div 在页面加载时逐渐淡入 到目前为止我有这个 我听说使用 css 将显示设置为 none 对于任何使用非 JavaScript 浏览器的人
  • Jquery 两个字段的时间差(以小时为单位)

    我的表单中有两个字段 用户可以在其中选择输入时间 start time end time 我想在更改这些字段时重新计算另一个字段的值 我想做的是获取两次之间的小时数 例如 如果我的开始时间为 5 30 结束时间为 7 50 我想将结果 2
  • WooCommerce 使用 AJAX 设置购物车数量?

    我已经为此绞尽脑汁好几天了 需要一些指导 我正在为 WooCommerce 网站完全从头开始制作自定义主题 现在我正在尝试让购物车功能正常工作 我一直试图使用按钮 来更新购物车中产品的数量 对我来说问题似乎是WC 我在functions p

随机推荐

  • arcgis不闭合线转面_ArcGIS不闭合线转面

    ArcGIS不闭合线转面 1 打开ArcMap用Add Data加载shp Polyline线文件 2 选Editor编辑 Start Editing开始编辑 3 选Editor编辑 More Editing Tools Topology拓
  • java:hashMap: get(null)引发的对其数据结构具体形态的思考

    ref 原文 https blog csdn net fenglongmiao article details 79656198 note 我们知道HashMap集合是允许存放null值的 hashMap是根据key的hashCode来寻找
  • 软工导论知识框架(五)面向对象方法学

    传统软件工程方法学适用于中小型软件产品开发 面向对象软件工程方法学适用于大型软件产品开发 一 四要素 对象 类 继承 传递消息实现通信 二 概念 1 对象 具有相同状态的一组操作的集合 对状态和操作的封装 2 类 对具有相同状态和相同操作的
  • 带你手写基于 Spring 的可插拔式 RPC 框架(三)通信协议模块

    在写代码之前我们先要想清楚几个问题 我们的框架到底要实现什么功能 我们要实现一个远程调用的 RPC 协议 最终实现效果是什么样的 我们能像调用本地服务一样调用远程的服务 怎样实现上面的效果 前面几章已经给大家说了 使用动态代理 在客户端生成
  • CentOS7中安装mysql

    1 确保本机的mysql已经卸载干净 需要将mariadb和mysql全部卸载 rpm qa grep i mariadb rpm qa grep i mysql 使用rpm ev nodeps 命令将查询出来的文件逐一卸载 sudo rp
  • Docker Compose、Docker Swarm (docker进阶 狂神)

    文章目录 Docker Compose 安装 开源项目 博客 实战 自己编写微服务上线 Docker Swarm 四台机器安装docker环境 Swarm集群搭建 Raft协议 体会 灰度发布 金丝雀发布 其他命令学习方式 Docker C
  • notepad++以16进制查看文件

    1 Notepad 可以编辑PE文件 二进制文件即HEX码 2进制 16进制都可以 通过附加的组件HexEditor即可实现 另外一款Notepad 自带插件TextFX也有这个功能 但实现效果不如Hex Editor 下载地址 https
  • CH1-绪论

    文章目录 算法时间复杂度的计算 一 冒泡排序简介 从小到大排序 算法时间复杂度的计算 我们一般只关心随着问题规模n趋于无穷时 函数中对函数结果影响最大的项 比如说 T n 3n 3 当n非常大的时候 常数3和n的系数3对函数结果的影响就很小
  • vue--综合组件间的通信

    二 综合组件之间的通信 实现一个ToDoList 完成所有的组件的创建和使用 add点击add按钮时候 将用户输入的内容 todoinput 显示在 todolist 核心代码 兄弟组件间通信 步骤1 var bus new Vue 步骤2
  • QT210烧写UBOOT到SD卡原理以及UBOOT启动

    原文地址 http blog csdn net shushi0123 article details 8018998 世界早已进入cortex a8了 我也得跟进一下所以买了QT210的开发板 长话短说开始搞SD卡烧写UBOOT 从SD启动
  • TCP选项之SO_LINGER

    SO LINGER这个选项在我以前带队改造haproxy的时候引出过一个reset RST 客户端连接的bug SO LINGER作用 设置函数close 关闭TCP连接时的行为 缺省close 的行为是 如果有数据残留在socket发送缓
  • 手动实现 call、apply、bind

    手动实现 call apply bind 改变 this的指向 就是将函数fn放入传入的context中 然后执行context fn 此时的fn中的this就变成了context 在函数执行完毕之后 需删除context中的fn call
  • 腾讯云2核4G服务器性能如何?能安装几个网站?

    腾讯云2核4G服务器能安装多少个网站 2核4g配置能承载多少个网站 一台2核4G服务器可以安装多少个网站 阿腾云2核4G5M带宽服务器目前安装了14个网站 从技术角度是没有限制的 只要云服务器性能够用 想安装几个网站就安装几个网站 但是从公
  • Windows系统下安装Ubuntu子系统

    总共分三步 1 网上是有Windows 10版本的安装教程 链接如下 14条消息 Windows系统中安装ubutu子系统 惜洛 Jankin的博客 CSDN博客 2 补充Windows 11版本的安装 大同小异 3 如果出现报错 14条消
  • linux非阻塞socket教程

    本文并非解释什么是非阻塞socket 也不是介绍socket API的用法 取而代替的是让你感受实际工作中的代码编写 虽然很简陋 但你可以通过man手册与其它资源非富你的代码 请注意本教程所说的主题 如果细说 内容可以达到一本书内容 你会发
  • csgo 直连服务器,csgo你只可以从大厅连接此服务器解决办法

    csgo你只可以从大厅连接此服务器解决办法 请完成以下步骤刷新您的 Steam 设置 您注销并完全退出 Steam 请打开 Internet Explore Safari 或 Firefox 和并在网址列中输入 Steam flushcon
  • 高斯模糊处理

    借鉴 http blog sina com cn s blog 861912cd0101957x html http www ruanyifeng com blog 2012 11 gaussian blur html 二维高斯曲面的公式
  • input js number 整数_JS通过正则限制 input 输入框只能输入整数、小数(金额或者现金) 两位小数...

    第一 限制只能是整数 如果不是整数就直接alert 第二 限制是两位的小数 原理 通过 正则表达式判断 不满足 执行alert 第一个正则表达式是 d 表示可以是一个或者多个数字 第二个正则表达式是 d d 0 2 表示必须是数字开头 数字
  • 别踩雷了!交互设计必须遵守这10大规范!

    UI 设计师需要理解交互设计 因为不懂交互的 UI 设计师不能成为优秀的 UI 设计师 交互设计涉及用户与产品及其使用的服务之间的关系 而 UI 设计不仅仅是将功能需求可视化 还需要创造卓越的用户体验 因此 大多数 UI 设计师需要了解交互
  • 第二十一节:JS中的继承

    上节回顾 1 所有 函数 都有一个特殊属性 prototype prototype指向一个对象 称之为原型对象 原型对象上只有一个属性 constructor constructor又指向了构造函数 形成了一个闭环 2 所有 对象 都有一个