SlowSoulWen / blog

个人博客
0 stars 0 forks source link

typeof、instanceof、Object.prototype.toString.call #3

Open SlowSoulWen opened 4 years ago

SlowSoulWen commented 4 years ago

typeof

typeof 操作符返回一个字符串,表示未经计算的操作数的类型。

通常而言typeof只能用来检测基本类型,以及一些特定的类型比如function,其他的对象都会返回object

类型 结果
Undefined "ndefined"
Null "object"
Boolean "boolean"
Number "number"
String "string"
Symbol "symbol"
Function "function"
宿主对象 "取决于具体实现"
其他任何对象 "object"

注意:除了Function之外,所有构造函数的类型(通过new生成的类型)都是“object”

为什么typeof null === 'object'?

JS在底层存储变量的时候,会在变量的机器码的低位1-3位存储其类型信息:

由于null对应的机器码为0,所以typeof在做判断的时候,就会把null当做对象来看待。

instanceof

instanceof 运算符用于检测构造函数的 prototype 属性是否出现在某个实例对象的原型链上。

语法:object instanceof constroctor

instanceof左值必须是Object,右值必须是函数。

注意:多个窗口意味着多个全局环境,不同的全局环境拥有不同的全局对象,从而拥有不同的内置类型构造函数。所以在存在iframe的情况下,慎重使用instanceof

instanceof的实现原理不难,只要判断右值构造函数的prototype在左值的原型链上,则返回true

关于原型链的问题盗一张图来解释会更明白:

e83bca5f1d1e6bf359d1f75727968c11_720w

有几个比较重要的点:

在方法的原型中添加属性和直接给方法添加属性的区别?

function Fun() {
    this.myName = 'wen';
}
Fun.myOtherName = 'other wen';
Fun.myFun = function() {
    console.log('my fun');
}
Fun.prototype.myOtherFun = function() {
    console.log('my other fun');
}

const fun = new Fun();

Fun.myName // undefined
Fun.myOtherName // 'other wen'
Fun.myFun() // 'my fun'
Fun.myOtherFun // undefined

fun.myName // 'wen'
fun.myOtherName // undefined
fun.myFun // undefined
fun.myOtherFun() // 'my other fun'

手写代码实现instanceof功能:

function instanceOf(object, constroctor) {
    let leftProto = object.__proto__;
    let rightPrototype = constroctor.prototype;

    if (!leftProto || !rightPrototype) return false;

    while(true) {
        if (leftProto === null) {
            return false;
        }
        if (leftProto === rightPrototype) {
            return true;
        }
        leftProto = leftProto.__proto__;
    }
}

Object.prototype.toString

Object.prototype.toString就比较牛叉了,属于万金油式的存在,是唯一一个能够准确判断内建类型的方法。

关于Object.prototype.toString的原理,这里贴一下ECMAScript 5对该方法的解释:

Object.prototype.toString () 在toString方法被调用时,会执行下面的操作步骤:

  1. 如果this的值为undefined,则返回”[object Undefined]”.
  2. 果this的值为null,则返回”[object Null]”.
  3. O成为调用ToObject(this)的结果.
  4. 让class成为O的内部属性[[Class]]的值.
  5. 返回三个字符串”[object “, class, 以及 “]”连接后的新字符串.

[[Class]] 一个字符串值,表明了该对象的类型. 然后给了一段解释:

所有内置对象的[[Class]]属性的值是由本规范定义的.所有宿主对象的[[Class]]属性的值可以是任意值,甚至可以是内置对象使用过的[[Class]]属性的值.[[Class]]属性的值可以用来判断一个原生对象属于哪种内置类型.需要注意的是,除了通过Object.prototype.toString方法之外,本规范没有提供任何其他方式来让程序访问该属性的值。

ECMAScript 6Object.prototype.toString又做了进一步的定义与修改,且改动较大,但是在使用上没有明显区别(相关链接)

pigpigever commented 4 years ago

优化一下 instanceOf 的写法 👇

function instanceOf(object, constroctor) {
    let leftProto = object.__proto__;
    let rightPrototype = constroctor.prototype;

    if (!leftProto || !rightPrototype) {
        return false;
    }
    while(leftProto) {
        if (leftProto === rightPrototype) {
            return true;
        }
        leftProto = leftProto.__proto__;
    }
    return false
}