原型链和继承

类、 prototype__proto__、实例、constructor之间的关联

1. prototype 和 constructor

我们创建的每个函数都有一个prototype(原型)属性,这个属性是一个指针,指向一个对象,而这个对象的用途是包含可以由特定类型的所有实例共享的属性和方法。
无论什么时候,只要创建了一个新函数,就会根据一组特定的规则为该函数创建一个prototype属性,这个属性指向函数的原型对象。
在默认情况下,所有原型对象都会自动获得一个constructor(构造函数)属性,这个属性包含一个指向 prototype属性所在函数的指针。

对于构造函数来说,生成实例的时候,这个原型对象prototype 会自动成为实例对象的原型

2. __proto__

每个实例对象(object )都有一个私有属性(称之为 proto)指向它的原型对象(prototype)。

图片来自MDN.png
function Cat(name){
    this.name = name;
}
Cat.prototype.sayName = function(){
    console.log('My name is :' + this.name);
}
var cat1 = new Cat('Munchkin')
cat1.sayName();
  • 通过函数定义了类 Cat,类(函数)自动获得属性prototype
  • Cat 的 prototype 也是一个对象,内部有一个 constructor 属性,该属性指向 Cat
Cat.prototype.constructor === Cat // true
  • 每个类的实例都会有一个内部属性__proto__,在这里是cat1 ,cat1 的 __proto__ 指向类的prototype属性
Cat.prototype === cat1.__proto__  // true
cat1.__proto__.constructor === Cat.prototype.constructor // true
image.png

原型链

JavaScript 规定,每个函数都有一个 prototype 属性,该属性指向一个对象。
对于构造函数来说,生成实例的时候,这个原型对象prototype 会自动成为实例对象的原型
每个实例对象(object )都有一个私有属性(称之为 __proto__)指向它的原型对象(prototype)。
该原型对象也有一个自己的原型对象 ,层层向上直到一个对象的原型对象为 null,这就形成了一个“原型链”。根据定义,null 没有原型,并作为这个原型链中的最后一个环节。

所有对象的原型都可以最终追溯到 Object.prototype,即Object构造函数的prototype属性。
原型链的尽头是null , null 没有任何属性和方法,也没有自己的原型。

一点点补充: 函数与对象的恩怨
  • 函数是一种特殊的对象
  • 所有的构造函数都是 Function 的实例~

所以:

Function.__proto__.__proto__ === Object.prototype  // true
 Object.__proto__ === Function.prototype // true

与原型和原型链相关的一些方法

instanceof

1. 作用
instanceof 运算符返回一个布尔值,表示对象是否为某个构造函数的实例。

2. 使用方法
实例对象 instanceof 构造函数

3. 原理
它会检查右边构造函数的原型对象(prototype),是否在左边实例对象的原型链上。
大概就是这样子:

function _instanceof(obj,Fn) {
 if(obj.__proto__){
    if(obj.__proto__ === Fn.prototype) {
    return true
  } else {
    return _instanceof(obj.__proto__,Fn)
  }
 } else {
   return false
 }
}

let arr = [];

_instanceof(arr, Array ); // true
_instanceof(arr, Object ); // true

3. tips

  • instanceof会遍历整个原型链,所以一个实例对象可能会对多个构造函数返回 true
  • 如果一个对象的原型是 null ,instanceof 的判断会失真
Object.create(null) instanceof Object // false
  • instanceof运算符只能用于对象,不适用原始类型的值
Object.create()

Object.create(proto, [propertiesObject])

Object.create()方法创建一个拥有指定原型和若干个指定属性的对象。

如果想生成一个不继承任何属性的对象,可以将Object.create() 的参数设为 null

hasOwnProperty

obj.hasOwnProperty(prop)

实例对象的 hasOwnProperty 方法返回一个布尔值,用于判断某个属性定义在对象自身还是定义在原型链上。

eg.

({}).hasOwnProperty('toString')    //  false

hasOwnProperty 方法使JavaScript 中唯一一个处理对象属性时,不会遍历原型链的方法。


继承

extends 关键字实现继承
  class Animal {
    constructor() {
      this.body = '肉体'
    }
    eat() {
      console.log('eat')
    }
  }
  class Human extends Animal {
    constructor(name) {
      super() // 子类中存在构造函数的话,则需要在使用“this”之前首先调用 super()
      this.name = name
    }
    cook() {
      console.log('cook')
    }
  }
  var p1 = new Human('xx')
原型链实现继承
使用Object.create()
function Animal() {
  this.body = 'body'
}
Animal.prototype.eat = function () {
  console.log('eat')
}

function Human(name) {
  Animal.call(this)
  this.name = name
}

Human.prototype = Object.create(Animal.prototype);
Human.prototype.constructor = Human; // 这一步不能省! 修改原型对象的同时,也要记得修改constructor属性的指向!
Human.prototype.cook = function () {
  console.log('cook')
}

var p1 = new Human('xx')
p1.__proto__.constructor === Human // true
不使用Object.create()
function Animal() {
  this.body = 'body'
}
Animal.prototype.eat = function () {
  console.log('eat')
}

function Human(name) {
  Animal.call(this)
  this.name = name
}

// Human.prototype.__proto__ = Animal.prototype // 非法,用下面三句话代替
var F = function() {}
F.prototype = Animal.prototype;
Human.prototype = new F()

Human.prototype.constructor = Human; // 这一步不能省! 修改原型对象的同时,也要记得修改constructor属性的指向!
Human.prototype.cook = function () {
  console.log('cook')
}

var p1 = new Human('xx')
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
【社区内容提示】社区部分内容疑似由AI辅助生成,浏览时请结合常识与多方信息审慎甄别。
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

相关阅读更多精彩内容

友情链接更多精彩内容