typeof 原理
typeof 概念
对于JS中的typeof
,一般会用来判断一个变量的类型,他返回的是一个类型的字符串,它可以分辨出8种数据类型:
boolean
, string
, number
, undefined
, object
, function
, symbol
,bigint
Symbol
是ES6中引入的一种原始数据
类型,表示独一无二的值。BigInt
(大整数)是 ES2020 引入的一种新的数据类型,用来解决 JavaScript中数字只能到 53 个二进制位(JavaScript 所有数字都保存成 64 位浮点数,大于这个范围的整数,无法精确表示的问题。(在平常的开发中,数据的id 一般用 string 表示的原因)。为了与 Number
类型区别,BigInt
类型的数据必须添加后缀 n
。 1234
为普通整数,1234n
为 BigInt
。
typeof 底层原理
不同的变量在底层储存的时候,都会表示为二进制,在 JS 中这个二进制的前(低)三位储存其类型信息。(在二进制中是从右往左数的3位)
用 typeof
判断变量类型就是判断这个变量表示的二进制的前三位。
- 000 : 对象
- 010 : 浮点数
- 100 : 字符串
- 110 : 布尔
- 1 : 整数
对于 undefined
和 null
来说,情况会有所不同:
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
的原型链,直到找到右边变量 B
的 prototype
,如果查找失败会返回 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]
常见问题FAQ
- 免费下载或者VIP会员专享资源能否直接商用?
- 本站所有资源版权均属于原作者所有,这里所提供资源均只能用于参考学习用,请勿直接商用。若由于商用引起版权纠纷,一切责任均由使用者承担。更多说明请参考 VIP介绍。
- 提示下载完但解压或打开不了?
- 找不到素材资源介绍文章里的示例图片?
- 模板不会安装或需要功能定制以及二次开发?
发表评论
还没有评论,快来抢沙发吧!