这些前端基础题你能答对几道附答案解析

9次阅读

共计 10046 个字符,预计需要花费 26 分钟才能阅读完成。

在三大框架盛行的时代, 基本上会个 Vue 就能在小公司浑水摸鱼。但是当想突破的时候就会意识到基础的重要性。

JavaScript中有很多重要特性及概念。比如原型, 原型链,this, 闭包, 作用域, 隐式转换等等。如果不能熟练掌握, 在进阶中级前端开发工程师的道路上必定是困难重重。

用一个小时把这些题做完。检测一下你的基础掌握程度。

正题

1.

if(false){
    var a = 1;
    let b = 2;
}
console.log(a);
console.log(b);

2.

var a = 1;
if(true){console.log(a);
    let a = 2;
}

3.

var a = {n: 1}
var b = a
a.x = a = {n: 2}

console.log(a.n, b.n);
console.log(a.x, b.x);

4.

console.log(c);
var c;
function c(a) {console.log(a);
    var a = 3;
    function a(){}
}
c(2);

5.

var c = 1
function c(c) {console.log(c);
    var c = 3;
}
console.log(c);
c(2);

6.

var name = 'erdong';
(function () {if (typeof name === 'undefined') {
        var name = 'chen'
        console.log(name)
    } else {console.log(name)
    }
})();

7.

var a = 10;  
function test() {  
    a = 100;  
    console.log(a);  
    console.log(this.a);  
    var a;  
    console.log(a); 
}
test();  

8.

if (!(a in window)) {var a = 1;}
console.log(a)

9.

var a = 1

function c(a, b) {console.log(a)
    a = 2
    console.log(a)
}
c()

10

var val=1;
var obj={
    val:2,
    del:function(){console.log(this);                    
        this.val*=2
        console.log(val) 
    }
}

obj.del();

11

var name = "erdong"
var object = {
    name: "chen",
    getNameFunc: function () {return function () {return this.name;}
    }
}
console.log(object.getNameFunc()());

12

var name = "erdong"
var object = {
    name: "chen",
    getNameFunc: function () {
        var that = this;
        return function () {return that.name;}
    }
}
console.log(object.getNameFunc()());

13

(function() {var a = b = 3;})();
console.log(typeof a === 'undefined');
console.log(typeof b === 'undefined');

14

var a = 6;
setTimeout(function () {a = 666;}, 0)
console.log(a);

15

function fn1() {
    var a = 2
    function fn2 () {
      a++
      console.log(a)
    }
    return fn2
}
var f = fn1()
f()
f()

16

var a = (function(foo){return typeof foo.bar;})({foo:{bar:1}});

console.log(a);

17

function f(){return f;}
console.log(new f() instanceof f);

18

function A () {}
A.prototype.n = 1

var b = new A();

A.prototype = {
    n: 2,
    m: 3
}
var c = new A()

console.log(b.n, b.m);
console.log(c.n, c.m);

19

var F = function(){}
var O = {};
Object.prototype.a = function(){console.log('a')
}
Function.prototype.b = function(){console.log('b')
}
var f = new F()

F.a();  
F.b();  
O.a();
O.b();  

20

function Person() {getAge = function () {console.log(10)
    }
    return this
}

Person.getAge = function () {console.log(20)
}

Person.prototype.getAge = function () {console.log(30)
}

var getAge = function () {console.log(40)
}

function getAge() {console.log(50)
}


Person.getAge();
getAge();
Person().getAge();
new Person.getAge();
getAge();
new Person().getAge();

21

console.log(false.toString()); 
console.log([1, 2, 3].toString()); 
console.log(1.toString()); 
console.log(5..toString());

22

console.log(typeof NaN === 'number');

23

console.log(1 + "2" + "2");

console.log(1 + +"2" + "2");

console.log(1 + -"1" + "2");

console.log(+"1" + "1" + "2"); 

console.log("A" - "B" + "2"); 

console.log("A" - "B" + 2); 

