代码输出结果相关知识点

347 阅读11分钟

前端面试题系列文章:

【1】「2023」HTML基础知识点

【2】「2023」ECMAScript基础知识点

【3】「2023」CSS基础知识点

【4】「2023」计算机网络基础知识

【5】「2023」计算机网络-HTTP知识点

【6】「2023」浏览器相关知识点

【7】「2023」React相关知识点

【8】「2023」TypeScript相关知识点

【9】「2023」Webpack相关知识点

【10】「2023」代码输出结果相关知识点

【11】「2023」手动实现代码相关知识点

【12】「2023」性能优化相关知识点

【13】「2023」H5相关知识点

X-Mind原图地址:

FE-eight-part-essay

输出是什么?

function sayHi() {
  console.log(name)
  console.log(age)
  var name = 'Lydia'
  let age = 21
}

sayHi()
  • A: Lydiaundefined
  • B: LydiaReferenceError
  • C: ReferenceError21
  • D: undefinedReferenceError

答案

D

考点:变量作用域提升、letconst变量的区别

解析:在函数内部,我们通过var关键字声明的变量,存在变量作用域提升。直到程序运行到定义变量位置之前默认值都是 undefined

通过letconst关键字声明的变量也会提升,但是和var不同,他们不会被初始化。在我们声明之前是不能够访问它们的,这个行为被称之为暂时性死区。当我们试图在声明之前访问它们时,JavaScript将会抛出一个ReferenceError错误。


输出是什么?

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

for (let i = 0; i < 3; i++) {
  setTimeout(() => console.log(i), 1)
}
  • A: 0 1 20 1 2
  • B: 0 1 23 3 3
  • C: 3 3 30 1 2

答案

C

考点:JS事件循环、作用域

解析:由于JavaScript的事件循环机制,setTimeout属于异步任务,只有等同步代码执行完了之后才会执行。而第一个循环中的i变量是以var的关键字声明的,所以这个值是全局作用域下的,当开始执行第一个setTimeout的回调时,i 已经为3了。

而在第二个遍历中,i是通过let关键字声明的。通过letconst关键字的变量都是在块级作用域内生效的。在每次的遍历过程中,i都会有一个新的值,并且每个值都在循环内的作用域中。


输出是什么?

const shape = {
  radius: 10,
  diameter() {
    return this.radius * 2
  },
  perimeter: () => 2 * Math.PI * this.radius
}

shape.diameter()
shape.perimeter()
  • A: 20 and 62.83185307179586
  • B: 20 and NaN
  • C: 20 and 63
  • D: NaN and 63

答案

B

考点:this的指向问题、箭头函数

解析:this永远指向最后调用他的那个对象。箭头函数的this是继承父执行上下文里面的this ,所以文中的调用diameter时this指向shape实例,调用perimeter时,this指向window。


输出是什么?

let c = { greeting: 'Hey!' }
let d

d = c
c.greeting = 'Hello'
console.log(d.greeting)
  • A: Hello
  • B: undefined
  • C: ReferenceError
  • D: TypeError

答案

A

考点:深浅拷贝

解析:由于c对象是一个复杂对象类型,所以在执行d=c时,实际上是将c变量的地址赋值给了d变量。修改了d变量的属性值本质上也将c变量存储的数据改变了。

输出是什么?

function getAge(...args) {
  console.log(typeof args)
}

getAge(21)
  • A: "number"
  • B: "array"
  • C: "object"
  • D: "NaN"

答案

C

考点:ES6语法 -> 拓展运算符

解析:扩展运算符(...args)会返回实参组成的数组。而数组是对象,因此 typeof args 返回 "object"。


输出是什么?

const obj = { 1: 'a', 2: 'b', 3: 'c' }
const set = new Set([1, 2, 3, 4, 5])

obj.hasOwnProperty('1')
obj.hasOwnProperty(1)
set.has('1')
set.has(1)
  • A: false true false true
  • B: false true true true
  • C: true true false true
  • D: true true true true

