前端面试题详解整理111|css选择器优先级,数据类型,数组遍历,箭头函数,普通哈数,var,let,const类型转换,

48 阅读15分钟

阿里系面试

阿里淘天 挂

  1. 基本数据类型 然后一堆判断,以及为什么?比较的时候是如何转换?

在 JavaScript 中,基本数据类型包括:

  1. Number: 表示数字,包括整数和浮点数。
  2. String: 表示字符串,包括一系列字符组成的文本。
  3. Boolean: 表示逻辑值,包括 true 和 false 两个值。
  4. Null: 表示空值,表示一个不存在的对象。
  5. Undefined: 表示未定义的值,通常用于变量声明但未赋值的情况。
  6. Symbol: 表示唯一的标识符。
  7. BigInt: 表示任意精度的整数。

在进行比较时,JavaScript 中的基本数据类型会根据需要进行隐式类型转换。比较时,如果两个值的类型不同,JavaScript 会尝试将它们转换为相同的类型,然后进行比较。

以下是 JavaScript 中比较操作符的一般规则:

  1. == 操作符: 如果两个操作数类型不同,则尝试将它们转换为相同的类型,然后比较。这种类型转换称为类型强制转换。例如,字符串会被转换成数字,然后再进行比较。但是,避免使用 == 操作符,因为它会导致一些意想不到的结果,推荐使用严格相等操作符 ===。

  2. === 操作符: 严格相等操作符,比较两个操作数的值和类型,如果类型不同,则返回 false,不会进行类型转换。

在进行比较时,JavaScript 会遵循以下规则:

  • 如果有一个操作数是布尔值,则先将其转换为数字(true 转换为 1,false 转换为 0),然后再进行比较。
  • 如果有一个操作数是字符串,另一个操作数是数字,则将字符串转换为数字,然后再进行比较。
  • 如果有一个操作数是对象,另一个操作数不是对象,则调用对象的 valueOf() 方法,将其转换为原始值,然后再进行比较。

总的来说,JavaScript 中的比较操作符会根据需要进行隐式类型转换,然后再进行比较。因此,在进行比较时需要注意类型的转换规则,避免产生意外的结果。

  1. var let const,const声明的对象可以修改属性吗?

在 JavaScript 中,varletconst 是用于声明变量的关键字,它们有不同的作用域和特性。

  1. var: 使用 var 声明的变量是函数作用域或全局作用域的,不存在块级作用域,且存在变量提升的特性。在 ES6 之前,var 是声明变量的主要方式。

  2. let: 使用 let 声明的变量是块级作用域的,只在当前代码块内有效,不存在变量提升的特性。let 在 ES6 中引入,逐渐替代了 var

  3. const: 使用 const 声明的变量也是块级作用域的,和 let 类似,但是 const 声明的变量必须进行初始化,并且一旦赋值后就不能再重新赋值。但需要注意的是,const 声明的变量如果是对象类型,对象的引用不可变,但对象内部的属性是可以修改的。

所以,对于 const 声明的对象,虽然不能修改变量指向的对象,但对象本身的属性是可以修改的。例如:

const obj = { key: 'value' };
obj.key = 'new value'; // 合法,修改了对象的属性值

但是,如果尝试重新赋值给 const 声明的变量,会导致语法错误:

const obj = { key: 'value' };
obj = { newKey: 'newValue' }; // 报错,不能重新赋值给 const 变量

总结来说,const 声明的变量一旦初始化赋值后就不能再修改其指向,但如果是对象类型,对象内部的属性是可以修改的。

5. 如何使一个对象不能被修改?

在 JavaScript 中,要使一个对象不能被修改,可以使用以下方法之一:

  1. 使用Object.freeze(): Object.freeze() 方法可以冻结一个对象,使其属性不能被添加、删除或修改。冻结后的对象是不可变的,即使尝试修改也不会生效。示例如下:
const obj = { key: 'value' };
Object.freeze(obj);
obj.key = 'new value'; // 这里修改操作无效
  1. 使用const关键字: 如果使用 const 关键字声明一个对象,该对象引用不可变,但对象本身的属性可以被修改。示例如下:
const obj = { key: 'value' };
obj.key = 'new value'; // 这里修改了对象的属性值
  1. 使用Object.seal(): Object.seal() 方法可以封闭一个对象,使其现有属性变为不可配置,但属性值可以修改。使用此方法后,不能添加或删除对象的属性。示例如下:
