面试利器原生JS灵魂拷问,你能答上多少(二)

Posted 战场小包

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了面试利器原生JS灵魂拷问,你能答上多少(二)相关的知识,希望对你有一定的参考价值。

前言

目前的前端世界,三大框架横行,原生javascript所用越来越少。但我认为JavaScript作为每一个前端工程师的立身之本,学再多遍都不为过。

因此我决定整理JavaScript中容易忽视或者混淆的知识点,写一系列文章,以灵魂拷问的方式,系统且完整的带大家遨游基础的的JavaScript,给大家带来不一样的体验。

系列文章链接:

JavaScript运算符之问

第十六问 你知道||与&&的返回值规则吗?

  1. 短路效应:&&||都会发生短路

    • && 只有在两个操作数都为 true 时,条件判断的结果才为 true ,如果操作数一为 false ,不会判断操作数二。
    • || 两个操作数只要有一个为 true ,条件判断的结果就为 true ,因此操作数一为 true 时,不会判断操作数二。
  2. 返回值规则

    • ||&& 首先会对操作数一执行条件判断,如果不是布尔值就先强制转换为布尔类型,然后再执行条件判断。
    • 对于 || 来说,如果条件判断结果为 true 就返回第一个操作数的值,如果为 false 就返回第二个操作数的值。
    • && 则相反,如果条件判断结果为 true 就返回第二个操作数的值,如果为 false 就返回第一个操作数的值。
    • ||&& 返回它们其中一个操作数的值,而非条件判断的结果
  3. (2<3)||(3<2)返回值是多少?

第十七问 1 + - + + + - + 1结果是多少?

+/- 号在 JavaScript 通常有三种用途:

  • 普通加减法: 二元运算符
  • ++/--: 自增自减,一元运算符
  • +/-: 正负,一元运算符

上面表达式中没有涉及自增与自减的情况,一元运算符的优先级大于二元运算符,上述表达式执行顺序为:
1 + (- (+ (+ (+ (- (+ 1)))))) ----> 1 + 1 = 2

第十八问 你会用位运算符吗?

  1. 判断奇偶数

二进制的奇数最低位是1,偶数最低位是0,可以通过& 1运算后可以判断奇偶。

num & 1 === 1 // num 为奇数
num & 1 === 0 // num 为偶数
  1. 交换a,b的值

可以采用异或来实现两个变量值的交换

let a = 1
let b = 2

a ^= b
b ^= a
a ^= b
console.log(a)   // 2
console.log(b)   // 1
  1. 取整

    ~~num

第十九问 你能准确的做出自增与自减的题目吗?

请回答:y 的值为?

var x = 1;
var y = x + ++x + 3 * (x = ++x + x + x++ + 1)
          + x++ + 3;
console.log(y); 

咱们来解剖一下这个长的要死的表达式:

第一个x值为1
第二个自增先加后用,x = 2++x + x + x++ + 1 的运算结果赋值给x
    ++x 先加后用,x = 3
    x = 3
    x++ 先用后加,x = 3
    ++x + x + x++ + 1 = 3 + 3 + 3 + 1 = 10    
    x++的后加,x = 4
将表达式的值赋给x,x由4变为10

x++ 先用后加,x = 10
此时所有的变量都已经求出
y = 1 + 2 + 3*10 + 10 + 3 = 46
x++,x最终值为11

第二十问 你知道new运算符的有两种优先级吗?

MDNnew 操作符的描述中,语法是:

    new constructor[([arguments])]

([arguments]) 意味着可以缺省,会存在 new constructor(...args)new constructor 两种模式,并且前者的优先级高于后者。更详细的优先级见下图:

这个知识点非常重要,只有区分开了 new 带参列表和不带参列表,才能准确并且透彻的理解下面这道面试题。

function Foo(){
    getName = function(){ console.log(1); };
    return this;
}
Foo.getName = function(){ console.log(2); };
Foo.prototype.getName = function(){ console.log(3); };
var getName = function(){ console.log(4); };
function getName(){ console.log(5) };

Foo.getName();         
getName();        
Foo().getName();
getName();        
new Foo.getName();
new Foo().getName();

JavaScript字符串之问

第二十一问:你知道toString()的妙用吗?

  1. 可以转换数字的进制(2-36)
// 参数为要转换的进制
var a = 10;
a.toString(2) // "1010" 
a.toString(8) // "12"
a.toString(16) // "a"
  1. 可以生成随机验证码

利用 toString 的进制转换,可生成随机验证码

Math.random().toString(36).substring(3,7) //生成四位数的随机验证码
  1. 判断数据类型
toString.call(()=>{})       // [object Function]
toString.call({})           // [object Object]
toString.call([])           // [object Array]
toString.call('')           // [object String]
toString.call(22)           // [object Number]
toString.call(undefined)    // [object undefined]
toString.call(null)         // [object null]
toString.call(new Date)     // [object Date]
toString.call(Math)         // [object Math]
toString.call(window)       // [object Window]

第二十二问:字符串是原始值,那为什么可以调用字符串方法那?

原始值是没有属性也没有方法的,那为什么字符串可以调用方法那?

JavaScript 为了便于基本类型操作,提供了三个特殊的引用类型(包装类),即 Number,String,Boolean ,它们的 [[PrimitiveValue]] 属性存储它们的本身值。

光说这些有可能有些难理解,咱们来举个例子:

var str = 'zcxiaobao'
str2 = str.toUpperCase()

其实js引擎内部会这样处理:

var str = 'zcxiaobao'
// 调用方法,创建String的一个实例
new String(str)
// 调用实例上的方法,并将值返回
str2 = new String(str).toUpperCase()
// 销毁实例