答案

C

考点:Object类型、Object类型和Set类型的区别

解析:所有对象的key(Symbol除外)在底层都是字符串,即使你没有将其作为字符串输入(假设设定了数字1为key,那么该对象同时存在1'1'属性)。

对于集合类型,它只判断集合中有没有该子项,是做强等判断的,所以set.has('1') 返回 false


输出是什么?

<div onclick="console.log('first div')">
  <div onclick="console.log('second div')">
    <button onclick="console.log('button')">
      Click!
    </button>
  </div>
</div>
  • A: Outer div
  • B: Inner div
  • C: button
  • D: 一个包含所有嵌套元素的数组。

答案

C

考点:浏览器事件机制

解析:导致事件的最深嵌套的元素是事件的 target。


输出是什么?

<div onclick="console.log('div')">
  <p onclick="console.log('p')">
    Click here!
  </p>
</div>
  • A: p div
  • B: div p
  • C: p
  • D: div

答案

A

考点:浏览器事件机制、事件触发的三个阶段

解析:在事件传播期间,有三个阶段:捕获、目标和冒泡。默认情况下,事件处理程序在冒泡阶段执行。


输出是什么?

[...'Lydia']
  • A: ["L", "y", "d", "i", "a"]
  • B: ["Lydia"]
  • C: [[], "Lydia"]
  • D: [["L", "y", "d", "i", "a"]]

答案

A

考点:ES6拓展运算符、可迭代对象

解析:string 类型是可迭代的。扩展运算符将迭代的每个字符映射成一个元素。


输出是什么?

const firstPromise = new Promise((res, rej) => {
  setTimeout(res, 500, "one");
});

const secondPromise = new Promise((res, rej) => {
  setTimeout(res, 100, "two");
});

Promise.race([firstPromise, secondPromise]).then(res => console.log(res));
  • A: "one"
  • B: "two"
  • C: "two" "one"
  • D: "one" "two"

答案

B

考点:Promise对象的理解

解析:Promise.race 当数组中的任意一个Promise被resolve或reject后,会立即调用.then.catch回调。


输出是什么?

let person = { name: "Lydia" };
const members = [person];
person = null;

console.log(members);
  • A: null
  • B: [null]
  • C: [{}]
  • D: [{ name: "Lydia" }]

答案

D

考点:JS的内存分配规则

解析: 当设置两个对象彼此相等时,会将两个对象指向同一块地址。但是当你将一个变量分配至另一个变量时,其实只是执行了一个 复制 操作(可能平时遇到的比较少)。


输出是什么?

console.log(3 + 4 + "5");

A: "345"

B: "75"

C: 12

D: "12"

答案

B

考点:运算符的优先级

解析:在这个例子中,我们只有一类运算符+,对于加法来说,结合顺序就是从左到右。首先计算3 + 4,得到数字7,由于类型的强制转换,7 + '5'的结果是"75"


输出是什么?

const num = parseInt("7*6", 10);
  • A: 42
  • B: "42"
  • C: 7
  • D: NaN

答案

C

考点:parseInt API 的转化原理

解析:用parseInt设定了进制后,parseInt会检查字符串中的字符是否合法,一旦遇到一个在制定进制中不合法的字符后,立即停止解析并且忽略后面所有的字符串。


输出是什么?

function Car() {
  this.make = "Lamborghini";
  return { make: "Maserati" };
}

const myCar = new Car();
console.log(myCar.make);
  • A: "Lamborghini"
  • B: "Maserati"
  • C: ReferenceError
  • D: TypeError

答案

B

考点:JS类的实现

解析:当我们在 new 一个对象,返回值有返回属性的时候,属性的值会覆盖构造函数中设定的值。


输出是什么?

(() => {
  let x = (y = 10);
})();

console.log(typeof x);
console.log(typeof y);
  • A: "undefined", "number"
  • B: "number", "number"
  • C: "object", "number"
  • D: "number", "undefined"

