首页 > 其他分享 >【前端面试】2024年前端面试题之JS篇

【前端面试】2024年前端面试题之JS篇

时间:2024-01-15 15:32:17浏览次数:31  
标签:面试题 console log const 前端 2024 bar foo 函数

请简述JavaScript中的this

JS 中的 this 是一个相对复杂的概念,不是简单几句能解释清楚的。粗略地讲,函数的调用方式决定了 this 的值。我阅读了网上很多关于 this 的文章,Arnav Aggrawal 写的比较清楚。this 取值符合以下规则:

在调用函数时使用 new 关键字,函数内的 this 是一个全新的对象。 如果 apply、callbind 方法用于调用、创建一个函数,函数内的 this 就是作为参数传入这些方法的对象。 当函数作为对象里的方法被调用时,函数内的 this 是调用该函数的对象。比如当 obj.method() 被调用时,函数内的 this 将绑定到 obj 对象。 如果调用函数不符合上述规则,那么 this 的值指向全局对象(global object)。浏览器环境下 this 的值指向 window 对象,但是在严格模式下('use strict'),this 的值为 undefined。 如果符合上述多个规则,则较高的规则(1 号最高,4 号最低)将决定 this 的值。 如果该函数是 ES2015 中的箭头函数,将忽略上面的所有规则,this 被设置为它被创建时的上下文。

说说你对 AMD 和 CommonJS 的了解。

它们都是实现模块体系的方式,直到 ES2015 出现之前,JavaScript 一直没有模块体系。CommonJS 是同步的,而 AMD(Asynchronous Module Definition) 从全称中可以明显看出是异步的。CommonJS 的设计是为服务器端开发考虑的,而 AMD 支持异步加载模块,更适合浏览器。

我发现 AMD 的语法非常冗长,CommonJS 更接近其他语言 import 声明语句的用法习惯。大多数情况下,我认为 AMD 没有使用的必要,因为如果把所有 JavaScript 都捆绑进一个文件中,将无法得到异步加载的好处。此外,CommonJS 语法上更接近 Node 编写模块的风格,在前后端都使用 JavaScript 开发之间进行切换时,语境的切换开销较小。

我很高兴看到 ES2015 的模块加载方案同时支持同步和异步,我们终于可以只使用一种方案了。虽然它尚未在浏览器和 Node 中完全推出,但是我们可以使用代码转换工具进行转换。

请解释下面代码为什么不能用作 IIFE:function foo(){ }();,需要作出哪些修改才能使其成为 IIFE?

IIFE(Immediately Invoked Function Expressions)代表立即执行函数。 JavaScript 解析器将 function foo(){ }(); 解析成 function foo(){ }和(); 。其中,前者是函数声明;后者(一对括号)是试图调用一个函数,却没有指定名称,因此它会抛出 Uncaught SyntaxError: Unexpected token ) 的错误。

修改方法是:再添加一对括号,形式上有两种:(function foo(){ })()(function foo(){ }())。以上函数不会暴露到全局作用域,如果不需要在函数内部引用自身,可以省略函数的名称。

你可能会用到 void 操作符:void function foo(){ }();。但是,这种做法是有问题的。表达式的值是 undefined,所以如果你的 IIFE 有返回值,不要用这种做法。例如

const foo = void (function bar() {
  return 'foo';
})();

console.log(foo); // undefined

null、undefined和未声明变量之间有什么区别?如何检查判断这些状态值?

当你没有提前使用 var、letconst 声明变量,就为一个变量赋值时,该变量是未声明变量(undeclared variables)。未声明变量会脱离当前作用域,成为全局作用域下定义的变量。在严格模式下,给未声明的变量赋值,会抛出 ReferenceError 错误。和使用全局变量一样,使用未声明变量也是非常不好的做法,应当尽可能避免。要检查判断它们,需要将用到它们的代码放在 try/catch 语句中。

function foo() {
  x = 1; // 在严格模式下,抛出 ReferenceError 错误
}

foo();
console.log(x); // 1