但这里有一个需要注意的点,new String('1')'1' 类型相同吗,我们来测试一下:

var str1 = new String('1');
var str2 = '1';
console.log(str1 === str2); // false
console.log(str1 == str2);  // true
console.log(typeof(str1))   // object
// new String()得到的字符串为object格式

第二十三问:修改string.length大小能改变字符串长度吗?为什么?

先看一个示例:

var str = '123456';
str.length = 0;
console.log(str, str.length); // 123456 6

很明显,修改 str.length 是无法做到修改字符串的长度的。

原因跟第二十二问是相同的,str 为原始值,调用 length 相当用 new String(str).length,修改的是 new String(str)length ,跟原始值 str 无关。

第二十四问:修改new String()生成字符串的length会生效吗?为什么?

如果将上面代码修改一下,str 是由 new String 产生,修改 length 属性会生效吗?

var str = new String('123456');
str.length = 0;
console.log(str, str.length); // String {"123456"} 6

答案告诉我们,还是失败了。

二十三问中:new String() 生成的字符串是对象类型,为啥还不能使用 length 属性。那说明 length 属性,很有可能配置了不可写,测试一下上述猜想:

Object.getOwnPropertyDescriptor(str, 'length')
/*
    {
        value: 6, 
        writable: false, 
        enumerable: false, 
        configurable: false
    }
*/

由控制台的打印可知:new String() 生成的字符串的 length 属性不止是不可写,而且是不可枚举、不可配置的。

第二十五问:你知道如何给数字添加千分符吗?

千分符:每隔三位数加进一个逗号,也就是千位分隔符,以便更加容易认出数值。最经典的案例就是银行账单:200,100,100.00 元。

  1. 调用字符串的 toLocaleString() 方法
var num = 1450068.12;
console.log(num.toLocaleString()) // 1,450,068.12

但这种方法经过测试,不管多少小数点后有多少位小数,都只会返回三位小数,并且经过四舍五入。

var num = 1450068.129999;
console.log(num.toLocaleString()) // 1,450,068.13
  1. 对字符串进行操作,每三位添加一个逗号。
function addThousands(num) {
    var result = '',
    counter = 0;
    [numInter, numDecimal] = (num || 0).toString().split('.');
    // 倒序处理整数部分
    for (var i = numInter.length - 1; i >= 0; i--) {
        counter++;
        result = numInter[i] + result;
        if (!(counter % 3) && i != 0) {
          result = ',' + result;
            
        }
    }
    // 大家可以补充一下小数部分的代码
    // 小数部分与整数部分类似
    return result;
}
console.log(addThousands(42371582378423))  // 42,371,582,378,423
  1. 正则表达式

该方法实现太过惊天动地了,这里只留答案,剩下的留到正则部分再详细讲述。

function toThousands(num) {
   var numStr = (num || 0).toString();
    return numStr.replace(/(\\d)(?=(?:\\d{3})+$)/g, '$1,');
}

第二十六问:你真的理解透 JSON.stringify 了吗?

  1. JSON.stringfy 的基本使用:

JSON.stringify 可以将数组或者对象转化成 JSON 字符串。JSON.parse 可以将 JSON 字符串转化为数组或对象。基于这两个方法,可以产生很多用处,例如:

  • 对象的深拷贝(存在缺点,对象那里再详细说明缺点)
  • localStorage 只能存取字符串格式的内容,因此存之前转换成 JSON 字符串,取出来用时,在转化成数组或对象。
  1. 学透 JSON.stringfy

语法

JSON.stringify(value[, replacer [, space]])

参数

  • value: 将要序列化成 一个 JSON 字符串的值。
  • replacer(可选):
    • 如果该参数是一个函数,则在序列化过程中,被序列化的值的每个属性都会经过该函数的转换和处理;
    • 如果该参数是一个数组,则只有包含在这个数组中的属性名才会被序列化到最终的 JSON 字符串中;
    • 如果该参数为 null 或者未提供,则对象所有的属性都会被序列化。
  • space:指定缩进用的空白字符串,用于美化输出
  1. (重要): 九大特性

特别要注意1、3、5条特性

  1. 布尔值、数字、字符串的包装对象在序列化过程中会自动转换成对应的原始值。
  2. 所有以 symbol 为属性键的属性都会被完全忽略掉,即便 replacer 参数中强制指定包含了它们。
  3. 对包含循环引用的对象(对象之间相互引用,形成无限循环)执行此方法,会抛出错误。
  4. NaNInfinity 格式的数值及 null 都会被当做 null
  5. undefined、任意的函数以及 symbol 值:
    • 出现在非数组对象的属性值中时在序列化过程中会被忽略
    • 出现在数组中时会被转换成 null
    • 单独转换时,会返回 undefined
  6. 转换值如果有 toJSON() 方法,该方法定义什么值将被序列化。
  7. Date 日期调用了 toJSON() 将其转换为了 string 字符串(同 Date.toISOString() ),因此会被当做字符串处理。
  8. 其他类型的对象,包括 Map/Set/WeakMap/WeakSet,仅会序列化可枚举的属性
  9. 非数组对象的属性不能保证以特定的顺序出现在序列化后的字符串中。

以上是关于面试利器原生JS灵魂拷问,你能答上多少(二)的主要内容,如果未能解决你的问题,请参考以下文章

面试利器 原生JS灵魂拷问,你能答上多少

面试利器 原生JS灵魂拷问,你能答上多少

面试利器原生JS灵魂拷问,你能答上多少(三)

面试利器原生JS灵魂拷问,你能答上多少(三)

面试利器原生JS灵魂拷问,你能答上多少(三)

面试利器 原生JS灵魂拷问,你能答上多少(建议收藏)