JS 原型链

简介: JS 原型链

JS 原型链

1. 原型和原型链的基础结论

1.1 函数与对象的关系

  • 函数是对象,对象都是通过函数创建的。
  • 函数与对象并不是简单的包含与被包含的关系。

1.2 原型的类别

  • 显示原型:prototype,是每个函数function独有的属性。
  • 隐式原型: __proto__,是每个对象都具有的属性。

1.3 原型和原型链

  • 原型:一个函数可以看成一个类,原型是所有类都有的一个属性,原型的作用就是给这个类的一个对象都添加一个统一的方法。
  • 原型链:每个对象都有一个__proto__,它指向它的prototype原型对象;它的prototype原型对象又有一个__proto__指向它的prototype原型对象,就这样层层向上直到最终找到顶级对象Objectprototype,这个查询路径就是原型链。

1.4 JavaScript 里最顶层的两个概念

  • Function 是最顶层的构造器

FunctionJavaScript 里最顶层的构造器,它构造了系统中的所有对象,包括定义对象、系统内置对象、甚至包括它自己。

  • Object 是最顶层的对象
  • 所有对象都是继承 Object 的原型
  • Object 也是被 Function 构造出来的

1.5 instanceof

obj instanceof F

  • **常见的不够正确描述:**用来判断一个对象是否是某个构造函数的实例,比如我们创建一个函数,并且将它实例化
  • df6a5aed1a31e09e3066d80f8559ccf6.png


  • 「正确的描述:」obj.__proto__.__proto__... => F.prototype。沿着对象obj的原型链查找是否存在对象F.prototype,若存在则返回true,若查找到原型链的终点Object.prototype仍未找到,则返回false

2. 经典的原型和原型链的分析

「接下来我们将主要讲解以下类别:」

92b3d5bb781f9c2192603f71c63546fe.png

2.1 函数.prototype

  • **前提结论:**函数都是对象,每个函数都自带一个属性叫做 prototype
  • 栗子:

0cb0c126ea44c56e61608c875edfd227.pngimage-20220110144624578

  • 「详细结构图:」

33833d286a2d0d71f0cafbe0eaf105f2.pngimage-20220110144700156

  • **最终结论:**每个函数下其实有个prototype属性,prototype的属性值是一个对象,这个对象默认的只有一个叫做constructor的属性,指向这个函数本身。

2.2 对象.__proto__

  • **前提结论:**每个对象都有一个隐藏的属性叫做__proto__
  • 例子:

cfb0b910a50b55cad6a0ccad4dc600a4.png

  • 「解释:」
  • [[Prototype]]:是对象的一个内部属性, chrome的引擎通过__proto__向外暴露了这个属性。实际上它可以看作就是对象的__proto__属性
  • __proto__的值:等于构造该对象的函数的prototype属性。testObj.__proto__ === testFn.prototype
  • **结论:**每个对象都有一个__proto__属性,指向创建该对象的函数的prototype

e6e7c9b661acd77b89716bf99419d525.png

2.3 函数.__proto__

  • **前提结论:**在JavaScript中,函数都是对象,是对象就有隐藏的__proto__属性
  • 「解释:」Function是最顶级的构造器,函数对象都是通过它构造的
  • 「结论:」函数.__proto__ === Function.prototype

2c1f0e39dde70e55ade0d79774ef8b74.png

2.4 函数.prototype.__proto__

  • 「解释:」函数 .prototype,它本质上是和var obj = {}是一样的,由new Object创建的
  • 「结论:」函数.protype.__proto__ === Object.prototype

e9f8769af53401619a7a1805237cf613.png

2.5 Object.__proto__

  • 「解释:」Object是由顶层构造函数Function构造的
  • 「结论:」Object.__proto__ === Function.prototype

59a1dc54b2c81a1baa3a72cf1136aca1.png

2.6 Object.prototype.__proto__

  • 「结论:」Object.prototype较为特殊,它是顶级对象的原型,所以它的__proto__指向是null

753a3e8af805c0679e41f3fccc0793a6.png

2.7 Function.__proto__

  • **解释:**函数对象都是由被顶级构造函数Function创建。所以Function是被自身创建的
  • 「结论:」Function.__proto__ === Function.prototype

c2624e1d12303b67a2151e508220c9bc.png

2.8 Function.prototype.__proto__

  • **解释:**函数 prototype,它本质上是和var obj = {}是一样的,由new Object创建的
  • 「结论:」Function.prototype.__proto__ === Object.prototype

