前端笔试题总结(一) - JS篇

1. 代码执行分析

var x = 0;
x = x ++;
console.log( x === window.x ); // true
console.log( x ); // 0
let foo = function () {
  var i = 0;
  return function () {
    console.log(i++);

  }
}

let f1 = foo(); // 每次调用foo,都被 return 一个新的函数
let f2 = foo();
f1();  // 0
f2();  // 0
f1();  // 1
let foo = function () {
  var i = 0;
  return function (i) {
    console.log(i++); // console.log(undefined++); -> NaN

  }
}

let f1 = foo();
let f2 = foo();
f1();  // NaN
f2();  // NaN
f1();  // NaN
let foo = function () {
  let i = 0;
  return function () {
    console.log(i++);

  }
}

let f1 = foo();
let f2 = foo();
f1();  // 0
f2();  // 0
f1();  // 1
let foo = function () {
  let i = 0;
  return function (i) {
    console.log(i++);

  }
}

let f1 = foo();
let f2 = foo();
f1();  // NaN
f2();  // NaN
f1();  // NaN
console.log('1: ', i)

for (var i = 0; i < 3; i++) {
  console.log('2: ', i);
  setTimeout(() => console.log('3: ', i), 0);
}

console.log('4: ', i);

for (let i = 0; i < 3; i++) {
  console.log('5: ', i);
  setTimeout(() => console.log('6: ', i), 0);
}

console.log('7: ', i);

/*
  1:  undefined
  2:  0
  2:  1
  2:  2
  4:  3
  5:  0
  5:  1
  5:  2
  7:  3
  3:  3 var 声明的变量没有块级作用域,i 被提升到了全局,第一个 setTimeout 异步回调执行的时候只能从全局获取 i 变量
  3:  3
  3:  3
  6:  0 let 声明的变量有块级作用域,i 被保存在了对应的块级作用域中,第二个 setTimeout 异步回调执行的时候,直接从当前作用域内获取 i 变量
  6:  1
  6:  2
*/

2. 不同类型的数值进行四则运算:

var c = "10", d = 10, f = false;
var y;
console.log( "c+d: "+(c+d)+", c+f: "+(c+f)+", d+f"+(d+f) );  // c+d: 1010, c+f: 10false, d+f: 10
console.log( d+y )   // NaN
+ (加法运算)Number(10)String("light")boolean(true/flase)undefinednullNaN总结
Number(10)2010light

10 + false = 10;

10 + true = 11

NaN10NaN
String("light")10lightlightlightlighttruelightundefinedlightnulllightNaN

对字符串做+相当于对字符串进行拼接

总会将另一个变量转换为字符串

boolean(true/flase)

10 + false = 10;

10 + true = 11

light + true = lighttrue;

light + false = lightfalse

true + true = 2;

true + false = 1;

false + false = 0

NaN

true + null = 1;

false + null = 0

NaN做四则运算时,true被看做1,false被看做0
undefinedNaNlightundefinedNaNNaNNaNNaN只能做字符串拼接
null10lightnull

true + null = 1;

false + null = 0

NaN0NaNnull 和能转化为数值类型的数据做运算,null 会转化为 0
NaNNaNlightNaNNaNNaNNaNNaN只能做字符串拼接
typeofnumberstringbooleanundefinedobjectnumber

3. JS中声明一个数组:

var r1 = ["arr1", "arr2", "arr3"];
var r2= new Array("arr1", "arr2", "arr3");
var r3= Array("arr1", "arr2", "arr3");

4. 一个前缀是 "$" 的 js 变量是:合法的 JS 语法,和其他任何字符一样。

5. 几个四舍五入的方法、属性:

  1. a.toFixed(m) :保留 m 个小数。规律:4舍6入5单独考虑。
    1. 第 m+1 个小数不是5:4舍6入;
    2. 第 m+1 个小数是5,看5后面是否还有数字:
      1. :整体表现为4舍5入
      2. 没有,就看5前面是奇数还是偶数:
        1. 奇数:4舍5入;
        2. 偶数:5舍6入;

        (总结:a.toFix(n) 保留 n 位小数,a 恰巧有 n+1 个小数,第 n+1 位是 5,第 n 位是偶数,表现为5舍6入,其它都是4舍5入。注意!chrome 并不是这个规律,暂时没有确定的规律)

  1. Math.round(a) 对a进行4舍5入,只保留整数部分。
  2. a.toPrecision(m)  对a进行4舍6入,当最后一位的下一位为5时,就很奇怪了,舍入规则稀奇古怪,避免使用。