const obj = { key: 'value' };
Object.seal(obj);
delete obj.key; // 这里删除操作无效
obj.key = 'new value'; // 这里修改了对象的属性值

以上方法可以根据实际需求选择,如果需要完全禁止修改对象,可以使用 Object.freeze();如果只想禁止添加和删除属性,但允许修改属性值,可以使用 Object.seal();如果只是希望对象引用不可变,但对象本身的属性可以修改,可以使用 const 关键字。

7. 闭包是什么?什么场景用过?

闭包(Closure)是指函数与其相关的引用环境组合而成的组合体,它包含了函数定义时的词法环境,可以访问其外部作用域中的变量。换句话说,闭包可以捕获并保存了函数声明时所处的上下文环境,使得函数能够在其定义的作用域之外被调用,且能够访问到其所在作用域中的变量。

闭包通常用于以下场景之一:

  1. 封装私有变量: 在 JavaScript 中,由于缺乏原生的私有变量支持,可以使用闭包来模拟私有变量的功能。通过在函数内部定义变量,并返回一个访问这个变量的函数,从而创建一个闭包,使外部无法直接访问这个变量,从而达到保护变量的目的。
function createCounter() {
  let count = 0;
  return function() {
    return ++count;
  };
}

const counter = createCounter();
console.log(counter()); // 输出 1
console.log(counter()); // 输出 2
console.log(counter()); // 输出 3
  1. 延迟执行: 闭包还可以用于实现延迟执行的功能,即在外部函数返回后,内部函数仍然可以访问外部函数的变量,并在后续的某个时刻执行。
function delayExecution() {
  let message = 'Hello, World!';
  setTimeout(function() {
    console.log(message);
  }, 1000);
}

delayExecution(); // 一秒后输出 "Hello, World!"

以上是闭包的简单介绍和两个常见的应用场景。在实际开发中,闭包还可以用于实现柯里化、高阶函数、模块化等方面,是 JavaScript 中非常重要的概念之一。

9. 箭头函数 普通函数的区别

箭头函数(Arrow Function)和普通函数(Regular Function)在语法和功能上有一些区别,下面是它们的主要区别:

  1. 语法:

    • 箭头函数使用箭头(=>)来定义函数,语法更加简洁。例如:(参数) => { 函数体 }
    • 普通函数使用关键字 function 来定义函数,语法相对较长。例如:function 函数名(参数) { 函数体 }
  2. this 的指向:

    • 箭头函数没有自己的 this,它会捕获所在上下文的 this 值。箭头函数的 this 始终指向在定义时所在的上下文,而不是调用时的上下文。
    • 普通函数的 this 指向在调用时确定,根据调用方式的不同而变化。
  3. arguments 对象:

    • 箭头函数没有自己的 arguments 对象,它会捕获所在上下文的 arguments 对象。
    • 普通函数有自己的 arguments 对象,可以访问函数的所有参数。
  4. 构造函数:

    • 箭头函数不能用作构造函数,不能使用 new 关键字调用。
    • 普通函数可以用作构造函数,通过 new 关键字调用时,会创建一个新的对象。
  5. 绑定:

    • 箭头函数的 thisargumentssupernew.target 都不能被绑定。
    • 普通函数的 thisargumentssupernew.target 可以被绑定。

总的来说,箭头函数适合在简单的函数或者需要捕获外部上下文的场景中使用,语法更加简洁;而普通函数则更适合需要更灵活的 this 绑定、构造函数等情况。选择使用哪种函数取决于具体的需求和场景。

11. 数组遍历的方式

在 JavaScript 中,数组遍历有多种方式,可以根据需求和场景选择合适的方法。以下是常见的数组遍历方式:

  1. for 循环: 使用传统的 for 循环可以遍历数组,并通过索引访问数组元素。
const array = [1, 2, 3, 4, 5];
for (let i = 0; i < array.length; i++) {
  console.log(array[i]);
}
  1. forEach 方法: forEach 方法是数组的原生方法,可以对数组的每个元素执行指定的函数。
const array = [1, 2, 3, 4, 5];
array.forEach(function(item) {
  console.log(item);
});
  1. for...of 循环: for...of 循环是 ES6 中引入的一种遍历数组的方式,可以在不引用索引的情况下遍历数组元素。