24

var a = 666;
console.log(++a);
console.log(a++);

25

console.log(typeof a);
function a() {}
var a;
console.log(typeof a);

26

var a;
var b = 'undefined';
console.log(typeof a);
console.log(typeof b);
console.log(typeof c);

27

var x = 1;
if(function f(){}){x += typeof f;}
 
console.log(x);

28

var str = "123abc";
console.log(typeof str++);

29

console.log('b' + 'a' + +'a'+'a');

30

var obj = {n: 1};
function fn2(a) {a.n = 2;}
fn2(obj);
console.log(obj.n);

31

var x = 10;
function fn() {console.log(x);
}
function show(f) {
    var x = 20;
    f();}
show(fn);

32

Object.prototype.bar = 1; 
var foo = {goo: undefined};

console.log(foo.bar);
console.log('bar' in foo);

console.log(foo.hasOwnProperty('bar'));
console.log(foo.hasOwnProperty('goo'));

33

Object.prototype.bar = 1;

var foo = {moo: 2};
for(var i in foo) {console.log(i); 
}

33

function foo1() {
    return {bar: "hello"};
}
function foo2() {
    return 
    {bar: "hello"};
}
console.log(foo1());
console.log(foo2());

35

console.log((function(){return typeof arguments;})());

36

console.log(Boolean(false));
console.log(Boolean('0'));
console.log(Boolean(''));
console.log(Boolean(NaN));

37

console.log(Array(3));

console.log(Array(2,3));

38

console.log(0.1 + 0.2 == 0.3);

39

var a=[1, 2, 3];
console.log(a.join());

40

var a = [3];
var b = [1];
console.log(a - b); 

解析

第 1 题解析

// 输出
undefined

ReferenceError: b is not defined

var不会产生块级作用域,let会产生块级作用域。

示例代码相当于:

var a = 1;
if(false){
    a = 1;
    let b = 2;
}
console.log(a); 
console.log(b);

第 2 题解析

// 输出

ReferenceError: Cannot access 'a' before initialization

let声明的变量不会提升, 并且会产生暂存死区。在 let 声明变量之前访问变量会抛出错误。

第 3 题解析

// 输出

2 1

undefined {n: 2}

var b = a, 此时 a 和 b 指向同一个对象。. 运算符比 = 运算符高, 先计算 `a.x`, 此时 
b = {
    n:1,
    x:undefined
}

相当于给对象添加了 x 属性。a.x = a = {n:2};

计算完 a.x, 再计算 = , 赋值是从右向左, 此时 a 指向一个新对象。a = {n:2}

a.x 已经执行过了, 此时对象的 x 属性赋值为 a, 此时

对象 = {
    n:1,
    x:{n:2}
}

即:
a = {n:2}

b = {
    n:1,
    x:{n:2}
}


查看运算符优先级

第 4 题解析

// 输出 

function c(){console.log(a);
    var c = 3;
    function a(){}
}

function a(){}

变量提升也有优先级, 函数声明 > arguments > 变量声明

第 5 题解析

//  输出

1

TypeError: c is not a function

由于函数声明会提升, 当函数外的 console.log(c) 执行时,c已经被赋值为 1。因此, 执行c(2) 时会抛出 TypeError, 因为1 不是函数。

第 6 题解析

// 输出 

chen

自执行函数执行时, 会先进行变量提升 ( 这里涉及到执行上下文不过多说, 一定要搞懂执行上下文), 在自执行函数执行时, 伪代码为:

var name = 'erdong';
(function () {
    var name;  // 变量 name 会提升到当前作用域顶部
    if (typeof name === 'undefined') {
        name = 'chen'
        console.log(name)
    } else {console.log(name)
    }
})();

所以会执行 if 中的console.log(name)

第 7 题解析

// 输出

100
10
100

test()为函数独立调用, 作用域中的 this 绑定为全局对象window

