面试篇小结一,数据类型、类型判断里的细节
类型
- 原始类型 (存储在栈中)
- string
- number
- 浮点数也是number的一种类型
- boolean
- null
- undefined
- Symbol
- bigInt(132456n)
- 引用类型(存储在堆中)
let a = 1 // new Number(1)
a.b = 2 // new Number(1).b = 2
console.log(a.b); // delete new Number(1).b undeifned
问1:
- 会报错吗? 不会,隐式转换
- 打印什么? undefined,原始类型,delete掉属性了
问2:
0.1 + 0.2 === 0.3 // false
打印结果? 答:false,在 JavaScript 中,出现这种情况是因为浮点数在计算机中的表示存在精度限制。0.1 和 0.2 在二进制表示中是无限循环的小数,当进行计算并转换回十进制时,就会产生微小的误差,导致结果与预期的 0.3 不完全相等
问3:
引用类型数据为什么存放在堆里?答:
- 大小不固定:引用类型的数据,如对象、数组等,其大小可能会动态变化。堆内存可以提供更灵活的空间分配方式,适应引用类型数据的大小变化。
- 共享和传递:将引用类型数据存放在堆中,可以通过引用(指针)来共享和传递数据。多个变量可以指向同一个堆内存中的对象,从而实现数据的共享和高效传递。
- 内存管理:堆内存的管理由 JavaScript 引擎负责,它可以自动进行垃圾回收,释放不再使用的对象所占用的内存。这样可以减轻程序员手动管理内存的负担。
let a = {
n: 1
}
let b = a;
b.n = 2;
console.log(a.n); // 2
问4:打印结果? 答:2,a,b引用地址相同。
const a = []
a = []
console.log(a);
问5:行得通吗? 答:行不通,const常数,a=[],new了一个新数组,更改了地址,报错。
function test(person) {
person.age = 26; p1.age=26
person = {
name: 'tom',
age: 30
}
}
const p1 = {
name: 'john',
age: 25
}
const p2 = test(p1);
console.log(p1);
问6 p1长什么样 答:{ name: 'john', age: 26 }
类型判断
- typeof
- instanceof
- Object.prototype.toString.call()
问7 手写instanceof
instanceof:
- 只能用来判断引用数据类型
- 通过原型链查找来判断类型
/**
* 自定义的instanceof检查函数。
* @param {Object} L - 左侧对象,需要检查是否是右侧构造函数的实例。
* @param {Function} R - 右侧构造函数,用来检查左侧对象是否是其实例。
* @returns {boolean} 如果L是R的实例,则返回true;否则返回false。
*/
function myinstanceof(L, R) {
// 当L不为null时,继续检查
while (L !== null) {
// 如果L的__proto__等于R的prototype,则说明L是R的实例
if (L.__proto__ === R.prototype) {
return true;
}
// 将L更新为其原型,继续向上遍历原型链
L = L.__proto__;
}
// 如果遍历完原型链仍未找到匹配的prototype,则L不是R的实例
return false;
}
// 测试代码
console.log(myinstanceof([], Array)); // true
console.log(myinstanceof([], Object)); // true
console.log(myinstanceof({}, Array)); // false
问8 toString()
js中每一种类型都有自己的toString函数,这些toString可以分成3类
- 对象上的toString:返回一个'[Object xxxx]'结构的字符串
- 数组上的toString:返回数组内元素以逗号拼接得到的字符串
- 其他:返回字符串字面量
- 对象上的toString
- toString(x),v8会读取到x的内部属性[[class]],这个属性记录的就是x的所属的构造函数
- toString最终会返回由'[object' + 'class' + ']'拼接而成的字符串
- 因此想要判断类型,可以直接用到对象的toString方法,Object.prototype.toString()
- 那么基础数据类型怎么办?Object.prototype.toString.call()
- 由此,就要谈一谈call是什么了,显示绑定-->谈谈其他的默认绑定?隐式绑定?
问9 call的实现原理
let obj = {
a:1
}
function foo(){
}
Function.prototype.call2 = function(context){
context.fn = this;
context.fn();
}
foo.call2(obj); // 隐式绑定call里的this指向foo
// 往obj上挂载一个fn属性,值是foo
// 执行foo的时候this指向obj(obj.fn())
问10 所以为什么Object.prototype.toString.call(123)能够判断类型?
就像上面的foo,往obj中植入了一份foo,然后赶紧调用掉,这里就相当于植入一份toString(),然后触发了toString,最终就相当于123在触发这个toString(),123触发的是对象原型上的toString()Object.prototype.toString,而不是自己构造函数身上自带的toString,而对象原型上的toString会返回[object xxx],因此就算我们传一个原始数据类型进去,也可以做到数据类型判断。
转载自:https://juejin.cn/post/7384266820465000500