var a = 3.1515
var b = 3.1525
var c = 3.1535
var d = 3.1545
var e = 3.1555
var f = 3.1565
var g = 3.1575
var h = 3.1585
var i = 3.1595
var j = 3.1505
console.log( a.toPrecision(4) );  // 3.151
console.log( b.toPrecision(4) );  // 3.152
console.log( c.toPrecision(4) );  // 3.154
console.log( d.toPrecision(4) );  // 3.155
console.log( e.toPrecision(4) );  // 3.155
console.log( f.toPrecision(4) );  // 3.156
console.log( g.toPrecision(4) );  // 3.158
console.log( h.toPrecision(4) );  // 3.159
console.log( i.toPrecision(4) );  // 3.159
console.log( j.toPrecision(4) );  // 3.151

6. typeof的两种用法:

var x = typeof 123;
var x = typeof(123);
console.log( x );

7. null == undefined, null !== undefined

var one;
var two = null;
console.log( one == two, one === two )  // true, false   也就是说null == undefined,null !== undefined

8. +new Date() 等价于调用 Date.prototype.valueOf() 方法,返回“ 从1970年1月1日0时0分0秒(UTC,即协调世界时)到该时间的毫秒数 ”。

console.log( +new Date() )  //1619089280052

9. 经典IIFE定时器问题:

for(var i = 0; i < 5; i++){
  (function(i){
    setTimeout(function(){
      console.log( i );
    }, 5000);
  })(i)
}

// 0,1,2,3,4
// for循环的每一圈,在IIFE中都传入了参数i,这样对于这个定时器来说,外层的函数作用域内就包含了一个带值的i,调用时就不需要向上查找i

 
10. eval()的作用:

eval(string)的作用是将string作为js代码执行,然后返回其中的值,如果没有值返回undefined。作用域为调用该函数的执行上下文。

有很多的缺点,例如并不会像普通的js代码那样对其中的代码进行预加载,不过这个缺点可以通过一些手段将其带来的损失降到最低。

11. JSON对象的方法:

JSON对象的方法有两个,一个是将一个JSON字符串转换为一个JSON对象,另一个则是将JSON对象转换为JSON字符串。

将JSON字符串转化为JSON对象:JSON.parse()

var info = '{"name":"guangtailang","age":22}';
var heroGuang = JSON.parse(info);
console.log(heroGuang.name);  // guangtailang

将JSON对象转化为JSON字符串:JSON.stringify() 【ify:动词后缀,...化的;stringify:字符串化的,在本环境中,就是将一个JSON对象字符串化为一个字符串】

var heroGuang = {name: "guangtailang", age: 22};
var info = JSON.stringify(heroGuang);
console.log(info);   // {name: "guangtailang", age: 22}
console.log( typeof info );   // string

12. 原生JS获取Dom对象的几种方法:

  • 通过id获取:getElementById("div1")。   参数是id,所有获取方法的参数均是一个字符串,不能加“ # ”返回一个Dom对象。
  • 通过class获取:getElementsByClassName("box")。   参数是类名,不能加“ . ”。返回一个集合。不能直接给集合绑定事件,需要获取到集合中的某一个元素,然后再为元素绑定事件。
  • 通过标签名获取:getElementsByTagName("p")。   参数是标签名。返回的也是一个集合。
  • 通过name属性获取:getElementsByName("user")。   参数是name属性的值。返回的也是一个集合。只有含有name属性的元素才能通过name属性获取。
  • 通过选择器获取:querySelector("#div1")。   参数是一个选择器。只能获取到选择器对应的第一个元素。
  • 通过选择器获取:querySelectorAll(".div1")。   参数也是一个选择器。获取到了所有匹配选择器的元素组成的集合。

13. html标签是否可以用过JS的方法来获取?

可以,上面的所有方法均可以获取到html标签。举个栗子:

<!DOCTYPE html>
<html lang="en" id="no1">
<head>
  ...
</head>
<body>
   
<script>
  var res = document.getElementById("no1");
  console.log( res );   // 会将整个html结构打印出来

</script>
</body>
</html>

看一下打印结果

展开当然就是完整的html结构,我就不展开了。

14. 调用一个对象的的属性的两种方法:“[ ]”、“  . ”

var heroGuang = {name: "guangtailang", age: 22};
// 方法一:
console.log( heroGuang['name'] );  // [ ]内的引号可以省略
// 方法二:
console.log( heroGuang.name );
  • "[  ]"
    • "[  ]"的方法常常用于动态地为一个对象添加属性。
    • 其次,正常情况下,我们并不能为对象定义一个以数字开头的属性,但heroGuang['111'] = 222; 的方式可以实现。
heroGuang['111'] = 222;
console.log( heroGuang['111'] );  // 222

15. 下面哪个语句表示调用了一个函数:

function.invoke(...)    // invoke并不是js中的方法
function.Execute(...)   // Execute并不是js中的方法
function.Apply(...)     // 没有大写的用法
function.exec(...)      // 匹配字符串
function.apply(...)     // 可以用于调用函数

exec:

exec() 方法用于对字符串字符串进行正则匹配

