总结回顾
- 这道JS笔试题你做对了吗?
- 前端面试基础网络问题(万字长文)
- 面试官:你能实现多少种水平垂直居中的布局(定宽高和不定宽高)
- 一道网红面试题(腾讯、百度面试中都出现过)
- 通过运行机制看this绑定 、作用域、作用域链和闭包
- JS操作小技巧,工作简单了
- 栈、堆、队列深入理解,面试无忧
- 面试官:请实现三栏布局,尽可能多的方式。
- 虚拟DOM的优劣如何
- 这样回答继承,面试官可能更满意
- 箭头函数和普通函数的10个区别
- html 空白汉字占位符
- 我想了解更多判断数组的方式
- HTML Email的编写
- Vue Router被我们忽略的知识点
JavaScript 是 ECMAScript 规范的一种实现,作为一个前端er,JavaScript基础知识是必备的技能。在面试中基础知识一定会被面试。
本着经验发现,小厂只问项目(他需要你入职既能上手干活);中厂先问基础(不是很深)再问项目;大厂从项目中问基础问的很深(深有体会),而且大厂的面试官真的是很nice,但是也很让人捉摸不透,不管回答的是对是错,他们表情和言语不会有任何的变化,经过专业的培训。
在接下来的几篇文章中,我会更新一些自己遇到的或者刷到的基础面试题分享给大家。
1、原始类型有哪些
- string
- boolean
- number
- null
- undefined
- symbol(ES6新增)
- bigint(新增阶段,在高版本的谷歌浏览器可以使用)
BigInt是一种新的数据类型,用于当整数值大于Number数据类型支持的范围时。这种数据类型允许我们安全地对大整数执行算术操作,表示高分辨率的时间戳,使用大整数id,等等,而不需要使用库。 重要的是要记住,不能使用Number和BigInt操作数的混合执行算术运算,需要通过显式转换其中的一种类型。 此外,出于兼容性原因,不允许在BigInt上使用一元加号(+)运算符。
原始类型是值存储,没有函数可以调用,但是我们在日常的开发中,发现字符串、数字、布尔类型都可以调用函数。例如:
var s1 = "some text"; var s2 = s1.substring(2);
原因是JS在后台做了操作,此时字符串已经不是原始类型,已经被转换为包装类型。
var s1 = new String("some text"); var s2 = s1.substring(2); s1 = null;
2、原始类型的判断方法
- typeof
// number类型 var num = 1; console.log(typeof num);// 返回的是number // string 类型 var str = '蜗牛'; console.log(typeof str);// 返回的是string // boolean类型 var boo =true; console.log(typeof boo);// 返回的是boolean // undefined类型 var und ; console.log(typeof und);// 返回的是undefined // null类型 var nul =null; console.log(typeof nul); // 返回的是object // symbol类型 var sym = Symbol(); console.log(typeof sym); // 返回的是symbol // bigint类型 var bigint = 1n; console.log(typeof bigint); // 返回的是bigint
- Object.prototype.toString.call
// number类型 var num = 1; console.log(Object.prototype.toString.call(num); // 返回的是'[object Number]' // string 类型 var str = '蜗牛'; console.log(Object.prototype.toString.call(str));// 返回的是'[object String]' // boolean类型 var boo = true; console.log(Object.prototype.toString.call(boo));// 返回的是'[object Boolean]' // undefined类型 var und ; console.log(Object.prototype.toString.call(und));// 返回的是'[object Undefined]' // null类型 var nul =null; console.log(Object.prototype.toString.call(nul)); // 返回的是'[object Null]' // symbol类型 var sym = Symbol(); console.log(Object.prototype.toString.call(sym)); // 返回的是'[object Symbol]' // bigint类型 var bigint = 1n; console.log(Object.prototype.toString.call(bigint)); // 返回的是'[object BigInt]'
3、为什么typeof null 返回的是object
原理是这样的。不同的对象在底层都表示为二进制,在javascript中二进制前三位都为0的话会被判断为object类型,很多人会认为他是个对象类型,其实这是错误的。虽然 typeof null 会输出 object,但是这只是 JS 存在的一个悠久 Bug。在 JS 的最初版本中使用的是 32位系统,为了性能考虑使用低位存储变量的类型信息,000 开头代表是对象,然而 null 表示为全零,所以将它错误的判断为 object 。虽然现在的内部类型判断代码已经改变了,但是对于这个 Bug却是一直流传下来。
4、0.1000000000000000002.toString().length 为什么返回3,为什么0.1+0.2 != 0.3
计算机中都是通过二级制存储数据,对于数字来说,由于JavaScript采用IEEE754标准,数值存储为64位双精度格式,数值精度最多可以达到53个二进制位(1个隐藏位,52个有效位)。如果数值的精度超过这个限度,第54位及后面的位就会被丢弃。
0.1(0.100000000000000002)转换为转换为二进制已经超过53个二级制位,导致后面的2被丢弃。很多十进制小数使用二进制表示都是无限循环的,这其实本身没有什么问题,但是JS采用浮点数标准会裁剪我们的数据。导致精度丢失,造成:
0.100000000000000002 === 0.1 // true 0.200000000000000002 === 0.2 // true 0.1 + 0.2 === 0.30000000000000004 // true // 所以0.1+0.2 != 0.3
解决方法:toFixed()
5、原始类型和引用类型有什么不同
- 原始类型值不可变,引用类型的值是可以改变的
var name = "原始类型"; var copy = name.substr(1); console.log(name); // 原始类型 console.log(copy); // 始类型 var o = {x:1}; o.x = 2; o.y = 3; console.log(o); // {x: 2, y: 3} var a = [1,2,3]; a[0] = 0; a[3] = 4; console.log(a); // [0, 2, 3, 4]
- 原始类型不可以添加属性和方法,引用类型可以添加属性和方法
var n = "原始类型"; n.name = 'haha'; n.method = function(){console.log(name)}; console.log(n.name); // undefined console.log(n.method); // undefined var person = {}; person.name = "引用类型"; person.say = function(){alert("hello");} console.log(person.name); // 引用类型 console.log(person.say); // function(){alert("hello");
- 原始类型的赋值是简单赋值,引用类型的赋值是对象引用
var a = 10; var b = a; a++; console.log(a); //11 console.log(b); //10
var a = {}; var b= a; a.name = "引用类型"; console.log(a.name); //引用类型; console.log(b.name); //引用类型 b.age = 29; console.log(a.age); //29 console.log(b.age); //29
- 原始类型的比较是值的比较,引用类型的比较是引用的比较
var str1 = '11'; var str2 = '11'; console.log(str1 == str2); // true
var person1 = {}; var person2 = {}; console.log(person1 == person2); //false
- 原始类型是存放在栈区,引用类型是同时保存在栈区和堆区中
var name = "jozo"; var city = "guangzhou"; var age = 22; 复制代码
var person1 = {name:"change1"}; var person2 = {name:"change2"}; var person3 = {name:"change3"};
来源:www.cnblogs.com/chaoyuehedy…
6、instanceof 能正确判断引用类型的原因
instanceof的内部机制是通过原型链来判断(判断左边对象的原型链上是否存在右边原型)
// 根据instanceof的原理实现一个instanceof function myInstanceof(left, right) { let right = right.prototype; let left = left.__proto__; while (true) { if (left === null || rigth === undefined) { return false; } else { if (left === right) { return true; } else { left = left.__proto__; } } } }
7、是否可以通过instanceof判断原始类型
一般情况来说instanceof是不能判断原始类型的。但是我们可以使用Symbol.hasInstance。它可以让我们自定义instanceof的行为。
class HasInstance { static [Symbol.hasInstance](x) { return typeof x === 'string' } } console.log('我是一个字符串' instanceof HasInstance); // true
8、怎么进行数组类型的判断
- instanceof
var array = []; array instanceof Array;
- constructor
var array = []; array.constructor === Array
PS:instanceof和constructor判断的变量,必须在当前页面申明的,比如,一个页面(父页面)一样一个框架,框架中引入一个页面(子页面),在子页面中声明的array,并将其复制给父元素的一个变量,判断该变量,将返回false。
原因:array是复合类型。在传递的过程中,仅仅是引用地址的传递。 每个页面的array原生对象引用的地址是不一样的,在子页面中声明的array,所对应的构造函数,是子页面的array对象,在父页面进行判断时,使用的并不是子页面的array;
- __ proto__.constructor
var array = []; array.__proto__.constructor === Array;
每个对象都有一个__proto__属性,该属性指向构造函数的原型对象,对象可以通过这个属性访问到构造函数的原型对象,__proto__属性是没有写入ES6正文的属性, 而是写入了附录中,原因是他本质上是一个内部属性,而不是一个正式的API,只是被浏览器广泛支持,才被加入ES6,只有浏览器有这个属性,其他环境是没有。所以重某种角度来说,我们是不建议在实际生产中使用的,而是使用Object.getPrototypeOf(target)来进行操作。
- Array.isArray
var array = []; Array.isArray(array);
es6中加入了新的判断方法,存在兼容性问题。
- toString(最常用)
if(!Array.isArray){ Array.isArray = function(arg){ return Object.prototype.toString.call(arg)==='[object Array]'; } } var array = []; Array.isArray(array);
9、类型转换
1、类型转换的规则
2、隐式类型装换
在执行过程中当js内部期望得到某种类型的值,而实际在那里的值是其他的类型,就会发生隐式类型转换。系统内部会自动调用我们前面说ToBoolean ( argument )、ToNumber ( argument )、ToString ( argument ),尝试转换成期望的数据类型。
- 期望得到boolean的值
if ( !undefined && !null && !0 && !NaN && !'') { // xxxx }
因为在if的括号中,js期望得到boolean的值,所以对括号中每一个值都使用ToBoolean ( argument ),将它们转化成boolean。
- 期望得到number的值
3 * { valueOf: function () { return 5 } };
因为在乘号的两端,js期望得到number类型的值,所以对右边的那个对象使用ToNumber ( argument ),得到结果5,再与乘号左边的3相乘。
- 加号有别于其他运算符
- 如果有一边是字符串,就把另外一边也转换为字符串
- 如果一方不是字符串或者数据,就转换为数据或者字符串
处了加号运算符,其他运算符,只要其中一方数据,那么另一方就被转换为数字
3、显示类型装换
手动调用Boolean(value)、Number(value)、String(value)完成的类型转换。
Boolean('some text'); // true Number("2019"); // 2019 String({a: 1}); // "[object Object]"
前面两个类型转换没有什么好解释的,我们看看最后一个String({a: 1});在内部发生的时候
- 执行转换String({a: 1})。
- 执行内部的ToString({a: 1})。
- {a: 1}不是原始类型,执行ToPrimitive({a: 1}, hint string)。
- 调用toString方法,返回"[object, object]"。
- 执行ToString("[object, object]"),返回"[object, object]"。
10、== 和 === 区别
对于 == 来说,如果对比双方的类型不一样的话,就会进行类型转换,这也就用到了第九个问题的内容。
- 对于==
假如我们需要对比 x 和 y 是否相同,就会进行如下判断流程:
- 首先会判断两者类型是否相同。相同的话就是比大小了
- 类型不相同的话,那么就会进行类型转换
- 会先判断是否在对比 null 和 undefined,是的话就会返回 true
- 判断两者类型是否为 string 和 number,是的话就会将字符串转换为 number
- 判断其中一方是否为 boolean,是的话就会把 boolean 转为 number 再进行判断
- 判断其中一方是否为 object 且另一方为 string、number 或者 symbol,是的话就会把 object 转为原始类型再进行判断
- 对于===
就是判断两者类型和值是否相同。
11、String你知道哪些方法
作者真实遇到,是哪个公司我就不说了,肯定一般情况也不会遇到。大家看看就行
- charAt(index) ,返回指定位置的字符
- charCodeAt(index),返回指定位置的字符编码
- concat(xx,xx),多个字符链接,返回新字符
- slice(start, end),截取字符,返回新字符
- substring(start, end),截取字符,返回新字符
- substr(start, length),截取字符,返回新字符
- indexOf(xx),返回指定字符的位置
- lastIndexOf(xx),返回指定字符的位置(倒叙)
- trim(),删除字符前后空格,返回新字符
- toLowerCase(),返回装换为小写的字符
- toUpperCase(),返回转换为大写的字符
- match(正则或者RegExp对象),返回匹配数组
- search(字符串或者RegExp对象),返回第一个匹配项的下标.
- replace(字符或者RegExp对象,字符串或者函数),返回替换后的字符,函数的时候,传递参数和匹配项的多少有关
- split(xx, length),把字符串拆分为数组,返回数组
- localeCompare(xx),比较两个字符,返回-1. 0. 1
- fromCharCode(一个或者多个字符编码),返回一个新字符
- fromCodePoint(一个或者多个字符编码),返回一个新字符,能处理四字节字符
- String.row(),斜杠转义方法,返回转义后的方法
- charCodeAt(index),返回指定位置的字符,能处理四字节字符
- normalize(),将字符的不同变现方式统一为同样的形式,Unicdoe的正规化
- incldues(xx),判断一个字符是否包含了另一个字符
- startsWith(xx),判断一个字符是否以另一个字符开头
- endsWith(xx),判断一个字符是否以另一个字符结尾
- repeat(n),把一个字符重复n次返回
- padStart(length, xx),指定长度下,在字符开头补全字符
- padEnd(length, xx),指定长度下,在字符的结尾补全字符
- trimStart(),返回消除了开始空格的新字符
- trimEnd(),返回消除了结尾空格的新字符
- matchAll(正则或者RegExp),返回在当前字符串的所有匹配
小结
以上是JS基础面试题系列的第一部分内容了。涉及到的知识点在我们日常的开发中经常可以看到,在面试中也会经常出现,大家看完有什么问题欢迎在评论区交流!
记下来更新《JS基础面试题系列二》。