20道精选的面试题附答案,进来看看能答对多少(二)

前端达人

共 9162字,需浏览 19分钟

 ·

2021-06-03 15:38

来源 | https://www.cnblogs.com/echoyya/p/14550258.html

以下题目,本人验证无误,查阅了相关资料,得出解析部分并做了相关总结,希望对正准备跳槽或找工作的你有帮助!

1、写出执行结果,并解释原因

var a = [0];if ([0]) {  console.log(a == true);} else {  console.log("wut");}// 输出什么?
答案及解析
答案 : false解析 : if(condition)判断时,会把condition转换成boolean然后做判断,[0]是一个有值的list,所以转成booleantrue,        而A == B的比较时,如果A和B的类型不一样,会先把A和B转化成相同的type,通常转为number  //分成以下步骤  //把true转化成number,true变成1  [0] == 1;  //list是object  //先看[0].valueOf(),结果还是[0]  //再看[0].toString(),结果是“0” type是string  "0" == 1;   //把“0” string转化成number,“0”变成0,0不等于1  0 == 1; //结果是false

2、写出执行结果,并解释原因

[1 < 2 < 3, 3 < 2 < 1]    //解析成什么?

答案及解析

答案 : [true,true]解析 : 运算符优先级,分步解析  1 < 2 < 3 => true < 3  => 1 < 3 => true   3 < 2 < 1 => false < 1 => 0 < 1 => true

3、写出执行结果,并解释原因

function foo() { }var oldName = foo.name;foo.name = "bar";[oldName, foo.name]

答案及解析

答案 : ['foo','foo']解析 : 函数的name是只读属性不可修改

4、写出执行结果,并解释原因

var lowerCaseOnly =  /^[a-z]+$/;[lowerCaseOnly.test(null), lowerCaseOnly.test()]

答案及解析

答案 : [truetrue]解析 : 正则容易忽视的坑,test在检测时会隐性将内容转为字符串,其实等同于:[lowerCaseOnly.test('null'), lowerCaseOnly.test('undefined')]

5、写出执行结果,并解释原因

if ('http://giftwrapped.com/picture.jpg'.match('.gif')) {  console.log('a gif file')} else {  console.log('not a gif file')}

答案及解析

答案 : 'a gif file'解析 : 正则的隐式转换,match方法第一个参数接收一个正则表达式或者一个字符串,但如果是字符串会隐式转为正则,  所以上述代码等同于:'http://giftwrapped.com/picture.jpg'.match(/.gif/)  而在正则中 点 . 表示通配符,所以成功匹配到/gif,匹配成功,输出a gif file。

6、写出执行结果,并解释原因

function user(obj) {  obj.name = "北京"  obj = new Object()  obj.name = "上海"} let person = new Object();user(person);console.log(person.name);

答案及解析

答案 : 北京解析 : 对象作为参数,传递进去的是这个对象的地址, 1. obj.name是给person这个对象赋值; 2. obj = new Object(),把obj指向另一个对象, 3. obj.name现在是给这个新对象赋值,不影响person这个变量指向的对象; 4. 两个obj指向的对象的引用地址不同。所有函数的参数都是按值传递的。 5. 基本类型的传递同基本类型变量的赋值一样,按值传递,在函数体内修改参数的值,不会影响到函数外部。 6. 引用类型的值传递同引用类型变量的赋值一样,按引用传递,传入函数的是原始值的地址,因此在函数内部修改参数,将会影响到原始值。

7、写出执行结果,并解释原因