当一个变量已经声明,但没有赋值时,该变量的值是 undefined。如果一个函数的执行结果被赋值给一个变量,但是这个函数却没有返回任何值,那么该变量的值是 undefined。要检查它,需要使用严格相等(===);或者使用 typeof,它会返回 'undefined' 字符串。请注意,不能使用非严格相等(==)来检查,因为如果变量值为 null,使用非严格相等也会返回 true

var foo;
console.log(foo); // undefined
console.log(foo === undefined); // true
console.log(typeof foo === 'undefined'); // true

console.log(foo == null); // true. 错误,不要使用非严格相等!

function bar() {}
var baz = bar();
console.log(baz); // undefined

null 只能被显式赋值给变量。它表示空值,与被显式赋值 undefined 的意义不同。要检查判断 null 值,需要使用严格相等运算符。请注意,和前面一样,不能使用非严格相等(==)来检查,因为如果变量值为 undefined,使用非严格相等也会返回 true

var foo = null;
console.log(foo === null); // true

console.log(foo == undefined); // true. 错误,不要使用非严格相等!

作为一种个人习惯,我从不使用未声明变量。如果定义了暂时没有用到的变量,我会在声明后明确地给它们赋值为 null

什么是闭包(closure),为什么使用闭包?

闭包是函数和声明该函数的词法环境的组合。词法作用域中使用的域,是变量在代码中声明的位置所决定的。闭包是即使被外部函数返回,依然可以访问到外部(封闭)函数作用域的函数。

为什么使用闭包?

  • 利用闭包实现数据私有化或模拟私有方法。这个方式也称为模块模式(module pattern)。
  • 部分参数函数(partial applications)柯里化(currying).

请说明.forEach循环和.map()循环的主要区别,它们分别在什么情况下使用?

为了理解两者的区别,我们看看它们分别是做什么的。

forEach

  • 遍历数组中的元素。
  • 为每个元素执行回调。
  • 无返回值。
const a = [1, 2, 3];
const doubled = a.forEach((num, index) => {
  // 执行与 num、index 相关的代码
});

// doubled = undefined

map

  • 遍历数组中的元素
  • 通过对每个元素调用函数,将每个元素“映射(map)”到一个新元素,从而创建一个新数组。
const a = [1, 2, 3];
const doubled = a.map((num) => {
  return num * 2;
});

// doubled = [2, 4, 6]

.forEach.map() 的主要区别在于 .map() 返回一个新的数组。如果你想得到一个结果,但不想改变原始数组,用 .map()。如果你只需要在数组上做迭代修改,用 forEach

匿名函数的典型应用场景是什么?

匿名函数可以在 IIFE 中使用,来封装局部作用域内的代码,以便其声明的变量不会暴露到全局作用域。

(function () {
  // 一些代码。
})();

匿名函数可以作为只用一次,不需要在其他地方使用的回调函数。当处理函数在调用它们的程序内部被定义时,代码具有更好地自闭性和可读性,可以省去寻找该处理函数的函数体位置的麻烦。

setTimeout(function () {
  console.log('Hello world!');
}, 1000);

匿名函数可以用于函数式编程或 Lodash(类似于回调函数)。

const arr = [1, 2, 3];
const double = arr.map(function (el) {
  return el * 2;
});
console.log(double); // [2, 4, 6]

.call和.apply有什么区别?

.call.apply 都用于调用函数,第一个参数将用作函数内 this 的值。然而,.call 接受逗号分隔的参数作为后面的参数,而 .apply 接受一个参数数组作为后面的参数。一个简单的记忆方法是,从 call 中的 C 联想到逗号分隔(comma-separated),从 apply 中的 A 联想到数组(array)。

function add(a, b) {
  return a + b;
}

console.log(add.call(null, 1, 2)); // 3
console.log(add.apply(null, [1, 2])); // 3

请说明Function.prototype.bind的用法。

摘自MDN:

bind()方法创建一个新的函数, 当被调用时,将其 this 关键字设置为提供的值,在调用新函数时,在任何提供之前提供一个给定的参数序列。

根据我的经验,将 this 的值绑定到想要传递给其他函数的类的方法中是非常有用的。在 React 组件中经常这样做。

请尽可能详细地解释 Ajax。