09cb25274389129edc7d7268ebfdffdf.png

「最终我们将形成一个经典的原型图:」

db95bc6fb5e5fdd88cfda462190c66bc.png

3. 基于原型链的继承

JavaScript 对象有一个指向一个原型对象的链。当试图访问一个对象的属性时,它不仅仅在该对象上搜寻,还会搜寻该对象的原型,以及该对象的原型的原型,依次层层向上搜索,直到找到一个名字匹配的属性或到达原型链的末尾。

遵循ECMAScript标准,someObject.[[Prototype]] 符号是用于指向 someObject 的原型。从 ECMAScript 6 开始,[[Prototype]] 可以通过 Object.getPrototypeOf()Object.setPrototypeOf() 访问器来访问。这个等同于 JavaScript 的非标准但许多浏览器实现的属性 __proto__

但它不应该与构造函数 funcprototype 属性相混淆。被构造函数创建的实例对象的 [[Prototype]] 指向 funcprototype 属性。Object.prototype 属性表示 Object 的原型对象。

这里演示当尝试访问属性时会发生什么:

// 让我们从一个函数里创建一个对象o,它自身拥有属性a和b的:
let f = function () {
   this.a = 1;
   this.b = 2;
}
/* 这么写也一样
function f() {
  this.a = 1;
  this.b = 2;
}
*/
let o = new f(); // {a: 1, b: 2}
// 在f函数的原型上定义属性
f.prototype.b = 3;
f.prototype.c = 4;
// 不要在 f 函数的原型上直接定义 f.prototype = {b:3,c:4};这样会直接打破原型链
// o.[[Prototype]] 有属性 b 和 c
//  (其实就是 o.__proto__ 或者 o.constructor.prototype)
// o.[[Prototype]].[[Prototype]] 是 Object.prototype.
// 最后o.[[Prototype]].[[Prototype]].[[Prototype]]是null
// 这就是原型链的末尾,即 null,
// 根据定义,null 就是没有 [[Prototype]]。
// 综上,整个原型链如下:
// {a:1, b:2} ---> {b:3, c:4} ---> Object.prototype---> null
console.log(o.a); // 1
// a是o的自身属性吗?是的,该属性的值为 1
console.log(o.b); // 2
// b是o的自身属性吗?是的,该属性的值为 2
// 原型上也有一个'b'属性,但是它不会被访问到。
// 这种情况被称为"属性遮蔽 (property shadowing)"
console.log(o.c); // 4
// c是o的自身属性吗?不是,那看看它的原型上有没有
// c是o.[[Prototype]]的属性吗?是的,该属性的值为 4
console.log(o.d); // undefined
// d 是 o 的自身属性吗?不是,那看看它的原型上有没有
// d 是 o.[[Prototype]] 的属性吗?不是,那看看它的原型上有没有
// o.[[Prototype]].[[Prototype]] 为 null,停止搜索
// 找不到 d 属性,返回 undefined

4. 检验

最后,我们通过四个小问题来检验一下是否真的掌握了它:

  • 题目 1
var A = function() {};
A.prototype.n = 1;
var b = new A();
A.prototype = {
  n: 2,
  m: 3
}
var c = new A();
console.log(b.n);
console.log(b.m);
console.log(c.n);
console.log(c.m);

请写出上面编程的输出结果是什么?

  • 题目 2
var F = function() {};
Object.prototype.a = function() {
  console.log('a');
};
Function.prototype.b = function() {
  console.log('b');
}
var f = new F();
f.a();
f.b();
F.a();
F.b();

请写出上面编程的输出结果是什么?

  • 题目 3
function Person(name) {
    this.name = name
}
let p = new Person('Tom');

问题1: p.__proto__等于什么?

问题2:Person.__proto__等于什么?

  • 题目 4
var foo = {},
    F = function(){};
Object.prototype.a = 'value a';
Function.prototype.b = 'value b';
console.log(foo.a);
console.log(foo.b);
console.log(F.a);
console.log(F.b);

请写出上面编程的输出结果是什么?

  • 题目 1 答案:
b.n -> 1
b.m -> undefined;
c.n -> 2;
c.m -> 3;
  • 题目 2 答案:
f.a() -> a
f.b() -> f.b is not a function
F.a() -> a
F.b() -> b
  • 题目 3 答案

答案1:Person.prototype

答案2:Function.prototype

  • 题目 4 答案
foo.a => value a
foo.b => undefined
F.a => value a
F.b => value b

