今天看到了一个骚写法,之前几乎没用到过类型判断,好奇所以查了一下

/**
 * @description: 判断值是否未某个类型
 */
export function is(val: unknown, type: string) {
  return toString.call(val) === `[object ${type}]`;
}

/**
 * @description:  是否为函数
 */
export function isFunction<T = Function>(val: unknown): val is T {
  return is(val, 'Function') || is(val, 'AsyncFunction');
}

首先明确如下几点

  1. 所有类型都继承于Object类

    str (String 对象)
      |
      V
    String.prototype  (字符串特有的方法,比如 charAt, slice)
      |
      V
    Object.prototype  (所有对象通用的方法,比如 toString, hasOwnProperty)
      |
      V
    null  (原型链的终点)
  2. Number和String等,会重写toString方法
  3. 'aaa'和new String('aa')显式创建是有区别的
  4. 'aaa'的toString是在调用时js自动封装到String类中调用完删除,所以'aaa'是字面量。
    显式创建的new是手动创建到类中他是一个String对象
let e = 'aa' // 先创建一个变量
e.prototype // 此时e是字面量,所以没有prototype
# undefined
e.__proto__ === String.prototype // 在获取其原型链时会自动封装到String对象中,因此能够获取proto,并且他因为封装在String中,所以和String.prototype是相等的
# true
String.prototype === Object.prototype // 这里false是因为String改写了部分方法,同时新增了部分方法,所以不相等
# false
e.toString() // 输出'aa'是因为String中重写了Object的toString方法
# 'aa'
delete String.prototype.toString // 让我们删了String类的toString
# true
e.toString() // 再次调用,从String原型链上查找toString没有,就再向上查找Object原型链上的toString,找到了调用
# '[object String]'

再来看一个

Object.toString === Object.prototype.toString
# false
/*
Object.toString 是作为静态方法调用,没有上下文,读取不了类的属性
Object.prototype.toString 是作为实例方法调用,能够调用类中的属性
*/
// 实例方法和静态方法区别
class Person {
    constructor(name) {
        this.name = name;
    }

    // 实例方法
    sayHello() {
        console.log(`Hello, my name is ${this.name}.`);
    }

    // 静态方法
    static introduce() {
        console.log('I am a Person class.',this.name);
    }
}

const person = new Person('Alice');
person.sayHello();
Person.introduce();
# Hello, my name is Alice. // 实例方法存在上下文,能够调用类中的属性
# I am a Person class. Person // 静态方法无上下文,无法调用类的属性,只能返回当前类名,并且只能通过类名来调用