var str = 'The Quick Brown Fox Jumps Over The Lazy Dog';
var re = /quick\s(brown).+?(jumps)/ig;
// 借助exec方法
var result1 = re.exec(str);
// 借助match方法
var result2 = str.match(re);
console.log( result1 );
console.log( result2 );

对于exec函数来说 

  • 如果 exec() 找到了匹配的文本,则返回一个结果数组。
    • 0:表示与正则表达式相匹配的文本,
    • 1:表示与 regExpObject 的第 1 个子表达式相匹配的文本(如果有的话),
    • 2:表示与 regExpObject 的第 2 个子表达式相匹配的文本(如果有的话),以此类推。
    • index:表示与正则表达式匹配的文本的第一个字符的位置。
    • input:则存放的是被检索的字符串 string。
    • groups:reg = /(\d+)/ 被 "()" 括住的部分叫做捕获,对应的的英文就叫做 group ,而这个 groups 中列举的就是“有名有姓”的捕获。我们将reg进行修改,reg2 = /(?<name>\d+)/,其中 ?<name> 就表示被捕获的名字为 name,这个时候 groups 中就会多一个 name 的属性。
  • 没有找到则返回 null。

在调用非全局的 RegExp 对象的 exec() 方法时,返回的数组与调用方法 String.match() 返回的数组是相同的。

但是,当 regExpObject 是一个全局正则表达式时,exec() 的行为就稍微复杂一些。它会在 RegExpObject 的 lastIndex 属性指定的字符处开始检索字符串 string。当 exec() 找到了与表达式相匹配的文本时,在匹配后,它将把 RegExpObject 的 lastIndex 属性设置为匹配文本的最后一个字符的下一个位置。这就是说,您可以通过反复调用 exec() 方法来遍历字符串中的所有匹配文本。当 exec() 再也找不到匹配的文本时,它将返回 null,并把 lastIndex 属性重置为 0。

Apply call bind的区别:

先说区别:

  • apply 和 call类似,仅在“除第一个参数外的剩余参数”的格式上存在一定差异。call是将剩余所有参数直接罗列进去,而 apply 则是传递了一个,保存了除第一个参数之外的“其他参数”的数组。这个“其他参数”是要传递给原函数,放在原函数的实参之前,作为原函数的实参,供原函数使用的。
  • bind 只接收一个参数,这个参数与 apply 第一个参数相同,均表示:调用者的 this 重新指向的对象。
  • 但是 bind 不像前两者,执行完关键字所在行就立即调用了原函数,而是会返回一个新的函数。只需要在调用新函数时,将剩余参数放入新函数中即可,具体用法下面会介绍

再说一下适用场合:

  • 当我们需要传递的参数不多时,可以选择 call:
fun.call(thisObj, arg1, arg2);
  • 当我们需要传递的参数比较多时,可以先将参数放至一个数组,然后选择apply:
args = [arg1,arg2,arg3...];
fun.apply(thisObj, args);
  • 当我们想要生成一个新的函数长期绑定某个函数给某个对象使用,就可以选择bind:
var newFun = Fun.bind(thisObj);
newFun(arg1, arg2, ...);

16. 声明提前 & 函数定义的方法

// 函数定义方式一:函数表达式
(
  function (){
    var x = foo();
    var foo = function(){
      console.log("foobar");
    };
    return x;  // Uncaught TypeError: foo is not a function
  }
)()

// 函数定义方式二:函数声明
(
  function (){
    var x = foo();
    function foo(){
      console.log("foobar");
    };
    return x;  // foobar
  }
)()

方式一之所以会提示foo is not a function,就是和声明提前有关。

在表达式式的函数声明中,声明一个函数就像声明一个变量一样,仅仅只是将var foo这个声明部分进行提前而已,后面的赋值函数的部分并没有执行。

因此,从上到下依次执行赋值的部分,先调用了foo函数,准备将其赋给了变量x,这个时候foo还仅仅只是一个普通的变量而已,尚未执行赋值函数的操作。下一步将一个函数赋给变量foo,foo才成为一个函数,接下来才可以执行这个函数。

将整个问题可以进行简化,将IIFE去掉,将问题抽象出来,就变成了这个样子:

console.log( y );  // Uncaught ReferenceError: y is not defined
console.log( x );  // undefined
var x = 1;

// 函数声明
foo1(); // foobar1
function foo1(){
  console.log("foobar1");
};

// 函数表达式
console.log( typeof(foo2) );  // undefined
foo2();  // Uncaught TypeError: foo2 is not a function
var foo2 = function (){
  console.log("foobar2");  
};

