第二十一节: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中的继承 的相关文章

  • d3力定向布局-链接距离优先

    在 d3 中使用力导向布局 如何使链接距离成为优先事项 同时仍然保持良好的图形布局 如果我指定动态链接距离 但保留默认费用 则我的图形距离会因费用函数而发生一些变形 并且不再是准确的距离 但是 如果我删除电荷 图表将如下所示 任何建议表示赞
  • 从未使用 mimeType 初始化的 MediaRecorder 获取 mimeType

    我正在使用 MediaRecorder API 在页面上录制一些媒体 在我的 MediaRecorder 初始化中 我没有指定内容类型 因为我不需要任何特别的内容 浏览器可以选择它想要的 var mediaRecorder new Medi
  • 无法从 JSON 请求获取数据,尽管我知道它已返回

    我试图获取从 getJSON 返回的数据 但我无法让它工作 我已经在 search twitter API 上尝试了相同的代码 效果很好 但它不适用于其他网站 我知道数据已返回 因为我在使用检查器时可以找到它 我通过检查器找到的值是 id
  • 在 Firefox 中使用 Javascript 检测键盘布局

    有没有办法在 Firefox 中检测客户端的键盘布局 我知道 Chrome 的答案是肯定的 请参阅https developer mozilla org en US docs Web API Navigator keyboard https
  • 如何制作过期/签名视频嵌入网址

    我是新来的 正在学习网络开发等等 我只知道如何将我的视频嵌入网站中 任何菜鸟都可以轻松获得源代码 他们也可以嵌入它 但在许多网站中 视频 src 均使用重定向器链接进行编码 例如 它会在一段时间后过期 在本例中是一天 我了解到这是一个签名网
  • React 错误:目标容器不是 DOM 元素

    我刚刚开始使用 React 所以这可能是一个非常简单的错误 但我们开始吧 我的html代码非常简单 load staticfiles
  • JavaScript 数组和对象除了 .length 属性之外有什么区别?

    我认为 JS 数组只是一个哈希映射 它只接受整数值作为键 length 属性只返回最大索引 1 这是正确的吗 还有其他区别吗 您错了 数组可以有任何你想要的键 此外 他们还继承了Array原型
  • 当最初在范围内设置值时,日期选择器弹出格式不起作用

    我正在使用 Angular UI 引导日期选择器弹出窗口 并在 Plunker 上使用此自定义指令 http plnkr co edit 053VJYm1MpZUiKwFTfrT p preview http plnkr co edit 0
  • popstate - 需要单击两次后退按钮才能真正返回

    我正在创建一个单页面并使用 PushState 来更改地址 现在 如果我向后推 则会触发 popstate 并且我想要使页面以动画方式从当前位置滚动到最后一个位置 当然 这是可行的 但页面会跳转到顶部 有谁知道如何防止这种行为 我正在使用
  • 在所有浏览器中启用我的网站的平滑滚动

    我正在开发一个视差滚动网站Stellar http markdalgleish com projects stellar js and Skrollr https github com Prinzhorn skrollr图书馆 该网站在 F
  • console.log 未显示正确的值[重复]

    这个问题在这里已经有答案了 我正在尝试控制台一个对象 尽管它没有抛出任何错误 但我想要的结果在一段时间后显示 但我无法检索它 它显示一个 i 图标 上面写着 刚刚评估了下面的值 但我无法获取该值 OUTPUT Promise state s
  • jQuery 动画延迟

    如何使用 jQuery 延迟动画 我需要获得一个导航来扩大宽度 然后扩大高度 然后反转以获得反向动画 Code function nav li not logo nav li ul li hover function this animat
  • 在单个 mongodb 查询中查找并计数

    我的文档看起来像这样 id ObjectId 572c4bffd073dd581edae045 name What s New in PHP 7 description PHP 7 is the first new major versio
  • Highcharts - 使用选定的饼图切片获得 3D 效果

    在 highcharts 中 我试图使当用户选择或将鼠标悬停在饼图的切片上时 该切片会产生沿 z 轴 朝向用户 上升的效果 我试图通过 css 设置阴影过滤器并使切片的边框更宽 填充颜色相同 来实现此目的 然而 我面临的问题是切片仍然可以位
  • Angular 4 显示其中的数据

    我不喜欢从 API 返回到我的 Angular 4 应用程序的数据 这是 JSON 的示例 我不关心美元 但这是我正在处理的数据类型 最终目标是在页面上展示 Coin Price BTC 4 281 28 ETH 294 62 etc JS
  • 如何计算单击的甜甜圈元素的中点与负 y 轴之间的角度

    Consider the following codesample donut chart using jquery flot http jsfiddle net c5zsg6y3 26 now as i have added the im
  • 使用 facebook 共享动态更新元标签 - Angular 6

    我需要动态更新元标记 如 og title og description 和 og image 并在 facebook 上共享相同的内容 我已经尝试了所有方法 但没有任何效果 首先 我尝试使用 javascript 设置元标记 如下所示 v
  • 使用 JavaScript 格式化日期

    JavaScript 中的日期格式有问题 这是我的函数代码 originalDate 2016 03 02 09 12 14 989522 var d new Date originalDate month d getMonth 1 day
  • 需要使用 iFrame API 隐藏 YouTube 品牌

    我正在使用 YouTube iFrame API 在我的自定义播放器 javascript 播放器 中加载视频 我需要隐藏 Youtube 品牌 但是在 iOS 设备上 它显示带有以下参数的徽标 playerVars fs 1 autopl
  • Chrome 扩展程序可以相互通信吗?

    我正在编写一个Chrome扩展程序 并且想要实现一个接口或api 以便我将来制作的其他扩展程序可以使用它 最终的效果可能如下 分机 B 呼叫extensionA someMethod someParameters 并向分机A发送一些数据 分

随机推荐

  • 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 所有 对象 都有一个