前端面试系列(一)

前端面试系列(一)

var和let的区别

var声明在函数中则作用域就是该函数,声明在函数外则作用于为全局,js引擎执行代码时会提升变量到顶部。

let作用域为当前所处的代码块,没有变量提升故而要先声明后使用,且不允许在相同作用域中重复声明同一个变量。

实例1:作用域对比

1
2
3
4
5
6
{
let a = 123;
var b = 321;
}
a // 引用错误
b // 321

实例2:let在for循环中的使用

1
2
3
4
5
for (let i = 0; i < 5; i++) {
console.log(i); // 0 1 2 3 4
}

console.log(i); // 引用错误

实例3:常见面试题

1
2
3
4
5
for (var i = 0; i < 10; i++) {
setTimeout(function() {
console.log(i); // 执行的时候同步代码for已经执行完毕
},0);
}
1
2
3
4
5
6
7
8
9
10
10
10
10
10
10
10
10
10
10
10
1
2
3
4
5
for (let i = 0; i < 10; i++) {
setTimeout(function() {
console.log(i); // 这里的i的作用域是循环体内,不受外界影响
},0);
}
1
2
3
4
5
6
7
8
9
10
0
1
2
3
4
5
6
7
8
9

实例4:for循环特点,()中的是父作用域,{}中是子作用域。

1
2
3
4
for (let i = 0; i < 3; i++) {
let i = 'love';
console.log(i);
}
1
2
3
love
love
love

== 和 === 有什么区别

==允许自动类型转换

===不允许自动类型转换


this

  • 在对象方法中,this指向调用它所在方法的对象。
  • 在全局环境中,this指向全局对象。
  • 在函数中,this指向函数的所有者。
  • 在函数中,严格模式,this指向undefined
  • 在事件中,this指向接收事件的元素。
  • call()apply()可以指定函数执行的上下文环境,即this绑定的对象,可以将this引用到任何对象。

以下实例均在浏览器环境中运行。

实例1:方法中的this指向调用该方法的对象。

1
2
3
4
5
6
7
8
9
var person = {
firstName: "mason",
lastName: "wong",
fullName: function() {
return this.firstName + ' ' + this.lastName;
}
};
person.fullName();
// 'mason wong'

实例2:单独使用this,指向全局对象(严格模式也是)。

1
2
console.log(this);
// window

实例3:函数中使用this,指向函数所有者,也就是全局对象。

1
2
3
4
5
function func() {
return this;
}
func();
// window

实例4:严格模式下,函数中使用this,指向undefined。

1
2
3
4
5
6
"use strict";
function func() {
return this;
}
func();
// undefined

实例5:事件中的this,指向接收事件的html元素。

1
<button onclick="this.style.display='none'">点击我消失</button>

实例6:对象方法中绑定,这里对象是函数的所有者。

1
2
3
4
5
6
7
8
var person = {
firstName : "John",
lastName : "Doe",
id : 5566,
myFunction : function() {
return this;
}
};

实例7:显式函数绑定

使用callapply切换函数执行上下文环境(context),也就是指定this指向的对象。

1
2
3
4
5
6
7
8
9
10
11
var person1 = {
fullName: function() {
return this.firstName + " " + this.lastName;
}
}
var person2 = {
firstName:"John",
lastName: "Doe",
}
person1.fullName.call(person2);
// "John Doe"

上面这个例子中调用person1.fullName()方法,使用call()指定了this指向的对象为person2,故而取得了person2中的firstNamelastName


什么是构造函数

使用new关键字调用的函数为构造函数,通常构造函数的函数名首字母大写。

构造函数的样子如下:

1
2
3
4
5
function Person(name, gender, hobby) {
this.name = name;
this.gender = gender;
this.hobby = hobby;
}

这样就可以通过new来调用,也就是通过new构造函数传入参数并快速创建对象:

1
var p1 = new Person('小明', '男', '足球');

这样就达到了代码复用。

确定一个函数是构造函数的方法是看是否使用new调用了它。

构造函数执行过程

  1. 使用new调用后会创建一个新的内存空间分配给实例。
  2. 函数题内部的this指向该内存空间。
  3. 执行函数题内代码
  4. 默认返回this。

如何将基于回调的函数转换为基于Promise的函数

实例:将getData回调函数转换为Promise

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
// the function itself
function getData(callback, errorCallback) {
try {
// Do some network/api stuff...
callback(result)
} catch (e) {
errorCallback(e);
}
}

// Here is how you would use it:
getData(result => console.log(result), error => console.error(error));

// Here is how to create a Promise-based function from it:

function getDataAsync() {
return new Promise((resolve, reject) => {
getData(resolve, reject);
});
}

getDataAsync()
.then(result => console.log(result))
.catch(error => console.error(error));

// OR

async functoin main() {
const result = await getDataAsync();
console.log(result);
}

Promise构造函数接受一个回调,该回调接收两个函数:resolvereject。在回调内部,你可以执行耗时的任务,并根据结果调用resolvereject


NaN === NaN ?

结果:false

NaN===于任何值


0.1 + 0.2 === 0.3 ?

结果:false

事实上:0.1 + 0.2 = 0.30000000000000004

原因:js中的数字以IEEE 754的双精度标准存储。无法精确表示0.1和0.2,故而结果对不对就看运气。

解决方法:将浮点数转化为整数运算。

1
0.1 + 0.2
1
(1 + 2) / 1e1

输出:

1
0.3

js中的原始数据类型是什么

Boolean

Number

String


什么是严格模式

1
'use strict';

以下省略一千字。


以下代码的输出是什么

1
2
3
4
5
6
7
8
function func() {
return
{
a: 'b';
}
}

console.log(func());

输出:

1
undefined

原因:

js会在第二行的return后面自动添加分号;,将后面的{}代码块视为作用域而不是对象定义。

评论