从Object.prototype.toString聊到原型

一般在 JavaScript 里使用 typeof 来判断数据类型,只能区分基本类型,即 “number”,”string”,”undefined”,”boolean”,”object”,“function”,“symbol” (ES6新增)七种。 可是仍是有些边界状况咱们没法判断的,好比:javascript

console.log(typeof null) // object
 console.log(typeof [1,2,3]) // object
复制代码

很明显上述得出来的结果不符合咱们的要求,那么就不得不使用Object.prototype.toString方法去解决咱们的技术痛点java

Object.prototype.toString实现类型检测

首先咱们先来介绍toString方法,这个方法就是转为字符串的方法,跟Object.prototype.toString对好比下:面试

let arr=[1,2,3];

//直接对一个数组调用toString()
 console.log(arr.toString()) // "1,2,3"
 console.log(Object.prototype.toString()) //[object Object]
复制代码

你会发现Object.prototype.toString方法返回的结果有咱们须要的,你能够经过Object.prototype.toString()=="[object Object]"来判断是否是object类型,那么有同窗就说我要检测的是Array类型呢,是这样写吗:数组

console.log(Array.prototype.toString()) 
复制代码

结果是打印出来的为空的,为何会这样这样子呢,Object.prototype中的toString方法是确实被继承下来了,可是数组重写了toString方法,因此直接调用数组对象上面的toString方法调用到的实际是重写后的方法,并非Object.prototype中的toString方法,因此要明确是只有Object.prototype.toString方法返回的结果是"[...]",也就是只有Object.prototype上的toString才能用来进行复杂数据类型的判断,而其余类型可能内部有他们本身的写法,那咱们若是想要判断其余类型如何调用这个方法呢,聪明的同窗可能想到去改变Object.prototype.toString上下文的执行环境,那么天然而然想到call方法(apply方法):app

let arr=[1,2,3];
 console.log(arr.toString()) // "1,2,3"
// 经过call指定arr数组为Object.prototype对象中的toString方法的上下文
 console.log(Object.prototype.toString.call(arr)) // "[object Array]"
复制代码

那么咱们的问题就解决了,很精确判断出类型,实际上咱们是经过Object.prototype的原型方法去实现的,那么这里就不得不聊下原型以及原型链方面的知识了函数

深刻了解原型及原型链

在面试题或者开发中常常会遇到prototype,_proto,constructor等一系列名词,好比:测试

打印的这个对象里面有__proto__属性,属性下包含着constructor属性和__proto__属性等,看起来很复杂那今天咱们就好好剖析这个知识点吧!!!

构造函数和原型的关系

prototype(翻译为原型):每一个函数都有一个 prototype 属性,那么构造函数和prototype是怎么样的一种指向关系ui

举个例子:this

function Animal() {

}
// prototype是函数才会有的属性
Animal.prototype.name = '旺财'
let animal1 = new Animal()
let animal2 = new Animal()
console.log(animal1.name) // 旺财
console.log(animal2.name) // 旺财
复制代码

这里有个问题:咱们Animal没有声明name字段,为何咱们的实例化对象animal1和animal2会具备name字段?还有构造函数跟原型之间又有什么关系?spa

其实,函数的prototype属性指向了一个对象,这个对象正是调用该构造函数而建立的实例的原型,当咱们进行new操做(new操做下面会重点讲到)的时候返回的animal1对象字段至关于由Animal和Animal.prototype实例原型组成,你能够看到toString方法,可是咱们Animal构造函数并无声明这个方法这是继承于Animal.prototype原型对象,name字段也是,其构造函数和原型的关系图以下:

这里咱们有必要弄清楚一些叫法,由于概念有点多容易混淆

  • 只有(构造)函数才具备prototype属性
  • prototype是属性不是原型,经过该属性才能找到原型
  • Animal.prototype才是咱们所说的原型,也叫实例原型

如何经过实例化对象指向实例原型(proto)

每个JavaScript对象(除了 null )都具备的一个属性,叫__proto__,这个属性会指向该对象的原型

举个例子:

function Animal() {

}
let animal = new Animal()
console.log(animal.__proto__ === Animal.prototype) // true
复制代码

因而构造函数、实例原型和__proto__属性之间的关系图以下:

咱们上述讲到的都是指向实例原型,那么咱们可不能够经过原型指向构造函数,答案确定是能够的

constructor属性指向构造函数

有同窗会说实例原型能够指向实例化对象,那是不行的由于咱们new不少个实例对象,可是要经过constructor指向实例化对象是不能的,那么咱们就来看看constructor如何指向构造函数 走下代码:

function Animal() {

}
let animal = new Animal()
console.log(animal)
console.log(Animal === Animal.prototype.constructor) // true
console.log(animal.__proto__.constructor=== Animal) // true
复制代码

那么咱们能够更新下关系图:

以上效果图也就是咱们常说的原型链,咱们花了大量时间去讲原型和原型链,那么哪些地方用到这方面的知识呢? 接下来咱们来看看new操做符是如何实例对象

new操做符是如何工做的

其实new操做符就干了三件事情

例子一:

function Animal() {
  console.log("发出声音")
}
let animal = new Animal()
复制代码

等同于

例子二:

function Animal() {
  console.log("发出声音")
}
let animal  = {};
animal.__proto__ = Animal.prototype;
Animal.call(animal) // // 这一步的操做是改变this指向,指向实例化对象 而且将其构造函数this关联的属性绑定给实例化对象
复制代码
  • 第一行,咱们建立了一个空对象animal
  • 第二行,咱们将这个空对象的__proto__成员指向了Animal函数对象prototype成员对象
  • 第三行,咱们将Animal函数对象的this指针(上下文环境)替换成animal,而后再调用Animal函数

这就是new操做内部所作的事,可能细心的同窗会发现例子一执行的时候会打印出来:

也就是执行了一次Animal函数,实际就是至关于Animal.call(animal)这样执行了,这样解释了为何打印出来了

还有一个问题:假设咱们在构造函数加上个return会发生什么?

function TestAnimal(){
     this.name="TestAnimal"
     console.log("我是测试TestAnimal")
 }
 function Animal() {
  console.log("发出声音")
  return TestAnimal
}
let animal = new Animal()
console.log(animal)
复制代码

效果图以下:

实际上new 操做符调用构造函数的时候,函数内部实际上发生如下变化:

1.建立一个空对象,而且 this 变量引用该对象,同时还继承了该函数的原型。

2.属性和方法被加入到 this 引用的对象中。

3.新建立的对象由 this 所引用,而且最后隐式的返回 this.

function Animal() {
  console.log("发出声音")
  console.log(this)
  return TestAnimal
}
// let animal = new Animal()
console.log(new Animal()) // 结果跟this打印的同样,这样就解释最后隐式的返回 this
// 而且当你使用new关键字的时候Animal被当作了构造函数:当Animal中包含return的时候 
// 且return的是一个对象而不是number这样的值,则会返回return后的对象,return后不是对象,则被忽略,返回的是Animal中的this对象
复制代码
相关文章
相关标签/搜索