前端面试题(一)

整理了一些之前面试遇到的题

NAN === NAN 是否成立,为什么?

不成立。因为JavaScript规定,NaN表示的是非数字,但是这个非数字也是不同的,因此 NaN 不等于 NaN,两个NaN永远不可能相等。

0.1+0.2 == 0.30.1+0.2 === 0.3 哪个成立,为什么?

都不成立.在正常的数学逻辑思维中,0.1+0.2=0.3这个逻辑是正确的,但是在JavaScript中0.1+0.2!==0.3,这是为什么呢?在JavaScript中的二进制的浮点数0.1和0.2并不是十分精确,在他们相加的结果并非正好等于0.3,而是一个比较接近的数字 0.30000000000000004 ,所以条件判断结果为false。

可以通过 (0.1 + 0.2).toFixed(2) // "0.30"parseFloat((0.1 + 0.2).toFixed(2)) //0.3 的方法使0.1+0.2==0.3

什么是闭包?

如果一个函数用到了它作用域外面的变量,那么这个变量和这个函数之间的环境就叫闭包。

举一个简单的例子

1
2
3
4
5
6
7
8
9
10
function fun(){
var Joker = "帅";
return function(){
return Joker
}
}
fun()();//输出:'帅'
//当然我们也可以这样,等同于fun()()
var temfun = fun();
console.log(temfun())

闭包的使用场景

封装局部变量

例如:一个游戏,主人公有10条命,我们如果把var live=10写在全局作用域的话,全局中任何地方都能调用修改,就会出现安全性,容易出bug。

但如果写在局部作用域,会有如下问题:

  • 在全局环境中调用不了

  • 局部环境执行完毕后,会被清出环境栈,局部环境中的变量和函数都会垃圾回收机制回收。

因此我们用闭包去将var live=10变量封装起来

1
2
3
4
5
6
7
8
9
10
11
12
13
14
function liveFun(){
var live = 10;
//以下是闭包函数代码
return function(condition){
if(condition=='add'){
return live+=1;
}else if(condition=='reduce'){
return live-=1
}
}
}
var live=liveFun()
console.log(live('add')); //执行闭包函数 ,10+1=11,输出:11
console.log(live('reduce')); //执行闭包函数 ,11-1=10,输出:10

箭头函数与普通函数的区别?

区别:

  • 箭头函数没有自己的this属性,arguments属性、而普通函数有,箭头函数的this指向当前函数作用域的this
  • 箭头函数没有不能使用new命令,因为没有自己的this,无法调用callapply,没有prototype显示原型,所以不能作为构造函数。
  • 不可以使用yield命令,因此箭头函数不能用作Generator函数。

箭头函数的好处:

  • 没有箭头函数的时候,函数闭包var that = this的事没少干,有了箭头函数,就不需要这么写了
  • 极简语法,函数式风格

js原型原型链

首先我们要清除明白两个概念

  • js分为函数对象和普通对象,每个对象都有__proto__属性,但是只有函数对象才有prototype属性

  • Object、Function都是js的内置函数,类似的还有我们常用的Array,RegExp、Date、Boolean、Number、String

  • 属性prototype是一个对象,他有两个属性,constructor__proto__;

  • 原型对象prototype有一个默认的constructor属性,用于记录实例是由哪个构造函数创建;

举个例子:

1
2
3
4
5
6
//有以下构造函数Person,他的原型上有所属国属性motherland='china'
function Person(name,age){
this.name=name;
this.age=age;
}
Person.prototype.motherland = 'china'

通过new Person()创建的persson01实例

1
let persion01 = new Person('小明',18)

js在设计原型、原型链的时候遵从以下两个准则:

1
2
1. Person.prototype.constructor == Person // **准则1:原型对象(即Person.prototype)的constructor指向构造函数本身**
2. person01.__proto__ == Person.prototype // **准则2:实例(即person01)的__proto__和原型对象指向同一个地方**

举个例子:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
function Foo();
let f1 = new Foo();
let f2 = new Foo();

f1.__proto__ = Foo.prototype;//准则2
f2.__proto__ = Foo.prototype;//准则2

Foo.prototype.__proto__ = Object.prototype; //准则2 (Foo.prototype本身也是普通对象,适用于准则2)
Object.prototype.__proto__ = null;//原型链到此停止

Foo.prototype.constructor = Foo; // 准则1
Foo.__proto__ = Function.prototype;//准则2
Function.prototype.__proto__ = Object.prototype;//准则2
Object.prototype.__proto__ = null;//原型链到此停止
// **此处注意FooFunction的区别, FooFunction的实例**

除了Object的原型对象(Object.prototype)的proto指向null,其他内置函数对象的原型对象(例如:Array.prototype)和自定义构造函数的proto都指向Object.prototype, 因为原型对象本身是普通对象。即:

1
2
3
Object.prototype.__proto__ = null;
Array.prototype.__proto__ = Object.prototype;
Foo.prototype.__proto__ = Object.prototype;

原型对象的作用,是用来存放实例中共有的那部份属性、方法,可以大大减少内存消耗。

em、rem 是什么?

  • em:em是相对于⽗元素的尺⼨单位

  • rem:相对于html元素的尺⼨单位(html默认font-size:16px)

Vue为什么使用虚拟dom?

promise和callback的区别

文章作者: Joker
文章链接: https://qytayh.github.io/2020/07/%E5%89%8D%E7%AB%AF%E9%9D%A2%E8%AF%95%E9%A2%98(%E4%B8%80)/
版权声明: 本博客所有文章除特别声明外,均采用 CC BY-NC-SA 4.0 许可协议。转载请注明来自 Joker's Blog