容易

图片标签 imgalt 属性有什么用?

当图片加载失败无法看到时,页面会显示alt 属性提供的信息。alt 属性是用于描述图片信息的,那些装饰性的图片除外,这些图片的 alt 信息应该置空。

善意:

  • 修饰性的图片的alt应该为 ''
  • Web crawlers网络爬虫使用 alt 标签识别图片内容,因此这些属性对 Search Engine Optimization (SEO) 搜索引擎优化很重要
  • alt 标签后加. 提高可访问性

CSS BEM 是什么?

BEM 方法是CSS类名的命名约定,目的是通过定义命名空间,使 CSS 更易于维护,解决类(class)的范围问题 。BEMBlock Element Modifier —— 块元素修饰符Block 是一个独立的部分,它可以在项目里重复利用并为子元素(Element)充当命名空间。当一个块或元素在某个状态或者结构样式不同时,Modifier可以用作标识符区别这些状态或差异。

/* block component */
.block {
}

/* element */
.block__element {
}

/* modifier */
.block__element--modifier {
}

这是一个类名标记的例子:

<nav class="navbar">
  <a href="/" class="navbar__link navbar__link--active"></a>
  <a href="/" class="navbar__link"></a>
  <a href="/" class="navbar__link"></a>
</nav>

在上例中,navbarBlocknavbar__link 元素 除了是 navbar 组件外没任何意义, navbar__link--active 是一个修饰符, 表示 navbar————link 元素的一种不同的状态。

由于修饰符过于冗长,更多情况下是使用 is-* 标识

<a href="/" class="navbar__link is-active"></a>

这些必须与元素链接不能独立使用,否则就会有范围问题。

.navbar__link.is-active {
}

善意:

  • 另一个解决范围问题的方案是 CSS-in-JS
范围问题: CSS只有一个全局命名空间。 在非平凡的应用程序中不可能避免选择器冲突。

cache busting 的用途以及如何实现

cache busting ——我暂时不知道如何翻译,大致就是解决缓存文件不刷新问题

浏览器有一个缓存机制来临时存储网站上的文件,所以在页面之间切换或重新加载相同的页面时,这些文件不需要被重新下载。服务器被设置为发送报头,报头告诉浏览器在给定的时间内存储文件。这大大提高了网站的速度,并保持了带宽。

但是,当开发人员更改了网站,因为用户的缓存仍然引用旧文件,这可能会导致问题。如果缓存的CSS和JavaScript文件引用的元素已不复存在、已移动或已重命名,则会使它们保留原有功能,或破坏网站。

cache busting是一种强制浏览器下载新文件的方式,通过将文件命名为与旧文件不同的名称来实现。

有一种强制浏览器重新下载文件的常见的方式是在文件名称末尾添加一个索引字符串

src="js/script.js" => src="js/script.js?v=2"

这种方式浏览器会将它视作不同的文件,并且不用重命名文件。

CSS 预处理器的好处是什么?

CSS预处理程序添加了本地CSS没有的有用功能,通常通过启用DRY(不要重复)原则使CSS更整洁、更易于维护。它们用于嵌套选择器的简洁语法减少了重复代码。它们为一致的主题化提供了变量(然而,CSS变量已经在很大程度上取代了这个功能),并提供了额外的工具,如color函数(变亮、变暗、透明等)、mixin和循环,这些工具使CSS更像一种真正的编程语言,并使开发人员有更多的能力生成复杂的CSS。

善意:

  • 允许我们书写更易于维护和扩展的css代码
  • 一些使用CSS预处理器的缺点: 安装,重新编译耗时等

===== 的区别

(===)检查严格的相等性,这意味着类型和值必须相同。另一方面,(==)首先执行类型强制转换,使两个操作数具有相同的类型,然后应用严格的比较。

善意:

  • 只要可能,使用===来测试等式,因为松散等式==可能会得到不直观的结果。
  • 类型强制转换表示将值转换为相同的类型。
  • 假值以及这些值的比较。