答案

A

考点:作用域、let和var的区别

解析:由于y=10相当于在全局作用域中声明了y。而x则声明在块作用域内,外层访问不到,所以打印出来的是undefined


输出是什么?

let num = 10;

const increaseNumber = () => num++;
const increasePassedNumber = number => number++;

const num1 = increaseNumber();
const num2 = increasePassedNumber(num1);

console.log(num1);
console.log(num2);
  • A: 10, 10
  • B: 10, 11
  • C: 11, 11
  • D: 11, 12

答案

A

考点:一元操作符、语言语法

解析:一元操作符num++先返回操作值,再进行累加操作。


使用哪个构造函数可以成功继承Dog类??

class Dog {
  constructor(name) {
    this.name = name;
  }
};

class Labrador extends Dog {
  // 1 
  constructor(name, size) {
    this.size = size;
  }
  // 2
  constructor(name, size) {
    super(name);
    this.size = size;
  }
  // 3
  constructor(size) {
    super(name);
    this.size = size;
  }
  // 4 
  constructor(name, size) {
    this.name = name;
    this.size = size;
  }

};
  • A: 1
  • B: 2
  • C: 3
  • D: 4

答案

B

考点:ES6 extends 语法继承

解析:使用extends语法继承类,在调用super之前不能访问this关键字。使用super关键字,需要用给定的参数来调用父类的构造函数。


输出是什么?

console.log(Number(2) === Number(2))
console.log(Boolean(false) === Boolean(false))
console.log(Symbol('foo') === Symbol('foo'))
  • A: true, true, false
  • B: false, true, false
  • C: true, false, true
  • D: true, true, true

答案

A

考点:强制类型转化、Symbol类型

解析:由于Number()Boolean()这里都是强制类型转化,转出来的都是简单类型,可以直接判断相等。每个Symbol都是完全唯一的,传递给Symbol的参数只是给Symbol的一个描述,所以不相等。


输出是什么?

async function getData() {
  return await Promise.resolve("I made it!");
}

const data = getData();
console.log(data);
  • A: "I made it!"
  • B: Promise {<resolved>: "I made it!"}
  • C: Promise {<pending>}
  • D: undefined

答案

C

考点:Promise基础知识

解析:异步函数始终返回一个Promiseawait任然需要等待Promise的解决:当我们调用getData()并将其赋值给data,此时datagetData方法返回的一个挂起的promise,该promise并没有解决。


输出是什么?

const promise = new Promise((resolve, reject) => {
  console.log(1);
  console.log(2);
});
promise.then(() => {
  console.log(3);
});
console.log(4);

答案

1 
2 
4

考点:微任务、Promise状态机制、Promise的构造函数

解析:首先,Promise的构造函数是同步任务,是立即执行的,所以先打印12。由于Promise内部的状态一直没有发生改变,一直处于pending状态,所以这里不输出3


输出是什么?

const promise1 = new Promise((resolve, reject) => {
  console.log('promise1')
  resolve('resolve1')
})
const promise2 = promise1.then(res => {
  console.log(res)
})
console.log('1', promise1);
console.log('2', promise2);

答案

promise1
1 Promise{<resolved>: resolve1}
2 Promise{<pending>}
resolve1

考点:宏任务 && 微任务、Promise状态机制

解析

  1. 首先可以将整个script看做是一个宏任务,按顺序执行这些代码
  2. 首先进入到Promise,执行构造函数的代码,打印promise1
  3. 碰到resolve函数,将promise1的状态改变为resolved,并将结果保存下来
  4. 碰到promise1.then这个微任务,放入微任务队列
  5. 执行同步任务1,打印出promise1的状态是resolved
  6. 执行同步任务2,打印出promise2的状态是pending
  7. 宏任务执行完毕,查找微任务队列,发现promise1.then这个微任务切状态为resovled,执行它

