JavaScript原型与原型链(进阶篇)

系列文章推荐

JavaScript原型与原型链(基础篇)
JavaScript原型与原型链(进阶篇)
JavaScript原型与原型链(总结篇)

1 intanceof运算符

instanceof运算符用于判断构造函数的prototype属性是否出现在对象的原型链中,使用方法如下:

function Cat(name, age) {
  this.name = name
  this.age = age
}
const kat = new Cat('kat', 2)

console.log(kat instanceof Cat) // true
console.log(kat instanceof Object) // true

在这个例子中,由于kat.__proto__ === Cat.prototype,所以kat instanceof Cat输出为true;由于原型链的终点为Object.prototype,所以Object.prototype肯定在kat的原型链上,所以kat instanceof Object输出也为true

下面是自定义instanceof的实现代码,实现思路为沿着原型链一个接一个的判断,当instance.__proto__ === constructor.prototype时返回true,否则返回false

function myInstanceof(instance, constructor) {
  // 获取实例对象的原型
  let proto = Object.getPrototypeOf(instance)
  // 获取构造函数的prototype对象
  let prototype = constructor.prototype;

  // 判断构造函数的prototype属性是否在实例对象的原型链上
  while (true) {
    // 若!proto === null则说明已经到原型的终点Object.prototype
    if (!proto) {
      return false
    }
    // 实例对象的原型与构造函数的原型相等
    if (proto === prototype) {
      return true
    }
    // 如果没有找到,就继续在原型上找
    proto = Object.getPrototypeOf(proto)
  }
}

2 函数的原型

2.1 函数也是一种对象

在JavaScript中函数也是一种对象类型,如下面代码中创建一个add函数:

function add(x, y) {
  return x + y
}

console.log(add instanceof Object) // true

等价于使用new操作符和Function构造函数创建一个函数对象add

let add = new Function('x', 'y', 'return x + y')

console.log(add instanceof Object) // true

上面两种写法是等价的,举这个例子是为了说明一个问题,即函数也是一个对象;同样的,构造函数也是函数,所以构造函数也是一个对象,既然是对象,那么就存在原型,所有构造函数的原型对象都是Object构造函数的实例对象,于是满足关系:

function Cat() {}
const kat = new Cat()

console.log(kat.__proto__.__proto__ === Object.prototype) // true
// kat.__proto__ === Cat.prototype
console.log(Cat.prototype.__proto__ === Object.prototype) // true

上述关系可以用如下图例表示:

在这里插入图片描述

2.2 函数的构造函数Function

在JavaScript中的所有函数都是由Function构造函数实例化而来,因此存在如下关系:

function Cat() {}
console.log(Cat.__proto__ === Function.prototype) // true
console.log(Cat.__proto__ === Cat.constructor.prototype) // true

上述关系可以用如下图例表示:

在这里插入图片描述

3 “奇怪”的ObjectFunction

3.1 引例

下面例子用于和后文中的例子作为对照,例中声明构造函数Cat,并实例化对象kat,并且所有的结论在前文中已经提到:

function Cat(name, age) {
  this.name = name
  this.age = age
}

const kat = new Cat('kat', 2)

console.log(kat.__proto__ === Cat.prototype) // true
console.log(Cat.__proto__ === Function.prototype) // true
console.log(Cat.prototype.__proto__ === Object.prototype) // true
console.log(kat instanceof Cat) // true
console.log(kat instanceof Object) // true
console.log(Cat instanceof Cat) // false
console.log(Cat instanceof Object) // true

3.2 ObjectFunction

这里直接给出结论,下面四个操作的运算结果都为true

Function instanceof Object
Object instanceof Function
Function instanceof Function
Object instanceof Object

已知instanceof运算符的原理是判断构造函数的prototype属性是否出现在对象的原型链上,根据上述结论还能推导及验证出如下关系:

  1. Function instanceof Object,推导出Function.__proto__.__proto__ === Object.prototype
  2. Object instanceof Function,推导出Object.__proto__ === Function.prototype
  3. Function instanceof Function,推导出Function.__proto__ === Function.prototype
  4. Object instanceof Object,推导出Object.__proto__.__proto__ === Object.prototype

由上述第三个关系可知:Function自身是构造函数,且自身又是自身构造函数的实例,这种关系是非常“奇怪”的,和“先有鸡还是先有蛋?”这个问题一样,在3.1节的例子中对于构造函数Cat是不存在这种关系的,这种关系只存在于Function上。

3.3 既是函数,也是对象

