轻松理解JS 原型原型链

2024-02-01
妖精的尾巴

前言

面试中经常会问到 JavaScript 原型和原型链相关的知识点,老是陷在业务逻辑代码里面,经常不看这些知识就容易忘掉。

这是一篇关于原型和原型链内容讲解的文章,转载过来,以备学习。

原文

理解js中原型、原型链这个概念,绝对是帮助我们更深入学习js的必要一步,比如,如果js开发者想理解js继承,new关键字原理,甚至封装组件、优化代码,弄明白js中原型、原型链更是前提条件。本篇文章,用最简洁的文字,清楚明白讲解原型链相等关系和原型、原型链存在的意义,看完这篇文章,你会发现,原型、原型链原来如此简单!

图示1

上面经典的原型链相等图,根据下文的学习,你会轻易掌握。

一:原型、原型链相等关系理解

首先我们要清楚明白两个概念:

js分为函数对象和普通对象每个对象都有 __proto__ 属性但是只有函数对象才有 prototype 属性
ObjectFunction都是js内置的函数, 类似的还有我们常用到的 ArrayRegExpDateBooleanNumberString

这两个概念大家跟我一起读三遍并记住,后面会用到

那么 __proto__prototype 到底是什么,两个概念理解它们

属性 __proto__ 是一个对象它有两个属性constructor __proto__
原型对象 prototype 有一个默认的 constructor 属性用于记录实例是由哪个构造函数创建

这两个概念大家跟我一起读三遍并记住,后面会用到

有以下构造函数 Person,他的原型上有所属国属性 motherland='China'

function Person(name, age){ 
    this.name = name;
    this.age = age;
 }
 
 Person.prototype.motherland = 'China'

通过 new Person() 创建的 person01 实例

let person01 = new Person('小明', 18);

js之父在设计 js 原型、原型链的时候遵从以下两个准则

准则1原型对象即Person.prototype的constructor指向构造函数本身
Person.prototype.constructor == Person
准则2实例即person01的__proto__和原型对象指向同一个地方
person01.__proto__ == Person.prototype

这两个准则大家跟我一起读三遍并记住,后面会用到

记住以上四个概念两个准则,任何原型链相等判断都能判断正确;

大家可以对照上图,看看自己概念准则是否弄清楚了,一定要对照上图哦

// 从上方 function Foo() 开始分析这一张经典之图
function Foo()
let f1 = new Foo();
let f2 = new Foo();

f1.__proto__ = Foo.prototype; // 准则2
f2.__proto__ = Foo.prototype; // 准则2
Foo.prototype.__proto__ = Object.prototype; // 准则2 (Foo.prototype本质也是普通对象,可适用准则2)
Object.prototype.__proto__ = null; // 原型链到此停止
Foo.prototype.constructor = Foo; // 准则1
Foo.__proto__ = Function.prototype; // 准则2
Function.prototype.__proto__  = Object.prototype; //  准则2 (Function.prototype本质也是普通对象,可适用准则2)
Object.prototype.__proto__ = null; // 原型链到此停止
// **此处注意Foo 和 Function的区别, Foo是 Function的实例**

// 从中间 function Object()开始分析这一张经典之图
function Object()
let o1 = new  Object();
let o2 = new  Object();

o1.__proto__ = Object.prototype; // 准则2
o2.__proto__ = Object.prototype; // 准则2
Object.prototype.__proto__ = null; // 原型链到此停止
Object.prototype.constructor = Object; // 准则1
// 所有函数的__proto__  都和 Function.prototype指向同一个地方
Object.__proto__ = Function.prototype // 准则2 (Object本质也是函数);
// 此处有点绕
Function.prototype.__proto__ =  Object.prototype; // 准则2 (Function.prototype本质也是普通对象,可适用准则2)
Object.prototype.__proto__ = null; // 原型链到此停止

// 从下方 function Function()开始分析这一张经典之图
function Function()
Function.__proto__ = Function.prototype // 准则2
Function.prototype.constructor = Function; // 准则1

由此可以得出结论:

除了 Object 的原型对象(Object.prototype)的 __proto__ 指向 null,其他内置函数对象的原型对象(例如:Array.prototype)和自定义构造函数的 __proto__ 都指向 Object.prototype, 因为原型对象本身是普通对象。 即:

Object.prototype.__proto__ = null;
Array.prototype.__proto__ = Object.prototype;
Foo.prototype.__proto__  = Object.prototype;

二:原型、原型链的意思何在

理解了这些相等关系之后,我们思考,原型、原型链的意思何在?原型对象的作用,是用来存放实例中共有的那部份属性、方法,可以大大减少内存消耗。

用我们文章开始的 Person 构造函数和 person01 实例举例说:

console.log(person01)

打印 person01, 他有自己属性 name = '小明',age = 18; 同时通过原型链关系,他有属性 motherland = 'China'

我们再创建 person2 实例

let person02 = new Person('小花', 20);
console.log(person02)

打印 person02, 他有自己属性 name = '小花',age = 20; 同时通过原型链关系,他有属性 motherland = 'China'

看出来了没有,原型对象存放了 person01person02 共有的属性所属国 motherland = 'China'。我们不用在每个实例上添加 motherland 属性,而是将这一属性存在他们的构造函数原型对象上,对于 Person 这样的构造函数。相同的属性、方法还有很多很多,比如我们是黑头发,我们都有吃,睡这样一个方法,当相同的属性、方法越多,原型、原型链的意义越大。

那我们可以这样操作

Person.prototype.hairColor = 'black';
Person.prototype.eat = function(){
    console.log('We usually eat three meals a day.')
}
console.log(person01)
console.log(person02)

此时我们再打印 person01person02,我们惊喜的发现,他们有了属性 hairColoreat 方法;实例们动态的获得了 Person 构造函数之后添加的属性、方法,这是就是原型、原型链的意义所在!可以动态获取,可以节省内存。

另外我们还要注意:如果 person01 将头发染成了黄色,那么 hairColor 会是什么呢?

person01.hairColor = 'yellow'console.log(person01)
console.log(person02)

可以看到,person01hairColor = 'yellow', 而 person02hairColor = 'black'

实例对象重写原型上继承的属相、方法,相当于“属性覆盖、属性屏蔽”,这一操作不会改变原型上的属性、方法,自然也不会改变由统一构造函数创建的其他实例,只有修改原型对象上的属性、方法,才能改变其他实例通过原型链获得的属性、方法。

三:结语

之前我都是的看别人的文章、博客学习,并从中收获颇多,这是我第一次发表技术类文章,希望能跟大家交流技术,共同进步。如果有觉得没写讲解清楚的地方,欢迎大家评论里面指正!

四:补充例题

例题一:

function A() { }
A.prototype.n = 2
var b = new A()
A.prototype = {
  n: 3,
  m: 4
}
var c = new A()
console.log(b.n, b.m, c.n, c.m) // 打印:2 undefined 3 4

例题二:

分析代码运行结果

function F() {}
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()

原文链接:https://juejin.cn/post/6844903989088092174

文中参考代码或参考文章内容,会注明作者与出处。如有侵权,请联系删除。转载此文章请注明出处。