IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> JavaScript知识库 -> JavaScript 原型、继承、原型链 -> 正文阅读

[JavaScript知识库]JavaScript 原型、继承、原型链


前言

部分理论参考《JavaScript权威指南》
每一个对象都有与之相关的原型(prototype), 原型是在实例创建之初就设定好的, 作用是继承属性.
使用new调用构造函数会自动创建一个新对象, 因此构造函数本身只需要初始化这个新对象的状态即可. 调用构造函数的一个重要特征是构造函数的prototype属性将会作为新对象的原型__proto__.
通过new表达式创建的对象通常会继承一个constructor属性, 这个属性指代创建这个对象的构造函数, 因此一个对象的原型应该为它的constructor.prototype, 即其构造函数的原型(原型属性), 但对于由Object.create()创建的对象往往不是如此.

一、何时出现继承情况?

在JavaScript中, 只有在查询属性时才能体会到继承的存在, 而设置属性(赋值和加入新属性)和继承无关, 这是JavaScript的一个重要特性, 该特性让编程者得以有选择性的覆盖继承来的属性.

举例一个设置属性的操作, 说明一下设置值过程中无法体现继承:

  1. 向对象内设置属性和赋值, 也总是只在原始对象里创建一个属性或者赋值已有的属性, 而不会对原型链进行操作.
function A() {
  this.do = function() { return 'foo'; }
}

A.prototype = function() {
  this.do = function() { return 'bar'; }
}

var x = new A().do();

new A(), 构造出原型指向function A的对象, 对象上有个do方法;
"调用时优先去自身实例上寻找方法, 如果找到将不再去原型上寻找."按照这个规则来, 不需要看A.prototype赋值这步即可得出foo的答案.

算上A.prototype赋值这步的话, A.prototype赋值只是改变A.prototype的值为一个表达式, 这并没有向原型上绑定任何方法(绑的话一般A.prototype.do=xxx格式).

但是哪怕只是在赋值, 这步也算在重写原型了, 重写原型后, 新原型与已存在的实例的联系遭到切断, 而已存在的实例仍旧会选择引用以前的原型(而以前的原型又不在了, 所以会找不到).
放在这里, 如果"优先去实例上寻找"这条规则已经无法解决问题, 实例上已经无法找到, 直接去原型A.prototype寻找do方法的话, 会发现A.prototype已经被赋值为函数, 无法找到而导致报错.

所以调用do()方法调用的实际上还是第一步在原型上的do方法, 那么结果就为foo;


二、继承的体现&原型链的形成

对比一个查找属性的操作:

  1. 现在要在对象中查找属性x, 如果对象中不存在x, 那么将会去这个对象的原型对象中继续寻找x;
  2. 如果原型对象里也没有x, 但是这个原型对象还有它自己的原型, 那么就再去这个原型对象的原型上寻找x;
  3. 就这样一级一级的往上找, 直到找到x或者查找到一个原型是null的原型为止;
  4. 由这一条也可以看出原型对象一级一级构成了一种链状结构, 即常说的原型链, 属性的继承即是通过原型链来实现的.

如果两个对象继承自同一个原型, 往往意味着他们是由同一个构造函数创建并初始化的.

一个简单的原型链, 从grandpa到son, son寻找num沿着原型链一路向上找, 最后在grandpa的原型里找到, 作为自己原型中的num.

function grandpa() {
  this.num = 935;
}
//此时new grandpa()会构造出对象"{ num: 935 }"

var vardad = function dad() { }
vardad.prototype = new grandpa();  //第一段链

var varson = function son() { };
varson.prototype = new vardad();  //第二段链

console.log(varson.prototype.num);

在这里插入图片描述


三、例-求以下代码的输出

function father() {
  this.num = 935;
  this.work = [ 'read', 'write', 'listen' ];
}

function son() { }
son.prototype = new father();
let son1 = new son();
let son2 = new son();
son1.num = 117;
son1.work.pop();
console.log(son2.num);
console.log(son2.work);

son.prototype本该为空对象, 现在被赋值为father构造的对象{num: 935, work: [ 'read', 'write', 'listen' ]};
let son1和son2以function son()为原型构造的仍旧都是空对象, 此时son1被加入num属性赋值为117(对象是可以直接这样加属性的, 所以不用去实例或者原型上找num);
但是work加入后没有值直接被执行了方法, 对于work如果依旧在实例上直接找那么完全不行, son1实例上本来就是空的, 现在只有个num, 那么必须从初始赋值了father构造对象的son原型上寻找work, 得到work为[ 'read', 'write', 'listen' ], 执行pop(), 直接把function son()原型的work改成了[ ‘read’, ‘write’ ];

然后做完发现和son1没有半毛钱关系, 他输出的son2.

那就只能num, work全去原型上找了, 得到结果935, 和被son1 pop()过的work: [ ‘read’, ‘write’ ]


总结

复习点理论.

  JavaScript知识库 最新文章
ES6的相关知识点
react 函数式组件 & react其他一些总结
Vue基础超详细
前端JS也可以连点成线(Vue中运用 AntVG6)
Vue事件处理的基本使用
Vue后台项目的记录 (一)
前后端分离vue跨域,devServer配置proxy代理
TypeScript
初识vuex
vue项目安装包指令收集
上一篇文章      下一篇文章      查看所有文章
加:2022-06-25 18:01:20  更:2022-06-25 18:03:21 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年11日历 -2024/11/23 16:59:13-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码