const array = [1, 2, 3, 4, 5];
for (const item of array) {
  console.log(item);
}
  1. map 方法: map 方法会创建一个新数组,其中的每个元素是原始数组中对应元素调用函数的结果。
const array = [1, 2, 3, 4, 5];
const newArray = array.map(function(item) {
  return item * 2;
});
console.log(newArray); // 输出 [2, 4, 6, 8, 10]
  1. filter 方法: filter 方法会创建一个新数组,其中包含通过函数测试的所有元素。
const array = [1, 2, 3, 4, 5];
const newArray = array.filter(function(item) {
  return item % 2 === 0;
});
console.log(newArray); // 输出 [2, 4]
  1. reduce 方法: reduce 方法对数组中的每个元素执行一个指定的函数,并将结果汇总为单个值。
const array = [1, 2, 3, 4, 5];
const sum = array.reduce(function(accumulator, currentValue) {
  return accumulator + currentValue;
}, 0);
console.log(sum); // 输出 15

以上是常见的数组遍历方式,根据实际需求和场景选择合适的方法可以提高代码的效率和可读性。

13. for in和for of的区别

for...infor...of 是两种 JavaScript 中用于遍历的不同方式,它们有一些区别:

  1. 遍历的对象:

    • for...in 遍历的是对象的可枚举属性,包括自身和继承的属性(如果对象是通过原型链继承的)。
    • for...of 遍历的是可迭代对象(包括数组、字符串、Set、Map 等),只能遍历对象的元素,而不能获取对象的键名。
  2. 使用场景:

    • for...in 通常用于遍历对象的属性,例如遍历对象的键名。但是在遍历数组时,可能会遍历到数组原型链上的其他属性,因此不推荐用于遍历数组。
    • for...of 通常用于遍历可迭代对象的元素,例如遍历数组、字符串等。它提供了一种简洁的语法来遍历数组或类数组对象的元素。
  3. 遍历顺序:

    • for...in 遍历的顺序是不确定的,可能不按照属性的顺序进行遍历。
    • for...of 遍历的顺序是按照迭代器返回元素的顺序进行遍历,对于数组来说就是按照数组的索引顺序。
  4. 可枚举属性:

    • for...in 遍历的是对象的可枚举属性,包括自身和继承的属性,但不包括不可枚举属性和 Symbol 属性。
    • for...of 只能遍历可迭代对象的元素,不会遍历对象的属性,因此不受可枚举属性的影响。

总的来说,for...in 适用于遍历对象的属性,而 for...of 适用于遍历可迭代对象的元素。在遍历数组时,推荐使用 for...of,因为它能够按照数组的索引顺序进行遍历,而且语法更加简洁清晰。

15. css选择器以及优先级

在 CSS 中,选择器用于选择需要应用样式的 HTML 元素。选择器有许多种类,每一种都有不同的用途和匹配规则。以下是常见的 CSS 选择器及其优先级:

  1. 元素选择器(Element Selector):

    • 匹配指定类型的 HTML 元素。
    • 例如:divpa 等。
  2. 类选择器(Class Selector):

    • 匹配具有指定 class 属性的 HTML 元素。
    • 以点(.)开头。
    • 例如:.container.button 等。
  3. ID 选择器(ID Selector):

    • 匹配具有指定 id 属性的 HTML 元素。
    • 以井号(#)开头。
    • 例如:#header#footer 等。
  4. 属性选择器(Attribute Selector):

    • 根据元素的属性值来选择元素。
    • 包括属性存在、属性值完全匹配、属性值包含特定字符串等方式。
    • 例如:[type="text"][href^="https://"] 等。
  5. 伪类选择器(Pseudo-class Selector):

    • 匹配处于特定状态的元素,如鼠标悬停、链接状态等。
    • 以冒号(:)开头。
    • 例如::hover:first-child:nth-child() 等。
  6. 伪元素选择器(Pseudo-element Selector):

    • 用于创建元素的虚拟子元素,如 ::before、::after。
    • 以双冒号(::)开头。
    • 例如:::before::after 等。

优先级规则:

  • ID 选择器 > 类选择器 > 元素选择器
  • 内联样式 > 内部样式表 > 外部样式表
  • !important > 行内样式 > 其他样式

如果多个选择器的优先级相同,则根据样式表中出现的顺序来决定哪个样式生效。

通过合理使用这些选择器,并理解其优先级规则,可以更好地控制页面样式,并确保所需的样式生效。

17. 多个组合选择器如何判断当前的样式该是谁的?

在 CSS 样式表中,如果多个组合选择器都匹配了同一个元素,那么样式的应用顺序遵循以下原则:

  1. 优先级: 根据选择器的优先级确定哪个样式应用于元素。优先级高的样式将覆盖优先级低的样式。通常情况下,选择器的优先级由选择器的特性决定,例如 ID 选择器 > 类选择器 > 元素选择器。

  2. 出现顺序: 如果多个选择器的优先级相同,则根据它们在样式表中出现的顺序来确定哪个样式应用于元素。后面出现的样式将覆盖先前出现的样式。

举例来说,假设有以下 CSS 样式表:

/* 样式1 */
div {
  color: red;
}

/* 样式2 */
#myDiv {
  color: blue;
}

