JS如何理解原型链之间的各种关系

80 阅读2分钟

image.png

原型、原型链

假设场景,我是一款游戏的作者,需要批量生产工具人NPC,已知基础属性有头发属性、肤色、性别以及着装等,如果每次生产的时候都需要重零打造,这样会比较耗时,这样我们会想到要构建一个模型原型,这个原型可以批量复制生产,之后再对单一属性进行个性化定制。

同样的情况在代码里的也有体现,当我们需要构造一批部分具有相同属性,但是又可以个别定制的对象时,如果我们每次都以原始的对象字面量方法来创建对象的话,就会比较耗费时间。所以在JavaScript里,我们通常会利用构造函数来创建一个对象原型,然后进行批量生产。在每一个构造函数的内部都设置有一个 prototype、__proto__、constructor 属性,

  • prototype: 包含了所有共享属性(遗传性质)
  • __proto__: 指向对象的原型(父亲)
  • constructor:指向构造函数

构造函数、原型的修改和重写

创建原始NPC通用原型

// 创建原始NPC通用原型
function Person(name) {
    this.name = name
    this.HP = 1000
    // others...
}
const npc1 = new Person('Jack')
const npc2 = new Person('Nick')

console.log(npc1) // Person {name: 'Jack', HP: 1000}
console.log(npc2) // Person {name: 'Nick', HP: 1000}

// 通过构造函数生成了两个除名字外,其他参数均相同的NPC角色

修改原型、重写原型

// 对NPC进行定制,比如另一个种族的NPC,他们比普通NPC的生命值更大
// 修改原型
Person.prototype.setHP = function(hp) {
    this.HP = hp
}
const npc1 = new Person('Jack')
const npc2 = new Person('Nick')
npc1.setHP(1500)
console.log(npc1) // Person {name: 'Jack', HP: 1500} 血量值修改成功
console.log(npc2) // Person {name: 'Nick', HP: 1000} 血量值不受影响,原有继承属性值不变

// 重写原型
Person.prototype.HP = 3000
const npc1 = new Person('Jack')
const npc2 = new Person('Nick')
// 共享遗传基础属性变更为3000
console.log(npc1) // Person {name: 'Jack', HP: 3000} 
console.log(npc2) // Person {name: 'Nick', HP: 3000} 

原型链的指向

npc1.__proto__ // __proto__指向原型(共有属性对象)
npc1.__proto__ === person.prototype
npc1.prototype // undefined 没有自己的共享属性
npc1.constructor === person

Person.__proto__ // 构造函数的原型是js里的标准内置函数
Person.__proto__ === Function.prototype // true
Person.constructor === Function

// js是单继承的,Object.prototype是原型链的顶端,但是Object本身也是构造函数,函数的继承的是函数的标准对象(不是顶层对象是Function)
Function.prototype.__proto__ == Object.prototype
Person.prototype.__proto__ === Object.prototype
==> Function.prototype.__proto__ === Person.prototype.__proto__
==> Person.prototype === npc1.__proto__ 
==> npc1.__proto__.__proto__ === Object.prototype
// 比较有意思的特点
// js是面向对象变成的,即一切皆对象
    Function === Object.constructor // 内置函数继承于内置对象
    Function.constructor === Function // 内置函数的构造函数等于他本身
    
    ==> Function.constructor === Object.constructor
    
    Function.__proto__ === Object.__proto__
    
// 根据上述等式推论测试练习
npc1.constructor.__proto__ === Function.prototype
npc1.constructor.prototype.__proto__ === Object.prototype
person.constructor.__proto__ === Object.prototype

原型链的终点

image.png

获取原型、判断是否在属于原型属性

  • 获取原型Object.getPrototypeOf()
  • 判断是否属于原型上的属性hasOwnProperty()