let x, y;try {    throw new Error();} catch (x) {    x = 1;    y = 2;    var a = 3
console.log(a); // ? console.log(x); // ?}console.log(a); // ?console.log(x); // ?console.log(y); // ?

答案及解析

答案 : 3  1  3  undefined  2解析 :   1. catch的作用域,其实并不是常见的块级作用域,且不能绑定自己的内部声明的变量(如a)。  2. catch创建的块作用域,只对catch的参数x有效。  3. 对于在内部声明的变量,catch并没有创建一个新的作用域,只是一个普通的代码块。因此块外仍可访问

8、写出执行结果,并解释原因

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

getValue(); // ?fn().getValue(); // ?getValue(); // ?new fn.getValue(); // ?new fn().getValue(); // ?

答案及解析

答案 : 4  1  1  2  3考察 : 变量定义提升、this指向、运算符优先级、原型、继承、全局变量污染、对象属性及原型属性优先级解析 : 为强调重点内容,在下方使用标记语言描述。
解析:
第一问 getValue():
  • 直接调用,关注点在4,5上:

  • JS存在一种变量声明被提升的机制,函数声明会被提升到作用域的最前面,即使写在最后,也还是会被提升至最前面。

  • 函数表达式和函数声明的区别,函数声明解析时会提升,函数表达式的值是在JS运行时确定,并且在表达式赋值完成后,该函数才能调用

  • 函数声明5被函数表达式4覆盖, 输出4

第二问 fn().getValue():
  • 执行fn函数,调用fn函数返回值对象的 getValue 属性函数;

  • 此时 getValue 函数没有用var进行声明,已将外层作用域的getValue函数修改;

  • fn函数返回this,此时函数执行确定this指向window对象,相当于执行window.getValue(),而getValue已经被修改成console.log(1), 输出1

第三问 getValue():
  • 执行完第6步,getValue函数已被修改,console.log(1), 输出1

第四问 new fn.getValue():
  • 考察JS的运算符优先级问题,

  • 点的优先级高于new无参数列表,相当于new (fn.getValue())

  • 当点运算完后有个括号(),此时就是变成new有参数列表,优先级高于函数执行,所以直接执行new。这也是为什么遇到()不先函数调用再new。

  • 最终相当于将 getValue函数,作为构造函数来执行, 输出2

第五问 new fn().getValue()
  • 这里带括号是new 有参数列表,new有参数列表的优先级与点的优先级相同,按从左到右的顺序执行。

  • 先执行有参数列表,再执行点的优先级,最后再函数调用

  • fn作为构造函数有返回值,在JS中构造函数的返回值可有可无

  1. 没有返回值:返回实例化的对象

  2. 有返回值:检查其返回值是否为引用类型

  1. 非引用类型:基本类型则与无返回值相同,实际返回其实例化对象。

  2. 引用类型:实际返回值为这个引用类型

  • fn 函数返回的是this,this在构造函数中本来就代表当前实例化对象, 最终fn返回实例化对象。调用对象的getValue方法,而构造函数中没有getValue,调用原型对象(prototype)上的getValue函数。输出3

9、写出执行结果,并解释原因

let length = 10;function fn() {  console.log(this.length);}var obj = {  length: 5,  method: function (fn) {    fn();    arguments[0]();  }};obj.method(fn, 1);

答案及解析

答案 : 0  2 解析 : 为强调重点内容,在下方使用标记语言描述。
解析
第一问:fn()
  1. 任意函数里嵌套非箭头函数,嵌套函数内this 在未指定的情况下,指向的是 window 对象,这里执行 fn会打印window.length,

  2. let声明的变量有形成块级作用域,且不存在声明提升,length属性并没有挂载到window对象中。(test:let a = 1; window.a // undefined)

  3. 此时打印的便是window自带的length属性,表示iframe个数,默认为0。输出0。

第二问:arguments[0]()
  1. arguments类数组是函数参数的引用, arguments[0]指向 fn,

  2. arguments[0]() 是作为 arguments对象的属性[0]来调用 fn的,谁调用 this 就指向谁;所以 fn 中的 this 指向arguments(对象的属性调用方法,this指向该对象)

  3. arguments有两个参数,fn和1,因此argumengts.length = 2 ,输出2。

扩展
  1. [function fn(){console.log(this.length)}][0](); // 1
  2. 数组也是对象,调用数组对象的0属性,函数作为数组对象的属性调用,函数中的this 当然指向这个数组,所以返回数组的length

10、写出执行结果,并解释原因

var a=10;var foo={  a:20,  bar:function(){      var a=30;      return this.a;    }}console.log(foo.bar());             // ?console.log((foo.bar)());           // ?console.log((foo.bar=foo.bar)());   // ?console.log((foo.bar,foo.bar)());   // ?

答案及解析

答案 : 20 20 10 10解析 : 为强调重点内容,在下方使用标记语言描述。
本题主要考察this指向问题,推荐阅读《一文搞懂 this、apply、call、bind
解析
第一问 foo.bar()
  • foo调用,this指向foo , 输出20。

第二问 (foo.bar)()
  • 表达式加了括号,括号的作用是改变表达式的运算顺序,而在这加与不加并无影响,相当于foo.bar(), 输出20。

第三问 (foo.bar=foo.bar)()
  • 等号运算相当于重新给foo.bar定义,相当于一个匿名函数赋值给一个全局变量,foo.bar是在window作用域下,this指代的是window,输出10。

foo.bar = function () {  var a = 10;  return this.a;}

第四问 (foo.bar,foo.bar)()

  • 逗号运算符求解过程是:先计算表达式1的值,再计算表达式2的值,……一直计算到表达式n的值,最后整个逗号运算符的返回值是最后一个表达式的值。

  • 经过逗号运算符后,就是纯函数,不再是对象方法的引用,所以this指向window,输出10。

  • 技巧:经过赋值,运算符运算后,都是纯函数,不是对象方法的引用。函数中this指向都是windows。

11、写出执行结果,并解释原因

function getName(){  return  {    name:'Echoyya'  }}console.log(getName());     // ?

答案及解析

答案 : undefined解析 : 如果continuebreakreturnthrow 这四个语句后面,直接跟换行符,则会自动添加分号

12、写出执行结果,并解释原因

const num = parseInt("2*4",10);console.log(num);      // ?

答案及解析

答案 : 2解析 : parseInt会检查字符串中的字符是否合法. 一旦遇到一个在指定进制(第二个参数)中不合法的字符后,立即停止解析并且忽略后面所有的字符。    *为非法数字。所以只解析到 2,并将其解析为十进制的2. 值即为 2

13、写出执行结果,并解释原因

var x = 20;var temp = {  x: 40,  foo: function () {    var x = 10;    console.log(this.x);  }};(temp.foo, temp.foo)();    // ?

答案及解析

答案 : 20技巧 : 经过赋值,运算符运算后,都是纯函数,不是对象方法的引用。函数中this指向都是windows。解析 : 逗号操作符会从左到右计算它的操作数,返回最后一个操作数的值。所以(temp.foo, temp.foo)();等价于var fun = temp.foo;       fun();fun调用时this指向window,因此返回 20。

14、写出执行结果,并解释原因

const company = { name: "Echoyya" };Object.defineProperty(company, "address", { value"北京" });console.log(company);               // ?console.log(Object.keys(company));  // ?

答案及解析

答案 : {name:"Echoyya",address:"北京"},  ["name"]解析 : defineProperty方法可以给对象添加一个新属性,或者修改已经存在的属性。而使用defineProperty给对象添加属性之后,属性默认为不可枚举,     Object.keys方法仅返回对象中可枚举的属性,因此只打印name

15、写出执行结果,并解释原因

let num = 10;const inNum = () => num++;const inPaNum = number => number++;const num1 = inNum();const num2 = inPaNum(num1);
console.log(num1); // ?console.log(num2); // ?

答案及解析

答案 : 10 10 解析 : 一元操作符 ++ 先返回操作值, 再执行自增操作值。    1. num1 是10,因为 inNum 函数先返回 num 的值,在执行 num 自增    2. num2 是10,因为 num1 作为参数传入 inPaNum,同理函数先返回 number 的值,在执行 number 自增

16、写出执行结果,并解释原因

const value = { number: 10 };const multiply = (x = { ...value }) => {  console.log(x.number *= 2);};
multiply(); // ?multiply(); // ?multiply(value); // ?multiply(value); // ?

答案及解析

答案 : 20 20 20 40解析 :   1. ES6中可以使用参数默认值, 函数未传参,或参数为undefined,将使用参数默认值。  2. 解构 value 对象并赋值给一个新对象,因此 x 的默认值为 {number:10} 。  3. 默认参数在调用时才会计算,每次调用函数,都会创建一个新的对象。调用 multiply(),x的默认值都为 {number:10},因此输出 20   4. 调用 multiply(value),实际上修改了 value.number的值,输出 20  5. 再次调用调用 multiply(value),value.number之前被修改为 20,因此输出 40

17、写出执行结果,并解释原因

// index.jsconsole.log('running index.js');import { sum } from './sum.js';console.log(sum(1, 2));
// sum.jsconsole.log('running sum.js');export const sum = (a, b) => a + b;

答案及解析

答案 : running sum.js,  running index.js,  3解析 : import命令是编译阶段执行的,在运行之前。因此被导入的模块会先运行,而导入模块的文件会后执行。    这是CommonJS 中 require() 和 import之间的区别。require()可以在运行代码时按需加载。    如果使用 require,那么running index.js、running sum.js、 3会被依次打印。

18、写出执行结果,并解释原因

function addToList(item, list) {  return list.push(item);}const result = addToList("company", ["yideng"]);console.log(result);    // ?

答案及解析

答案 : 2解析 : push()方法返回新数组的长度。若想返回新数组,应该在push之后返回list。

19、实现(5).add(3).minus(2) 功能

// 实现 (5).add(3).minus(2) 功能console.log((5).add(3).minus(2)); // 6

答案及解析

答案 :     Number.prototype.add = function (number) {        if (typeof number !== 'number') {            throw new Error('请输入数字~');        }        return this + number;    };    Number.prototype.minus = function (number) {        if (typeof number !== 'number') {            throw new Error('请输入数字~');        }        return this - number;    };    console.log((5).add(3).minus(2));    // 6

20、不使用模运算符的情况下,检查一个数是否是偶数

isEven(num)   // true Or false

答案及解析

答案 : 1)递归方式  function isEven(num){    const number = Math.abs(num);   // 取绝对值    if(number === 1)  return false;    if(number == 0 )  return true;    return isEven(number -2);  }-------------------------------------------------2)通过Math.round,利用奇数除以2会有小数的特点  function isEven(num){    return parseInt(num/2) === Math.round(num/2);  }

本文完~


推荐阅读

20道精选的面试题附答案,进来看看能答对多少(一)


学习更多技能

请点击下方公众号

浏览 38
点赞
评论
收藏
分享

手机扫一扫分享

分享
举报
评论
图片
表情
推荐
点赞
评论
收藏
分享

手机扫一扫分享

分享
举报