Ajax(asynchronous JavaScript and XML)是使用客户端上的许多 Web 技术,创建异步 Web 应用的一种 Web 开发技术。借助 Ajax,Web 应用可以异步(在后台)向服务器发送数据和从服务器检索数据,而不会干扰现有页面的显示和行为。通过将数据交换层与表示层分离,Ajax 允许网页和扩展 Web 应用程序动态更改内容,而无需重新加载整个页面。实际上,现在通常将 XML 替换为 JSON,因为 JavaScript 对 JSON 有原生支持优势。

XMLHttpRequest API 经常用于异步通信。此外还有最近流行的 fetch API

使用 Ajax 的优缺点分别是什么?

优点
  • 交互性更好。来自服务器的新内容可以动态更改,无需重新加载整个页面。
  • 减少与服务器的连接,因为脚本和样式只需要被请求一次。
  • 状态可以维护在一个页面上。JavaScript 变量和 DOM 状态将得到保持,因为主容器页面未被重新加载。
  • 基本上包括大部分 SPA 的优点。
缺点
  • 动态网页很难收藏。
  • 如果 JavaScript 已在浏览器中被禁用,则不起作用。
  • 有些网络爬虫不执行 JavaScript,也不会看到 JavaScript 加载的内容。
  • 基本上包括大部分 SPA 的缺点

请说明 JSONP 的工作原理,它为什么不是真正的 Ajax?

JSONP(带填充的 JSON)是一种通常用于绕过 Web 浏览器中的跨域限制的方法,因为 Ajax 不允许跨域请求。

JSONP 通过 <script> 标签发送跨域请求,通常使用 callback 查询参数,例如:https://example.com?callback=printData。 然后服务器将数据包装在一个名为 printData 的函数中并将其返回给客户端。

<!-- https://mydomain.com -->
<script>
  function printData(data) {
    console.log(`My name is ${data.name}!`);
  }
</script>

<script src="https://example.com?callback=printData"></script>
// 文件加载自 https://example.com?callback=printData
printData({name: 'Yang Shun'});

客户端必须在其全局范围内具有 printData 函数,并且在收到来自跨域的响应时,该函数将由客户端执行。

JSONP 可能具有一些安全隐患。由于 JSONP 是纯 JavaScript 实现,它可以完成 JavaScript 所能做的一切,因此需要信任 JSONP 数据的提供者。

现如今,跨来源资源共享(CORS) 是推荐的主流方式,JSONP 已被视为一种比较 hack 的方式。

请解释变量提升(hoisting)。

变量提升(hoisting)是用于解释代码中变量声明行为的术语。使用 var 关键字声明或初始化的变量,会将声明语句“提升”到当前作用域的顶部。 但是,只有声明才会触发提升,赋值语句(如果有的话)将保持原样。我们用几个例子来解释一下。

// 用 var 声明得到提升
console.log(foo); // undefined
var foo = 1;
console.log(foo); // 1

// 用 let/const 声明不会提升
console.log(bar); // ReferenceError: bar is not defined
let bar = 2;
console.log(bar); // 2

函数声明会使函数体提升,但函数表达式(以声明变量的形式书写)只有变量声明会被提升。

// 函数声明
console.log(foo); // [Function: foo]
foo(); // 'FOOOOO'
function foo() {
  console.log('FOOOOO');
}
console.log(foo); // [Function: foo]

// 函数表达式
console.log(bar); // undefined
bar(); // Uncaught TypeError: bar is not a function
var bar = function () {
  console.log('BARRRR');
};
console.log(bar); // [Function: bar]

请描述事件冒泡。

当一个事件在 DOM 元素上触发时,如果有事件监听器,它将尝试处理该事件,然后事件冒泡到其父级元素,并发生同样的事情。最后直到事件到达祖先元素。事件冒泡是实现事件委托的原理(event delegation)。

==和===的区别是什么

== 是抽象相等运算符,而 === 是严格相等运算符。== 运算符是在进行必要的类型转换后,再比较。=== 运算符不会进行类型转换,所以如果两个值不是相同的类型,会直接返回 false 。使用 == 时,可能发生一些特别的事情,例如:

