最新公告
  • 欢迎您光临起源地模板网,本站秉承服务宗旨 履行“站长”责任,销售只是起点 服务永无止境!立即加入钻石VIP
  • JS的 3种类型判断 和 原型链 原理

    正文概述 掘金(放学别走君)   2021-08-20   672

    typeof 原理

    typeof 概念

    对于JS中的typeof,一般会用来判断一个变量的类型,他返回的是一个类型的字符串,它可以分辨出8种数据类型:

    booleanstringnumberundefinedobjectfunctionsymbolbigint

    Symbol 是ES6中引入的一种原始数据类型,表示独一无二的值。BigInt(大整数)是 ES2020 引入的一种新的数据类型,用来解决 JavaScript中数字只能到 53 个二进制位(JavaScript 所有数字都保存成 64 位浮点数,大于这个范围的整数,无法精确表示的问题。(在平常的开发中,数据的id 一般用 string 表示的原因)。为了与 Number 类型区别,BigInt 类型的数据必须添加后缀 n1234 为普通整数,1234nBigInt

    typeof 底层原理

    不同的变量在底层储存的时候,都会表示为二进制,在 JS 中这个二进制的前(低)三位储存其类型信息。(在二进制中是从右往左数的3位)

    typeof 判断变量类型就是判断这个变量表示的二进制的前三位。

    - 000 : 对象
    - 010 : 浮点数
    - 100 : 字符串
    - 110 : 布尔
    - 1   : 整数
    

    对于 undefinednull 来说,情况会有所不同:

    null 的二进制表示全为 0 , 所以前三位也就都是 0 ,所以会被当做 对象 看待,typeof 判断会返回 "object"

    undefined 是用 -2^30 整数来表示,所以会被单独看待,typeof 判断会返回 "undefined"

    对于 Array 这样的类型也会被看成是 对象,因为他们的前三位也是 000

    Array: 1000100010001000

    代码实测

    let obj = {}
    let arr = []
    function fn() {}
    let ins_func = new fn();
    let ins_obj = new Object();
    let ins_arr = new Array();
    const symbol = Symbol(40);
    
    
    console.log(typeof a)           // undefined
    console.log(typeof "b")         // string
    console.log(typeof 1)           // number
    console.log(typeof true)        // boolean
    console.log(typeof null)        // object
    console.log(typeof undefined)   // undefined
    console.log(typeof Object)      // function
    console.log(typeof Array)       // function
    console.log(typeof Function)    // function
    console.log(typeof Date)        // function
    console.log(typeof new Date)    // object
    console.log(typeof Symbol)    // function
    console.log(typeof obj)         // object
    console.log(typeof arr)         // object
    console.log(typeof fn)          // function
    console.log(typeof ins_func)    // object
    console.log(typeof ins_obj)     // object
    console.log(typeof ins_arr)     // object
    console.log(typeof symbol)    // symbol
    console.log(typeof 123n)      // bigint
    

    instanceof 原理

    要理解 instanceof 的底层原理,必须先理解 原型链 的基本概念。

    原型链

    首先,构造函数原型对象 ,和 实例 的关系是:

    1. 每个 构造函数 都有一个 原型对象

    2. 原型对象 中包含一个指向 构造函数指针

    3. 实例 中包含一个指向 原型对象指针

    当一个 类型A原型对象 等于另一个 类型B实例 的时候,类型A原型对象 中就会包含一个指向另一个 类型B原型对象指针, 然后我们又让另一个 类型B原型对象 等于 在另一个 类型C实例 的时候,类型B实例 中又包含了一个指向 类型C实例指针 , 这样层层递进,就构成了实例和原型对象的链条,这就是原型链的基本概念

    代码实测

    构造函数,原型对象,和 实例 之间的关系

    function fn() {
        this.a = 1;
    }
    fn.prototype.b = 2;
    let instance = new fn();
    
    // 每个构造函数都有一个原型对象
    console.log(fn.prototype)                                     // {b: 2,constructor: ƒ fn()}
    
    // 每个原型对象都有一个指向构造函数的指针
    console.log(fn.prototype.constructor === fn)                  // true
    
    // 每个实例都有一个指向原型对象的指针
    console.log(instance.__proto__ === fn.prototype)              // true
    
    // 根据原型链的查找机制,Object.prototype 是原型链的顶端
    console.log(instance.__proto__.__proto__ === Object.prototype)// true
    
    // Object.prototype 的 __proto__ 属性值为 null
    console.log(Object.prototype.__proto__ === null)              // true
    

    原型链 的查找顺序

    // 定义A B C 三个构造函数
    function A() {this.a = 1;}
    A.prototype.b = 2;
    
    function B() {this.c = 3;}
    B.prototype.d = 4;
    
    function C() {this.e = 5;}
    C.prototype.f = 6;
    
    // 构造原型链
    // 让 B 的原型 等于 C 的 实例
    // 让 A 的 原型 等于 B 的 实例
    // 注意:要从后往前构造原型链
    B.prototype = new C();
    A.prototype = new B();
    
    // 创建一个 A 的 实例
    let ins_A = new A();
    
    // 可以访问到 实例A 的 构造函数中的属性
    // 但访问不到 实例A 的 原型对象中的属性
    // 因为 A 的 原型对象 已经等于或者说变成了 B 的实例
    console.log(ins_A.a);   // 1
    console.log(ins_A.b);   // undefined
    
    // 可以访问到 B 的构造函数中的属性
    // 因为 A 的 原型对象 已经等于了 B 的 实例,
    // 所以 按照原型链的查找顺序:
    // 先在 A 的 构造函数中查找,再到 A 的 原型对象中查找,
    // 因为 A 的 原型对象 已经等于了 B 的 实例, 
    // 所以会到 B 的 构造函数中查找
    // 访问不到 B 的 原型对象中的属性
    // 因为 B 的 原型对象 已经等于或者说变成了 C 的实例
    console.log(ins_A.c);   // 3
    console.log(ins_A.d);   // undefined
    
    // 同理,先查找A的构造函数,再查找A的原型对象,也就是 B 的实例,
    // 当还没有查找到,会去 B 的 原型对象中查找,也就是 C 的 实例,
    // C 的 构造函数中没有找到,会去 C 的 原型对象中查找
    console.log(ins_A.e);   // 5 
    console.log(ins_A.f);   // 6
    

    instanceof 底层原理

    instanceof 一般用来判断一个对象的具体类型,也就是一个用 typeof 判断出类型为 "object" 的变量具体属于哪种数据类型。

    通俗来讲, instanceof 用来判断一个对象是否是一个构造函数的实例。

    A instanceof B

    instanceof 判断的是 右边的prototype 是否在左边的原型链上,也就是:

    instanceof 在查找过程中会遍历左边变量 A 的原型链,直到找到右边变量 Bprototype ,如果查找失败会返回 false

    注意: 对于 null 的判断 不能使用 instanceof,会报错

    // Uncaught TypeError: Right-hand side of 'instanceof' is not an object
    console.log(null instanceof null)       
    

    代码实测

    实例测试

    // 定义A B C 三个构造函数
    function A() {this.a = 1;}
    A.prototype.b = 2;
    
    function B() {this.c = 3;}
    B.prototype.d = 4;
    
    function C() {this.e = 5;}
    C.prototype.f = 6;
    
    // 构造原型链
    // 让 B 的原型 等于 C 的 实例
    // 让 A 的 原型 等于 B 的 实例
    // 注意:要从后往前构造原型链
    B.prototype = new C();
    A.prototype = new B();
    
    // 创建一个 A 的 实例
    let ins_A = new A();
    
    console.log(ins_A.a);   // 1
    console.log(ins_A.b);   // undefined
    console.log(ins_A.c);   // 3
    console.log(ins_A.d);   // undefined
    console.log(ins_A.e);   // 5 
    console.log(ins_A.f);   // 6
    
    
    // instanceof 判断的是右边的prototype是否在左边的原型链上
    console.log(ins_A instanceof A)         // true
    console.log(ins_A instanceof B)         // true
    console.log(ins_A instanceof C)         // true
    console.log(ins_A instanceof Object)    // true
    

    其他测试

    let arr = [];
    let obj = {};
    function fn() {}
    let ins_fn = new fn();
    
    console.log(null instanceof Object)     // false
    console.log(Object instanceof Object)   // true
    console.log(Function instanceof Object) // true
    console.log(Array instanceof Object)    // true
    console.log(obj instanceof Object)      // true
    console.log(arr instanceof Object)      // true
    console.log(fn instanceof Object)       // true
    console.log(ins_fn instanceof Object);  // true
    console.log(ins_fn instanceof fn);      // true
    console.log(arr instanceof Array);      // true
    

    实现一个 instanceof

    
    function instanceof2(left, right) {
        let rightPrototype  = right.prototype;
        let leftProto = left.__proto__;
        while(true) {
            if(leftProto === null) {
                return false;
            }
            if(leftProto === rightPrototype) {
                return true;
            }
            leftProto = leftProto.__proto__;
        }
    }
    
    
    /*测试*/
    
    // 定义A B C 三个构造函数
    function A() {this.a = 1;}
    A.prototype.b = 2;
    
    function B() {this.c = 3;}
    B.prototype.d = 4;
    
    function C() {this.e = 5;}
    C.prototype.f = 6;
    
    B.prototype = new C();
    A.prototype = new B();
    
    // 创建一个 A 的 实例
    let ins_A = new A();
    // instanceof 判断的是右边的prototype是否在左边的原型链上
    console.log(ins_A instanceof A)         // true
    console.log(ins_A instanceof B)         // true
    console.log(ins_A instanceof C)         // true
    console.log(ins_A instanceof Object)    // true
    console.log(ins_A instanceof Array)     // false
    
    
    // 自己实现的instanceof2
    console.log(instanceof2(ins_A, A))      // true
    console.log(instanceof2(ins_A, B))      // true
    console.log(instanceof2(ins_A, C))      // true
    console.log(instanceof2(ins_A, Object)) // true
    console.log(instanceof2(ins_A, Array))  // false
    

    Object.prototype.toString.call() 原理

    Object.prototype.toString.call() 用于判断某个对象值属于哪种内置类型

    Object.prototype.toString() 调用

    每个对象都有一个 toString() 方法,他返回一个 [object type] 形式的字符串, type 代表这个对象的类型。

    但是很多自定义对象可能会覆盖掉这个 toString() 方法,比如

    let A = {};
    let  B = {
        toString: () => {return "hello";}
    }
    console.log(A.toString());  // "[object Object]"
    console.log(B.toString());  // "hello"
    

    所以为了检测出一个对象的类型,就要使用原本的 Object.prototype 上的 Object.prototype.toString() 方法,为了让每个对象都能调用这个方法,就要使用

    Object.prototype.toString.call()

    Object.prototype.toString() 原理

    对于 Object.prototype.toString.call()

    1. 如果参数为 null 或者 undefined ,直接返回结果。

    2. 如果参数不为 null 或者 undefined ,现将参数转换为对象再做判断。

    3. 如果该参数转换成的对象有 [Symbol.toStringTag] 属性值,那么将该属性值作为 tag ;如果没有,就使用这些类型的对象拥有的自己的特定的内部属性作为 tag(比如 Boolean 对象就有 [[BooleanData]] 插槽,值为原始的 Boolean 值;Number 对象有 [[NumberData]] 插槽,值为原始的 Number 值)。最后返回 "[object " + tag + "]" 形式的字符串。

    // [Symbol.toStringTag] 属性值需要是一个字符串,否则会被忽略。
    let o1 = { [Symbol.toStringTag]: "A" };
    let o2 = { [Symbol.toStringTag]: null };
    
    console.log(Object.prototype.toString.call(o1));    // [object A]
    console.log(Object.prototype.toString.call(o2));    // [object Object]
    

    代码实测

    
    console.log(Object.prototype.toString.call(true))       // [object Boolean]
    console.log(Object.prototype.toString.call("a"))        // [object String]
    console.log(Object.prototype.toString.call(1))          // [object Number]
    console.log(Object.prototype.toString.call({b:2}))      // [object Object]
    console.log(Object.prototype.toString.call([1,2]))      // [object Array]
    console.log(Object.prototype.toString.call(() => {}))   // [object Function]
    console.log(Object.prototype.toString.call(Symbol(1)))  // [object Symbol]
    console.log(Object.prototype.toString.call(123n))       // [object Bigint]
    
    console.log(Object.prototype.toString.call(new Set()))  // [object Set]
    console.log(Object.prototype.toString.call(new Map()))  // [object Map]
    
    let  A = {
        toString: () => {
            return "hello";
        }
    }
    console.log(A.toString())                               // hello
    console.log(Object.prototype.toString.call(A))          // [object Object]
    

    起源地下载网 » JS的 3种类型判断 和 原型链 原理

    常见问题FAQ

    免费下载或者VIP会员专享资源能否直接商用?
    本站所有资源版权均属于原作者所有,这里所提供资源均只能用于参考学习用,请勿直接商用。若由于商用引起版权纠纷,一切责任均由使用者承担。更多说明请参考 VIP介绍。
    提示下载完但解压或打开不了?
    最常见的情况是下载不完整: 可对比下载完压缩包的与网盘上的容量,若小于网盘提示的容量则是这个原因。这是浏览器下载的bug,建议用百度网盘软件或迅雷下载。若排除这种情况,可在对应资源底部留言,或 联络我们.。
    找不到素材资源介绍文章里的示例图片?
    对于PPT,KEY,Mockups,APP,网页模版等类型的素材,文章内用于介绍的图片通常并不包含在对应可供下载素材包内。这些相关商业图片需另外购买,且本站不负责(也没有办法)找到出处。 同样地一些字体文件也是这种情况,但部分素材会在素材包内有一份字体下载链接清单。
    模板不会安装或需要功能定制以及二次开发?
    请QQ联系我们

    发表评论

    还没有评论,快来抢沙发吧!

    如需帝国cms功能定制以及二次开发请联系我们

    联系作者

    请选择支付方式

    ×
    迅虎支付宝
    迅虎微信
    支付宝当面付
    余额支付
    ×
    微信扫码支付 0 元