输出是什么?

const promise = new Promise((resolve, reject) => {
    resolve('success1');
    reject('error');
    resolve('success2');
});
promise.then((res) => {
    console.log('then:', res);
}).catch((err) => {
    console.log('catch:', err);
})

答案

then: success1

考点Promise的状态一旦发生改变,就不会再发生变化

解析:开始状态由pending变为resolve,说明已经变为已完成状态,下面的两个状态的就不会再执行,同时下面的catch也不会捕获到错误。


输出是什么?

Promise.resolve('1')
  .then(res => {
    console.log(res)
  })
  .finally(() => {
    console.log('finally')
  })
Promise.resolve('2')
  .finally(() => {
    console.log('finally2')
  	return '我是finally2返回的值'
  })
  .then(res => {
    console.log('finally2后面的then函数', res)
  })

答案

1
finally2
finally
finally2后面的then函数 2

考点PromisefinallyAPI

解析

  1. .finally()方法不管Promise对象最后的状态如何都会执行。
  2. .finally()方法的回调函数不接受任何的参数,也就是说你在.finally()函数中是无法知道Promise最终的状态是resolved还是rejected的。
  3. 它最终返回的默认会是一个上一次的Promise对象值,不过如果抛出的是一个异常则返回异常的Promise对象。
  4. finally本质上是then方法的特例

输出是什么?

function runAsync (x) {
  const p = new Promise(r => setTimeout(() => r(x, console.log(x)), 1000))
  return p
}
function runReject (x) {
  const p = new Promise((res, rej) => setTimeout(() => rej(`Error: ${x}`, console.log(x)), 1000 * x))
  return p
}
Promise.all([runAsync(1), runReject(4), runAsync(3), runReject(2)])
       .then(res => console.log(res))
       .catch(err => console.log(err))

答案

// 1s后输出
1
3
// 2s后输出
2
Error: 2
// 4s后输出
4

考点Promise.all

解析Promise.all只有所有的Promise都变成resolved才会执行.then的回调。只要有一个Promise异常就会进入到.catch中。但是!并不会影响数组中其它的异步任务的执行。


输出是什么?

async function async1() {
  console.log("async1 start");
  await async2();
  console.log("async1 end");
}
async function async2() {
  console.log("async2");
}
async1();
console.log('start')

答案

async1 start
async2
start
async1 end

考点:async、await 语法

解析

  1. 首先执行async1中的同步代码async1 start,接着遇到了await,它会阻塞async2后面代码的执行,因此会先去执行async2中的同步代码。
  2. 接着执行同步代码start
  3. 在一轮宏任务全部执行完之后,再来执行await后面的内容async1 end

这里最好理解的方法是:await 后面的语句相当于放到了 new Promise的构造函数中,下面一行及之后的语句相当于放在了Promise.then中


输出是什么?

async function async1 () {
  console.log('async1 start');
  await new Promise(resolve => {
    console.log('promise1')
  })
  console.log('async1 success');
  return 'async1 end'
}
console.log('srcipt start')
async1().then(res => console.log(res))
console.log('srcipt end')

答案

script start
async1 start
promise1
script end

考点:async、await语法

解析:这里需要注意的是在async1await后面的Promise是没有返回值的,也就是它的状态始终是pending状态,所以在await之后的内容是不会执行的,包括async1后面的 .then


输出是什么?

async function async1 () {
  console.log('async1 start');
  await new Promise(resolve => {
    console.log('promise1')
    resolve('promise1 resolve')
  }).then(res => console.log(res))
  console.log('async1 success');
  return 'async1 end'
}
console.log('srcipt start')
async1().then(res => console.log(res))
console.log('srcipt end')

答案

script start
async1 start
promise1
script end
promise1 resolve
async1 success
async1 end

考点:async、await语法

解析:这里和上一题不同的是,await后的Promise的状态最终为resolvedawait之后的代码会继续执行。async1后面的.then也会执行。