为了解释3.2节中的问题,这里把3.2节中推导出的四个关系分为两组:

  1. 对象组:
    1. Function.__proto__.__proto__ === Object.prototype
    2. Object.__proto__.__proto__ === Object.prototype
  2. 函数组:
    1. Object.__proto__ === Function.prototype
    2. Function.__proto__ === Function.prototype

之所以这么分组,是因为看待事物的角度不同,会导致结果的不同。

3.3.1 对象组

先看下面例子:

function Cat(name, age) {
  this.name = name
  this.age = age
}

const kat = new Cat('kat', 2)
console.log(kat.__proto__.__proto__ === Object.prototype) // true
// Cat.__proto__ === Function.prototype
// Function.prototype.__proto__ === Object.prototype
console.log(Cat.__proto__.__proto__ === Object.prototype) // true

在这个例子中声明构造函数Cat,并实例化对象kat,前面提到构造函数Cat也是一种对象,所以满足关系:

  1. kat.__proto__.__proto__ === Object.prototype
  2. Cat.__proto__.__proto__ === Object.prototype

上述关系可以用如下图例表示:

在这里插入图片描述


把这个结论推广到FunctionObject上,由于可以把FunctionObject看作一种对象,所以满足关系:

  1. Function.__proto__.__proto__ === Object.prototype
  2. Object.__proto__.__proto__ === Object.prototype

3.3.2 函数组

先看下面例子:

function Cat() {}
console.log(Cat.__proto__ === Function.prototype) // true

在JavaScript中的所有函数都是由Function构造函数实例化而来,在这个例子中构造函数Cat是由Function构造函数实例化而来,具体见2.2节,因此满足如下关系:

  1. Cat.__proto__ === Function.prototype

把这个结论推广到FunctionObject上,由于可以把FunctionObject看作一种函数,所以满足关系:

  1. Object.__proto__ === Function.prototype
  2. Function.__proto__ === Function.prototype

3.4 function anonymous()

其实在3.3.1节中还有一个疑点,即:

kat.__proto__.__proto__ === Object.prototype // true
Cat.__proto__.__proto__ === Object.prototype // true

此时读者可能觉得kat.__proto__ === Cat.__proto__也是成立的,其实不然,这个问题的落脚点在于kat.__proto__Cat.__proto__到底是什么,见下面例子:

function Cat(name, age) {
  this.name = name
  this.age = age
}

const kat = new Cat('kat', 2)
console.dir(kat.__proto__)
console.dir(Cat.__proto__)

输出的结果如下:

在这里插入图片描述


对于kat.__proto__,根据前文中的结论,可以判断出kat.__proto__ === Cat.prototype,输出结果也是如此。

对于Cat.__proto__输出的是之前没有见过的函数function anonymous(),译为匿名函数,由于构造函数Cat是由构造函数Function实例化而来,所以Cat.__proto__ === Function.prototype,事实上所有构造函数的__proto__值都为function anonymous(),又由于Function.prototype === Function.__proto__,所以Function.prototypeFunction.__proto__的值也为函数function anonymous()

  • 当把Function看作函数时,Function.prototype === function anonymous(),即所有由Function构造函数实例化的其他构造函数的原型都为function anonymous()
  • 当把Function看作对象时,Function.__proto__ === function anonymous(),由于Function自身是构造函数,且自身又是自身构造函数的实例,所以实例对象Function的原型为function anonymous()

上述关系可以用如下图例表示:

在这里插入图片描述


根据结论“所有构造函数的原型对象都是Object构造函数的实例对象”,所以Cat.__proto__.__proto__ === Object.prototype

4 其他内置构造函数

JavaScript中的内置构造函数参考:MDN - Global_Objects,其他内置构造函数指的是除去FunctionObject之外的构造函数,这里仅列举常见的几种,如NumberStringArray,具有如下特点:

  1. FunctionObject一样,其他内置构造函数的__proto__属性也等于Function.prototype
console.log(Number.__proto__ === Function.prototype) // true
console.log(String.__proto__ === Function.prototype) // true
console.log(Array.__proto__ === Function.prototype) // true
  1. 其他内置构造函数不满足instanceof运算符自身与自身计算为true的特点:
console.log(Number instanceof Number) // false
console.log(String instanceof String) // false
console.log(Array instanceof Array) // false

注意区分内置构造函数和内置对象,JavaScript中的内置对象,如MathJSON是以对象形式存在的,满足如下关系:

  • Math.__proto__ === Object.prototype
  • JSON.__proto__ === Object.prototype
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值