test函数执行时,var a被提升到了作用域顶部, 因此函数作用域中存在一个变量 a。所以在函数中访问的a 都是局部作用域中的a

第 8 题解析

// 输出
undefined

由于 if 后的 {} 不会产生块级作用域(不包含 let,const 时), 此时的伪代码为:

var a;
if (!(a in window)) {a = 1;}
console.log(a);

var a相当于 window.a。因此!(a in window) 转成布尔值为 false, 不会执行a = 1。所有console.log(a) 输出undefined

第 9 题解析

// 输出

undefined 

2

跟第 4 题类似。

第 10 题解析

// 输出
obj(指向的值)

1

当通过 obj.del() 调用 del 函数时,del函数作用域中的 this 绑定为obj

在函数作用域中访问 val 时, 由于函数中并没有变量val, 因此实际上访问的是全局作用域中的val, 即 1

这里考察的是 this 的指向, 一定要熟练掌握。

第 11 题解析

// 输出

erdong

object.getNameFunc()(), 先执行 object.getNameFunc() 返回一个函数:

function () {return this.name;}

返回的函数再执行, 相当于

(function () {return this.name;})();

此时的 this 绑定为 window。因此输出全局变量name 的值erdong

第 12 题解析

// 输出

chen

object.getNameFunc()执行时, 此时 getNameFunc 中的 this 绑定为 object, 因此that = objectobject.getNameFunc() 返回的函数再执行时, 产生闭包, 因此返回的函数也能访问到外层作用域中的变量 that, 因此object.nameobject.name, 即 chen

第 13 题解析

// 输出

true

false

首先要明白 a = b = 3 是怎样执行的, 伪代码:

b = 3;
var a = b;

因此在自执行函数执行时,b由于为经 var 等操作符声明, 因为为全局变量。a为函数作用域中的变量。因此在外面访问 ab时, 其值分别为 ReferenceError: a is not defined3。但是 typeof 检测未声明的变量不会抛出错误, 会返回 'undefined'。因此typeof atypeof b分别返回 'undefined''number'

第 14 题解析

// 输出

6

setTimeout为宏任务。即使设置延迟为 0ms, 也是等待微任务执行完才会执行。因此console.log(a) 输出 6

第 15 题解析

// 输出

3
4

由于 fn1 函数执行后返回函数 fn2, 此时产生了闭包。因此fn2a访问的是 fn1 作用域中的变量 a, 因此第一次a++, 之后a3, 第二次之后 a4

第 16 题解析

// 输出

undefined