/* 样式3 */
.container div {
  color: green;
}

假设页面中有一个 div 元素,并且它的 idmyDiv,那么根据以上样式表,这个 div 元素的文字颜色将是蓝色,因为 ID 选择器的优先级高于元素选择器,并且优先级相同的情况下,后面的样式将覆盖先前的样式。

因此,要确定当前样式应该是哪个选择器定义的,需要考虑选择器的优先级以及样式表中出现的顺序。

19. margin塌陷?如何解决?Bfc如何开启?

  1. Margin Collapse(外边距塌陷): 外边距塌陷是指在垂直方向上相邻的两个元素的外边距会合并成一个外边距的现象。这种情况通常发生在嵌套的块级元素之间,导致元素之间的间距出现异常。

  2. 解决外边距塌陷的方法:

    • 将元素设置为 BFC(块级格式化上下文),以隔离外边距的合并。
    • 使用边框、内边距、浮动、绝对定位等方法来隔离外边距。
    • 使用 overflow: autooverflow: hidden 在父元素上创建一个新的 BFC。
  3. BFC(Block Formatting Context,块级格式化上下文): BFC 是 CSS 布局的一部分,它是一个独立的渲染区域,其中的元素布局是不受外部影响的。BFC 规定了内部的块级元素如何布局,并且与外部元素相互隔离,防止外边距塌陷、浮动元素重叠等问题。

  4. 如何开启 BFC:

    • 设置元素的 overflow 属性为除了 visible 以外的值(例如 autohidden)。
    • 设置元素的 float 属性为除了 none 以外的值。
    • 将元素设置为 position: absoluteposition: fixed
    • 将元素设置为 display: inline-blockdisplay: table-celldisplay: table-caption 中的任意一种。

通过将元素设置为 BFC,可以有效地解决外边距塌陷等布局问题,提升页面布局的稳定性和可靠性。

21. http和https的区别

HTTP(HyperText Transfer Protocol)和HTTPS(HyperText Transfer Protocol Secure)是用于在网络上传输数据的两种协议,它们之间的主要区别在于安全性:

  1. 安全性:

    • HTTP 是明文传输的协议,数据在传输过程中不加密,因此容易被窃听和篡改。
    • HTTPS 则使用 SSL/TLS 协议对数据进行加密和身份验证,确保数据在传输过程中的安全性和完整性,有效防止了信息被窃听和篡改的风险。
  2. 加密方式:

    • HTTP 不提供加密机制,数据以明文形式传输。
    • HTTPS 使用 SSL/TLS 协议对通信过程中的数据进行加密,采用对称加密和非对称加密相结合的方式,保障了通信的安全性。
  3. 端口号:

    • HTTP 默认使用端口号 80 进行通信。
    • HTTPS 默认使用端口号 443 进行通信。
  4. 证书:

    • HTTPS 在建立连接时需要服务器提供有效的数字证书,证书用于对服务器身份进行验证,确保通信的安全性和可靠性。
    • HTTP 不需要证书,因为通信是明文的,没有加密的需求。
  5. SEO:

    • 由于搜索引擎越来越重视网站的安全性,使用 HTTPS 可以提高网站的排名和信誉。
    • 使用 HTTP 的网站可能会被搜索引擎降权,影响网站的可见性和流量。

总的来说,HTTPS 是对 HTTP 的安全增强版,通过加密和身份验证保障了数据在传输过程中的安全性和完整性,适用于对数据安全要求较高的网站和应用。