其实就是在考察我们,声明提前的是哪个部分?

  • 打印一个从来没有声明过的变量会报错;
  • 打印一个后面会借助 var 声明的变量,仅仅只会提示 undefined;
  • 所有 var 声明的变量,会将声明过程提升到最前面;
  • 对于一个函数来说,
    • 表达式的声明方法,就像声明一个变量一样,赋值之前都是 undefined,赋值之前像调用函数一样执行这个变量,就会报错;
    • 但是如果是函数声明的方式,就会将整个函数体进行提前声明,这样不论在何处,我们都可以随时调用这个函数。

17. Object.keys()

var a = {1: "one", 2: "two", 3: "three"};
var b = Object.keys(a);
console.log( b )  // (3) ["1", "2", "3"]

也就是将 a 的所有的 key 组成了一个数组打印了出来。

18. typeof undefined === "undefined" & typeof typeof undefined === "string"

console.log([typeof a, typeof y]); // ['undefined', 'undefined']
var a = [typeof a, typeof y][0];
console.log( typeof a ); // 'string'
var b = typeof typeof a;
console.log( `a: ${a}` ); // 'a: undefined'
console.log( `b: ${b}` ); // 'b: string'

console.log( typeof undefined)  // 'undefined'
console.log( typeof typeof undefined)  // 'string'
console.log( typeof undefined == undefined )  // false
console.log( typeof undefined === undefined )  // false
console.log( typeof undefined === "undefined" )  // true
console.log( typeof typeof undefined === "string" )  // true

简单总结,typeof 返回的是一个字符串。

19. JavaScript 捕获异常的方法

  1. try / catch / finally 语句
  2. window 的 onerror 事件

20. JS获取对象属性的各种方式和区别(自身/原型属性、可枚举/不可枚举)  

  • Object.keys():返回自身的可枚举属性组成的数组 (不包含symbol)
  • Object.getOwnPropertyNames():返回所有的自身属性组成的数组 (不包含symbol)
  • Object.getOwnPropertySymbols():返回自身所有的Symbol属性组成的数组
  • for...in:以任意顺序迭代一个对象的除​Symbol​以外的
    可枚举属性,包括继承的可枚举属性

21. for...of 和 for...in 的区别

  • for let i in obj 是以任意顺序遍历数组、对象可枚举属性,i 是 key
let arr = ['a', 'b', 'c'];
let obj = {
  name: 'Jack',
  age: 18,
  sex: 'man'
}

console.log('---- for...in... 遍历数组 ----');
for (let i in arr) {
  console.log('i: ', i);
  console.log('arr[i]: ', arr[i]);
}
console.log('---- for...in... 遍历对象 ----');
for (let j in obj) {
  console.log('j: ', j);
  console.log('obj[j]: ', obj[j]);
}
---- for...in... 遍历数组 ----
i:  0
arr[i]:  a
i:  1
arr[i]:  b
i:  2
arr[i]:  c
---- for...in... 遍历对象 ----
j:  name
obj[j]:  Jack
j:  age
obj[j]:  18
j:  sex
obj[j]:  man

  •  for let value of iterable 是按顺序迭代一个可迭代对象,i 是值

iterable可迭代对象包括:Array、Map、Set、String、TypedArray、arguments

遍历器(Iterator)它是一种接口,为各种不同的数据结构提供统一的访问机制。 任何数据结构只要部署 Iterator 接口,就可以完成遍历操作(即依次处理该数据结构的所有成员)。
它的作用有三个:
一是为各种数据结构,提供一个统一的、简便的访问接口;
二是使得数据接口的成员能够按照某种次序排列;
三是ES6创造了一种新的遍历命令 for...of 循环,Iterator 接口主要提供 for...of 消费

let arr = ['a', 'b', 'c'];
let obj = {
  name: 'Jack',
  age: 18,
  sex: 'man'
}

console.log('---- for...of... 遍历数组 ----');
for (let i of arr) {
  console.log('i: ', i);
}
console.log('---- for...of... 遍历对象 ----');
for (let j of obj) {
  console.log('j: ', j);
  console.log('obj[j]: ', obj[j]);
}
---- for...of... 遍历数组 ----
i:  a
i:  b
i:  c
---- for...of... 遍历对象 ----
/Users/xxx/Desktop/myTest/MyReact/test.js:24
for (let j of obj) {
              ^

TypeError: obj is not iterable

22. 结构赋值默认值,如果对象中没有该属性,就赋予默认值

var { foo: F, bar: B = "123", car: C = "123" } = { f00: "aaa", bar: "bbb" }
console.log(F);   // undefined
console.log(B);   // bbb
console.log(C);   // 123
console.log(foo);

/*
console.log(foo);
            ^

ReferenceError: foo is not defined
*/

23. 所有函数严格意义上讲本身都是闭包吗?

是的,所有函数都有 [[Environment]] 隐藏属性,该属性保存了对创建该函数的词法环境的引用

  • 7
    点赞
  • 28
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 4
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

麦田里的POLO桔

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值