实参 foo 的值为 {foo:{bar:1}, 因此typeof foo.barundefined

typeof foo.foo.barnumber

第 17 题解析

// 输出

false

由于构造函数 f 的返回值为 f。因此new f() 的值为 f。所以console.log(new f() instanceof f)console.log(f instanceof f), 即 false

第 18 题解析

// 输出

1,undefined

2,3

var b = new A(); 实例化 b 时,Aprototype

A.prototype = {
    constructor:A,
    n:1
}

当访问 b.nb.m时, 通过原型链找到 A.prototype 指向的对象上, 即b.n = 1,b.m = undefined

var c = new A(); 实例化 c 时,Aprototype

A.prototype = {
    n: 2,
    m: 3
}

当访问 a.na.m时, 通过原型链找到 A.prototype 指向的对象上, 此时 A.prototype 重写, 因此a.n = 2,b.m = 3

第 19 题解析

// 输出

a
b
a
TypeError: o.b is not a function

F为函数, 它也能访问 Object 原型上的方法,O为对象, 不能访问 Function 原型上的方法。

F的原型链为:

F => F.__proto__ => Function.prototype => Function.prototype.__proto__ => Object.prototype

由于 Object.prototypeF的原型链上, 所以 F 能访问 Object.prototype 上的属性和方法。即: F.a(),F.b()能正常访问。

O的原型链为:

O => O.__proto__ => Object.prototype

由于 Function.prototype 不在 O 的原型链上, 因此 O 不能访问 Function.prototype 上的方法, 即 O.b() 抛出错误。

如果你对原型和原型链掌握的好, 试着理解下面的示例:

console.log(Object instanceof Function);

console.log(Function instanceof Object);

console.log(Function instanceof Function);

第 20 题解析

// 输出

20
40
10
20
10
30

Person.getAge();此时执行的是 Person 函数上 getAge 方法。

Person.getAge = function () {console.log(20)
}

所以输出:20。

getAge();此时执行的是全局中的 getAge 方法。此时全局 getAge 方法为:

function () {console.log(40)
}

所以输出:40。

Person().getAge();由于 Person() 单独执行所以, 作用域中的 this 绑定为 window, 相当于window..getAge()。同上, 执行的都是全局getAge
方法, 但是 Person 执行时, 内部执行了

getAge = function () {console.log(10)
}

因此全局 getAge 方法现在为:

function () {console.log(10)
}

所以输出:10。

new Person.getAge();此时相当于实例化 Person.getAge 这个函数, 伪代码:

var b = Person.getAge;
new b();

所以输出:20

getAge();执行全局 getAge 方法, 由于在 Person().getAge() 执行时把全局 getAge 方法赋值为:

function () {console.log(10)
}

所以输出:10。

new Person().getAge();此时调用的是 Person 原型上的 getAge 方法:

Person.prototype.getAge = function () {console.log(30)
}

所以输出:30。

这里要注意:1. 变量提升及提升后再赋值。2. 调用构造函数时, 带 () 和不带 () 的区别。

第 21 题解析

// 输出

'false'
'1,2,3'
Uncaught SyntaxError: Invalid or unexpected token
'5'

当执行 1.toString(); 时, 由于 1. 也是有效数字, 因此此时变成 (1.)toString()。没有用. 调用 toString 方法, 因此抛出错误。

正确的应该是:

1..toString();
1 .toString();
(1).toString();

第 22 题解析

// 输出

true

NaN为不是数字的数字。虽然它不是数字, 但是它也是数字类型。

第 23 题解析

// 输出

'122'
'32'
'02'
'112'
'NaN2'
NaN

首先要明白两点:

  1. +a, 会把 a 转换为数字。-a会把 a 转换成数字的负值(如果能转换为数字的话, 否则为NaN)。
  2. 字符串与任何值相加都是字符串拼接。

console.log(1 + "2" + "2");简单的字符串拼接, 即结果为:'122'

console.log(1 + +"2" + "2");这里相当于console.log(1 + 2 + "2");, 然后再字符串拼接。即结果为:'32'

console.log(1 + -"1" + "2");这里相当于console.log(1 + -1 + "2");, 然后再字符串拼接。即结果为:'02'

console.log(+"1" + "1" + "2");这里相当于console.log(1 + "1" + "2");, 然后再字符串拼接。即结果为:'112'

console.log("A" - "B" + "2");, 由于'A' - 'B' = NaN, 所以相当于console.log(NaN + "2");, 然后再字符串拼接。即结果为:'NaN2'

console.log("A" - "B" + 2);同上, 相当于console.log(NaN + 2), 由于NaN+ 任何值还是NaN, 即结果为:NaN

第 24 题解析

// 输出

666
668

a++先执行取值操作, 在执行 +1。此时输出666, 随后a 的值变为667

++a先执行 +1 在执行取值操作。此时 a 的值为667 + 1 = 668

--aa-- 同理。

使用这类运算符时要注意:

1)这里的 ++-- 不能用作于常量。比如

    1++; // 抛出错误

2)如果 a 不是数字类型, 会首先通过 Number(a), 将a 转换为数字。再执行 ++ 等运算。

第 25 题解析

// 输出

'function'
'function'

跟第 4 题类似。函数会优先于变量声明提前。因此会忽略var a

第 26 题解析

// 输出
'undefined'
'string'
'undefined'

a为声明未赋值, 默认为 undefined,b 的值为字符串 'undefined',c 为未定义。