大家都掌握了吗?

相关文章
|
4月前
|
JavaScript 前端开发
谈谈对 JavaScript 中的原型链的理解。
JavaScript中的原型链是实现继承和共享属性的关键机制,它通过对象的`prototype`属性连接原型对象。当访问对象属性时,若对象本身没有该属性,则会查找原型链。此机制减少内存占用,实现代码复用。例如,实例对象可继承原型对象的方法。原型链也用于继承,子类通过原型链获取父类属性和方法。然而,原型属性共享可能导致数据冲突,且查找过程可能影响性能。理解原型链对JavaScript面向对象编程至关重要。如有更多问题,欢迎继续探讨😊
32 3
|
4月前
|
JavaScript 前端开发 安全
JavaScript原型链的使用
【4月更文挑战第22天】JavaScript中的原型链是理解继承的关键,它允许对象复用属性和方法,减少代码冗余。示例展示如何通过原型链实现继承、扩展内置对象、构造函数与原型链的关系以及查找机制。应注意避免修改`Object.prototype`,使用安全方式设置原型链,并谨慎处理构造函数和副作用。
|
26天前
|
开发者 图形学 iOS开发
掌握Unity的跨平台部署与发布秘籍,让你的游戏作品在多个平台上大放异彩——从基础设置到高级优化,深入解析一站式游戏开发解决方案的每一个细节,带你领略高效发布流程的魅力所在
【8月更文挑战第31天】跨平台游戏开发是当今游戏产业的热点,尤其在移动设备普及的背景下更为重要。作为领先的游戏开发引擎,Unity以其卓越的跨平台支持能力脱颖而出,能够将游戏轻松部署至iOS、Android、PC、Mac、Web及游戏主机等多个平台。本文通过杂文形式探讨Unity在各平台的部署与发布策略,并提供具体实例,涵盖项目设置、性能优化、打包流程及发布前准备等关键环节,助力开发者充分利用Unity的强大功能,实现多平台游戏开发。
49 0
|
1月前
|
JavaScript 前端开发 开发者
揭开JavaScript的神秘面纱:原型链背后隐藏的继承秘密
【8月更文挑战第23天】原型链是JavaScript面向对象编程的核心特性,它使对象能继承另一个对象的属性和方法。每个对象内部都有一个[[Prototype]]属性指向其原型对象,形成链式结构。访问对象属性时,若当前对象不存在该属性,则沿原型链向上查找。
25 0
|
3月前
|
设计模式 JavaScript 前端开发
【JavaScript】深入浅出JavaScript继承机制:解密原型、原型链与面向对象实战攻略
JavaScript的继承机制基于原型链,它定义了对象属性和方法的查找规则。每个对象都有一个原型,通过原型链,对象能访问到构造函数原型上的方法。例如`Animal.prototype`上的`speak`方法可被`Animal`实例访问。原型链的尽头是`Object.prototype`,其`[[Prototype]]`为`null`。继承方式包括原型链继承(通过`Object.create`)、构造函数继承(使用`call`或`apply`)和组合继承(结合两者)。ES6的`class`语法是语法糖,但底层仍基于原型。继承选择应根据需求,理解原型链原理对JavaScript面向对象编程至关重要
83 7
【JavaScript】深入浅出JavaScript继承机制:解密原型、原型链与面向对象实战攻略
|
3月前
|
JavaScript
js奥义:原型与原型链(1)
js奥义:原型与原型链(1)
|
3月前
|
JavaScript 前端开发
JavaScript进阶-原型链与继承
【6月更文挑战第18天】JavaScript的原型链和继承是其面向对象编程的核心。每个对象都有一个指向原型的对象链,当查找属性时会沿着此链搜索。原型链可能导致污染、效率下降及构造函数与原型混淆的问题,应谨慎扩展原生原型、保持原型结构简洁并使用`Object.create`或ES6的`class`。继承方式包括原型链、构造函数、组合继承和ES6的Class继承,需避免循环引用、方法覆盖和不当的构造函数使用。通过代码示例展示了这两种继承形式,理解并有效利用这些机制能提升代码质量。
61 5
|
2月前
|
JavaScript C++
js【详解】原型 vs 原型链
js【详解】原型 vs 原型链
30 0
|
3月前
|
JavaScript 前端开发
JS原型链
JS原型链
22 0
|
4月前
|
前端开发 JavaScript
前端 js 经典:原型对象和原型链
前端 js 经典:原型对象和原型链
38 1