1 == '1'; // true
1 == [1]; // true
1 == true; // true
0 == ''; // true
0 == '0'; // true
0 == false; // true

我的建议是从不使用 == 运算符,除了方便与 nullundefined 比较时,a == null 如果 anullundefined 将返回 true

var a = null;
console.log(a == null); // true
console.log(a == undefined); // true

请解释关于 JavaScript 的同源策略。

同源策略可防止 JavaScript 发起跨域请求。源被定义为 URI、主机名和端口号的组合。此策略可防止页面上的恶意脚本通过该页面的文档对象模型,访问另一个网页上的敏感数据。

你对 Promises 及其 polyfill 的掌握程度如何?

掌握它的工作原理。Promise 是一个可能在未来某个时间产生结果的对象:操作成功的结果或失败的原因(例如发生网络错误)。 Promise 可能处于以下三种状态之一:fulfilledrejectedpending。 用户可以对 Promise 添加回调函数来处理操作成功的结果或失败的原因。

一些常见的 polyfill$.deferred、Q 和 Bluebird,但不是所有的 polyfill 都符合规范。ES2015 支持 Promises,现在通常不需要使用 polyfills。

Promise代替回调函数有什么优缺点?

优点

  • 避免可读性极差的回调地狱。
  • 使用.then()编写的顺序异步代码,既简单又易读。
  • 使用Promise.all()编写并行异步代码变得很容易。

缺点

  • 轻微地增加了代码的复杂度(这点存在争议)。
  • 在不支持 ES2015 的旧版浏览器中,需要引入 polyfill 才能使用。

请解释同步和异步函数之间的区别。

同步函数阻塞,而异步函数不阻塞。在同步函数中,语句完成后,下一句才执行。在这种情况下,程序可以按照语句的顺序进行精确评估,如果其中一个语句需要很长时间,程序的执行会停滞很长时间。

异步函数通常接受回调作为参数,在调用异步函数后立即继续执行下一行。回调函数仅在异步操作完成且调用堆栈为空时调用。诸如从 Web 服务器加载数据或查询数据库等重负载操作应该异步完成,以便主线程可以继续执行其他操作,而不会出现一直阻塞,直到费时操作完成的情况(在浏览器中,界面会卡住)。

什么是事件循环?调用堆栈和任务队列之间有什么区别?

事件循环是一个单线程循环,用于监视调用堆栈并检查是否有工作即将在任务队列中完成。如果调用堆栈为空并且任务队列中有回调函数,则将回调函数出队并推送到调用堆栈中执行。

使用let、var和const创建变量有什么区别?

var 声明的变量的作用域是它当前的执行上下文,它可以是嵌套的函数,也可以是声明在任何函数外的变量。letconst 是块级作用域,意味着它们只能在最近的一组花括号(function、if-else 代码块或 for 循环中)中访问。

function foo() {
  // 所有变量在函数中都可访问
  var bar = 'bar';
  let baz = 'baz';
  const qux = 'qux';

  console.log(bar); // bar
  console.log(baz); // baz
  console.log(qux); // qux
}

console.log(bar); // ReferenceError: bar is not defined
console.log(baz); // ReferenceError: baz is not defined
console.log(qux); // ReferenceError: qux is not defined
if (true) {
  var bar = 'bar';
  let baz = 'baz';
  const qux = 'qux';
}

// 用 var 声明的变量在函数作用域上都可访问
console.log(bar); // bar
// let 和 const 定义的变量在它们被定义的语句块之外不可访问
console.log(baz); // ReferenceError: baz is not defined
console.log(qux); // ReferenceError: qux is not defined

var 会使变量提升,这意味着变量可以在声明之前使用。letconst 不会使变量提升,提前使用会报错。

console.log(foo); // undefined

var foo = 'foo';

console.log(baz); // ReferenceError: can't access lexical declaration 'baz' before initialization

let baz = 'baz';

console.log(bar); // ReferenceError: can't access lexical declaration 'bar' before initialization

const bar = 'bar';

var 重复声明不会报错,但 letconst 会。

var foo = 'foo';
var foo = 'bar';
console.log(foo); // "bar"