typeof一个未定义的变量时, 不会抛出错误, 会返回 'undefined'。注意typeof 返回的都是字符串类型。

第 27 题解析

// 输出

1undefined

function f(){}当做 if 条件判断, 其隐式转换后为 true。但是在() 中的函数不会声明提升, 因此 f 函数在外部是不存在的。因此 typeof f = 'undefined', 所以x += typeof f, 相当于x = x + 'undefined''1undefined'

第 28 题解析

// 输出

'number'

在 24 题解析时提到, 使用 ++ 运算符时 (无论是前置还是后置), 如果变量不是数字类型, 会首先用Number() 转换为数字。因此 typeof str++ 相当于 typeof Number(str)++。由于后置的++ 是先取值后计算, 因此相当于typeof Number("123abc")。即typeof NaN, 所以输出'number'

第 29 题解析

// 输出

baNaNa

'b' + 'a' + +'a'+'a'相当于 'ba' + +'a'+'a',+'a' 会将 'a' 转换为数字类型, 即+'a' = NaN。所以最终得到'ba' + NaN +'a', 通过字符串拼接, 结果为:baNaNa

第 30 题解析

// 输出

4
2
2

函数传递参数时, 如果是基本类型为值传递, 如果是引用类型, 为引用传递。因此实参 aobj指向对象的一个引用。当执行 a.n, 实际上共同引用的对象修改了, 添加了个n 属性, 因此 obj.n2

第 31 题解析

// 输出

10

JavaScript采用的是词法作用域, 它规定了函数内访问变量时, 查找变量是从函数声明的位置向外层作用域中查找, 而不是从调用函数的位置开始向上查找。因此 fn 函数内部访问的 x 是全局作用域中的 x, 而不是show 函数作用域中的x

第 32 题解析

// 输出

1
true
false
true

in操作符: 检测指定对象 (右边) 原型链上是否有对应的属性值。
hasOwnProperty方法: 检测指定对象自身上是否有对应的属性值。两者的区别在于 in 会查找原型链, 而 hasOwnProperty 不会。

示例中对象 foo 自身上存在 goo 属性, 而它的原型链上存在 bar 属性。

通过这个例子要注意如果要判断 foo 上是否有属性 goo, 不能简单的通过if(foo.goo){} 判断, 因为 goo 的值可能为 undefined 或者其他可能隐式转换为 false 的值。

第 33 题解析

// 输出

'moo'
'bar'

for...in...遍历对象上除了 Symbol 以外的可枚举属性, 包括原型链上的属性。

第 34 题解析

// 输出

{bar: "hello"}

undefined

两个函数唯一区别就是 return 后面跟的值, 一个换行一个不换行。

当我们书写代码时忘记在结尾书写 ; 时,JavaScript解析器会根据一定规则自动补上;

return
{bar: "hello"}
=> 会被解析成
return;
{bar: "hello"};

因此函数执行后会返回undefined

第 35 题解析

// 输出

'object'

arguments为类数组, 类型为object。因此typeof arguments = 'object'

第 36 题解析

// 输出

false
true
false
fasle

只有下面几种值在转换为布尔值时为false:

+0,-0,NaN,false,'',null,undefined。

除此之外的值在转换为布尔值的时候全部为true

第 37 题解析

// 输出

[empty × 3] 

[2,3]

使用 Array() 创建数组时, 要注意传入的值的类型和数量。

// 输出

false

第 39 题解析

// 输出

1,2,3

join方法如果省略参数, 默认以 , 分隔。

第 40 题解析

// 输出

2

在执行 a - b 时,ab 都要转换为数字。首先 a 先转换为字符串,[3] => [3].toString() => '3', 然后 Number(3) => 3b 同理。因此转换之后为3 - 1 = 3

最后

如果文中有错误,请务必留言指正,万分感谢。

点个赞哦,让我们共同学习,共同进步。

GitHub

正文完
 0