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介绍。
- 提示下载完但解压或打开不了?
- 找不到素材资源介绍文章里的示例图片?
- 模板不会安装或需要功能定制以及二次开发?
发表评论
还没有评论,快来抢沙发吧!