关于原型、原型链我所知道的

771 阅读3分钟

持续创作,加速成长!这是我参与「掘金日新计划 · 10 月更文挑战」的第5天,点击查看活动详情

结论:prototype 指向函数的原型对象,__proto__ 是指针。原型链则是通过 __proto__ 指向其原型对象,串联起链表。主要解决对象的继承问题。

什么是原型 prototype

JS 中的函数也是对象。如何证明函数也是对象?函数作为表达式被创建,并且可以添加任意的属性。

function fn() {}
fn.sex = 'male'
console.log(fn.sex) // male 函数可以添加任意的属性

// 利用 Object.create 方法,这个方法只有对象才能使用
function fn() {}
const obj = Object.create(fn); 
// Object.create('') 则会报错,报错内容: Object prototype may only be an Object or null
console.log(Object.getPrototypeOf(obj) === fn); // true

在 JS 中,每当定义一个对象的时候,对象中都会包含一些预定义的属性。其中每个函数对象都有一个 prototype 属性,这个属性指向函数的原型对象

使用原型对象的好处是所有对象实例共享它所包含的属性和方法。在做构造函数的时候经常会用到这个特性。

什么是原型链?什么是__proto__

__proto__ 是指针。原型链则是通过__proto__ 串联起来的链表。主要解决对象的继承问题。

先来看一段代码:

function Parent (name) {
  this.name = name
}

Parent.prototype.showName = function() {
  console.log(this.name)
}

const p = new Parent('kane')
p.showName()

console.log(p.__proto__ === Parent.prototype) // true

构造函数 Parent、Parent.prototype 和 实例 p 之间的关系:

image.png

每个实例对象都拥有一个原型对象,通过 __proto__ 指向其原型对象,并从中继承方法和属性。

同时原型对象也可能拥有他的原型对象,这样一层一层,最终指向 null(Object.proptotype.__proto__ 指向的是 null)。这种关系被称为原型链 (prototype chain)。

通过原型链,一个对象可以拥有定义在其他对象中的(继承过来的)属性和方法。

prototype__proto__ 区别是什么?

prototype 是构造函数的属性。

__proto__ 是每个实例都有的属性,可以访问 [[prototype]]

实例的 __proto__ 与其构造函数的 prototype 指向的是同一个对象。

function Parent (name) {
  this.name = name
}

Parent.prototype.showName = function() {
  console.log(this.name)
}

const p = new Parent('kane')
p.showName()

console.log(p.__proto__)

console.log(Object.getPrototypeOf(p))

console.log(Parent.prototype);

console.log(p.__proto__ === Parent.prototype) // true

class Human {
  constructor(name) {
    this.name = name;
  }
  say() {
    console.log('hello');
  }
}

class Man extends Human {
  constructor(name) {
    super(name);
  }
  run() {
    console.log('running')
  }
}

const man = new Man('kane')
man.say()
man.run()

console.log(Object.getPrototypeOf(man) === Man.prototype) // true
console.log(Object.getPrototypeOf(man) === Human.prototype) // false

proto.png

如何实现 instanceof

特点

  1. 可以准确的判断复杂数据类型,但是不能正确判断基本数据类型
  2. 在原型链上的结果未必准确
  3. 不能检测 null,undefined

通过原型链判断的,A instanceof B:

  1. 在 A 的原型链中层层查找,是否有原型等于 B.prototype
  2. 如果一直找到 A 的原型链的顶端 (null; 即 Object.prototype.__proto__), 仍然不等于 B.prototype,那么返回 false,否则返回 true。
// L instanceof R
function myInstanceOf(L, R) {// L 表示左表达式,R 表示右表达式
  let O = R.prototype;// 取 R 的显式原型
  L = L.__proto__;    // 取 L 的隐式原型 
  // L = Object.getPrototypeOf(L) // 获取对象的原型
  while (true) {
      if (L === null) // 已经找到顶层
          return false;
      if (O === L)   // 当 O 严格等于 L 时,返回 true
          return true;
      L = L.__proto__;  // 继续向上一层原型链查找
  }
}

let a = [1,2,3]
console.log(myInstanceOf(a, String)); // false