let baz = 'baz';
let baz = 'qux'; // Uncaught SyntaxError: Identifier 'baz' has already been declared

letconst 的区别在于:let 允许多次赋值,而 const 只允许一次。

// 这样不会报错。
let foo = 'foo';
foo = 'bar';

// 这样会报错。
const baz = 'baz';
baz = 'qux';

你能给出一个使用箭头函数的例子吗,箭头函数与其他函数有什么不同

一个很明显的优点就是箭头函数可以简化创建函数的语法,我们不需要在箭头函数前面加上 function 关键词。并且箭头函数的 this 会自动绑定到当前作用域的上下文中,这和普通的函数不一样。普通函数的 this 是在执行的时候才能确定的。箭头函数的这个特点对于回调函数来说特别有用,特别对于 React 组件而言。

高阶函数(higher-order)的定义是什么?

高阶函数是将一个或多个函数作为参数的函数,它用于数据处理,也可能将函数作为返回结果。高阶函数是为了抽象一些重复执行的操作。一个典型的例子是 map,它将一个数组和一个函数作为参数。map 使用这个函数来转换数组中的每个元素,并返回一个包含转换后元素的新数组。JavaScript 中的其他常见示例是 forEachfilterreduce。高阶函数不仅需要操作数组的时候会用到,还有许多函数返回新函数的用例。Function.prototype.bind 就是一个例子。

Map 示例

假设我们有一个由名字组成的数组,我们需要将每个字符转换为大写字母。

const names = ['irish', 'daisy', 'anna'];

不使用高阶函数的方法是这样:

const transformNamesToUppercase = function (names) {
  const results = [];
  for (let i = 0; i < names.length; i++) {
    results.push(names[i].toUpperCase());
  }
  return results;
};
transformNamesToUppercase(names); // ['IRISH', 'DAISY', 'ANNA']

使用 .map(transformerFn) 使代码更简明

const transformNamesToUppercase = function (names) {
  return names.map((name) => name.toUpperCase());
};
transformNamesToUppercase(names); // ['IRISH', 'DAISY', 'ANNA']

请给出一个解构(destructuring)对象或数组的例子。

解构是 ES6 中新功能,它提供了一种简洁方便的方法来提取对象或数组的值,并将它们放入不同的变量中。

数组解构

// 变量赋值
const foo = ['one', 'two', 'three'];

const [one, two, three] = foo;
console.log(one); // "one"
console.log(two); // "two"
console.log(three); // "three"
// 变量交换
let a = 1;
let b = 3;

[a, b] = [b, a];
console.log(a); // 3
console.log(b); // 1

对象解构

// 变量赋值
const o = {p: 42, q: true};
const {p, q} = o;

console.log(p); // 42
console.log(q); // true

你能举出一个柯里化函数(curry function)的例子吗?它有哪些好处?

柯里化(currying)是一种模式,其中具有多个参数的函数被分解为多个函数,当被串联调用时,将一次一个地累积所有需要的参数。这种技术帮助编写函数式风格的代码,使代码更易读、紧凑。值得注意的是,对于需要被 curry 的函数,它需要从一个函数开始,然后分解成一系列函数,每个函数都需要一个参数。

function curry(fn) {
  if (fn.length === 0) {
    return fn;
  }

  function _curried(depth, args) {
    return function (newArgument) {
      if (depth - 1 === 0) {
        return fn(...args, newArgument);
      }
      return _curried(depth - 1, [...args, newArgument]);
    };
  }

  return _curried(fn.length, []);
}

function add(a, b) {
  return a + b;
}

var curriedAdd = curry(add);
var addFive = curriedAdd(5);

var result = [0, 1, 2, 3, 4, 5].map(addFive); // [5, 6, 7, 8, 9, 10]

PHP项目学习资料:点此下载

标签:面试题,console,log,const,前端,2024,bar,foo,函数
From: https://blog.51cto.com/u_15723831/9255119