使用弹性盒子模型创建一个三列布局,每一列占据容器col-{n} / 12的比例

<div class="row">
  <div class="col-2"></div>
  <div class="col-7"></div>
  <div class="col-3"></div>
</div>

设置 .row的父元素display: flex;使用flex的 简写属性 给列类设置一个 与它的比例对应的 flex-grow

.row {
  display: flex;
}

.col-2 {
  flex: 2;
}

.col-7 {
  flex: 7;
}

.col-3 {
  flex: 3;
}

网页能包含多个 <header>元素么? <footer>呢?

两者都可以。W3文档声明标记表示其最近祖先“部分”的页眉(<header>)和页脚(<footer>)区域。因此,不仅页面<body>可以包含页眉和页脚,而且每个<article><section>元素也可以包含页眉和页脚。

善意:

  • W3建议你想要多少就有多少,但是对于你页面的每一个“部分”,比如正文、部分等,每个部分只能有一个。

简要描述以下HTML5语义元素的正确用法:<header><article><section><footer>

  • <header>用于包含关于页面某个部分的介绍和导航信息。这可以包括章节标题、作者姓名、出版时间和日期、目录或其他导航信息。
  • <article>是一个自包含的组合,逻辑上可以在页面之外独立地重新创建,而不会失去它的意义。个人博客文章或新闻故事就是很好的例子。
  • <section>是一个灵活的容器,用于保存具有相同信息主题或目的的内容。
  • <footer>用于保存应该出现在内容部分末尾的信息,并包含关于该部分的附加信息。作者姓名、版权信息和相关链接就是此类内容的典型例子。

善意:

  • 其他语义元素是<form><table>

你能说出 @media 属性的四种类型吗?

  • all 适用于所有媒体类型的设备
  • print 它只适用于打印机
  • screen 仅适用于屏幕(台式机、平板电脑、手机等)
  • speech 这只适用于屏幕阅读器

中级

值比较(下列代码输出?)

const a = [1, 2, 3]
const b = [1, 2, 3]
const c = "1,2,3"

console.log(a == c)
console.log(a == b)

第一个console.log输出true,因为JavaScript的编译器执行类型转换,因此它根据字符串的值与字符串进行比较。另一方面,第二个console.log输出false,因为数组是对象,对象通过引用进行比较(比较的是内存地址)。

善意之言:

  • JavaScript执行自动类型转换
  • 通过引用来比较对象
  • 按值比较基本类型(null, undefined, number, string, boolean)数据的值

rel="noopener"的使用情景和使用目的

rel="noopener"<a>元素(超链接)中使用的一个属性。它防止页面拥有window.opener属性,这个属性指向原来的的页面,并允许从超链接打开的页面操作超链接所在的页面,即新打开的页面控制原来跳转页。务必在target='_blank'后加上此属性,兼容火狐写法rel="noopener norefferrer"

善意之言

  • rel=“noopener”应用于超链接。
  • rel="noopener"防止打开的链接操作源页面。

JavaScript 的自动分号插入(ASI)

下列代码输出什么?

function greet() {
  return
  {
    message: "hello"
  }
}
undefined

由于 JavaScript的自动分号插入机制 automatic semicolon insertion (ASI), 编译器在return关键字后放置分号,因此它返回undefined,不会抛出错误。

善意之言:

  • 自动分号插入机制会导致一些耗时难缠的bug

nullundefined之间的区别是什么?

JavaScript中,两个值分别表示——undefinednull。它们之间的具体区别是null是显式的,而undefined是隐式的。当属性不存在或变量没有被赋予值时,该值是undefined。设置值null是显式地指示“no value”。在本质上,undefined用于未知的情况,null用于已知的情况。也就是说,变量只声明不知道它指代什么时,也就是说它是未知的,是undefined,若变量已知,但在一些时候是没有内容的,则用null赋值

善意之言:

  • typeof undefined的值为“undefined”
  • typeof null计算“object”。然而,它仍然是一个原始值,这在JavaScript中被认为是一个实现错误。
  • undefined == null的值为true。

