JavaScript基础篇-原型与原型链

157 阅读3分钟

前言

首先对象都有原型和原型链,通过原型链我们可以找到对象的"继承",可以通过原型链找到父类以及顶级 Object 的参数和方法,并且使用它们。在对象中又包含一个隐式原型(__proto__)和一个显式原型(prototype),构造函数通过 prototype 可以找到它们的原型,而实例函数可以通过 __proto__ 找到它们的原型。

创建对象

创建对象一般有三种方法,字面量、构造函数、Object.create

// 第一种:字面量
var o1 = { name: 'o1' }
var o2 = new Object({ name: 'o2' })

// 第二种:构造函数
var M = function(name) { this.name = name }
var o3 = new M('o3')

// 第三种:Object.create
var P = { name: 'p' }
var o4 = Object.create(P)

构造函数和普通函数的区别

  • 如果一个函数被 new 实例后,那么这个函数就是构造函数
  • 一般构造函数的函数名首字母都要大写
  • 构造函数中都会有自己的 this,而普通函数中的 this 是指向 window 的

构造函数、实例函数、原型对象、原型链

名词的解释

  • 构造函数:创建一个函数并且该函数被 new 实例化过
  • 实例函数:通过 new 实例化一个函数
  • 原型对象:构造函数的原型,可以在上面新增一些属性和方法
  • 原型链:实例可以通过 __proto__ 拿到构造函数的原型

它们之间的关联

这里的核心人物就是构造函数,它可以使用 prototype 获得它的原型,而原型可以通过 constructor 指回构造函数,也可以使用 new 创建一个实例,实例可以通过 __proto__ 获取构造函数的原型,而该构造函数的原型对象你可以看作是父类函数的一个 实例函数

关系如下图: 1620365488.png

instanceof的原理

instanceof 用于检测构造函数的 prototype 属性是否出现在某个实例对象的原型链上,我们知道 typeof 是判断数据类型的,但是它判断不了 Object 类型的继承,所以我们可以使用 instanceof 进行判断,判断某个实例对象是否在对象的原型链上。

但是 instanceof 也有不严谨的地方,它无法分辨判断出来的结果是否是它本身的构造函数,所以想要更精准的判断可以使用 实例对象.__proto__.constructor === 构造函数

new 运算符

原理

  • 一个新对象被创建,它继承自 foo.prototype
  • 构造函数 foo 被执行:执行的时候,相应的传参会被传入,同时上下文(this)会被指定为这个新实例。new foo 等同于 new foo(),只能用在不传递任何参数的情况
  • 如果构造函数返回了一个"对象",那么这个对象会取代整个 new 出来的结果,如果构造函数没有返回对象,那么 new 出来的结果为步骤1创建的对象

模拟 new 运算符

使用 Object.create 设置原型对象

// 模拟一个 new 运算符
function imitationNew(fn) {
  let o = Object.create(fn.prototype);
  let k = fn.call(o);
  console.log(k)
  if(typeof k === 'object') {
    return k;
  } else {
    return o;
  }
}
function fn() {
  this.name = 'oyzx';
  this.sayName = function() {
    return this.name;
  }
}
var a = imitationNew(fn)
console.log(a)
var b = new fn();
console.log(b)
console.log(a.__proto__ === b.__proto__);   // true