相关文章

  • 2024年AI预测报告-金融
    AndreessenHorowitz公司的合伙人MarcAndrusko表示银行和交易新工具到2024年,我们将看到雄心勃勃的创始人着手解决金融机构所面临的最为棘手的问题。 尽管全球投资银行和交易服务市场年收入接近3500亿美元,但目前仍主要依赖于20世纪80年代建立的内部部署系统和企业软件。尽管......
  • 前端歌谣-第六拾课-express安装
    前言我是歌谣微信公众号关注前端小歌谣一起学习前端知识今天继续给大家讲解获取静态资源的讲解安装npminit-ynpmiexpress案例constexpress=require("express")constapp=express()app.get("/",(req,res)=>{res.write("hellogeyao")res.end()})app.listen......
  • 2024年AI预测报告-法律安全
    美国《福布斯》双周刊网站发布:至少一家美国法院将裁定在互联网上训练的生成式人工智能模型构成版权侵犯。该问题将开始向美国最高法院提起诉讼。如今,一个重大且未被充分认识的法律风险笼罩在整个生成人工智能领域:世界领先的生成人工智能模型是在大量受版权保护的内容上进行训练的,......
  • 【JaveWeb教程】(2)Web前端基础:JavaScript入门不再难:一篇文章教你轻松搞定JavaScript的
    目录1介绍2引入方式3基础语法3.1书写语法3.2变量3.3数据类型和运算符4函数4.1第一种定义格式4.2第二种定义格式html完成了架子,css做了美化,但是网页是死的,我们需要给他注入灵魂,所以接下来我们需要学习JavaScript,这门语言会让我们的页面能够和用户进行交互。1介绍通过代......
  • 【JaveWeb教程】(1)Web前端基础:HTML+CSS入门不再难:一篇文章教你轻松搞定HTML与CSS!
    目录1.前端开发介绍2.HTML&CSS2.1HTML快速入门2.1.1操作2.1.2总结2.2开发工具2.3基础标签&样式2.3.1新浪新闻-标题实现2.3.1.1标题排版2.3.1.1.1分析2.3.1.1.2标签2.3.1.1.2实现2.3.1.2标题样式2.3.1.2.1CSS引入方式2.3.1.2.2颜色表示2.3.1.2.3标题字体颜色2.......
  • 软件测试笔试面试题目完全汇总
    软件缺陷:1)软件未实现产品说明书要求的功能2)软件出现了产品说明书指明不应该出现的错误3)软件实现了产品说明书未提到的功能4)软件未实现产品说明书虽未明确提及但应该实现的目标5)软件难以理解、不易使用、运行缓慢或者从测试员的角度看最终用户会认为不好。软件测试:为了发现......
  • 软件测试常考面试题-软件测试面试宝典(一篇足矣)
    软件测试常考面试题-软件测试面试宝典(一篇足矣) 介绍解决锻炼思维问题的思路和方法,从不同角度分析问题,让思维变强大,变宽广的同时在测试方面进行学习和方法的拓展,也会分享读书心得和问题思考。 问:软件测试的原则?答:软件测试的八个原则_山鬼谣弋痕夕的博客-CSDN博客_软件测试......
  • Photoshop 2024:数字图像处理的行业标准 mac/win版
    Photoshop2024是一款功能强大的数字图像处理软件,被广泛用于创意设计和视觉效果制作。这款软件提供了广泛的工具和功能,使用户能够进行各种复杂的图像编辑和合成工作。→→↓↓载Photoshop2024mac/win版Photoshop2024在图像处理方面具有许多优势。首先,它支持各种图像格式,包括......
  • 从前端角度浅谈性能 | 京东物流技术团队
    1前言自网站诞生以来,页面白屏时间、用户交互的响应速度等一直都是开发者关心的问题,这直接影响了一个网站能否为用户的浏览提供舒适的服务,而这种舒适度,直接关系着对用户的吸引力,毕竟谁都不能忍受一个页面长达10秒的空白屏时间,更别说点击按钮后,页面长时间的毫无反应。web的性能......
  • 20款最好的免费 Bootstrap 后台管理和前端模板
    20款最好的免费Bootstrap后台管理和前端模板 AdminBootstrapTemplatesFreeDownload 1.SBAdmin2Preview | Details&Download2.AdminLitePreview | Details&Download3.DirectorResponsiveAdminTemplateFreePreview | Details&Download4......