MIME类型是什么?它的用途是什么?

MIMEMulti-purpose Internet Mail Extensions的缩写。它是用作在Internet上对文件类型进行分类的标准方法。

善意之言:

  • MIME type实际上有两个部分:一个类型和一个用斜杠(/)分隔的子类型。例如,Microsoft Word文件的MIME类型是application/msword(类型为application,子类型为msword)。

typeof 的运算结果

求下列代码的值

typeof typeof 0

输出: "string"

先得到 0的类型"number", 然后 typeof "number" 得出 "string"

typeof 操作符返回一个字符串表示未鉴定的操作数的类型

JavaScript 的数据类型

最新的 ECMAScript 标准定义了种数据类型,六种基本/原始数据类型:Boolean, Null, Undefined, Number, String, Symbol ,以及一种非原始数据类型: Object.

对以上数据类型使用 typeof(结果都是小写): boolean, object, undefined, number, string, symbol, object

善意之言

  • 新补充的基本数据类型 Symbol
  • Array, Date and Function 都是 object 对象类型(typeof 函数的出来function)
  • JavaScript中的函数是能被调用的对象
判断类型:
object instanceof constructor
instanceof 运算符用来检测 constructor.prototype 是否存在于参数 object 的原型链上。

参数parameterarguments 的区别

参数Parameter 是函数定义时形参的变量名,而arguments 是函数调用时给定的值

function myFunction(parameter1, parameter2) {
  console.log(arguments[0]) // "argument1"
}
myFunction("argument1", "argument2")

友情提示

  • arguments 是一个类数组对象,包含了函数触发调用时得到的参数信息
  • myFunction.length 只表示函数的参数数量(本例中为2),无论函数调用时传入多少参数

示例如下

function myFunction(parameter1, parameter2) {
   console.log(myFunction.length) //2
   console.log(arguments[2]) // "3"  这里虽然函数只接受两个参数,但调用时传了多的两个`3,4`在arguments中也取到了
}
console.log(myFunction.length) // 2
myFunction("argument1", "argument2", 3, 4)

创建一个函数,使用指定符号maskCahr屏蔽除最后count为之外的字符

mask("123456789") // "#####6789"

有很多方法解决:

使用 String.prototype.slice() 传入参数-4 获取字符串最后 4 位字符 ,然后用 String.prototype.padStart() 传入字符串长度和 替换符号 把字符串用 指定符号填充至 指定长度.

const mask = (str, maskChar = "#") =>
  str.slice(-4).padStart(str.length, maskChar)

善意之言

  • 如果问题的解决方案是有效的,那么应该首选简短、单行的功能性解决方案

困难

递归recursion 是什么 ? 什么时候用?

递归是一个过程的重复应用。在JavaScript中,递归涉及不断调用自身的函数,直到它们达到一个基本条件。基本条件打破递归循环,否则函数将无限递归循环下去。当处理嵌套若干层的数据时递归非常有用。

例如,有一个数据库返回的一系列评论,它存在于一个平面数组中,但是需要嵌套以在UI中显示。每个注释要么是顶级注释(没有父注释),要么是对父注释的回复。评论可以是回复的回复,回复的回复…我们事先不知道一个评论可能有多少层深度。这就是递归可以提供帮助的地方。

const nest = (items, id = null, link = "parent_id") =>
  items
    .filter(item => item[link] === id)
    .map(item => ({ ...item, children: nest(items, item.id) }))

const comments = [
  { id: 1, parent_id: null, text: "First reply to post." },
  { id: 2, parent_id: 1, text: "First reply to comment #1." },
  { id: 3, parent_id: 1, text: "Second reply to comment #1." },
  { id: 4, parent_id: 3, text: "First reply to comment #3." },
  { id: 5, parent_id: 4, text: "First reply to comment #4." },
  { id: 6, parent_id: null, text: "Second reply to post." }
]

nest(comments)
/*
[
  { id: 1, parent_id: null, text: "First reply to post.", children: [...] },
  { id: 6, parent_id: null, text: "Second reply to post.", children: [] }
]
*/

