likes
comments
collection
share

Object.prototype.toString引发的原型链学习

作者站长头像
站长
· 阅读数 80

前言

判断数据格式时,都会使用Object.prototype.toString.call()进行判断。某次使用时,不小心写成了Object.toString.call(),结果浏览器直接报错了: Object.prototype.toString引发的原型链学习 我直接疑惑,调用Object.toString关你Function.prototype.toString什么事?

后来才发现写错了😅,但为什么是Object.prototype.toString而不是Object.toString呢?它们不一样吗?Object上找不到toString不是直接去原型上面找吗?

只能说,原型的水很深,没有理解原型的话,容易跟本人一样,产生这个疑问。

注意:后续的内容会提及类class,但是在js中,class只是语法糖,本质上还是函数Function

1. 函数的prototype

一般情况下,prototype这个属性只有函数拥有,也就是Function的实例拥有(通过function或者class定义的,箭头函数除外)。 没有特殊处理的情况下,其他的类型都不会拥有prototype:

const num = 123
console.log('num.prototype:', num.prototype)   // undefined
const str = 'abc'
console.log('str.prototype:', str.prototype)   // undefined
const bool = true
console.log('bool.prototype:', bool.prototype)   // undefined
const sym = Symbol('sym')
console.log('sym.prototype:', sym.prototype)   // undefined
const obj = {}
console.log('obj.prototype:', obj.prototype)   // undefined
const arr = []
console.log('arr.prototype:', arr.prototype)   // undefined
function fun () {}
console.log('fun.prototype:', fun.prototype)   // {constructor: ƒ}

Object.prototype.toString引发的原型链学习 在代码中,可以直接对prototype进行操作,如下:

function Person () {}
// 直接对函数的prototype进行操作
Person.prototype.walk = () => {
  console.log('walking !')
}
// 打印结果
console.log('Person.prototype:', Person.prototype)

Object.prototype.toString引发的原型链学习 可以看到,walk函数已经存在Person函数的prototype上。 到这里为止,只需要记住,prototype只有函数的拥有,且prototype里面的属性可以进行修改。 而且prototype虽然翻译过来是原型,但是Person是无法直接调用的,需要通过prototype进行调用,即Person.prototype.walk()。 然而Person通过new创建的实例却是可以直接调用。

2. 原型与原型链

对于任何数据或者对象而言,my.__proto__就是指向自身原型,而my.__proto__.__proto__就是指向自身原型的原型,以此类推,多个__proto__组成了原型链。

还是上面的例子:

首先,把上面的Person函数通过new实例化,看看其原型:

const person = new Person()
console.log('person:', person)
// 打印原型
console.log('person.__proto__:', person.__proto__)

Object.prototype.toString引发的原型链学习 通过__proto__可以访问person的原型,而且是不是觉得person.__proto__有些熟悉,其实就是Person.prototype函数的prototype就是函数实例化之后的原型

console.log('person.__proto__ === Person.prototype:', person.__proto__ === Person.prototype)

Object.prototype.toString引发的原型链学习 可以看到,person.__proto__Person.prototype是完全相等的,因为就是同一个对象。

然而js中,不管是数据也好,函数也好等等一切都是由某个构造函数实例化而来的,也就是说万物皆有原型(nullundefine除外), 还是拿上面的person说明:

  1. 上面已经说了,person的原型就是Person.prototype,也就是person.__proto__ === Person.prototype;
  2. Person.prototype是一个object,就是一个对象,那么它的原型就是对象构造函数的prototype,也就是Object.prototype,即Person.prototype.__proto__ === Object.prototypeperson.__proto__.__proto__ === Object.prototype;
  3. Object.prototype.__proto__null,原型链到此为止。 从上面可以看出,person有原型,而person的原型有原型,依次类推,这就是原型链

可以试着调用一下原型链上toString函数:

console.log('person.toString():', person.toString())
// person.toString(): [object Object]

执行成功了 !! 虽然person本身没有toString函数,但是上游的某个原型上有这个函数;person可以通过原型链,访问到这个函数。 使用代码判断一下toString在原型链的位置:

let flag = false
let prototype = person
let path = 'person'
while(!flag && prototype) {
  flag = prototype.hasOwnProperty('toString')
  if (!flag) {
    prototype = prototype.__proto__
    path += '.__proto__'
  }
}
console.log('flag:', flag)
console.log('prototype:', prototype)
console.log('path:', path)

Object.prototype.toString引发的原型链学习 可以看到,toString位于person的原型的原型上,即person.__proto__.__proto__person可以通过这条原型链访问toString函数。

3. Object.toStringObject.prototype.toString的区别

回归初心,为什么是Object.prototype.toString而不是Object.toString呢?

  1. 因为Object本身没有toString方法,所以当调用Object.toString时,会去原型链上查找;
  2. 又因为Object是一个构造函数,所以它的原型是Function.prototype,即Object.__proto__ === Function.prototype,而Function.prototype中刚好有toString方法,所以直接调用了这个方法;
  3. 但是因为Function.prototype.toString是用于处理函数的,当调用者不是函数时,无法处理,报错。 Object.prototype.toString引发的原型链学习 通过代码进行判断,可以确定Object.toString调用的就是Function.prototype.toString

注意Object.__proto__Object.prototype是完全不同的:

  1. Object.__proto__指向本身的原型,而Object就是一个函数,它的原型就是 构造函数prototype,也就是Function.prototype
  2. Object.prototype,虽然名称是prototype,但它不是Object原型,只是Object函数的一个属性而已,只是这个属性比较特殊。
  3. 总结起来就一句话,如果需要访问某个数据或者对象的原型时(包括函数),只能通过__proto__获取,本身的prototype不是自己的原型

总结

  1. nullundefine没有原型;
  2. prototype一般只有函数拥有(箭头函数除外);
  3. js中,基本所有的对象和数据类型都是由对应构造函数创建的,比如数字的构造函数是Number,所有数字的原型都指向Number.prototype
  4. 而且,各种各样构造函数和类class本身也是由构造函数(Function)创建的,即原型都是Function.prototype,包括Function自身也是: Object.prototype.toString引发的原型链学习
转载自:https://juejin.cn/post/7117961909478883341
评论
请登录