今天看啥  ›  专栏  ›  蜗牛的北极星之旅

查漏补缺面试题系列一(文章有点长)

蜗牛的北极星之旅  · 掘金  ·  · 2019-12-31 07:24
阅读 46

查漏补缺面试题系列一(文章有点长)

总结回顾

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);
复制代码

来源:juejin.im/post/5dee66…

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});在内部发生的时候

  1. 执行转换String({a: 1})。
  2. 执行内部的ToString({a: 1})。
  3. {a: 1}不是原始类型,执行ToPrimitive({a: 1}, hint string)。
  4. 调用toString方法,返回"[object, object]"。
  5. 执行ToString("[object, object]"),返回"[object, object]"。

来源:juejin.im/post/5dc431…

10、== 和 === 区别

对于 == 来说,如果对比双方的类型不一样的话,就会进行类型转换,这也就用到了第九个问题的内容。

  • 对于==

假如我们需要对比 x 和 y 是否相同,就会进行如下判断流程:

  1. 首先会判断两者类型是否相同。相同的话就是比大小了
  2. 类型不相同的话,那么就会进行类型转换
  3. 会先判断是否在对比 null 和 undefined,是的话就会返回 true
  4. 判断两者类型是否为 string 和 number,是的话就会将字符串转换为 number
  5. 判断其中一方是否为 boolean,是的话就会把 boolean 转为 number 再进行判断
  6. 判断其中一方是否为 object 且另一方为 string、number 或者 symbol,是的话就会把 object 转为原始类型再进行判断
  • 对于===

就是判断两者类型和值是否相同。

来源:juejin.im/book/5bdc71…

11、String你知道哪些方法

作者真实遇到,是哪个公司我就不说了,肯定一般情况也不会遇到。大家看看就行

  1. charAt(index) ,返回指定位置的字符
  2. charCodeAt(index),返回指定位置的字符编码
  3. concat(xx,xx),多个字符链接,返回新字符
  4. slice(start, end),截取字符,返回新字符
  5. substring(start, end),截取字符,返回新字符
  6. substr(start, length),截取字符,返回新字符
  7. indexOf(xx),返回指定字符的位置
  8. lastIndexOf(xx),返回指定字符的位置(倒叙)
  9. trim(),删除字符前后空格,返回新字符
  10. toLowerCase(),返回装换为小写的字符
  11. toUpperCase(),返回转换为大写的字符
  12. match(正则或者RegExp对象),返回匹配数组
  13. search(字符串或者RegExp对象),返回第一个匹配项的下标.
  14. replace(字符或者RegExp对象,字符串或者函数),返回替换后的字符,函数的时候,传递参数和匹配项的多少有关
  15. split(xx, length),把字符串拆分为数组,返回数组
  16. localeCompare(xx),比较两个字符,返回-1. 0. 1
  17. fromCharCode(一个或者多个字符编码),返回一个新字符
  18. fromCodePoint(一个或者多个字符编码),返回一个新字符,能处理四字节字符
  19. String.row(),斜杠转义方法,返回转义后的方法
  20. charCodeAt(index),返回指定位置的字符,能处理四字节字符
  21. normalize(),将字符的不同变现方式统一为同样的形式,Unicdoe的正规化
  22. incldues(xx),判断一个字符是否包含了另一个字符
  23. startsWith(xx),判断一个字符是否以另一个字符开头
  24. endsWith(xx),判断一个字符是否以另一个字符结尾
  25. repeat(n),把一个字符重复n次返回
  26. padStart(length, xx),指定长度下,在字符开头补全字符
  27. padEnd(length, xx),指定长度下,在字符的结尾补全字符
  28. trimStart(),返回消除了开始空格的新字符
  29. trimEnd(),返回消除了结尾空格的新字符
  30. matchAll(正则或者RegExp),返回在当前字符串的所有匹配

小结

以上是JS基础面试题系列的第一部分内容了。涉及到的知识点在我们日常的开发中经常可以看到,在面试中也会经常出现,大家看完有什么问题欢迎在评论区交流!

明天更新《JS基础面试题系列二》。




原文地址:访问原文地址
快照地址: 访问文章快照