在上例中,基础条件是filter()返回空数组。链式的map() 不会触发递归调用的回调函数,进而跳出递归循环。

善意提醒:

  • 递归在处理包含未知层数的结构的数据时非常有用
  • 递归必须有一个跳出递归循环避免无限回调的基本条件

JavaScript中唯一不等于自身的值是什么?

当与任何比较运算符比较时,NaN (not -a- number) 是唯一不等于自身的值。NaN通常是没有意义的数学计算的结果,所以两个NaN值被认为相等没有意义。

善意提醒

  • isNaN()Number.isNaN()的区别

                //isNaN
                console.log(isNaN(null));            //false
                console.log(isNaN(true));            //false
                console.log(isNaN(false));           //false
                console.log(isNaN(0));               //false
                console.log(isNaN(undefined));       //true
                console.log(isNaN("AB"));            //true
                console.log(isNaN({a: 1}));          //true
                console.log(isNaN(NaN));             //true
                
                //Number.isNaN
                console.log(Number.isNaN(null));      //false
                console.log(Number.isNaN(true));      //false
                console.log(Number.isNaN(false));     //false
                console.log(Number.isNaN(0));         //false
                console.log(Number.isNaN(undefined)); //false
                console.log(Number.isNaN("AB"));      //false
                console.log(Number.isNaN({a: 1}));    //false
                console.log(Number.isNaN(NaN));       //true
  • const isNaN = x => x !== x

Node.js中的事件循环是什么?

事件循环处理所有异步回调。回调将在循环中排队,而其他代码将运行,并将在收到每个代码的响应后逐个运行。

善意提醒

  • 事件循环允许Node.js执行非阻塞I/O操作,尽管JavaScript是单线程的

如何避免回调地狱?

getData(function(a) {
  getMoreData(a, function(b) {
    getMoreData(b, function(c) {
      getMoreData(c, function(d) {
        getMoreData(d, function(e) {
          // ...
        })
      })
    })
  })
})

重构使用返回promise或者使用 async/await的函数通常是最好的选择。 它们没有为函数提供导致深度嵌套的回调,而是返回一个可以等待的promise,并且在数据到达后将被解析,从而允许以类似于同步的方式计算下一行代码。

以上代码可以被重构成这样:

async function asyncAwaitVersion() {
  const a = await getData()
  const b = await getMoreData(a)
  const c = await getMoreData(b)
  const d = await getMoreData(c)
  const e = await getMoreData(d)
  // ...
}

有许多方式解决回调地狱的问题:

  • 模块化: 把回调分解成独立的函数
  • 使用控制流库,比如async
  • 使用Promise的生成器
  • 使用async/await (>es7)

善意提醒

  • 作为一个高效的JavaScript开发人员,您必须避免不断增长的缩进级别,生成干净且可读的代码,并能够处理复杂的流。

什么是闭包?你能举一个有用的例子吗?

闭包是在另一个函数中定义的函数,即使在其词法作用域之外执行,也可以访问其词法作用域。

闭包可以访问三个范围内的变量:

  • 在它自己的作用域中声明的变量
  • 在父函数范围内声明的变量
  • 在全局范围中声明的变量

JavaScript中,所有函数都是闭包,因为它们可以访问外部范围,但是大多数函数没有利用闭包的有用性:状态的持久性。闭包有时也因此被称为有状态函数
此外,闭包是存储无法从JavaScript外部访问的私有数据的唯一方法。它们是UMD (Universal Module Definition)模式的关键,UMD模式经常用于只公开公共API但保持实现细节私有的库中,以防止与其他库或用户自己的代码发生名称冲突。

友情提醒:

  • 闭包非常有用,因为它们允许您将数据与操作该数据的函数关联起来。
  • 闭包只能用一个方法替换对象。
  • 闭包可以用来模拟私有属性和方法。
MDN的闭包解释,也很清晰

Mr_zhang
395 声望10 粉丝

步步向“前”