前端小智

前端小智 查看完整档案

厦门编辑Plymouth  |  前端 编辑大迁世界  |  Web前端攻城狮 编辑 github.com/qq449245884/xiaozhi 编辑
编辑

我不是什么大牛,我其实想做的就是一个传播者。内容可能过于基础,但对于刚入门的人来说或许是一个窗口,一个解惑之窗。我要先坚持分享20年,大家来一起见证吧。

个人动态

前端小智 发布了文章 · 1月27日

10个 Chrome 开发工具和技巧

作者:FelDev
译者:前端小智
来源:medium
点赞再看,微信搜索大迁世界,B站关注前端小智这个没有大厂背景,但有着一股向上积极心态人。本文 GitHubhttps://github.com/qq44924588... 上已经收录,文章的已分类,也整理了很多我的文档,和教程资料。

最近开源了一个 Vue 组件,还不够完善,欢迎大家来一起完善它,也希望大家能给个 star 支持一下,谢谢各位了。

github 地址:https://github.com/qq44924588...

1. 模拟慢速网络和慢速设备

我们可能习惯了在城市的网速,那是杠杠的,并不意味网速在中国哪个都一样的,在一些偏远地方,网速依然慢的可怜,所以有时候我们所做的产品是需要考虑网速慢的情况的,那怎么模拟呢?

打开谷个浏览器的performance选项卡,然后单击右上角的齿轮图标就可以看到 NewworkCPU的模拟情况。

clipboard.png

2. 颜色选择器

单击表示颜色的小方块,弹出颜色选择器。

clipboard.png

clipboard.png

启用颜色选择器后,可以将网页悬停并使用颜色选择器来获取该像素的颜色。

clipboard.png

clipboard.png

弹出颜色选择器的小方块还有快捷键按住Shift并单击以更改颜色格式。

3. Audits

Audits(审计),这个功能其实一直存在,只不过在chrome 60之后,发生了翻天覆地的变化:引入了Google开源的另外一个项目:LightHouse

Audits主要从5个方面来给网页打分,最终会生成一个report:

4.Pretty Print(显示可读代码)

clipboard.png

我们知道许多网站都对Javascript代码进行了压缩,但这对开发者和学习者来说,读起来很费劲,谷歌提供一个功能给我们,可以更好查看压缩文件。

我们点击下方的大括号{}图标,即可使用Pretty Print功能了

clipboard.png

5.快速文件切换器

如果你知道文件名,则不必打开“Sources”选项卡。只需按cmd/ctrl + p,然后输入你想查找的文件名,接下按下回车就 ok 了。

6. 响应模式

我们在桌面和移动设备上开发网站,通常我们倾向于最初的桌面体验。 但是这与越来越多的用户使用移动设备访问网络的趋势相脱离。 为了提高网站的用户体验,我们需要准确地知道网站在移动设备上的效果。 Chrome 开发者工具包里加入了手机模拟器特性,帮助我们测试:

clipboard.png

对于大多数人而言,大多数时间只需要通过不同的屏幕尺寸和方向查看他们的网站即可。

clipboard.png

大家都说简历没项目写,我就帮大家找了一个项目,还附赠【搭建教程】

我和阿里云合作服务器,折扣价比较便宜:89/年,223/3年,比学生9.9每月还便宜,买了搭建个项目,熟悉技术栈比较香(老用户用家人账号买就好了,我用我妈的)推荐买三年的划算点,点击本条就可以查看。

Icomo

7.屏幕截图

1、F12

2、【ctrl+shift+p】

3、输入“capture”

4、选择以下任意

  1. capture full size screenshot”【整个网页】
  2. capture node screenshot”【节点网页】
  3. “capture screenshot”【当前屏幕】

8. Extensions

我们可以将扩展程序安装到Chrome开发者控制台。 许多框架都有自己的扩展名,以简化其技术(Vue,Angular,React等)的开发。 这是Featured DevTools扩展的列表

9. Coverage

Coverage 是chrome开发者工具的一个新功能,从字面意思上可以知道它是可以用来检测代码在网站运行时有哪些js和css是已经在运行,而哪些js和css是还没有用到的,如图,这是我在打开csdn网页时,所显示的已运行和尚未运行的代码情况。

clipboard.png

如何打开caverage 前提:chrome浏览器的版本必须是59或以上,在ctrl+shift+i快速打开devtools,点击右上角的... More tools 有个Coverage。

那这个新功能有什么作用呢?

如上图所示,最右边显示的是我们加载的css和js文件数量,红色区域表示已运行的代码,而青色表示已加载但未运行的代码。可用来发现页面中尚未用到的js 和 css代码,你可以为用户只提供必要的代码,这样就可以提升页面的性能。这对于找出可以进行拆分的脚本以及延迟加载非关键脚本来说非常有用。

10. 实时跟进新功能

Chrome 的开发工具会不断更新,它会在What's New In DevTools 上发布更新的视频,我们可以时不是去看看,了解一些新出来的功能,这样我们就能实时知道谷歌的一些好用的功能了。


代码部署后可能存在的BUG没法实时知道,事后为了解决这些BUG,花了大量的时间进行log 调试,这边顺便给大家推荐一个好用的BUG监控工具 Fundebug

来源:https://medium.com/better-pro...


交流

文章每周持续更新,可以微信搜索「 大迁世界 」第一时间阅读和催更(比博客早一到两篇哟),本文 GitHub https://github.com/qq449245884/xiaozhi 已经收录,整理了很多我的文档,欢迎Star和完善,大家面试可以参照考点复习,另外关注公众号,后台回复福利,即可看到福利,你懂的。

查看原文

赞 6 收藏 5 评论 0

前端小智 发布了文章 · 1月20日

5个好用的 CSS 函数,快来试试手吧!

作者:Milos Protic
译者:前端小智
来源:devinduct
点赞再看,微信搜索大迁世界,B站关注前端小智这个没有大厂背景,但有着一股向上积极心态人。本文 GitHubhttps://github.com/qq44924588... 上已经收录,文章的已分类,也整理了很多我的文档,和教程资料。

最近开源了一个 Vue 组件,还不够完善,欢迎大家来一起完善它,也希望大家能给个 star 支持一下,谢谢各位了。

github 地址:https://github.com/qq44924588...

简介

CSS 包含了许多函数,而且它能够完成许多早期需要用 JavaScript才能完成的事情。每年都有新的特性被添加进来,这让我们的开发更加轻松,也减少了对JavaScript的依赖。CSS 函数是它所具有的最强大的特性之一,在本文中,我将介绍一些我认为有用的函数。

attr()

attr 函数用于获取所选元素的属性值。 它接受三个参数,属性名称类型默认值

语法: attr( attribute-name <type-or-unit>? [, <fallback> ]? )

事例:

<p data-text="the attr function"
  data-tooltip="Hi from attr!" class="attr">This text is combined with</p>

css

p::after {
  content: ' ' attr(data-text);
}

p.attr:hover::after {
  content: ' ' attr(data-tooltip);
  background-color: orange;
  color: white
}

效果:

图片描述

源码:https://codepen.io/protic_milos/pen/GRpYJKd

calc()

这个函数使我们能够计算CSS值,而不是指定确切的值。通常用于计算元素的大小或位置。它支持加法、减法、乘法和除法。

需要特别注意重要一点+-运算符必须用空格隔开,不然无法正常工作。*/运算符不有这限制,但出于一致性的考虑,建议添加空格。

另外,很棒的是,我们可以混合CSS单位,例如,我们可以减去百分比和像素。

我们可以用calc构建一个带有居中元素的示例:

<p class="calc">Centered with calc</p>

css

p.calc {
  padding: 10px;
  background-color: orange;
  color: white;
  width: 200px;
  text-align:center;
  margin-left: calc(50% - 100px)
}

效果:

clipboard.png

源码:https://codepen.io/protic_milos/pen/GRpYJKd

var()

通过这个函数,我们可以使用一个自定义属性的值作为另一个CSS属性的值。简单地说,我们可以定义一个颜色,例如,将它放在自定义属性(CSS变量)中,然后通过调用var函数重用该属性值。

与CSS变量一起,该函数提高了可维护性并减少了重复。一个用例是为网站创建主题。

此函数接受两个参数,即自定义属性和一个默认值,如果出现问题,将使用它们。

:root {
  --bg-color: green;
  --color: white
}

p.var {
  background-color: var(--bg-color);
  color: var(--color)
}

效果:

clipboard.png

源码:https://codepen.io/protic_milos/pen/GRpYJKd

counter()

就我个人而言,我从未使用过这种方法,但它看起来是很有趣。这个函数返回指定计数器的当前值,需要与 counter-resetcounter-increment 配合使用。

我们可以用它来计算其他元素,比如有序列表。

<div class="counter">
  <span>Mars</span>
  <span>Bounty</span>
  <span>Snickers</span>
</div>

clipboard.png

源码:https://codepen.io/protic_milos/pen/GRpYJKd

circle()

这个函数创建一个圆形区域来屏蔽它所应用的元素。你可以指定它的半径和位置。通常与图像一起使用来创建圆角形状。此函数是clip-path属性值。

另外,值得一提的是,除了圆之外,您还可以创建椭圆和多边形形状。

<img class="circle" 
  data-original="https://devinduct.com/Uploads/PostImages/1122dcb9-954a-4641-9ca6-c38e9472698f.png"
/>

css

img.circle {
  clip-path: circle(30%);
}

clipboard.png

源码:https://codepen.io/protic_milos/pen/GRpYJKd

总结

正如我之前多次提到的,在很多情况下,开发人员都忽视了CSS的可能性,因此失去了web站点的简单性。每一年我们都可以依靠CSS为我们提供所需的设计能力,这很好,JavaScript 应该把注意力放在其他事情上,而不是设计上。


代码部署后可能存在的BUG没法实时知道,事后为了解决这些BUG,花了大量的时间进行log 调试,这边顺便给大家推荐一个好用的BUG监控工具 Fundebug

原文:https://devinduct.com/63/5-us...

交流

文章每周持续更新,可以微信搜索「 大迁世界 」第一时间阅读和催更(比博客早一到两篇哟),本文 GitHub https://github.com/qq449245884/xiaozhi 已经收录,整理了很多我的文档,欢迎Star和完善,大家面试可以参照考点复习,另外关注公众号,后台回复福利,即可看到福利,你懂的。

查看原文

赞 12 收藏 6 评论 1

前端小智 发布了文章 · 1月18日

快速优化 Web 性能的10 个手段

作者:Marc
译者:前端小智
来源:dev

本人已经过原作者制授权翻译。

点赞再看,微信搜索大迁世界,B站关注前端小智这个没有大厂背景,但有着一股向上积极心态人。本文 GitHubhttps://github.com/qq44924588... 上已经收录,文章的已分类,也整理了很多我的文档,和教程资料。

最近开源了一个 Vue 组件,还不够完善,欢迎大家来一起完善它,也希望大家能给个 star 支持一下,谢谢各位了。

github 地址:https://github.com/qq44924588...

在这篇文章中,主要介绍10种快速提高网站性能的方法,你只需5分钟内就可以将它应用到你的网站上,废话不多说,让我们进入正题吧 💎。

1. 文件压缩

文件压缩,可以减少网络传输的字节数。有几种压缩算法。Gzip是最流行的,但是对于Brotli,你可以使用一种更新的、甚至更好的压缩算法。如果想检查您的服务器是否支持Brotli,可以使用 Brotli.pro

如果你的服务器不支持Brotli,则可以按照以下简单指南进行安装 👍:

2. 图像压缩

未压缩的图像是一个巨大的潜在性能瓶颈。如果在将图像提供给用户之前没有压缩它们,那么就会传输不必要的字节。有几个有用的工具可以用于快速压缩图像且不损失可见质量。我主要使用Imagemin。它支持许多图像格式,您w你以将其用作命令行界面或npm模块。

imagemin img/* --out-dir=dist/images

你还可以 将npm 引入到项目里,使用imagemin-mozjpeg,可以将JPEG图像压缩到原有的60%

const imagemin = require('imagemin');
const imageminMozjpeg = require('imagemin-mozjpeg');

(async() => {
  const files = await imagemin(
      ['img/*.jpg'],
      {
        destination: 'dist/img',
        plugins: [
          imageminMozjpeg({quality: 60}),
        ]
      }
  );
  console.log(files);
})();

就我而言,它将文件大小从4MB减小到100kB:

clipboard.png

3.图像格式

使用现代图像格式可以真正提高性能。 WebP 图像比 JPEG 和 PNG 小,通常小25%-35%WebP 也被浏览器广泛支持。

我们使用imagemin npm 包并为其添加WebP插件。 以下代码将我的图像的WebP版本输出到dist文件夹中。

const imagemin = require('imagemin');
const imageminWebp = require('imagemin-webp');

(async() => {
  const files = await imagemin(
      ['img/*.jpg'],
      {
        destination: 'dist/img',
        plugins: [
          imageminWebp({quality: 50})
        ]
      }
  );
  console.log(files);
})();

我们再次看一下文件大小:

clipboard.png

结果表明,与原始图像相比,文件大小减少了98%,与压缩的 JPG 文件相比,WebP 对图像的压缩效果更加明显,WebP版本比压缩的JPEG版本小43%

4.图像延迟加载

延迟加载图像是一种稍后而不是提前加载屏幕外图像的技术。当解析器遇到正确加载的图像时,会减慢初始页面加载速度。通过延迟加载,可以加快这个过程并在以后加载图像。使用lazysize很容易做到这一点。使用lazysize脚本和浏览器对loading属性的支持,你可以这样优化:

![](image.jpg)

改成:

![](image.jpg)

该库会处理其余的工作,你可以使用浏览器验证这一点。打开你的网站,找到你的图像标签。如果类从lazyload更改为lazyloaded,它就可以工作。

5.缓存 http 头

缓存是一种快速提高站点速度的方法。它减少了访问者的页面加载时间。我们可以告诉浏览器在特定的时间缓存文件,如果你对后台的知识有些了解,那么配置缓存方不是很难的事情。

我们可以使用以下 API 进行缓存:

6. 内联关键的 CSS

CSS 是阻塞渲染的,这意味着浏览器必须先下载并处理所有CSS文件,然后才能绘制像素。 通过内联关键的 CSS,可以大大加快此过程。 我们可以通过以下步骤完成此操作:

识别关键的 CSS

如果你不知道你的关键CSS是什么,你可以使用Critcal, CriticalCSSPenthouse。所有这些库都从给定视口可见的HTML文件中提取CSS。

criticalCSS 事例如下:

var criticalcss = require("criticalcss");

var request = require('request');
var path = require( 'path' );
var criticalcss = require("criticalcss");
var fs = require('fs');
var tmpDir = require('os').tmpdir();

var cssUrl = 'https://web.dev/app.css';
var cssPath = path.join( tmpDir, 'app.css' );
request(cssUrl).pipe(fs.createWriteStream(cssPath)).on('close', function() {
  criticalcss.getRules(cssPath, function(err, output) {
    if (err) {
      throw new Error(err);
    } else {
      criticalcss.findCritical("https://web.dev/", { rules: JSON.parse(output) }, function(err, output) {
        if (err) {
          throw new Error(err);
        } else {
          console.log(output);
          // save this to a file for step 2
        }
      });
    }
  });
});

内联关键 CSS

HTML解析器遇到样式标签,并立即处理关键的CSS。

<head>
  <style>
  body {...}
  /* ... rest of the critical CSS */
  </style>
</head>

滞后非关键CSS

非关键的CSS不需要立即进行处理。 浏览器可以在onload事件之后加载它,因此用户不必等待。

<link rel="preload" href="/assets/styles.css" as="style" onload="this.onload=null;this.rel='stylesheet'">
<noscript><link rel="stylesheet" href="/assets/styles.css"></noscript>

7. JavaScript 异步/延迟加载/延迟加载

HTML 也是阻塞渲染,浏览器必须等待 JS 执行后才能完成对HTML的解析。但是我们可以告诉浏览器等待JavaScript执行。

异步加载JavaScript

使用属性async,可以告诉浏览器异步加载脚本。

<script data-original="app.js" async></script>

延迟JavaScript

defer属性告诉浏览器在 HTML 解析器解析完文档之后运行脚本,但在事件发生之前,DOMContentLoaded会被触发。

<script data-original="app.js" defer></script>

重复排序内联的脚本

内联脚本立即执行,浏览器对其进行解析。 因此,您可以将它们放在HTML的末尾,紧接在body标记之前。

8.使用资源提示优化性能

HTML5的资源提示(Resource Hints)可以简单地理解为预加载,浏览器根据开发者提供的后续资源的提示进行有选择性的加载和优化。“有选择性”这一项是必须的且极其重要的,也是有别早先替代方案的重点,因为很多情况下,预加载会受到所分配到的计算资源以及带宽资源的限制,浏览器有权放弃那些成本较高的加载项。

资源提示帮助开发人员告诉浏览器稍后可能加载的页面。该规范定义了四种原语

  • preconnect
  • dns-prefetch
  • prefetch
  • prerender

此外,对于资源提示,我们使用了链接属性的preload关键字。

preconnect

预链接, 使用方法如下:

 <link rel="preconnect" href="https://example.com">

我们访问一个站点时,简单来说,都会经过以下的步骤:

  1. DNS 解析
  2. TCP 握手
  3. 如果为 Https 站点,会进行TLS握手

使用preconnect后,浏览器会针对特定的域名,提前初始化链接(执行上述三个步骤),节省了我们访问第三方资源的耗时。需要注意的是,我们一定要确保preconnect的站点是网页必需的,否则会浪费浏览器、网络资源。

DNS Prefetch

DNS 预解析, 这个大多数人都知道,用法也很简单:

 <link rel="dns-prefetch" href="http://example.com">

DN S解析,简单来说就是把域名转化为ip地址。我们在网页里使用域名请求其他资源的时候,都会先被转化为ip地址,再发起链接。dns-prefeth使得转化工作提前进行了,缩短了请求资源的耗时。

什么时候使用呢?当我们页面中使用了其他域名的资源时,比如我们的静态资源都放在cdn上,那么我们可以对cdn的域名进行预解析。浏览器的支持情况也不错。

prefetch

预拉取, 使用方法如下:

<link rel="prefetch" href="index.html" as="document">
<link rel="prefetch" href="main.js" as="script">
<link rel="prefetch" href="main.css" as="style">
<link rel="prefetch" href="font.woff" as="font">
<link rel="prefetch" href="image.webp" as="image">

link标签里的as参数可以有以下取值:

audio: 音频文件
video: 视频文件  
Track: 网络视频文本轨道 
script: javascript文件
style: css样式文件
font: 字体文件   
image: 图片   
fetch: XHR、Fetch请求
worker: Web workers
embed: 多媒体<embed>请求 
object:  多媒体<object>请求
document: 网页

预拉取用于标识从当前网站跳转到下一个网站可能需要的资源,以及本网站应该获取的资源。这样可以在将来浏览器请求资源时提供更快的响应。

如果正确使用了预拉取,那么用户在从当前页面前往下一个页面时,可以很快得到响应。但是如果错误地使用了预拉取,那么浏览器就会下载额外不需要的资源,影响页面性能,并且造成网络资源浪费。

这里需要注意的是,使用了prefetch,资源仅仅被提前下载,下载后不会有任何操作,比如解析资源。

prerender

预渲染,使用方法如下:

<link rel="prerender" href="//example.com/next-page.html">

prerender比prefetch更进一步。不仅仅会下载对应的资源,还会对资源进行解析。解析过程中,如果需要其他的资源,可能会直接下载这些资源。这样,用户在从当前页面跳转到目标页面时,浏览器可以更快的响应。

preload

<link rel="preload" href="..." as="...">
<link rel="prefetch" href="...">

注意preload需要写上正确的as属性,才能正常工作喔(prefetch不需要)。但是它们有什么区别呢?

  • preload 是用于预加载当前页的资源,浏览器会优先加载它们
  • prefetch 是用于预加载后续导航使用的资源,浏览器也会加载它们,但优先级不高

9. 固定好你的谷歌字体

Google字体很棒,它们提供优质的服务,并被广泛使用。 如果你不想自己托管字体,那么Google字体是一个不错的选择。 你需要的是学习如何引用它们,哈里·罗伯茨(Harry Roberts)写了一篇有关《The Fastest Google Fonts》的出色深度文章。 强烈建议你阅读它。

如果你快速取用,那么可以使用下面集成片段的谷歌字体:

<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin/>
<link rel="preload" as="style" href="https://fonts.googleapis.com/css2?family=...&display=swap"/>
<link rel="stylesheet" href="https://fonts.googleapis.com/css2?family=...&display=swap" media="print" onload="this.media='all'"/>
<noscript><link rel="stylesheet" href="https://fonts.googleapis.com/css2?family=...&display=swap"/></noscript>

10. 使用 service worker 缓存资源

service worker是浏览器在后台运行的脚本。缓存可能是最常用的特性,也是你应该使用的特性。我认为这不是一个选择的问题。通过使用 service worker实现缓存,可以使 用户 与站点的交互更快,并且即使用户不在线也可以访问站点。

总结

在这篇文章中,展示了 10 种快速的网络性能,你可以在5分钟内应用到你的网站,以提高你的网站速度。

感谢大家的观看与支持,我们下期再见,不要忘了三连哦。


代码部署后可能存在的BUG没法实时知道,事后为了解决这些BUG,花了大量的时间进行log 调试,这边顺便给大家推荐一个好用的BUG监控工具 Fundebug

原文:https://dev.to/marcradziwill/...

交流

文章每周持续更新,可以微信搜索「 大迁世界 」第一时间阅读和催更(比博客早一到两篇哟),本文 GitHub https://github.com/qq449245884/xiaozhi 已经收录,整理了很多我的文档,欢迎Star和完善,大家面试可以参照考点复习,另外关注公众号,后台回复福利,即可看到福利,你懂的。

查看原文

赞 12 收藏 9 评论 0

前端小智 发布了文章 · 1月11日

JavaScript Promise 完整指南

作者:Adrian Mejia
译者:前端小智
来源:adrianmjia
点赞再看,微信搜索大迁世界,B站关注【前端小智】这个没有大厂背景,但有着一股向上积极心态人。本文 GitHubhttps://github.com/qq44924588... 上已经收录,文章的已分类,也整理了很多我的文档,和教程资料。

最近开源了一个 Vue 组件,还不够完善,欢迎大家来一起完善它,也希望大家能给个 star 支持一下,谢谢各位了。

github 地址:https://github.com/qq44924588...

这篇文章算是 JavaScript Promises 比较全面的教程,该文介绍了必要的方法,例如 thencatchfinally。 此外,还包括处理更复杂的情况,例如与Promise.all并行执行Promise,通过Promise.race 来处理请求超时的情况,Promise 链以及一些最佳实践和常见的陷阱。

1.JavaScript Promises

Promise 是一个允许我们处理异步操作的对象,它是 es5 早期回调的替代方法。

与回调相比,Promise 具有许多优点,例如:

  • 让异步代码更易于阅读。
  • 提供组合错误处理。

* 更好的流程控制,可以让异步并行或串行执行。

回调更容易形成深度嵌套的结构(也称为回调地狱)。 如下所示:

a(() => {
  b(() => {
    c(() => {
      d(() => {
        // and so on ...
      });
    });
  });
});

如果将这些函数转换为 Promise,则可以将它们链接起来以生成更可维护的代码。 像这样:

Promise.resolve()
  .then(a)
  .then(b)
  .then(c)
  .then(d)
  .catch(console.error);

在上面的示例中,Promise 对象公开了.then.catch方法,我们稍后将探讨这些方法。

1.1 如何将现有的回调 API 转换为 Promise?

我们可以使用 Promise 构造函数将回调转换为 Promise。

Promise 构造函数接受一个回调,带有两个参数resolvereject

  • Resolve:是在异步操作完成时应调用的回调。
  • Reject:是发生错误时要调用的回调函数。

构造函数立即返回一个对象,即 Promise 实例。 当在 promise 实例中使用.then方法时,可以在Promise “完成” 时得到通知。 让我们来看一个例子。

Promise 仅仅只是回调?

并不是。承诺不仅仅是回调,但它们确实对.then.catch方法使用了异步回调。 Promise 是回调之上的抽象,我们可以链接多个异步操作并更优雅地处理错误。来看看它的实际效果。

Promise 反面模式(Promises 地狱)

a(() => {
  b(() => {
    c(() => {
      d(() => {
        // and so on ...
      });
    });
  });
});

不要将上面的回调转成下面的 Promise 形式:

a().then(() => {
  return b().then(() => {
    return c().then(() => {
      return d().then(() =>{
        // ⚠️ Please never ever do to this! ⚠️
      });
    });
  });
});

上面的转成,也形成了 Promise 地狱,千万不要这么转。相反,下面这样做会好点:

a()
  .then(b)
  .then(c)
  .then(d)

超时

你认为以下程序的输出的是什么?

const promise = new Promise((resolve, reject) => {
  setTimeout(() => {
    resolve('time is up ⏰');
  }, 1e3);

  setTimeout(() => {
    reject('Oops 🔥');
  }, 2e3);
});

promise
  .then(console.log)
  .catch(console.error);

是输出:

time is up ⏰
Oops! 🔥

还是输出:

time is up ⏰

是后者,因为当一个Promise resolved 后,它就不能再被rejected

一旦你调用一种方法(resolvereject),另一种方法就会失效,因为 promise 处于稳定状态。 让我们探索一个 promise 的所有不同状态。

1.2 Promise 状态

Promise 可以分为四个状态:

  • ⏳ Pending:初始状态,异步操作仍在进行中。
  • ✅ Fulfilled:操作成功,它调用.then回调,例如.then(onSuccess)
  • ⛔️ Rejected: 操作失败,它调用.catch.then的第二个参数(如果有)。 例如.catch(onError).then(..., onError)
  • 😵 Settled:这是 promise 的最终状态。promise 已经死亡了,没有别的办法可以解决或拒绝了。 .finally方法被调用。

clipboard.png

大家都说简历没项目写,我就帮大家找了一个项目,还附赠【搭建教程】

1.3 Promise 实例方法

Promise API 公开了三个主要方法:thencatchfinally。 我们逐一配合事例探讨一下。

Promise then

then方法可以让异步操作成功或失败时得到通知。 它包含两个参数,一个用于成功执行,另一个则在发生错误时使用。

promise.then(onSuccess, onError);

你还可以使用catch来处理错误:

promise.then(onSuccess).catch(onError);

Promise 链

then 返回一个新的 Promise ,这样就可以将多个Promise 链接在一起。就像下面的例子一样:

Promise.resolve()
  .then(() => console.log('then#1'))
  .then(() => console.log('then#2'))
  .then(() => console.log('then#3'));

Promise.resolve立即将Promise 视为成功。 因此,以下所有内容都将被调用。 输出将是

then#1
then#2
then#3

Promise catch

Promise .catch方法将函数作为参数处理错误。 如果没有出错,则永远不会调用catch方法。

假设我们有以下承诺:1秒后解析或拒绝并打印出它们的字母。

const a = () => new Promise((resolve) => setTimeout(() => { console.log('a'), resolve() }, 1e3));
const b = () => new Promise((resolve) => setTimeout(() => { console.log('b'), resolve() }, 1e3));
const c = () => new Promise((resolve, reject) => setTimeout(() => { console.log('c'), reject('Oops!') }, 1e3));
const d = () => new Promise((resolve) => setTimeout(() => { console.log('d'), resolve() }, 1e3));

请注意,c使用reject('Oops!')模拟了拒绝。

Promise.resolve()
  .then(a)
  .then(b)
  .then(c)
  .then(d)
  .catch(console.error)

输出如下:

图片描述

在这种情况下,可以看到abc上的错误消息。

我们可以使用then函数的第二个参数来处理错误。 但是,请注意,catch将不再执行。

Promise.resolve()
  .then(a)
  .then(b)
  .then(c)
  .then(d, () => console.log('c errored out but no big deal'))
  .catch(console.error)

图片描述

由于我们正在处理 .then(..., onError)部分的错误,因此未调用catchd不会被调用。 如果要忽略错误并继续执行Promise链,可以在c上添加一个catch。 像这样:

Promise.resolve()
  .then(a)
  .then(b)
  .then(() => c().catch(() => console.log('error ignored')))
  .then(d)
  .catch(console.error)

图片描述

当然,这种过早的捕获错误是不太好的,因为容易在调试过程中忽略一些潜在的问题。

Promise finally

finally方法只在 Promise 状态是 settled 时才会调用。

如果你希望一段代码即使出现错误始终都需要执行,那么可以在.catch之后使用.then

Promise.resolve()
  .then(a)
  .then(b)
  .then(c)
  .then(d)
  .catch(console.error)
  .then(() => console.log('always called'));

或者可以使用.finally关键字:

Promise.resolve()
  .then(a)
  .then(b)
  .then(c)
  .then(d)
  .catch(console.error)
  .finally(() => console.log('always called'));

1.4 Promise 类方法

我们可以直接使用 Promise 对象中四种静态方法。

  • Promise.all
  • Promise.reject
  • Promise.resolve
  • Promise.race

Promise.resolve 和 Promise.reject

这两个是帮助函数,可以让 Promise 立即解决或拒绝。可以传递一个参数,作为下次 .then 的接收:

Promise.resolve('Yay!!!')
  .then(console.log)
  .catch(console.error)

上面会输出 Yay!!!

Promise.reject('Oops 🔥')
  .then(console.log)
  .catch(console.error)

使用 Promise.all 并行执行多个 Promise

通常,Promise 是一个接一个地依次执行的,但是你也可以并行使用它们。

假设是从两个不同的api中轮询数据。如果它们不相关,我们可以使用Promise.all()同时触发这两个请求。

在此示例中,主要功能是将美元转换为欧元,我们有两个独立的 API 调用。 一种用于BTC/USD,另一种用于获得EUR/USD。 如你所料,两个 API 调用都可以并行调用。 但是,我们需要一种方法来知道何时同时完成最终价格的计算。 我们可以使用Promise.all,它通常在启动多个异步任务并发运行并为其结果创建承诺之后使用,以便人们可以等待所有任务完成。

const axios = require('axios');

const bitcoinPromise = axios.get('https://api.coinpaprika.com/v1/coins/btc-bitcoin/markets');
const dollarPromise = axios.get('https://api.exchangeratesapi.io/latest?base=USD');
const currency = 'EUR';

// Get the price of bitcoins on
Promise.all([bitcoinPromise, dollarPromise])
  .then(([bitcoinMarkets, dollarExchanges]) => {
    const byCoinbaseBtc = d => d.exchange_id === 'coinbase-pro' && d.pair === 'BTC/USD';
    const coinbaseBtc = bitcoinMarkets.data.find(byCoinbaseBtc)
    const coinbaseBtcInUsd = coinbaseBtc.quotes.USD.price;
    const rate = dollarExchanges.data.rates[currency];
    return rate * coinbaseBtcInUsd;
  })
  .then(price => console.log(`The Bitcoin in ${currency} is ${price.toLocaleString()}`))
  .catch(console.log);

如你所见,Promise.all接受了一系列的 Promises。 当两个请求的请求都完成后,我们就可以计算价格了。

我们再举一个例子:

const a = () => new Promise((resolve) => setTimeout(() => resolve('a'), 2000));
const b = () => new Promise((resolve) => setTimeout(() => resolve('b'), 1000));
const c = () => new Promise((resolve) => setTimeout(() => resolve('c'), 1000));
const d = () => new Promise((resolve) => setTimeout(() => resolve('d'), 1000));

console.time('promise.all');
Promise.all([a(), b(), c(), d()])
  .then(results => console.log(`Done! ${results}`))
  .catch(console.error)
  .finally(() => console.timeEnd('promise.all'));

解决这些 Promise 要花多长时间? 5秒? 1秒? 还是2秒?

这个留给你们自己验证咯。

Promise race

Promise.race(iterable) 方法返回一个 promise,一旦迭代器中的某个promise解决或拒绝,返回的 promise就会解决或拒绝。

const a = () => new Promise((resolve) => setTimeout(() => resolve('a'), 2000));
const b = () => new Promise((resolve) => setTimeout(() => resolve('b'), 1000));
const c = () => new Promise((resolve) => setTimeout(() => resolve('c'), 1000));
const d = () => new Promise((resolve) => setTimeout(() => resolve('d'), 1000));

console.time('promise.race');
Promise.race([a(), b(), c(), d()])
  .then(results => console.log(`Done! ${results}`))
  .catch(console.error)
  .finally(() => console.timeEnd('promise.race'));

输出是什么?

输出 b。使用 Promise.race,最先执行完成就会结果最后的返回结果。

你可能会问:Promise.race的用途是什么?

我没胡经常使用它。但是,在某些情况下,它可以派上用场,比如计时请求或批量处理请求数组。

Promise.race([
  fetch('http://slowwly.robertomurray.co.uk/delay/3000/url/https://api.jsonbin.io/b/5d1fb4dd138da811182c69af'),
  new Promise((resolve, reject) => setTimeout(() => reject(new Error('request timeout')), 1000))
])
.then(console.log)
.catch(console.error);

图片描述

如果请求足够快,那么就会得到请求的结果。

图片描述

大家都说简历没项目写,我就帮大家找了一个项目,还附赠【搭建教程】

1.5 Promise 常见问题

串行执行 promise 并传递参数

这次,我们将对Node的fs使用promises API,并将两个文件连接起来:

const fs = require('fs').promises; // requires node v8+

fs.readFile('file.txt', 'utf8')
  .then(content1 => fs.writeFile('output.txt', content1))
  .then(() => fs.readFile('file2.txt', 'utf8'))
  .then(content2 => fs.writeFile('output.txt', content2, { flag: 'a+' }))
  .catch(error => console.log(error));

在此示例中,我们读取文件1并将其写入output 文件。 稍后,我们读取文件2并将其再次附加到output文件。 如你所见,writeFile promise返回文件的内容,你可以在下一个then子句中使用它。

如何链接多个条件承诺?

你可能想要跳过 Promise 链上的特定步骤。有两种方法可以做到这一点。

const a = () => new Promise((resolve) => setTimeout(() => { console.log('a'), resolve() }, 1e3));
const b = () => new Promise((resolve) => setTimeout(() => { console.log('b'), resolve() }, 2e3));
const c = () => new Promise((resolve) => setTimeout(() => { console.log('c'), resolve() }, 3e3));
const d = () => new Promise((resolve) => setTimeout(() => { console.log('d'), resolve() }, 4e3));

const shouldExecA = true;
const shouldExecB = false;
const shouldExecC = false;
const shouldExecD = true;

Promise.resolve()
  .then(() => shouldExecA && a())
  .then(() => shouldExecB && b())
  .then(() => shouldExecC && c())
  .then(() => shouldExecD && d())
  .then(() => console.log('done'))

如果你运行该代码示例,你会注意到只有ad被按预期执行。

另一种方法是创建一个链,然后仅在以下情况下添加它们:

const chain = Promise.resolve();

if (shouldExecA) chain = chain.then(a);
if (shouldExecB) chain = chain.then(b);
if (shouldExecC) chain = chain.then(c);
if (shouldExecD) chain = chain.then(d);

chain
  .then(() => console.log('done'));

如何限制并行 Promise?

要做到这一点,我们需要以某种方式限制Promise.all

假设你有许多并发请求要执行。 如果使用 Promise.all 是不好的(特别是在API受到速率限制时)。 因此,我们需要一个方法来限制 Promise 个数, 我们称其为promiseAllThrottled

// simulate 10 async tasks that takes 5 seconds to complete.
const requests = Array(10)
  .fill()
  .map((_, i) => () => new Promise((resolve => setTimeout(() => { console.log(`exec'ing task #${i}`), resolve(`task #${i}`); }, 5000))));

promiseAllThrottled(requests, { concurrency: 3 })
  .then(console.log)
  .catch(error => console.error('Oops something went wrong', error));

输出应该是这样的:

图片描述

以上代码将并发限制为并行执行的3个任务。

实现promiseAllThrottled 一种方法是使用Promise.race来限制给定时间的活动任务数量。

/**
 * Similar to Promise.all but a concurrency limit
 *
 * @param {Array} iterable Array of functions that returns a promise
 * @param {Object} concurrency max number of parallel promises running
 */
function promiseAllThrottled(iterable, { concurrency = 3 } = {}) {
  const promises = [];

  function enqueue(current = 0, queue = []) {
    // return if done
    if (current === iterable.length) { return Promise.resolve(); }
    // take one promise from collection
    const promise = iterable[current];
    const activatedPromise = promise();
    // add promise to the final result array
    promises.push(activatedPromise);
    // add current activated promise to queue and remove it when done
    const autoRemovePromise = activatedPromise.then(() => {
      // remove promise from the queue when done
      return queue.splice(queue.indexOf(autoRemovePromise), 1);
    });
    // add promise to the queue
    queue.push(autoRemovePromise);

    // if queue length >= concurrency, wait for one promise to finish before adding more.
    const readyForMore = queue.length < concurrency ? Promise.resolve() : Promise.race(queue);
    return readyForMore.then(() => enqueue(current + 1, queue));
  }

  return enqueue()
    .then(() => Promise.all(promises));
}

promiseAllThrottled一对一地处理 Promises 。 它执行Promises并将其添加到队列中。 如果队列小于并发限制,它将继续添加到队列中。 达到限制后,我们使用Promise.race等待一个承诺完成,因此可以将其替换为新的承诺。 这里的技巧是,promise 自动完成后会自动从队列中删除。 另外,我们使用 race 来检测promise 何时完成,并添加新的 promise 。

人才们的 【三连】 就是小智不断分享的最大动力,如果本篇博客有任何错误和建议,欢迎人才们留言,最后,谢谢大家的观看。


代码部署后可能存在的BUG没法实时知道,事后为了解决这些BUG,花了大量的时间进行log 调试,这边顺便给大家推荐一个好用的BUG监控工具 Fundebug

原文:https://adrianmejia.com/promi...

交流

文章每周持续更新,可以微信搜索 【大迁世界 】 第一时间阅读,回复 【福利】 有多份前端视频等着你,本文 GitHub https://github.com/qq449245884/xiaozhi 已经收录,欢迎Star。

查看原文

赞 18 收藏 12 评论 0

前端小智 发布了文章 · 1月8日

CSS中的混合模式,制作高级特效的必备技巧

作者: Ahmad Shaeed
译者:前端小智
来源:css-tricks
点赞再看,微信搜索大迁世界,B站关注【前端小智】这个没有大厂背景,但有着一股向上积极心态人。本文 GitHubhttps://github.com/qq44924588... 上已经收录,文章的已分类,也整理了很多我的文档,和教程资料。

最近开源了一个 Vue 组件,还不够完善,欢迎大家来一起完善它,也希望大家能给个 star 支持一下,谢谢各位了。

github 地址:https://github.com/qq44924588...

什么是混合?

根据维基百科:

数字图像编辑和计算机图形中的混合模式(或混合模式)用于确定两个图层如何相互混合。在大多数应用程序中,默认的混合模式只是通过用顶层的内容覆盖底层来隐藏底层。

在CSS中,有两个属性负责混合。 mix-blend-mode用于混合DOM元素,background-blend-mode用于组合多个CSS背景。

进入mix-Blend-Mode

基础范例

clipboard.png

我们以一个基本的例子来看一下它是如何工作的。 我的标题上方有一个圆圈。 我要做的是将文本与圆混合。

HTML

<div class="circle"></div>
<p>Blend Me</p>

CSS

为文本元素添加了mix-blend-mode: overlay,从而将其与圆混合。

事例源码:https://codepen.io/shadeed/pe...

带文字的图片

我认为这是一个广泛使用的混合模式。文字在上面,图片在下面。

clipboard.png

在标题中添加了以下内容:

.hero-title {
    color: #000;
    mix-blend-mode: overlay;
}

不仅仅是改变混合模式。 例如,我们可以通过创建动画来提高创意。

图片描述

在此示例中,我想探讨文本如何与树叶背景融合。 由于图像中包含暗点和亮点,因此在使文本看起来像在每片叶子下移动一样,这将起到非常有用的作用。

clipboard.png

事例源码:https://codepen.io/shadeed/pe...

带有SVG图形的文本

个有趣的效果是在带有矢量和形状的背景上有一个标题。 当形状的颜色不同时,它会变得更加有趣。

clipboard.png

我们能用这些斑点形状做什么?我使用MorphSVG插件改变每个博客形状的路径。这产生了一个有趣的结果。

图片描述

事例源码:https://codepen.io/shadeed/pe...

混合真实元素

clipboard.png

吸引我眼球的效果是当元素有白色背景和黑色前景使用mix-blend-mode: screen

大家都说简历没项目写,我就帮大家找了一个项目,还附赠【搭建教程】

放大镜类

我使用了SVG,并对其应用了以下内容。 注意使用屏幕时黑色区域如何变为透明。

clipboard.png

事例源码:https://codepen.io/shadeed/pe...

视频封面

对我来说,这是一个非常有用的用例。 我经常需要添加播放图标以指示文章中有视频,因此我最终使用了从中心透明的SVG。

clipboard.png

这听起来似乎正确,但有一定局限性。 如果要添加悬停效果以填充三角形怎么办? 由于在SVG中减去了形状,因此这是不可能的。 一种解决方法是在SVG后面放置一个圆圈,并在悬停时对其进行着色。

clipboard.png

对我来说,这还不够。我也想反过来,三角形必须是白色的,其余的是蓝色的。

多亏了混合模式,我可以通过在悬停时控制嵌入式SVG快速实现改效果。

.article__play {
    mix-blend-mode: screen;  
}

.article:hover .article__play {
    mix-blend-mode: normal;
}

.article:hover .article__play {
    .play__base {
      fill: #005FFF;
    }
    
    .play__icon {
      fill: #fff;
    }
}

clipboard.png

事例源码:https://codepen.io/shadeed/pe...

储值卡

另一种情况是使用裁切图像并将其与其下方的背景融合,结果非常有趣。

img {
    position: absolute;
    right: -15px;
    top: 0;
    width: 110px;
    mix-blend-mode: screen;
}

这个想法是把图片放在右边,左边有标题和描述。

clipboard.png

同样,通过为每张卡添加多个背景可以更好:

clipboard.png

事例源码:https://codepen.io/shadeed/pe...

大家都说简历没项目写,我就帮大家找了一个项目,还附赠【搭建教程】

从徽标背景中删除白色

我在Photoshop的早期就知道这个技巧。有时,我需要一个品牌的标志,它是很难得到一个透明的PNG版本。使用混合模式,这很容易解决。

我模拟了Facebook和Amazon徽标,并在每个徽标下添加了白色背景。

clipboard.png

现在来解决这个问题,添加了以下CSS:

img {
    mix-blend-mode: multiply;
    filter: contrast(2);
}

注意,我添加了filter: contrast(2)来增加徽标的对比度。应用混合效果使他们比原来的颜色深一点。

clipboard.png

问题已解决! 当然,我不建议使用此功能。 但是,如果我被迫这么做,我将使用它来节省时间,当原始徽标到达时,我可以替换它并消除混合效果。

事例源码:https://codepen.io/shadeed/pe...

Isolation

isolation CSS属性定义该元素是否必须创建一个新的层叠上下文(stacking context)。

该属性的主要作用是当和background-blend-mode属性一起使用时,可以只混合一个指定元素栈的背景:它允许使一组元素从它们后面的背景中独立出来,只混合这组元素的背景。

html

<div>
  <span>CSS is Awesome</span>
</div>

css

div {
  isolation: isolate; /* Creates a new stacking context */
}

span {
    mix-blend-mode: difference;
}

clipboard.png

如你所见,文本“ CSS很棒”仅在其父代的边界内融合。 外面的东西不会混在一起。 换句话说,它是孤立的。

事例源码:https://codepen.io/shadeed/pe...

isolation 可以通过使用创建新的堆栈上下文的属性来实现。例如,如果父元素具有opacity 属性,这将影响结果。

html

<div>
  <img data-original="cake.jpg" alt="">
</div>

css

div {
  opacity: 0.99; /* Creates a new stacking context, which result to an isolated group */
}

img {
  mix-blend-mode: difference;
}

图片描述

事例源码:https://codepen.io/shadeed/pe...

进入Background-Blend-Mode

它的工作方式类似mix-blend-mode,但具有多个背景图像。 每个背景可以有自己的混合模式,举个例子。

clipboard.png

在此示例中,将三层混合在一起:基础图像,实心填充(Solid Fill)和渐变填充(radient Fill.)。

.elem {
    background: linear-gradient(45deg, #000 10%, transparent), 
              linear-gradient(#3754C7, #3754C7), 
              url(nature.jpg);
  background-size: cover;
  background-blend-mode: overlay, color;
}

在CSS中,应该以正确的方式对每个背景进行排序。 堆叠顺序从上到下,如上图所示。

clipboard.png

事例源码:https://codepen.io/shadeed/pe...

着色图像

通过使用径向梯度,有一些有趣的结果比有用。这个想法是添加一个彩色的图像,使它与它混合。

:root {
  --color: #000;
  --size: 250px; /* Gradient Size */
}

.elem-1 {
  background: radial-gradient(circle var(--size) at center, transparent, var(--color)), 
              url(nature.jpg);
}

clipboard.png

通过对元素应用background-blend-mode: color,结果是图像的去饱和版本。

clipboard.png

事例源码:https://codepen.io/shadeed/pe...

浏览器支持

clipboard.png


原文:https://css-tricks.com/basics...

代码部署后可能存在的BUG没法实时知道,事后为了解决这些BUG,花了大量的时间进行log 调试,这边顺便给大家推荐一个好用的BUG监控工具 Fundebug


交流

文章每周持续更新,可以微信搜索「 大迁世界 」第一时间阅读和催更(比博客早一到两篇哟),本文 GitHub https://github.com/qq449245884/xiaozhi 已经收录,整理了很多我的文档,欢迎Star和完善,大家面试可以参照考点复习,另外关注公众号,后台回复福利,即可看到福利,你懂的。

查看原文

赞 24 收藏 21 评论 3

前端小智 发布了文章 · 2020-12-28

使用 CSS Viewport 单位快速布局!

作者:Ahmad shaded
译者:前端小智
来源:sitepoint
点赞再看,微信搜索大迁世界,B站关注前端小智这个没有大厂背景,但有着一股向上积极心态人。本文 GitHubhttps://github.com/qq44924588... 上已经收录,文章的已分类,也整理了很多我的文档,和教程资料。

最近开源了一个 Vue 组件,还不够完善,欢迎大家来一起完善它,也希望大家能给个 star 支持一下,谢谢各位了。

github 地址:https://github.com/qq44924588...

CSS Viewport units(视口单位)在过去几年已经出现了,随着时间的推移,越来越多的开发人员开始使用它们。它们的好处在于为我们提供了一种不需要使用J avaScript 就能以动态的方式调整大小的方法。而且,如果它失效,也有很多备用方案。

在本文中,我们将学习 CSS Viewport units(视口单位)以及如何使用它们,并用列举一些常见问题及其解决方案和用例,让我们开始吧。

简介

根据CSS规范,视口百分比单位相对于初始包含块的大小,它是web页面的根元素。

视口单位为:vwvhvminvmax

vw单位表示根元素宽度的百分比。1vw等于视口宽度的1%

视口宽度

vw单位表示根元素宽度的百分比,1vw等于视口宽度的1%

clipboard.png

假设我们有一个元素与以下CSS:

.element {
    width: 50vw;
}

当视口宽度为500px时,50vw计算如下

width = 500*50% = 250px

视口高度

vh单位表示根元素高度的百分比,一个vh等于视口高度的1%

clipboard.png

我们有一个元素与以下CSS:

.element {
    height: 50vh;
}

当视口高度为290px时,70vh计算如下:

height = 290*70% = 202px

大家都说简历没项目写,我就帮大家找了一个项目,还附赠【搭建教程】

Vmin 单位

vmin表示视口的宽度和高度中的较小值,也就是vwvh 中的较小值。如果视口宽度大于其高度,则将根据高度计算该值。

我们以下面的例子为例。

clipboard.png

我们有一个横屏手机,其中有一个元素具有vmin单元。在这种情况下,值将根据视口高度计算,因为它小于宽度。

.element {
    padding-top: 10vmin;
    padding-bottom: 10vmin;
}

这是vmin的计算方式:

clipboard.png

正如你所猜测的,计算结果如下所示

padding-top = (10% of height) = 10% * 164 = 16.4px 

padding-bottom = (10% of height) = 10% * 164 = 16.4px

Vmax 单位

vmaxvmin相反,该值是vwvh 中的较大值。

clipboard.png

我们以下面的例子为例。

.element {
    padding-top: 10vmax;
    padding-bottom: 10vmax;
}

clipboard.png

计算结果如下:

padding-top = (10% of width) = 10% * 350 = 35px 
padding-bottom = (10% of width) = 10% * 350 = 35px

视口单位与百分比有何不同?

视口单位基于页面的根元素,而百分比则基于它们所在的容器。因此,它们彼此不同,但各自都有各自的用处。

视口单位的用例

字体大小

clipboard.png

CSS 视口单位非常适合响应式排版。 例如,我们可以将以下内容用作文章标题:

.title {
    font-size: 5vw;
}

标题的font-size将根据视口宽度增加或缩小。 就像提供的字体大小是视口宽度的5%一样。 但是,如果没有适当的测试就直接使用它可能会踩到坑。 让我们看下面的视频:

图片描述

体大小变得非常小,这不利于可访问性和用户体验。据我所知,移动设备上的最小字体大小不应该不于14px。在GIF中,不小于10px

要解决该问题,我们需要为标题提供最小字体大小,可以使用 calc()

.title {
    font-size: calc(14px + 2vw);
}

calc()CSS函数将具有一个最小值14px,并在些基础上添加2vw的值,有了这些,字体大小值就不会变得太小。

另一个需要考虑的重要问题是字体大小在大屏幕上的表现,例如 27” iMac。会发生什么呢?你猜对了,字体大小为95px左右,这是一个很大的值。为了防止这种情况,我们应该在某些断点上使用媒体查询并更改字体大小。

@media (min-width: 1800px) {
    .title {
        font-size: 40px;
    }
}

通过重置字体大小,我们可以确保大小不会太大。您可能还需要添加多个媒体查询,但这取决于你自己,也取决于项目的上下文。

事例地址:https://codepen.io/shadeed/pe...

全屏

有时,我们需要一个section来获取100%的视口高度,为此,我们可以使用viewport高度单位。

clipboard.png

.section {
    height: 100vh;
    display: flex;
    flex-direction: column;
    justify-content: center;
    align-items: center;
}

通过添加 height: 100vh,我们可以确保section 高度将采取100%的视口。此外,我们添加了一些flexbox来处理水平和垂直居中的内容。

事例源码:https://codepen.io/shadeed/pe...

粘性布局(footer)

在大屏幕上,网站内容有时候很少,footer 没有粘在底部。这很正常,也不被认为是一种不好的做法。但是,还有改进的余地。考虑下面代表问题的示图:

clipboard.png

为了解决这个问题,我们需要给内容(content)一个高度,它等于视口高度- (header + footer)。动态地做到这一点是很困难的,但是使用CSS的视口,这是很容易的。

大家都说简历没项目写,我就帮大家找了一个项目,还附赠【搭建教程】

第一种解决方案:calc和视口单位

如果headerfooter 的高度是固定的,那么可以将calc()函数和视口单位结合起来,如下所示:

header,
footer {
    height: 70px;
}

main {
    height: calc(100vh - 70px - 70px);
}

不能保证此解决方案始终有效,尤其是对于footer而言。 在我的职业生涯中,我没有使用固定高度的页脚,因为在例如不同的屏幕尺寸下,此footer是不可行的。

2.第二种解决方案:Flexbox和视口单位(推荐

通过将100vh设置为body元素的高度,然后可以使用flexbox来使main元素占用剩余空间。

body {
    min-height: 100vh;
    display: flex
    flex-direction: column;
{

main {
    /* This will make the main element take the remaining space dynamically */
    flex-grow: 1;
}

这样,问题就解决了,无论内容长度如何,我们都有一个粘性footer

clipboard.png

事例源码:https://codepen.io/shadeed/pe...

响应式元素

假设我们有一个作品集来展示我们的响应式设计工作,并且我们有三种设备(移动设备、平板电脑和笔记本电脑)。每个设备包含一个图像。目标使用 CSS 来响应适配这些页面。

clipboard.png

通过使用CSS网格和视口单位,我们可以使其完全动态的响应式。

<div class="wrapper">
  <div class="device laptop"></div>
  <div class="device mobile"></div>
  <div class="device tablet"></div>
</div>

视口单位也可以用于grid- *属性,也用于borderborder-radius和其他属性。

.wrapper {
  display: grid;
  grid-template-columns: repeat(20, 5vw); 
  grid-auto-rows: 6vw;
}

.mobile { 
  position: relative;
  z-index: 1;
  grid-column: 2 / span 3;
  grid-row: 3 / span 5;
}
 
.tablet {
  position: relative;
  z-index: 1;
  grid-column: 13 / span 7; 
  grid-row: 4 / span 4;
  border-bottom: 1vw solid #a9B9dd;
  border-right: solid 3vw #a9B9dd;
}
 
.laptop {
  position: relative;
  grid-column: 3/span 13;
  grid-row: 2 / span 8;
}

/* Viewport units are used for the bottom, left, right, height, and border-radius. Isn't that cool? */
.laptop:after {
    content:"";
    position:absolute;
    bottom: -3vw;
    left: -5.5vw;
    right: -5.5vw;
    height: 3vw;
    background-color: #a9B9dd;
    border-radius: 0 0 1vw 1vw;
}

!
clipboard.png
例:https://codepen.io/shadeed/pe...

从容器中挣脱出来

我注意到一个用例最适合编辑版面。 一个子元素,即使父元素的宽度受到限制,它也会占据视口100%的宽度。 考虑下面:

clipboard.png

.break-out {
    width: 100vw;
    position: relative;
    left: 50%;
    right: 50%;
    margin-left: -50vw;
    margin-right: -50vw;
}

让我们把它分解一下,这样我们就能一点一点地理解所有这些属性是如何工作的。

1.添加 width: 100vw

最重要的一步,将图像的宽度设置为100%的视口。

clipboard.png

2.添加 margin-left: -50vw

为了使图像居中,我们需要给它一个负的边距,其宽度为视口宽度的一半。

clipboard.png

3.添加 left: 50%

最后,我们需要将图像向右推,其值为父宽度的50%

clipboard.png

事例地址:https://codepen.io/shadeed/pe...

垂直和水平间距

我想到的另一个有趣的用例是使用视口单位来表示元素之间的间距。这可以与margintopbottomgrid-gap等值一起使用。使用时,间距将基于视口宽度或高度,这对于使布局更具动态性可能很有用。

模态框

对于模态,我们需要将它们从视口顶部推入。 通常,使用top属性进行此操作,并使用百分比或像素值。 但是,对于视口单位,我们可以添加一个可以根据视口高度改变的间距。

clipboard.png

.modal-body {
    top: 20vh;
}

图片描述

事例地址:https://codepen.io/shadeed/pe...

大家都说简历没项目写,我就帮大家找了一个项目,还附赠【搭建教程】

页面头部

页面header 是充当页面介绍的部分。 它通常具有标题和描述,并且其中上下边缘的高度固定或填充

例如,有以下的CSS的样式:

.page-header {
    padding-top: 10vh;
    padding-bottom: 10vh;
}

.page-header h2 {
    margin-bottom: 1.5vh;
}

使用vh单位用于页面标题的 paddding,以及标题下方的边距。 注意间距如何变化!

图片描述

事例源码:https://codepen.io/shadeed/pe...

Vmin 和 Vmax 用例

该用例是关于页面标题元素的顶部和底部padding 。 当视口较小(移动)时,通常会减少padding 。 通过使用vmin,我们可以在视口较小尺寸(宽度或高度)的基础上获得合适的顶部和底部 padding

.page-header {
    padding: 10vmin 1rem;
}

图片描述

事例源码:https://codepen.io/shadeed/pe...

纵横比

我们可以使用vw单位创建响应元素,以保持其纵横比,而不管视口大小如何。

首先,需要先确定所需的纵横比,对于此示例,使用9/16

section {
    /* 9/16 * 100 */
    height: 56.25vw;
}

图片描述

事例源码:https://codepen.io/shadeed/pe...

流行的顶部边框

你知道大多数网站使用的顶部边框吗? 通常,它的颜色与品牌颜色相同,这会赋予一些个性。

clipboard.png

我们支持边框的初始值为3px。 如何将固定值转换为视口对象?下面是如何计算它的等效的vw

vw = (Pixel Value / Viewport width) * 100

视口宽度用于估计像素值与所需vw单位之间的比率。

对于我们的示例,我们为 header 添加以下样式:

.header {
    border-top: 4px solid #8f7ebc;  
}

在我的情况下,视口宽度为1440(这不是固定数字,请用你自己的数字替换)

vw = (4 / 1440) * 100 = 0.277
.header {
    border-top: 0.277vw solid #8f7ebc;  
}

更好的是,我们可以使用一个基本像素值,而视口单元可以是一个附加的。

.header {
    border-top: calc(2px + 0.138vw) solid $color-main;
}

移动端滚动问题

移动设备中存在一个常见问题,即使使用100vh,也会滚动,原因是地址栏的高度可见。Louis Hoebregts 写了一篇关于这个问题的文章,并给出了一个简单的解决方案。

.my-element {
  height: 100vh; /* 不支持自定义属性的浏览器的回退 */
  height: calc(var(--vh, 1vh) * 100);
}
// 首先,我们得到视口高度,我们乘以 1% 得到一个vh单位的值
let vh = window.innerHeight * 0.01;
// 然后,将`--vh`自定义属性中的值设置为文档的根目录一个属性
document.documentElement.style.setProperty('--vh', `${vh}px`);

clipboard.png

事例源码:https://codepen.io/shadeed/pe...


代码部署后可能存在的BUG没法实时知道,事后为了解决这些BUG,花了大量的时间进行log 调试,这边顺便给大家推荐一个好用的BUG监控工具 Fundebug

原文:https://ishadeed.com/viewport...


交流

文章每周持续更新,可以微信搜索「 大迁世界 」第一时间阅读和催更(比博客早一到两篇哟),本文 GitHub https://github.com/qq449245884/xiaozhi 已经收录,整理了很多我的文档,欢迎Star和完善,大家面试可以参照考点复习,另外关注公众号,后台回复福利,即可看到福利,你懂的。

查看原文

赞 27 收藏 18 评论 0

前端小智 发布了文章 · 2020-12-25

使用这些技巧优化 IF 语句

作者:Damian Ciplat
译者:前端小智
来源:dev
点赞再看,微信搜索大迁世界,B站关注前端小智这个没有大厂背景,但有着一股向上积极心态人。本文 GitHubhttps://github.com/qq44924588... 上已经收录,文章的已分类,也整理了很多我的文档,和教程资料。

最近开源了一个 Vue 组件,还不够完善,欢迎大家来一起完善它,也希望大家能给个 star 支持一下,谢谢各位了。

github 地址:https://github.com/qq44924588...

双12阿里服务器27块,通用点击这里购买可以找我返现30,等于27块就能买到了,只限新用户,可以用家人的手机号购买!

最近在重构代码时,我发现早期的代码使用太多的 if 语句,其程度是我从未见过的。这就是为什么我认为分享这些简单的技巧是非常重要的,这些技巧可以帮助我们避免过多的使用 if 语句。

接下来会介绍6种方式来代替 if 的使用,这样做不是坚决不使用 if 偏执狂,而是换个方式思考我们的编码思路。

1. 三元运算符

事例1

带有IF的代码:

function saveCustomer(customer) {
  if (isCustomerValid(customer)) {
    database.save(customer)
  } else {
    alert('customer is invalid')
  }
}

重构后代码:

function saveCustomer(customer) {
  return isCustomerValid(customer)
    ? database.save(customer)
    : alert('customer is invalid')
}    

使用 ES6

const saveCustomer = customer =>
   isCustomerValid(customer)?
     database.save(customer) : alert('customer is invalid')    

事例2

带有IF的代码:

function customerValidation(customer) {
  if (!customer.email) {
    return error('email is require')
  } else if (!customer.login) {
    return error('login is required')
  } else if (!customer.name) {
    return error('name is required')
  } else {
    return customer
  }
}

重构后代码:

const customerValidation = customer =>
  !customer.email   ? error('email is required')
  : !customer.login ? error('login is required')
  : !customer.name  ? error('name is required')
                    : customer

事例3

带有IF的代码:

function getEventTarget(evt) {
    if (!evt) {
        evt = window.event;
    }
    if (!evt) {
        return;
    }
    const target;
    if (evt.target) {
        target = evt.target;
    } else {
        target = evt.srcElement;
    }
    return target;
}

重构后代码:

function getEventTarget(evt) {
  evt = evt || window.event;
  return evt && (evt.target || evt.srcElement);
}

2.短路运算符

事例1

带有IF的代码:

const isOnline = true;
const makeReservation= ()=>{};
const user = {
    name:'Damian',
    age:32,
    dni:33295000
};

if (isOnline){
    makeReservation(user);
}

重构后代码:

const isOnline = true;
const makeReservation= ()=>{};
const user = {
    name:'Damian',
    age:32,
    dni:33295000
};

isOnline&&makeReservation(user);

事例2

带有IF的代码:

const active = true;
const loan = {
    uuid:123456,
    ammount:10,
    requestedBy:'rick'
};

const sendMoney = ()=>{};

if (active&&loan){
    sendMoney();
}

重构后代码:

const active = true;
const loan = {
    uuid:123456,
    ammount:10,
    requestedBy:'rick'
};

const sendMoney = ()=>{};

active && loan && sendMoney();

3.函数委托:

事例1

带有IF的代码:

function itemDropped(item, location) {
    if (!item) {
        return false;
    } else if (outOfBounds(location) {
        var error = outOfBounds;
        server.notify(item, error);
        items.resetAll();
        return false;
    } else {
        animateCanvas();
        server.notify(item, location);
        return true;
    }
}

重构后代码:

function itemDropped(item, location) {
    const dropOut = function() {
        server.notify(item, outOfBounds);
        items.resetAll();
        return false;
    }

    const dropIn = function() {
        server.notify(item, location);
        animateCanvas();
        return true;
    }

    return !!item && (outOfBounds(location) ? dropOut() : dropIn());
}

大家都说简历没项目写,我就帮大家找了一个项目,还附赠【搭建教程】

我和阿里云合作服务器,折扣价比较便宜:89/年,223/3年,比学生9.9每月还便宜,买了搭建个项目,熟悉技术栈比较香(老用户用家人账号买就好了,我用我妈的)推荐买三年的划算点,点击本条就可以查看。

4.非分支策略

此技巧尝试避免使用switch语句,相反是用键/值创建一个映射并使用一个函数访问作为参数传递的键的值。

事例1

带有switch的代码:

switch(breed){
    case 'border':
      return 'Border Collies are good boys and girls.';
      break;  
    case 'pitbull':
      return 'Pit Bulls are good boys and girls.';
      break;  
    case 'german':
      return 'German Shepherds are good boys and girls.';
      break;
    default:
      return 'Im default'
}

重构后代码:

const dogSwitch = (breed) =>({
  "border": "Border Collies are good boys and girls.",
  "pitbull": "Pit Bulls are good boys and girls.",
  "german": "German Shepherds are good boys and girls.",  
})[breed]||'Im the default';


dogSwitch("border xxx")

5.作为数据的函数

我们知道在JS中函数是第一个类,所以使用它我们可以把代码分割成一个函数对象。

带有IF的代码:


const calc = {
    run: function(op, n1, n2) {
        const result;
        if (op == "add") {
            result = n1 + n2;
        } else if (op == "sub" ) {
            result = n1 - n2;
        } else if (op == "mult" ) {
            result = n1 * n2;
        } else if (op == "div" ) {
            result = n1 / n2;
        }
        return result;
    }
}

calc.run("sub", 5, 3); //2

重构后代码:

const calc = {
    add : function(a,b) {
        return a + b;
    },
    sub : function(a,b) {
        return a - b;
    },
    mult : function(a,b) {
        return a * b;
    },
    div : function(a,b) {
        return a / b;
    },
    run: function(fn, a, b) {
        return fn && fn(a,b);
    }
}

calc.run(calc.mult, 7, 4); //28

6.多态性

多态性是对象具有多种形式的能力。OOP中多态性最常见的用法是使用父类引用来引用子类对象。

带有IF的代码:

const bob = {
  name:'Bob',
  salary:1000,
  job_type:'DEVELOPER'
};

const mary = {
  name:'Mary',
  salary:1000,
  job_type:'QA'
};

const calc = (person) =>{

    if (people.job_type==='DEVELOPER')
        return person.salary+9000*0.10;

    if (people.job_type==='QA')
        return person.salary+1000*0.60;
}

console.log('Salary',calc(bob));
console.log('Salary',calc(mary));

重构后代码:


const qaSalary  = (base) => base+9000*0.10;
const devSalary = (base) => base+1000*0.60;

//Add function to the object.
const bob = {
  name:'Bob',
  salary:1000,
  job_type:'DEVELOPER',
  calc: devSalary
};

const mary = {
  name:'Mary',
  salary:1000,
  job_type:'QA',
  calc: qaSalary
};

console.log('Salary',bob.calc(bob.salary));
console.log('Salary',mary.calc(mary.salary));

代码部署后可能存在的BUG没法实时知道,事后为了解决这些BUG,花了大量的时间进行log 调试,这边顺便给大家推荐一个好用的BUG监控工具 Fundebug

原文:https://dev.to/damxipo/avoid-...


交流

文章每周持续更新,可以微信搜索「 大迁世界 」第一时间阅读和催更(比博客早一到两篇哟),本文 GitHub https://github.com/qq449245884/xiaozhi 已经收录,整理了很多我的文档,欢迎Star和完善,大家面试可以参照考点复习,另外关注公众号,后台回复福利,即可看到福利,你懂的。

查看原文

赞 11 收藏 8 评论 3

前端小智 发布了文章 · 2020-12-21

20 个值得研究的 Vue 开源项目

译者:前端小智
作者:Nastassia Ovchinnikova
来源:flatlogic.com
点赞再看,微信搜索大迁世界,B站关注前端小智这个没有大厂背景,但有着一股向上积极心态人。本文 GitHubhttps://github.com/qq44924588... 上已经收录,文章的已分类,也整理了很多我的文档,和教程资料。

最近开源了一个 Vue 组件,还不够完善,欢迎大家来一起完善它,也希望大家能给个 star 支持一下,谢谢各位了。

github 地址:https://github.com/qq44924588...

Vue 相对不于 React 的一个优点是它易于理解和学习,且在国内占大多数。咱们可以在 Vue 的帮助下创建任何 Web 应用程序。 因此,时时了解一些新出现又好用的Vue 开源项目也是挺重要,一方面可以帮助咱们更加高效的开发,另一方面,咱们也可以模范学习其精华部分。

接下来看看新出的有哪些好用的开源项目。

uiGradients

网址:http://uigradients.com/

GitHub:https://github.com/ghosh/uiGr...

GitHub Stars:4.6k

clipboard.png

彩色阵列和出色的UX使是这个项目的一个亮点,渐变仍然是网页设计中日益增长的趋势。 咱们可以选择所需的颜色,并可以获得所有可能的渐变,并获取对应的 CSS 代码, 赶紧收藏起来吧。

CSSFX

CSS 过度效果的集合

网址:https://cssfx.dev

GitHub:https://github.com/jolaleye/c...

GitHub Stars:3.5k

图片描述

CSSFX 里面有很多 CSS 过滤效果,咱们可以根据需求选择特定的动画,点击对应的效果即可看到生成的 CSS 代码,动手搞起来吧。

Sing App Vue Dashboard

一个管理模板

网址:https://flatlogic.com/templat...

GitHub:https://github.com/flatlogic/...

GitHub Stars:254

事例:https://flatlogic.com/templat...

文档:https://demo.flatlogic.com/si...

clipboard.png

这是基于最新 Vue 和 Bootstrap 免费和开源的管理模板,其实跟咱们国内的 vue-admin-template 差不多。咱们不一定要使用它,但可以研究学习源码,相信可以学到很多实用的技巧,加油少年。

Vue Storefront

网址:https://www.vuestorefront.io

GitHub:https://github.com/DivanteLtd...

GitHub Stars:5.8k

clipboard.png

这是一个PWA,可以连接到任何后端(或几乎任何后端)。这个项目的主要优点是使用了无头架构。这是一种全面的解决方案,为咱们提供了许多可能性(巨大的支持稳步增长的社区,服务器端渲染,将改善网页SEO,移动优先的方法和离线模式。

Faviator

图标生成的库

网址:https://www.faviator.xyz

GitHub:https://www.faviator.xyz/play...

GitHub Stars:94

clipboard.png

如果需要创建一个图标增加体验度。 可以使用任何 Google 字体以及任何颜色。只需通过首选的配置,然后选择PNG,SVG或JPG格式即可。

iView

Vue UI 组件库

网址:https://iviewui.com/

GitHub:https://github.com/iview/iview

GitHub Stars:22.8k

clipboard.png

不断迭代更新使这组UI组件成为具有任何技能水平的开发人员的不错选择。

要使用iView,需要对单一文件组件有充分的了解,该项目具有友好的API和大量文档。

Postwoman

API请求构建器

网址:https://postwoman.io/

GitHub:https://github.com/liyasthoma...

GitHub Stars:10.5k

clipboard.png

这个与 Postman 类似。 它是免费的,具有许多参与者,并且具有多平台和多设备支持。 这个工具真的非常快,并且有大量的更新。 该工具的创建者声称在不久的将来会有更多功能。

Vue Virtual Scroller

快速滚动

网址:https://akryum.github.io/vue-...

GitHub:https://github.com/Akryum/vue...

GitHub Stars:3.4k

clipboard.png

Vue Virtual Scroller具有四个主要组件。 RecycleScroller可以渲染列表中的可见项。 如果咱们不知道数据具体的数量,最好使用DynamicScrollerDynamicScrollerItem将所有内容包装在DynamicScroller中(以处理大小更改)。 IdState简化了本地状态管理(在RecycleScroller内部)。

Mint UI

移动端的 UI 库

网址:http://mint-ui.github.io/#!/en

GitHub:https://github.com/ElemeFE/mi...

GitHub Stars:15.2k

clipboard.png

使用现成的CSS和JS组件更快地构建移动应用程序。使用此工具,咱们不必承担文件大小过大的风险,因为可以按需加载。动画由CSS3处理,由此来提高性能。

大家都说简历没项目写,我就帮大家找了一个项目,还附赠【搭建教程】

V Calendar

用于构建日历的无依赖插件

网址:https://vcalendar.io

GitHub:https://github.com/nathanreye...

GitHub Stars:1.6k

clipboard.png

您可以选择不同的视觉指示器来装饰日历。 V Calendar还为咱们提供了三种日期选择模式:

  • 单选
  • 多选
  • 日期范围

Vue Design System

一组UI工具

网址:https://vueds.com/

GitHub:https://github.com/viljamis/v...

GitHub Stars:1.7k

clipboard.png

这是一种组织良好的工具,对于任何web开发团队来说,它的命名都很容易理解。其中一个很大的优点是使用了更漂亮的代码格式化器,它可以在提交到Git之前自动排列代码。

Proppy

UI组件的功能道具组合

网址:https://proppyjs.com

GitHub:https://github.com/fahad19/pr...

GitHub Stars:856

clipboard.png

ProppyJS 是一个很小的库,用于组合道具,它附带了各种集成包,让您可以自由地使用它流行的渲染库。

我们的想法是首先将Component的行为表达为props,然后使用Proppy的相同API将其连接到您的Component(可以是React,Vue.js或Preact)。

API还允许您访问其他应用程序范围的依赖项(如使用Redux的商店),以方便组件树中的任何位置。

Light Blue Vue Admin

vue 后台展示模板

网址:https://flatlogic.com/templat...

GitHub:https://github.com/flatlogic/...

GitHub Stars:79

图片描述

事例:https://demo.flatlogic.com/li...

文档:https://demo.flatlogic.com/li...

模板是用Vue CLIBootstrap 4构建的。从演示中可以看到,这个模板有一组非常基本的页面:排版、地图、图表、聊天界面等。如果咱们需要一个扩展的模板,可以看看Light Blue Vue Full,它有60多个组件,无 jquery,有两个颜色主题。

Vue API Query

为 REST API 构建请求

GitHub:https://github.com/robsonteno...
GitHub Stars: 1.1k

clipboard.png

关于这个项目没什么好说的。它所做的与描述行中所写的完全一样:它帮助咱们构建REST API的请求。

Vue Grid Layout

Vue 的网格布局

Website:https://jbaysolutions.github....
GitHub:https://github.com/jbaysoluti...
GitHub Stars: 3.1k

clipboard.png

所有网格相关问题的简单解决方案。它有静态的、可调整大小的和可拖动的小部件。还是响应和布局可以恢复和序列化。如果还需要再添加一个小部件,则不必重新构建所有网格。

Vue Content Loader

创建一个占位符加载

Website:http://danilowoz.com/create-v...
GitHub:https://github.com/egoist/vue...
GitHub Stars: 2k

clipboard.png

当咱们开发网站或者 APP 时,遇到内容过多加载速度慢时,会导致用户打开页面有大量空白页,vue-content-loader正是解决这个问题的一个组件,使加载内容之前生成一个dom模板,提高用户体验。

Echarts with Vue2.0

数据可视化

Website:https://simonzhangiter.github...
GitHub:https://github.com/SimonZhang...
GitHub Stars: 1.3k

clipboard.png

在图片中,咱们可以看到非常漂亮的图表。这个项目使任何数据都更具可读性,更容易理解和解释。它允许咱们在任何数据集中轻松地检测趋势和模式。

大家都说简历没项目写,我就帮大家找了一个项目,还附赠【搭建教程】

Vue.js Modal

高度可定制的模态框

Website:http://vue-js-modal.yev.io/
GitHub:https://github.com/euvl/vue-j...
GitHub Stars: 2.9k

clipboard.png

可以在该网站上查看所有不同类型的模态。 有15个按钮,按任意一个按钮,看到一个模态示例。

Vuesax

框架组件

Website:https://lusaxweb.github.io/vu...
GitHub:https://github.com/lusaxweb/v...
GitHub Stars: 3.7k

clipboard.png

这个项目在社区中很受欢迎。 它使咱们可以为每个组件设计不同的风格。 Vuesax的创建者强调,每个Web开发人员在进行Web设计时都应有选择的自由。

Vue2 Animate

vue2.0 —使用animate.css 构建项目和创建组件

Website:https://the-allstars.com/vue2...
GitHub:https://github.com/asika32764...
GitHub Stars: 1.1k

clipboard.png

这个库是跨浏览器的,咱们可以选择从5种类型的动画: rotateslidefadebouncezoom。在网站上有一个演示。动画的默认持续时间是1秒,但是咱们可以自定义该参数。

Vuetensils

Vue.js的工具集

Website:https://vuetensils.stegosourc...
GitHub:https://github.com/stegosourc...
GitHub Stars: 111

clipboard.png

这个UI库有一个标准的功能,但是最酷的是它没有额外的样式。你可以让设计尽可能的个性化,应用所有的需求。只需编写需要的样式,将其添加到项目中,并包含需要的尽可能多的组件。

人才们的 【三连】 就是小智不断分享的最大动力,如果本篇博客有任何错误和建议,欢迎人才们留言,最后,谢谢大家的观看。


编辑中可能存在的bug没法实时知道,事后为了解决这些bug,花了大量的时间进行log 调试,这边顺便给大家推荐一个好用的BUG监控工具 Fundebug

原文:

https://flatlogic.com/blog/ne...
https://flatlogic.com/blog/ne...


交流

文章每周持续更新,可以微信搜索「 大迁世界 」第一时间阅读和催更(比博客早一到两篇哟),本文 GitHub https://github.com/qq449245884/xiaozhi 已经收录,整理了很多我的文档,欢迎Star和完善,大家面试可以参照考点复习,另外关注公众号,后台回复福利,即可看到福利,你懂的。

查看原文

赞 36 收藏 28 评论 1

前端小智 发布了文章 · 2020-12-14

17 个提升用户体验图像特效库

作者:lindelof
译者:前端小智
来源:github
点赞再看,微信搜索大迁世界,B站关注前端小智这个没有大厂背景,但有着一股向上积极心态人。本文 GitHubhttps://github.com/qq44924588... 上已经收录,文章的已分类,也整理了很多我的文档,和教程资料。

最近开源了一个 Vue 组件,还不够完善,欢迎大家来一起完善它,也希望大家能给个 star 支持一下,谢谢各位了。

github 地址:https://github.com/qq44924588...

1.imagehover

地址:https://github.com/ciar4n/imagehover.css

imagehover.css- 纯CSS3鼠标滑过图片效果动画库,44种鼠标滑过效果

2. ImageTiltEffect

地址:https://github.com/codrops/ImageTiltEffect

TiltEffect是一款让图像跟随鼠标产生微妙立体倾斜效果的插件,使其有层次感和深度感。

3.Magnifier.js

地址:https://github.com/mark-rolich/Magnifier.js

Magnifier 是一款实用纯js制作的图片放大镜插件。它有以下一些特定:

  • 可以使用鼠标滚轮放大缩小图片。
  • 可以通过js或data属性来设置选项。
  • 一次调用可以附加多张图片。
  • 用户可以自定义鼠标进入、离开、移动事件。
  • 加载大图片时可以显现等待文本。

4.gl-react-image-effects

地址:https://github.com/gre/gl-react-image-effects

通用的withReact示例应用程序与 Web。iOS和Android实现一起运行一个代码库( 一些特定的代码被设计成在平台上创建不同的UI )。

clipboard.png

clipboard.png

5.StickyImageEffect

受ultanoir网站启发的幻灯片显示,具有粘性图像效果。

地址:https://github.com/Anemolo/StickyImageEffect

6.HeatDistortionEffect

WebGL热变形效果全屏背景。

地址:https://github.com/lbebber/HeatDistortionEffect

7.ImageDraggingEffects

使用各种技术为图像设置的一组有趣的拖动效果。

地址:https://github.com/codrops/ImageDraggingEffects

8.jQuery.BgSwitcher

jQuery.BgSwitcher实现背景图像切换效果。

地址:https://github.com/rewish/jquery-bgswitcher

9.FullImageReveal

一个完整的图像显示精美的缩略图滑动效果。

地址:https://github.com/codrops/FullImageReveal

10.diaporama

Diaporama是一种图像/视频/内容幻灯片引擎,提供高质量的动画效果,包括Kenburns效果和GLSL Transitions。

地址:https://github.com/gre/diaporama

11. FollowCursor

图像的旋转跟随光标移动而变化。

地址:https://github.com/bersLucas/FollowCursor

12.react-native-kenburns-view

KenBurns 用于React Native应用程序的图像效果

地址: https://github.com/nHiRanZ/react-native-kenburns-view

大家都说简历没项目写,我就帮大家找了一个项目,还附赠【搭建教程】

13.vintageJS

VINTAGEJS 是一个jQuery插件,它使用HTML5 canvas为你的图像/照片添加惊叹的复古效果。它预置了三种效果,并且可以很容易定制。

地址:https://github.com/lindelof/awesome-web-effect

14.ThumbnailGridExpandingPreview

有关如何使用扩展的图像预览创建缩略图网格的教程,类似于在Google图片上看到的效果。

地址:https://tympanus.net/Tutorials/ThumbnailGridExpandingPreview/

15.gridder

一个jQuery插件,显示缩略图网格扩展预览,类似于在Google图片上看到的效果。

地址:https://github.com/oriongunning/gridder

16.MotionTransitionEffect

图像幻灯片的快速运动过渡效果。

地址:https://github.com/codrops/MotionTransitionEffect

17.tiltedpage_scroll

tiltedpage_scroll.js是一款支持鼠标滚动、设置图片角度的一款插件。当页面滚动的时候,图片会有倾斜3D的效果,作为产品展示是个不错的选择。

地址:http://peachananr.github.io/tiltedpage_scroll/demo/tiltedpage_scroll_demo.html


代码部署后可能存在的BUG没法实时知道,事后为了解决这些BUG,花了大量的时间进行log 调试,这边顺便给大家推荐一个好用的BUG监控工具 Fundebug

原文:https://github.com/lindelof/a...

交流

文章每周持续更新,可以微信搜索 【大迁世界 】 第一时间阅读,回复 【福利】 有多份前端视频等着你,本文 GitHub https://github.com/qq449245884/xiaozhi 已经收录,欢迎Star。

查看原文

赞 51 收藏 42 评论 1

前端小智 发布了文章 · 2020-12-07

这 10 个事例,有助于你理解 ES 中的 Promise

作者:Jay Chow
译者:前端小智
来源:jamesknelson
点赞再看,微信搜索大迁世界,B站关注前端小智这个没有大厂背景,但有着一股向上积极心态人。本文 GitHubhttps://github.com/qq44924588... 上已经收录,文章的已分类,也整理了很多我的文档,和教程资料。

最近开源了一个 Vue 组件,还不够完善,欢迎大家来一起完善它,也希望大家能给个 star 支持一下,谢谢各位了。

github 地址:https://github.com/qq44924588...

在开发中,了解 JavaScript 和 Promise 基础,有助于提高我们的编码技能,今天,我们一起来看看下面的 10 片段,相信看完这 10 个片段有助于我们对 Promise 的理解。

片段1:

const prom = new Promise((res, rej) => {
  console.log('first');
  res();
  console.log('second');
});
prom.then(() => {
  console.log('third');
});
console.log('fourth');

// first
// second
// fourth
// third

Promise同步执行,promise.then异步执行。

片段2:

const prom = new Promise((res, rej) => {
  setTimeout(() => {
    res('success');
  }, 1000);
});
const prom2 = prom.then(() => {
  throw new Error('error');
});

console.log('prom', prom);
console.log('prom2', prom2);

setTimeout(() => {
  console.log('prom', prom);
  console.log('prom2', prom2);
}, 2000);

// prom 
// Promise {<pending>}
// __proto__: Promise
// [[PromiseStatus]]: "resolved"
// [[PromiseValue]]: "success"

// 2 秒后还会在打一遍上面的两个

promise 有三种不同的状态:

  • pending
  • fulfilled
  • rejected

一旦状态更新,pending->fulfilledpending->rejected,就可以再次更改它。 prom1prom2不同,并且两者都返回新的Promise状态。

片段3:

const prom = new Promise((res, rej) => {
  res('1');
  rej('error');
  res('2');
});

prom
  .then(res => {
    console.log('then: ', res);
  })
  .catch(err => {
    console.log('catch: ', err);
  });

// then: 1

即使reject后有一个resolve调用,也只能执行一次resolvereject ,剩下的不会执行。

片段 4:

Promise.resolve(1)
  .then(res => {
    console.log(res);
    return 2;
  })
  .catch(err => {
    return 3;
  })
  .then(res => {
    console.log(res);
  });

// 1
// 2

Promises 可以链接调用,当提到链接调用 时,我们通常会考虑要返回 this,但Promises不用。 每次 promise 调用.then.catch时,默认都会返回一个新的 promise,从而实现链接调用。

片段 5:

const promise = new Promise((resolve, reject) => {
  setTimeout(() => {
    console.log('first')
    resolve('second')
  }, 1000)
})

const start = Date.now()
promise.then((res) => {
  console.log(res, Date.now() - start, "third")
})
promise.then((res) => {
  console.log(res, Date.now() - start, "fourth")
})

// first
// second 1054 third
// second 1054 fourth

promise 的 .then.catch可以被多次调用,但是此处Promise构造函数仅执行一次。 换句话说,一旦promise的内部状态发生变化并获得了一个值,则随后对.then.catch的每次调用都将直接获取该值。

片段 6:

const promise = Promise.resolve()
  .then(() => {
    return promise
  })
promise.catch(promise )

// [TypeError: Chaining cycle detected for promise #<Promise>]
// Uncaught SyntaxError: Identifier 'promise' has already been declared
//    at <anonymous>:1:1
// (anonymous) @ VM218:1

.then.catch返回的值不能是promise本身,否则将导致无限循环。


大家都说简历没项目写,我就帮大家找了一个项目,还附赠【搭建教程】

我和阿里云合作服务器,折扣价比较便宜:89/年,223/3年,比学生9.9每月还便宜,买了搭建个项目,熟悉技术栈比较香(老用户用家人账号买就好了,我用我妈的)推荐买三年的划算点,点击本条就可以查看


片段 7:

Promise.resolve()
  .then(() => {
    return new Error('error');
  })
  .then(res => {
    console.log('then: ', res);
  })
  .catch(err => {
    console.log('catch: ', err);
  });

// then: Error: error!
// at Promise.resolve.then (...)
// at ...

.then.catch中返回错误对象不会引发错误,因此后续的.catch不会捕获该错误对象,需要更改为以下对象之一:

return Promise.reject(new Error('error')) throw new Error('error')

因为返回任何非promise 值都将包装到一个Promise对象中,也就是说,返回new Error('error')等同于返回Promise.resolve(new Error('error'))

片段 8:

Promise.resolve(1)
  .then(2)
  .then(Promise.resolve(3))
  .then(console.log)

  // 1

.then.catch的参数应为函数,而传递非函数将导致值的结果被忽略,例如.then(2).then(Promise.resolve(3)

片段 9:

Promise.resolve()
  .then(
    function success(res) {
      throw new Error('Error after success');
    },
    function fail1(e) {
      console.error('fail1: ', e);
    }
  )
  .catch(function fail2(e) {
    console.error('fail2: ', e);
  });

//   fail2:  Error: Error after success
//     at success (<anonymous>:4:13)

.then可以接受两个参数,第一个是处理成功的函数,第二个是处理错误的函数。 .catch是编写.then的第二个参数的便捷方法,但是在使用中要注意一点:.then第二个错误处理函数无法捕获第一个成功函数和后续函数抛出的错误。 .catch捕获先前的错误。 当然,如果要重写,下面的代码可以起作用:

Promise.resolve()
  .then(function success1 (res) {
    throw new Error('success1 error')
  }, function fail1 (e) {
    console.error('fail1: ', e)
  })
  .then(function success2 (res) {
  }, function fail2 (e) {
    console.error('fail2: ', e)
  })

片段 10:

process.nextTick(() => {
  console.log('1')
})
Promise.resolve()
  .then(() => {
    console.log('2')
  })
setImmediate(() => {
  console.log('3')
})
console.log('4');

// Print 4
// Print 1
// Print 2
// Print 3

process.nextTickpromise.then都属于微任务,而setImmediate属于宏任务,它在事件循环的检查阶段执行。 在事件循环的每个阶段(宏任务)之间执行微任务,并且事件循环的开始执行一次。


代码部署后可能存在的BUG没法实时知道,事后为了解决这些BUG,花了大量的时间进行log 调试,这边顺便给大家推荐一个好用的BUG监控工具 Fundebug

原文:http://jamesknelson.com/grokk...


交流

干货系列文章汇总如下,觉得不错点个Star,欢迎 加群 互相学习。

https://github.com/qq44924588...

我是小智,公众号「大迁世界」作者,对前端技术保持学习爱好者。我会经常分享自己所学所看的干货,在进阶的路上,共勉!

关注公众号,后台回复福利,即可看到福利,你懂的。

clipboard.png

查看原文

赞 33 收藏 25 评论 1

前端小智 发布了文章 · 2020-12-02

26 个 CSS 面试题目增强你的 CSS 基础

译者:前端小智
来源:codersera.
点赞再看,微信搜索大迁世界,B站关注前端小智这个没有大厂背景,但有着一股向上积极心态人。本文 GitHubhttps://github.com/qq44924588... 上已经收录,文章的已分类,也整理了很多我的文档,和教程资料。

最近开源了一个 Vue 组件,还不够完善,欢迎大家来一起完善它,也希望大家能给个 star 支持一下,谢谢各位了。

github 地址:https://github.com/qq44924588...

CSS是层叠样式表( Cascading Style Sheets )的缩写,是一种样式表语言,用于描述以 HTML 之类的标记语言编写的文档的布局。 它是用于设计Web页面的三剑客之一,另外两位浩客是HTMLJavascript

CSS 的设计目的是使样式和内容分离,包括布局、颜色和字体。这种分离可以提高内容的可访问性,在样式特征的规范中提供更多的灵活性和控制,通过在一个单独的. .css 文件中指定相关的 CSS,使多个 web 页面能够共享格式,并减少结构内容中的复杂性和重复。

它具有简单的语法,并使用大量的英文关键字来指定各种样式属性的名称。

既然我们已经讨论了CSS的基础知识,让我们来观察一下基于CSS的重要面试问题。

clipboard.png

问题1:什么是 CSS?

CSS(层叠样式表)是一种样式语言,对于 HTML 元素来说足够简单。 它在网页设计中非常流行,其应用在XHTML中也很常见。

问题2:为什么要开发CSS?

CSS是在1997年开发的,作为一种web开发人员设计他们正在创建的web页面布局的方法。它的目的是让开发者将网站代码的内容和结构从视觉设计中分离出来。

这种结构和设计的分离允许HTML执行比原来更多的功能。

问题3:CSS的主要版本有哪些?

CSS的不同版本:

  1. CSS1
  2. CSS2
  3. CSS2.1
  4. CSS3

问题4:CSS样式的组成部分是什么?

一个样式规则由三部分组成:

  1. 选择器–选择器是 HTML 标记,用于选择要设置样式的内容。 它根据其ID,类和名称选择 HTML元素。
  2. 属性–属性是 HTML 标签的一种属性。 简而言之,所有 HTML 属性都转换为 CSS 属性。
  3. – CSS中的值定义CSS属性的一组有效值。

问题 5:有多少种方法可以将 CSS 集成为 web 页面

CSS 可以集成为三种方式

  1. 内联:直接在HTML元素上使用
<p style=”colour:skyblue;”>hello world</p>
  1. 外部:在工作空间中创建单独的CSS文件,然后在创建的每个web页面中链接它们
<head>

<link rel=”text/css”href=”your_CSS_file_location”/>

</head>
  • 内部: web 页面的 head 元素在其中实现了内部 CSS。
head>
     <style> 
             P{
                   color : lime;
               background-color:black;
                }
     </style>
</head>

问题 6:谁在维护 CSS 规范?

万维网协会维护 CSS规范。

问题 7:伪元素是什么意思?

伪元素是添加到选择器的关键字,它允许一种样式,即所选元素的特定部分。CSS用于在HTML标记中应用样式,它允许在不影响实际文档的情况下对文档进行额外标记。它可以用来:

  1. 为第一个字母、行或元素设置样式。
  2. 插入内容

语法:

Selector: :pseudo-element
{Property1 :value;
Property2 :value;}

问题 8:CSS有什么优势?

CSS的优点是:

  1. 一致性 – CSS有助于构建一致的框架,设计人员可以使用该框架来构建其他站点。 因此,网页设计师的效率也提高了。
  2. 易于使用 – CSS 是非常容易学习和简化网站开发。所有代码都放在一个页面上,这意味着对代码行进行改进或编辑不需要重复修改多个页面.
  3. *网站速度 *– 通常,一个网站使用的代码最多可以达到 2 页或更多。但是对于CSS,这不是问题。它只需要2-3行代码,因此,网站数据库保持整洁,消除任何网站加载问题。
  4. 设备兼容性 – 由于人们使用不同类型的智能设备访问互联网,因此需要响应式web设计。CSS 在这里的作用是使 web 页面的响应性更好,这样它们就可以在所有设备中以相同的方式显示。
  5. 多浏览器支持 – CSS享有多浏览器的支持,它与所有主要的互联网浏览器兼容。
  6. 重新定位 – CSS允许您定义页面上 web 元素位置的变化。通过它的实现,开发人员可以将 HTML 元素放置在他们喜欢的位置,以便与页面的美学吸引力或其他考虑因素保持一致。

问题9:CSS 渐变是什么?

渐变是指我们在两幅图像之间创建中间帧,以获得第一幅图像的外观,然后发展成第二幅图像的过程,它主要用于创建动画。

问题10:什么是 CSS 特异性?

CSS 特定性是一个分数或等级,它决定了元素必须使用哪种样式声明。 CSS 中有四类可以授权选择器的特异性级别:

  1. 内联样式
  2. ID
  3. 类,属性和伪类
  4. 元素和伪元素

问题12:CSS有什么缺点

CSS的缺点有:

  1. 版本太多 – 与HTML或Javascript等其他参数相比,CSS有很多版本-CSS1,CSS2,CSS2.1,CSS3。 因此,CSS变得非常混乱,尤其是对于初学者。
  2. 缺乏安全性 - 由于CSS是基于开放文本的系统,因此它没有内置的安全系统来防止其被覆盖。 通过对其读/写操作的访问,任何人都可以更改 CSS 文件并更改链接。
  3. Fragmentation - 使用 CSS,可能无法在一个浏览器上使用另一浏览器。 因此,在网站上线之前,Web 开发人员必须通过在多个浏览器上运行程序来测试兼容性。
  4. 复杂性–使用 Microsoft FrontPage 等第三方软件会使CSS变得复杂。

问题13:什么是 RWD (Responsive Web Design)?

RWD(响应式Web设计)技术用于在每种屏幕尺寸以及移动,平板电脑,台式机和笔记本电脑等设备上完美显示设计页面,让我们无需为每个设备创建不同的页面。

问题14:CSS 精灵有什么好处?

CSS精灵的好处有:

  1. 通过将各种小图像组合成一个图像,减少了web页面的加载时间。
  2. 减少HTTP请求,从而减少加载时间。

问题 15:什么是 CSS 上下文选择器?

上下文选择器,严格来讲,叫后代组合式选择器,就是一组以空格分隔的标签名。用于选择作为指定祖先元素后代的标签。只要有标签在它的层次结构“上游”存在这么一个祖先,那么就会选中该标签。无论从该标签到作为祖先的上下文之间隔着多少层次都没有关系。


大家都说简历没项目写,我就帮大家找了一个项目,还附赠【搭建教程】

我和阿里云合作服务器,折扣价比较便宜:89/年,223/3年,比学生9.9每月还便宜,买了搭建个项目,熟悉技术栈比较香(老用户用家人账号买就好了,我用我妈的)推荐买三年的划算点,点击本条就可以查看。


问题 16:什么是渐进增强和平稳退化?

渐进增强的概念是指从最基本的可用性出发,在保证站点页面在低级浏览器中 的可用性和可访问性的基础上,逐步增加功能及提高用户体验。本质上讲,我们日常的一些开发习惯,例如首先使用标记语言编写页面,然后通过样式表来控制页面 样式等,都属于渐进增强的概念;其他更为明显的行为包括使用HTML5、CSS3等新技术,针对高级浏览器为页面提高用户体验的丰富程度。

平稳退化的概念是指首先使用最新的技术面向高级浏览器构建最强的功能及用户体验,然后针对低级浏览器的限制,逐步衰减那些无法被支持的功能及体验;在我们日常的开 发中,一个典型的平稳退化的例子就是首先针对Chrome编写页面代码,然后修复IE中的异常或针对IE去除那些无法被实现的功能特色.

所以, 这两个概念方法其实早已并存在我们的日常开发工作中了,只是“渐进增强”与“平稳退化”这样的措辞是近些年才开始被普及。在我们眼下的HTML5与 CSS3实战用,这两个概念就尤其重要了,怎样保证使用不断变化的新技术来构建在主流浏览器下都具有基本可用性的站点,并针对高级浏览器进行体验提升,这 些是我们在开发过程中需要明确的思路。

问题 17:我们如何在网页上添加图标?

我们可以使用诸如font-awesome或者阿里的 iconfont 之类的图标库将图标添加到HTML网页。 我们必须将给定图标类的名称添加到任何内联HTML元素中。 (<i><span>)。 图标库中的图标是可缩放的矢量,可以使用CSS进行自定义。

问题 18:哪个属性指定边框的宽度?

border-width指定边框的宽度。

问题 19:如何区分物理标签和逻辑标签?

物理标签被称为表示标记,而逻辑标签对于外观是无用的。物理标签是较新的版本,而逻辑标签是旧的并且专注于内容。

如题,我们的标签元素写上后,浏览器就会渲染出结果,但不仅仅是这么简单

//物理元素
<b>我想用b标签加粗</b>
 
//逻辑元素
<strong>我想用strong标签加粗</strong>
 
//两段文字都加粗了,而且视觉效果完全一样

确实,文字加粗了,两者都达到了我们想要的目的,但是我们忽略了一个问题,既然b标签可以加粗,那么strong这个标签同样是加粗,存在的 意义又是什么呢?既然W3C定义了两个,它们之间的不同点是什么呢?它们之间的相同点又是什么呢?

物理元素

物理元素,又叫实体标签,它所做的是一种物理行为,比如上面我把一段文字用b标签加粗了,它所传达的给浏览器,告诉浏览器 我要加粗这段文字,从单词Bold中也可以看出来,英文中仅仅是加粗的意思,并没有其他作用。总结来说就是一句话: 物理元素就是告诉浏览器该怎么显示出来。

逻辑元素

逻辑元素,从英文字面上Strong就可以看出它是强调的意思,所以我们用这个逻辑元素(如上strong)来向浏览器传达 一个强调某段文字重要性的消息,说明此文字较为重要,也有利于搜索引擎收录。

Web标准主张XHTML不涉及具体的表现形式,“强调”可以用加粗来强调,也可以用别的方式强调,也可以通过css来改变strong的具体表现 ,还有就是并不是有了strong逻辑标签,就不用b标签来表示字体加粗了,b标签和strong标签默认情况下强调的效果一致,strong完全可以定义成别的样式,用来强调 效果,但是最好符合W3C标准,它更提倡内容与样式分离,所以单纯为了达到加粗而使用b标签不建议这样做, 从XHTML文档有意义性及用户体验角度来说,strong逻辑标签更加合适,而SEO方面,则针对优化情况而定。

问题 20:如何在CSS中定义一个伪类?它们是用来干什么的

CSS伪类是用来添加一些选择器的特殊效果。伪类的语法

selector:pseudo-class{property:value;}

问题 21:CSS和SCSS有什么区别?

CSSSCSS 之间的区别如下:

  1. CSS是一种用于设计web页面的样式语言,而SCSS用于为浏览器组合CSS样式表。
  2. SCSS 提供了一些变量,可以使用这些变量来缩短代码,这是与 CSS 相比的一大优势。

问题 22:嵌入式样式表的优缺点是什么?

嵌入式样式表的优点:

  1. 可以在一个文档中创建多种标签类型。
  2. 在复杂情况下,可以使用选择器和分组方法来应用样式。
  3. 无需额外下载。

嵌入式样式表的缺点:

无法控制多个文档。

问题 23:列出使用的各种媒体类型。

不同的介质不区分大小写,因此它们具有不同的属性。 他们是:

  1. aural - 用于语音和音频合成器
  2. print - 用于打印机
  3. projection - 用于方案展示,比如幻灯片
  4. handheld - 用于小的手持的设备
  5. screen - 用于电脑显示器

问题 24:font 的属性有哪些?

  1. Font-style
  2. Font-variant
  3. Font-weight
  4. Font-size/line-weight
  5. Font-family

问题 25:“规则集”是什么意思?

该指令告诉浏览器如何在HTML页面上渲染特定元素。 它由一个选择器和一个遵循规则集的声明块组成。 选择器可以附加到其他选择器,以通过规则集进行标识。

问题 26:什么是 CSS 框架?

CSS 框架是一个库,它允许使用CSS语言进行更轻松,更符合标准的Web设计。 这些框架中的大多数至少包含一个网格以及更多功能和其他基于Javascript的功能。 一些著名的CSS框架有:ACSS,Bulma,YAML,Foundation等。


代码部署后可能存在的BUG没法实时知道,事后为了解决这些BUG,花了大量的时间进行log 调试,这边顺便给大家推荐一个好用的BUG监控工具 Fundebug

原文:https://codersera.com/blog/to...


交流

文章每周持续更新,可以微信搜索【大迁世界 】第一时间阅读,回复【福利】有多份前端视频等着你,本文 GitHub https://github.com/qq449245884/xiaozhi 已经收录,欢迎Star。

查看原文

赞 3 收藏 2 评论 0

前端小智 发布了文章 · 2020-11-30

window.location 备忘单,助你理解有着地址问题!

作者:Samantha Ming
译者:前端小智
来源:medium
点赞再看,微信搜索大迁世界,B站关注前端小智这个没有大厂背景,但有着一股向上积极心态人。本文 GitHubhttps://github.com/qq44924588... 上已经收录,文章的已分类,也整理了很多我的文档,和教程资料。

最近开源了一个 Vue 组件,还不够完善,欢迎大家来一起完善它,也希望大家能给个 star 支持一下,谢谢各位了。

github 地址:https://github.com/qq44924588...

如果你想获取站点的URL信息,那么window.location对象什么很适合你! 使用其属性获取有关当前页面地址的信息,或使用其方法进行某些页面重定向或刷新💫

https://segmentfault.com/sear...
window.location.origin    → '"https://segmentfault.com'
               .protocol  → 'https:'
               .host      → 'segmentfault.com'
               .hostname  → 'segmentfault.com'
               .port      → ''
               .pathname  → '/search'
               .search    → '?q=前端小智'
               .hash      → '#2'
               .href      → 'https://segmentfault.com/search?q=前端小智#2'
window.location.assign('url')
               .replace('url')
               .reload()
               .toString()

window.location 属性

window.location返回值
.origin站点主地址(协议 + 主机名 + 端口)
.protocol协议架构 (http: 或者 htts:)
.host域名 + 端口
.port端口
.pathname最前页的 '/' 后面跟的路径
.search? 后跟的查询字符串
.hash# 号开始的部分
.href完整网址

host 和 hostname 的区别

在上面的示例中,你可能注意到hosthostname返回相同的值。 那么为什么要这些属性。 好吧,这与端口号有关,让我们来看看。

没有端口的 URL

https://segmentfault.com/search
window.location.host; // 'segmentfault.com'
window.location.hostname; // 'segmentfault.com'

window.location.port; // ''

带端口的 URL

https://segmentfault.com/search"8080
window.location.host; // 'segmentfault.com:8080'
window.location.hostname; // 'segmentfault.com'

window.location.port; // '8080'

因此,host将包括端口号,而hostname将仅返回主机名。

如何更改 URL 属性

我们不仅可以调用location` 属性来检索URL信息,还可以使用它来设置新属性并更改URL。

// 开始 'https://segmentfault.com/'

window.location.pathname = '/tidbits'; // 设置 pathname

// 结果 'https://segmentfault.com/tidbits'

下面是你可以更改的属性的完整列表

// 事例
window.location.protocol = 'https'
               .host     = 'localhost:8080'
               .hostname = 'localhost'
               .port     = '8080'
               .pathname = 'path'
               .search   = 'query string' // (这里不用写 `?`)
               .hash     = 'hash' // (这里不用写 `#`)
               .href     = 'url'

唯一不能设置的属性是window.location.origin,此属性是只读的。

Location 对象

window.location返回一个Location对象。 它为我们提供有关页面当前地址的信息。 但是我们还可以通过几种方式访问​​Location对象。

window.location          → Location
window.document.location → Location
document.location        → Location
location                 → Location

我们这样做的原因是这些是我们浏览器中的全局变量。

clipboard.png

window.location vs location

上面四个属性都指向同一个Location对象。 我个人更喜欢window.location并且实际上会避免使用location。 主要是因为location看起来像一个普通变量,并且我们有时可能会不小心将其命名为变量,这将覆盖全局变量。 举个例子:

// https://www.samanthaming.com

location.protocol; // 'https'

function localFile() {
  const location = '/sam';

  return location.protocol;
  // ❌ undefined
  //    b/c local "location" has override the global variable
}

我想大多数开发人员都知道window是一个全局变量。这样就不太可能引起混淆。老实说,直到我写了这篇文章,我才知道location 是一个全局变量。建议大家多使用 window.location 来代替其它写法。

window.location 方法

方法作用
.assign()加载一个新的文档
.replace()用新的文档替换当前文档
.reload()重新加载当前页面
.reload()返回的URL

大家都说简历没项目写,我就帮大家找了一个项目,还附赠【搭建教程】

window.location.toString

根据 MDN :

此方法返回 URL 的 USVString,它是 Location.href 的只读版本。

换句话说,我们可以这样得到 href 的值:

// https://www.samanthaming.com

window.location.href; // https://www.samanthaming.com
window.location.toString(); // https://www.samanthaming.com

assign vs replace

这两种方法都是重定向或导航到另一个URL。 区别在于assign 是将当前页面保存在历史记录中,因此用户可以使用“后退”按钮导航到该页面。 而使用replace方法时,不会保存它。 让我们来看一个例子。

Assign

1. 打开一个新的空白页
2. 输入 www.samanthaming.com (当前页)

3. 使用 `window.location.assign('https://www.w3schools.com')` 载入新页面
4. 按 "返回上一页"
5. 返回到了 👉 www.samanthaming.com

Replace

1. 打开一个新的空白页
2. 输入 www.samanthaming.com (当前页)

3. 使用 `window.location.assign('https://www.w3schools.com')` 载入新页面
4. 按 "返回上一页"
5. 返回到一个空白页

如何让页面重定向

如何重定向到另一个页面,有3种方法。

window.location.href = 'https://www.samanthaming.com';

window.location.assign('https://www.samanthaming.com');

window.location.replace('https://www.samanthaming.com');

replace vs assign vs href

这三个都可以重定向,区别在于浏览器的历史记录。 hrefassign 会把当前页面保存在历史记录中,而replace则不会。 因此,如果你想创建一种导航无法回到原始页面的体验,请使用replace👍

现在的问题是hrefassign。 我更喜欢assign,因为它是一种方法,因此感觉好像我正在执行一些操作。 还有一个额外的好处是它更易于测试。 我已经编写了许多Jest测试,因此通过使用一种方法,它使其更易于模拟。

window.location.assign = jest.fn();

myUrlUpdateFunction();

expect(window.location.assign).toBeCalledWith('http://my.url');

最终希望备忘单,希望能对你有所帮助,在需要的时候,能快速给你带来答案。

人才们的 【三连】 就是小智不断分享的最大动力,如果本篇博客有任何错误和建议,欢迎人才们留言,最后,谢谢大家的观看。


原文:https://morioh.com/p/b444d291...

代码部署后可能存在的BUG没法实时知道,事后为了解决这些BUG,花了大量的时间进行log 调试,这边顺便给大家推荐一个好用的BUG监控工具 Fundebug


交流

文章每周持续更新,可以微信搜索【大迁世界 】第一时间阅读,回复【福利】有多份前端视频等着你,本文 GitHub https://github.com/qq449245884/xiaozhi 已经收录,欢迎Star。

查看原文

赞 7 收藏 6 评论 1

前端小智 发布了文章 · 2020-11-23

箭头函数适合哪些场景?

作者:Dmitri Pavlutin
译者:前端小智
来源:Dmitri Pavlutin

点赞再看,微信搜索大迁世界,B站关注前端小智这个没有大厂背景,但有着一股向上积极心态人。本文 GitHubhttps://github.com/qq44924588... 上已经收录,文章的已分类,也整理了很多我的文档,和教程资料。

最近开源了一个 Vue 组件,还不够完善,欢迎大家来一起完善它,也希望大家能给个 star 支持一下,谢谢各位了。

github 地址:https://github.com/qq44924588...

这些年来,ES6 将 JS 的可用性提升到一个新的水平时: 箭头函数、类等等,这些都很棒。

箭头函数是最有价值的新功能之一,有很多好文章描述了它的上下文透明性和简短的语法。

但每个事务都有两面。通常,新特性会带来一些混乱,其中之一就是箭头函数被误导了。本文将介绍一些场景,在这些场景中,你应该绕过箭头函数,转而使用良好的旧函数表达式或较新的简写语法。并且要注意缩短代码,因为这会影响代码的可读性。

1.定义对象上的方法

在JS中,方法是存储在对象属性中的函数。当调用该方法时,this 将指向该方法所属的对象。

Object literal

由于箭头函数语法简短,所以使用它来定义方法是很有吸引力的,让咱们来试一试:

const calculate = {
  array: [1, 2, 3],
  sum: () => {
    console.log(this === window); // => true
    return this.array.reduce((result, item) => result + item);
  }
};
console.log(this === window); // => true
// Throws "TypeError: Cannot read property 'reduce' of undefined"
calculate.sum();

calculate.sum方法用箭头函数定义。 但是在调用时,calculate.sum() 会抛出一个TypeError,因为this.arrayundefined

当调用calculate对象上的方法sum()时,上下文仍然是 window。之所以会发生这种情况,是因为箭头函数按词法作用域将上下文绑定到 window 对象。

执行this.array等同于window.array,它是undefined

解决方法是使用常规函数表达式来定义方法。 this 是在调用时确定的,而不是由封闭的上下文决定的,来看看修复后的版本:

const calculate = {  
  array: [1, 2, 3],
  sum() {
    console.log(this === calculate); // => true
    return this.array.reduce((result, item) => result + item);
  }
};
calculate.sum(); // => 6

因为sum是常规函数,所以在调用 calculate.sum()thiscalculate 对象。 this.array是数组引用,因此正确计算元素之和:6

Object prototype

同样的规则也适用于在原型对象上定义方法。使用一个箭头函数来定义sayCatName方法,this 指向 window

function MyCat(name) {
  this.catName = name;
}
MyCat.prototype.sayCatName = () => {
  console.log(this === window); // => true
  return this.catName;
};
const cat = new MyCat('Mew');
cat.sayCatName(); // => undefined

使用早期的方式定义函数表达式:

function MyCat(name) {
  this.catName = name;
}
MyCat.prototype.sayCatName = function() {
  console.log(this === cat); // => true
  return this.catName;
};
const cat = new MyCat('Mew');
cat.sayCatName(); // => 'Mew'

sayCatName常规函数在作为方法调用时将上下文更改为cat对象:cat.sayCatName()

2. 动态上下文的回调函数

this 在JS中是一个强大的特性,它允许根据调用函数的方式更改上下文。通常,上下文是调用发生的目标对象,这使得代码更加自然,就像这个对象发生了什么。

但是,箭头函数会在声明上静态绑定上下文,并且无法使其动态化,但这种方式有坏也有好,有时候我们需要动态绑定。

在客户端编程中,将事件侦听器附加到DOM元素是一项常见的任务。事件触发处理程序函数,并将this作为目标元素,这里如果使用箭头函数就不够灵活。

下面的示例尝试为这样的处理程序使用箭头函数:

const button = document.getElementById('myButton');
button.addEventListener('click', () => {
  console.log(this === window); // => true
  this.innerHTML = 'Clicked button';
});

在全局上下文中 this 指向 window。 当发生单击事件时,浏览器尝试使用按钮上下文调用处理函数,但箭头函数不会更改其预定义的上下文。this.innerHTML相当于window.innerHTML,没有任何意义。

必须应用函数表达式,该表达式允许根据目标元素更改 this

const button = document.getElementById('myButton');
button.addEventListener('click', function() {
  console.log(this === button); // => true
  this.innerHTML = 'Clicked button';
});

当用户单击按钮时,处理程序函数中的 this 指向 button。因此这个问题。innerHTML = 'Clicked button' 正确地修改按钮文本以反映已单击状态。

3.调用构造函数

this 在构造调用中是新创建的对象。当执行new MyFunction()时,构造函数MyFunction的上下文是一个新对象:this instanceof MyFunction === true

注意,箭头函数不能用作构造函数。 JavaScript通过抛出异常隐式阻止这样做。

无论如何,this是来自封闭上下文的设置,而不是新创建的对象。换句话说,箭头函数构造函数调用没有意义,而且是模糊的。

让我们看看如果尝试这样做会发生什么:

const Message = (text) => {
  this.text = text;
};
// Throws "TypeError: Message is not a constructor"
const helloMessage = new Message('Hello World!');

执行new Message('Hello World!'),其中Message是一个箭头函数,JavaScript抛出一个 TypeError 错误,Message不能用作构造函数。

上面的例子可以使用函数表达式来修复,这是创建构造函数的正确方法(包括函数声明):

const Message = function(text) {
  this.text = text;
};
const helloMessage = new Message('Hello World!');

简写语法

箭头函数有一个很好的属性,它可以省略参数圆括号()、块大括号{},如果函数主体只有一条语句,则返回。这有助于编写非常短的函数。

原文作者的大学编程教授给学生一个有趣的任务:编写 用C语言计算字符串长度的最短函数,这是学习和探索新语言的好方式。

然而,在实际应用程序中,许多开发人员都会阅读代码。 最短的语法并不总是适合帮助你的同事即时了解该方法的用途。

在某种程度上,简写的函数变得难以阅读,所以尽量不要过度使用。让各位们看一个例子

const multiply = (a, b) => b === undefined ? b => a * b : a * b;
const double = multiply(2);
double(3);      // => 6
multiply(2, 3); // => 6

multiply返回两个数字的乘法结果或与第一个参数绑定的闭包,以便以后的乘法运算。

该函数运行良好,看起来很短。但从一开始就很难理解它是做什么的。

为了使其更具可读性,可以从箭头函数恢复可选花括号和return语句,或使用常规函数:

function multiply(a, b) {
  if (b === undefined) {
    return function(b) {
      return a * b;
    }
  }
  return a * b;
}
const double = multiply(2);
double(3);      // => 6
multiply(2, 3); // => 6

在简短和冗长之间找到一个平衡点是很好的,这样可以使代码更加直观。

总结

毫无疑问,箭头函数是一个很好的补充。当正确使用时,它会使前面必须使用.bind()或试图捕获上下文的地方变得简单,它还简化了代码。

某些情况下的优点会给其他情况带来不利。 当需要动态上下文时,不能使用箭头函数:定义方法,使用构造函数创建对象,在处理事件时从 this 获取目标。


代码部署后可能存在的BUG没法实时知道,事后为了解决这些BUG,花了大量的时间进行log 调试,这边顺便给大家推荐一个好用的BUG监控工具 Fundebug

原文:https://dmitripavlutin.com/wh...


交流

文章每周持续更新,可以微信搜索「 大迁世界 」第一时间阅读和催更(比博客早一到两篇哟),本文 GitHub https://github.com/qq449245884/xiaozhi 已经收录,整理了很多我的文档,欢迎Star和完善,大家面试可以参照考点复习,另外关注公众号,后台回复福利,即可看到福利,你懂的。

查看原文

赞 6 收藏 3 评论 1

前端小智 发布了文章 · 2020-11-16

【前端面试】如果使用 JavaScript 原型实现继承

作者:Indermohan Sing
译者:前端小智
来源:blog
点赞再看,微信搜索大迁世界,B站关注前端小智这个没有大厂背景,但有着一股向上积极心态人。本文 GitHubhttps://github.com/qq44924588... 上已经收录,文章的已分类,也整理了很多我的文档,和教程资料。

最近开源了一个 Vue 组件,还不够完善,欢迎大家来一起完善它,也希望大家能给个 star 支持一下,谢谢各位了。

github 地址:https://github.com/qq44924588...

在这篇文章中,我们将讨论原型以及如何在 JS 中使用它们进行继承。我们还将会看到原型方法与基于类的继承有何不同。

继承

继承是编程语言的一个显著特征,随着面向对象编程语言的引入而出现。这些语言大多是基于类的语言。在这里,类就像一个蓝图,对象是它的展现形式。就是说,要创建一个对象,首先我们必须创建一个类,然后我们可以从一个类创建任意数量的对象。

想象一下,我们有一个表示智能手机的类。这个类具有像其他智能手机一样的可以拍照、有GPS定位等功能。下面是使用 c++ 来描述这样的一个类:

class SmartPhone {
  public:
  void captureImages() {}
}

SmartPhone x;
x.captureImages()

我们创建了一个名为SmartPhone的类,它有一个名为capturePictures的方法用来拍照。

如果我们需要一个iPhone类,它可以捕捉图像和一些特殊的功能,比如面部ID扫描。下面是两种可能的解决方案:

1.将捕获图像功能与其他常见的智能手机功能,以及iPhone的特定功能一起重写到一个新类中。但是这种方法需要更多的时间和精力,并且会引入更多的bug。

  1. 重用SmartPhone类中的功能,这就是继承的作用,继承也是重用其他类/对象中功能的一种方式。

这里是我们如何从SmartPhone类中继承capturePictures方法,使用 c++ 实现如下:

class Iphone: public SmartPhone {
  public:
  void faceIDScan() {}
}

Iphone x

x.faceIDScan()

x.captureImages()

上面是一个简单的继承示例。 但是,它表明继承可以使我们以某种方式重用代码,从而使所生成的程序更不易出错,并且花费更少的时间进行开发。

以下是关于类的一些重要信息:

  • 继承该功能的类称为子类
  • 被继承的类称为父类
  • 一个类可以同时从多个类中继承
  • 我们可以具有多个继承级别。 例如,类C继承自类B,而类B继承自类A

值得注意的是,类本身并没有做任何事情。在从类创建对象之前,实际上没有完成任何工作。我们将看到它为什么不同于JavaScript。

大家都说简历没项目写,我就帮大家找了一个项目,还附赠【搭建教程】

原型是什么?

在 JS 中,所有对象都有一个特殊的内部属性,该属性基本上是对另一个对象的引用。 此引用取决于对象的创建方式。 在 ECMAScript/JavaScript规范中,它表示为[[Prototype]]

由于[[Prototype]]链接到一个对象,所以该对象有自己的[[Prototype]]引用。这就是建立原型链的方式。

这个[[Prototype]]链是 JS 中继承的构建块。

__proto__ 对象

为了访问对象的[[Prototype]],大多数浏览器都提供__proto__属性。访问方式如下:

obj.__proto__

需要注意的是,这个属性不是 ECMAScript 标准的一部分,它实际上是由浏览器实现的。

获取和设置原型方法

除了__proto__属性外,还有一种访问[[Prototype]]的标准方法:

Object.getPrototypeOf(obj);

对应的有个类似的方法来设置对象的[[Prototype]]

Object.setPrototypeOf(obj, prototype);

[[Prototype]].prototype属性

[[Prototype]] 只不过是一种用来表示物体原型的标准符号。 许多开发人员将其与.prototype属性混淆,这是完全不同的事情,接着我们来研究一下.prototype属性。

在 JS 中,有许多创建对象的方法。一种方法是使用构造函数,像这样使用new关键字来调用它:

function SmartPhone(os) {
  this.os = os
}

let phone = new SmartPhone('Android')

在控制台打印 phone 对象:

{
  os: "IPhone"
  __proto__{
    constructor: ƒ SmartPhone(os)
   __proto__: Object
  }
}

现在,如果我们希望在phone对象上有一些方法,我们可以在函数上使用.prototype属性,如下所示:

SmartPhone.prototype.isAndroid = function () {
  return this.os === 'Android' || 'android'
}

再次创建phone对象时,打印 phone 对象如下:

{
  os: "Android"
  __proto__{
    isAndroid: ƒ()
    constructor: ƒ SmartPhone(os)
   __proto__: Object
  }
}

我们可以在对象的[[Prototype]]中看到isAndroid()方法。

简而言之,.prototype属性基本上就像由给定的构造函数创建的[[Prototype]]对象的蓝图。 在.prototype属性/对象中声明的所有内容都会在对象的[[Prototype]]中弹出。

实上,如果将 SmartPhone.prototype 与phone 的[[Prototype]]进行比较,就会发现它们是相同的:

console.log(Object.getPrototypeOf(phone) === SmartPhone.prototype);
// true

值得注意的是,我们还可以在构造函数中创建方法:

function ObjectA() {
  this.methodA = function () {}
}

let firstObj = new ObjectA()
console.log(firstObj)

这种方法的问题是当我们初始化一个新对象时。所有实例都有自己methodA的副本。相反,当我们在函数的原型上创建它时,对象的所有实例只共享方法的一个副本,显然使用原型的方式效率会过高。

大家都说简历没项目写,我就帮大家找了一个项目,还附赠【搭建教程】

当我们访问属性时这里发生了什么?

当我们访问一个属性以获取它时,会发生以下情况:

JS 引擎查找对象上的属性,如果找到了该属性,然后返回它。否则,JS 引擎将通过查看[[Prototype]]来检查对象的继承属性,如果找到该属性,则返回它,否则,它会查找 [[Prototype]][[Prototype]]。 找到属性或没有[[Prototype]]时,该链结束,这意味着我们已经到达原型链的末端。

当我们设置/创建属性时,JS 总是在对象本身上进行设置。 即使[[Prototype]]链上存在相同的属性,下面是一个例子:

function MyObject() {}
MyObject.prototype.propA = 10; // 在原型上创建属性

let myObject = new MyObject();
console.log(myObject.propA); // [[Prototype]]上的属性
// 10

myObject.propA = 20; // 对象的属性
console.log(myObject.propA);
// 20

在上面的示例中,我们创建了一个构造函数,该函数的[[Prototype]]上具有属性propA。 当我们尝试对其进行读取操作时,会在控制台中看到该值。 但是,当我们尝试在对象本身上设置相同的属性时;JS 使用给定值在对象上创建一个新属性。 现在,如果我们不能直接访问[[Prototype]]上的属性。

值得注意的是,普通对象的[[Prototype]]链的末尾是内置的Object.prototype。 这就是为什么大多数对象共享许多方法(例如toString())的原因。 因为它们实际上是在Object.prototype上定义的。

使用原型继承的各种方法

在 JS 中,无论我们如何创建对象,只有原型继承,但这些方式还有一些区别,来看看:

对象字面量

在JavaScript中创建对象的最简单方法是使用对象字面量:

let obj = {}

如果在浏览器的控制台中打印obj,我们将看到以下内容:

clipboard.png

基本上,所有用文字面量创建的对象都继承了Object.prototype的属性。

需要注意的是__proto__对象引用了创建它的构造函数。 在这种情况下,constructor属性指向Object构造函数。

使用对象构造函数

另一种不太常见的创建对象的方法是使用对象构造函数。JS 提供了一个名为Object的内置构造函数方法来创建对象。

let obj = new Object();

这种方法的结果与对象字面量的方式相同。它从Object.prototype继承属性。因为我们使用Object作为构造函数。

Object.create 方法

使用此辅助方法,我们可以创建一个带有[[Prototype]]的对象,如下所示:

let SmartPhone = {
  captureImages: function() {}
}

let Iphone = Object.create(SmartPhone)

Iphone.captureImages()

这是在 JS 中使用继承的最简单方法之一。猜猜我们如何在没有任何[[Prototype]]引用的情况下创建对象?

构造方法

与 JS 运行时提供的对象构造函数相似。 我们还可以创建自己的构造函数,以创建适合我们需求的对象,如下所示:

function SmartPhone(os) {
  this.os = os;
}

SmartPhone.prototype.isAndroid = function() {
  return this.os === 'Android';
};

SmartPhone.prototype.isIOS = function() {
  return this.os === 'iOS';
};

现在,我们想创建一个iPhone类,它应该有'iOS'作为它 os 属性的值。它还应该有faceIDScan方法。

首先,我们必须创建一个Iphone构造函数,在其中,我们应该调用SmartPhone构造函数,如下所示:

function Iphone() {
   SmartPhone.call(this, 'iOS');
}

这会将Iphone构造函数中的this.os属性设置为’iOS‘

之所以调用SmartPhone.call方法,是因为我们需要更改 this 值以引用Iphone。 这类似于在面向对象的世界中调用父级的构造函数。

接下来的事情是,我们必须从SmartPhone构造函数继承方法。 我们可以在此处使用Object.create朋友,如下所示:

Iphone.prototype = Object.create(SmartPhone.prototype);

现在,我们可以使用.prototypeIphone添加方法,如下所示:

Iphone.prototype.faceIDScan = function() {};

最后,我们可以使用Iphone创建一个对象,如下所示:

let x = new Iphone();

// calling inherited method
console.log(x.isIOS()):
// true

ES6 class

使用ES6,整个过程非常简单。 我们可以创建类(它们与C ++或其他任何基于类的语言中的类不同,只是在原型继承之上的语法糖),然后从其他类派生新的类。

下面是我们如何在ES6中创建类:

class SmartPhone {
  constructor(os) {
    this.os = os;
  }
  isAndroid() {
    return this.os === 'Android';
  }
  isIos() {
    return this.os === 'iOS';
  }
};

现在,我们可以创建一个派生自SmartPhone的新类,如下所示:

class Iphone extends SmartPhone {
   constructor() {
     super.call('iOS');
   }
   faceIDScan() {}
}

我们不是调用SmartPhone.call,而是调用super.call。 在内部,JavaScript引擎会自动为我们执行此操作。

最后,我们可以使用Iphone创建一个对象,如下所示

let x = new Iphone();

x.faceIDScan();

// calling inherited method
console.log(x.isIos()):
// true

该ES6示例与先前的构造方法示例相同。 但是阅读和理解起来要干净得多。


原文:https://javascript.info/proto...

代码部署后可能存在的BUG没法实时知道,事后为了解决这些BUG,花了大量的时间进行log 调试,这边顺便给大家推荐一个好用的BUG监控工具 Fundebug


交流

文章每周持续更新,可以微信搜索「 大迁世界 」第一时间阅读和催更(比博客早一到两篇哟),本文 GitHub https://github.com/qq449245884/xiaozhi 已经收录,整理了很多我的文档,欢迎Star和完善,大家面试可以参照考点复习,另外关注公众号,后台回复福利,即可看到福利,你懂的。

查看原文

赞 9 收藏 5 评论 0

前端小智 发布了文章 · 2020-11-13

要提升前端布局能力,这些 CSS 属性需要学习下!

作者:John
译者:前端小智
来源:smashingmagazine
点赞再看,微信搜索大迁世界,B站关注前端小智这个没有大厂背景,但有着一股向上积极心态人。本文 GitHubhttps://github.com/qq44924588... 上已经收录,文章的已分类,也整理了很多我的文档,和教程资料。

最近开源了一个 Vue 组件,还不够完善,欢迎大家来一起完善它,也希望大家能给个 star 支持一下,谢谢各位了。

github 地址:https://github.com/qq44924588...

属性选择器非常神奇。它们可以使你摆脱棘手的问题,帮助你避免添加类,并指出代码中的一些问题。但是不要担心,虽然属性选择器非常复杂和强大,但是它们很容易学习和使用。在本文中,我们将讨论它们是如何运行的,并给出一些如何使用它们的想法。

通常将 HTML 属性放在方括号中,称为属性选择器,如下:

[href] {
   color: red;
}

这样任何具有href属性的且没有更特定选择器的元素的文本颜色都会是红色的。属性选择器的特性与类相同。

:更多关于笼匹配的CSS特异性,你可以阅读CSS特性:你应该知道的事情,或者如果你喜欢星球大战:CSS特性战争

但是你可以使用属性选择器做得更多。就像你的 DNA 一样,它们有内在的逻辑来帮助你选择各种属性组合和值。它们可以匹配属性中的任何属性,甚至字符串值,而不是像标签、类或id选择器那样精确匹配。

属性选择器

属性选择器可以独立存在,更具体地说,如果需要选择所有具有title属性的div标签,可以这么做:

div[title]

但你也可以通过以下操作选择具有 title 属性的 div 的子元素

div [title]

需要说明的是,它们之间没有空格意味着属性位于相同的元素上(就像元素和类之间没有空格一样),而它们之间的空格意味着后代选择器,即选择具有该属性的元素的子元素。

你可以更精细地选择具体属性值的属性。

div[title="dna"]

上面选择了所有具有确切名称dna的div,虽然有选择器算法可以处理每种情况(以及更多),但这里不会选择“dna is awesome”“dnamutation”的标题。

注意:在大多数情况下,属性选择器中不需要引号,但是我使用它们,因为我相信它可以提高清代码的可读性,并确保边界用例能够正常工作。

如果你想选择 title 包含 dna的元素,如 “my beautiful dna” 或者 “mutating dna is fun!” ,可以使用波浪号(~)。

div[title~="dna"]

如果你想匹配以 dna 结尾的 title,如  “dontblamemeblamemydna” 或 “his-stupidity-is-from-upbringing-not-dna” ,刚可以使用$标志符:

[title$="dna"]

如果你想匹配以 dna 开头的 title,如  “dnamutants” 或 “dna-splicing-for-all” ,刚可以使用^标志符:

[title^="dna"]

虽然精确匹配是有帮助的,但它可能选择太紧,并且^符号匹配可能太宽而无法满足你的需要。 例如,可能不想选择 “genealogy” 的标题,但仍然选择“gene”和“gene-data”。 管道特征(|)就是这样,属性中必须是完整且唯一的单词,或者以-分隔开。

[title|="gene"]

最后,还有一个匹配任何子字符串的模糊搜索属性操作符,属性中做字符串拆分,只要能拆出来dna这个词就行:

[title*="dna"]

使这些属性选择器更加强大的是,它们是可堆叠的,允许你选择具有多个匹配因子的元素。

如果你需要找到一个a 标签,它有一个 title ,并且有一个以“genes” 结尾的 class,可以使用如下方式:

a[title][class$="genes"]

你不仅可以选择 HTML 元素的属性,还可以使用伪类型元素来打印出文本:

    <span class="joke" title="Gene Editing!">What’s the first thing a biotech journalist does after finishing the first draft of an article?</span>
.joke:hover:after {
   content: "Answer:" attr(title);
   display: block;
}

上面的代码在鼠标悬停时将显示一串自定义的字符串。

最后要知道的是,您可以添加一个标志,让属性搜索不区分大小写。 在结束方括号之前添加i

[title*="DNA" i]

因此它会匹配dna, DNA, dnA等。

现在我们已经看到了如何使用属性选择器进行选择,让我们看看一些用例。 我将它们分为两类:一般用途诊断

一般用途

输入类型样式的设置

你可以对输入类型使用不同的样式,例如电子邮件和电话。

input[type="email"] {
   color: papayawhip;
}
input[type="tel"] {
   color: thistle;
}

显示电话链接

你可以隐藏特定尺寸的电话号码并显示电话链接,以便在手机上轻松拨打电话。

span.phone {
   display: none;
}
a[href^="tel"] {
   display: block;
}

内部链接 vs 外部链接,安全链接 vs 非安全链接

你可以区别对待内部和外部链接,并将安全链接设置为与不安全链接不同:

a[href^="http"]{
   color: bisque;
}
a:not([href^="http"]) {
  color: darksalmon;
}

a[href^="http://"]:after {
   content: url(unlock-icon.svg);
}
a[href^="https://"]:after {
   content: url(lock-icon.svg);
}

下载图标

HTML5 给我们的一个属性是“下载”,它告诉浏览器,你猜对了,下载该文件而不是试图打开它。这对于你希望人们访问但不希望它们立即打开的 PDFDOC 非常有用。它还使得连续下载大量文件的工作流程更加容易。下载属性的缺点是没有默认的视觉效果将其与更传统的链接区分开来。通常这是你想要的,但如果不是,你可以做类似下面的事情:

a[download]:after { 
   content: url(download-arrow.svg);
}

还可以使用不同的图标(如PDF与DOCX与ODF等)来表示文件类型,等等。

a[href$="pdf"]:after {
   content: url(pdf-icon.svg);
}
a[href$="docx"]:after {
   content: url(docx-icon.svg);
}
a[href$="odf"]:after {
   content: url(open-office-icon.svg);
}

你还可以通过叠加属性选择器来确保这些图标只出现在可下载链接上。

a[download][href$="pdf"]:after {
   content: url(pdf-icon.svg);
}

覆盖或重新使用已废弃/弃用的代码

我们都遇到过时代码过时的旧网站,在 HTML5 之前,你可能需要覆盖甚至重新应用作为属性实现的样式。

<div bgcolor="#000000" color="#FFFFFF">Old, holey genes</div>

div[bgcolor="#000000"] { /*override*/
   background-color: #222222 !important;
}
div[color="#FFFFFF"] { /*reapply*/
   color: #FFFFFF;
}

重写特定的内联样式

有时候你会遇到一些内联样式,这些样式会影响布局,但这些内联样式我们又没修改。那么以下是一种方法。

如果你道要覆盖的确切属性和值,并且希望在它出现的任何地方覆盖它,那么这种方法的效果最好。

对于此示例,元素的边距以像素为单位设置,但需要在 em 中进行扩展和设置,以便在用户更改默认字体大小时可以正确地重新调整元素。

<div style="color: #222222; margin: 8px; background-color: #EFEFEF;"Teenage Mutant Ninja Myrtle</div>

div[style*="margin: 8px"] {
   margin: 1em !important;
}

显示文件类型

默认情况下,文件输入的可接受文件列表是不可见的。 通常,我们使用伪元素来暴露它们:

<input type="file" accept="pdf,doc,docx">

[accept]:after {
   content: "Acceptable file types: " attr(accept);
}

html 手风琴菜单

detailssummary标签是一种只用HTML做扩展/手风琴菜单的方法,details 包括了summary标签和手风琴打开时要展示的内容。点击summary会展开details标签并添加open属性,我们可以通过open属性轻松地为打开的details标签设置样式:

details[open] {
   background-color: hotpink;
}

打印链接

在打印样式中显示URL使我走上了理解属性选择器的道路。 你现在应该知道如何自己构建它, 你只需选择带有href的所有标签,添加伪元素,然后使用attr()content打印它们。

a[href]:after {
   content: " (" attr(href) ") ";
}

自定义提示

使用属性选择器创建自定义工具提示既有趣又简单:

[title] {
  position: relative;
  display: block;
}
[title]:hover:after {
  content: attr(title);
  color: hotpink;
  background-color: slateblue;
  display: block;
  padding: .225em .35em;
  position: absolute;
  right: -5px;
  bottom: -5px;
}

便捷键

web 的一大优点是它提供了许多不同的信息访问选项。一个很少使用的属性是设置accesskey的能力,这样就可以通过键组合和accesskey设置的字母直接访问该项目(确切的键组合取决于浏览器)。但是要想知道网站上设置了哪些键并不是件容易的事

下面的代码将显示这些键:focus。我不使用鼠标悬停,因为大多数时候需要accesskey的人是那些使用鼠标有困难的人。你可以将其添加为第二个选项,但要确保它不是惟一的选项。

a[accesskey]:focus:after {
   content: " AccessKey: " attr(accesskey);
}

诊断

这些选项用于帮助我们在构建过程中或在尝试修复问题时在本地识别问题。将这些内容放在我们的生产网站上会使用户产生错误。

没有 controls 属性的 audio

我不经常使用audio标签,但是当我使用它时,我经常忘记包含controls属性。 结果:没有显示任何内容。 如果你在 Firefox,如果隐藏了音频元素,或者语法或其他一些问题阻止它出现(仅适用于Firefox),此代码可以帮助你解决问题:

audio:not([controls]) {
  width: 100px;
  height: 20px;
  background-color: chartreuse;
  display: block;
}

没有 alt 文本

没有 alt 文本的图像是可访问性的噩梦。只需查看页面就很难找到它们,但如果添加它们,它们就会弹出来(当页面图片加载失败时,alt文字可以更好的解释图片的作用):

img:not([alt]) { /* no alt attribute */ 
  outline: 2em solid chartreuse; 
}
img[alt=""] { /* alt attribute is blank */ 
  outline: 2em solid cadetblue; 
}

异步 Javascript 文件

网页可以是内容管理系统和插件,框架和代码的集合,确定哪些JavaScript异步加载以及哪些不加载可以帮助你专注于提高页面性能。

script[src]:not([async]) {
  display: block;
  width: 100%;
  height: 1em;
  background-color: red;
}
script:after {
  content: attr(src);
}

JavaScript 事件元素

你可以突出显示具有JavaScript事件属性的元素,以便将它们重构到JavaScript文件中。这里我主要关注OnMouseOver属性,但是它适用于任何JavaScript事件属性。

[OnMouseOver] {
   color: burlywood;
}
[OnMouseOver]:after {
   content: "JS: " attr(OnMouseOver);
}

隐藏项

如果需要查看隐藏元素或隐藏输入的位置,可以使用它们来显示

[hidden], [type="hidden"] {
  display: block;
}

原文:https://www.smashingmagazine....

代码部署后可能存在的BUG没法实时知道,事后为了解决这些BUG,花了大量的时间进行log 调试,这边顺便给大家推荐一个好用的BUG监控工具 Fundebug


交流

文章每周持续更新,可以微信搜索「 大迁世界 」第一时间阅读和催更(比博客早一到两篇哟),本文 GitHub https://github.com/qq449245884/xiaozhi 已经收录,整理了很多我的文档,欢迎Star和完善,大家面试可以参照考点复习,另外关注公众号,后台回复福利,即可看到福利,你懂的。

查看原文

赞 19 收藏 11 评论 0

前端小智 发布了文章 · 2020-11-10

学了这 7 个 CSS 属性,我的 CSS 技能又进步啦!

作者:Mustapha Aouas
译者:前端小智
来源:dev
点赞再看,养成习惯

本文 GitHubhttps://github.com/qq44924588... 上已经收录,更多往期高赞文章的分类,也整理了很多我的文档,和教程资料。欢迎Star和完善,大家面试可以参照考点复习,希望我们一起有点东西。

大家都说简历没项目写,我就帮大家找了一个项目,还附赠【搭建教程】

学习CSS是构建好看网页的一种方式。 但是,在学习过程中,我们倾向于(大部分时间)限制自己,一遍又一遍地使用相同的属性。 毕竟,我们是一种习惯性的动物,我们会使用自己习惯且熟悉的东西。

因此,在这篇文章中,向你介绍7个 比较少见且好用的 CSS 属性,希望对你有所帮助。

1. vertical-align

CSS 的属性 vertical-align 用来指定行内元素(inline)或表格单元格(table-cell)元素的垂直对齐方式。

就像定义说的,这个属性允许你垂直对齐文本。它对于顺序指示器(st, nd等)、需要的输入星号(*)或没有正确居中的图标特别有用。vertical-align取其中一个值:super | top | middle | bottom | baseline (default) | sub | text-top | text-bottom,或从基线开始的长度(px%em, rem等等)。

baseline: 使元素的基线与父元素的基线对齐。HTML规范没有详细说明部分可替换元素的基线,如<textarea> ,这意味着这些元素使用此值的表现因浏览器而异。

sub:使元素的基线与父元素的下标基线对齐。

super:使元素的基线与父元素的上标基线对齐。

text-top:使元素的基线与父元素的上标基线对齐。

text-bottom:使元素的底部与父元素的字体底部对齐。

middle:使元素的中部与父元素的基线加上父元素x-height(译注:x高度)的一半对齐。

图片描述

注意 vertical-align 只对行内元素、表格单元格元素生效:不能用它垂直对齐块级元素。

资源:MDN

2. writing-mode

writing-mode 属性定义了文本水平或垂直排布以及在块级元素中文本的行进方向。为整个文档设置书时,应在根元素上设置它(对于 HTML 文档应该在 html 元素上设置)。 它采用以下值之一horizontal-tb (default) | vertical-rl | vertical-lr

clipboard.png

horizontal-tb:对于左对齐(ltr)脚本,内容从左到右水平流动。对于右对齐(rtr)脚本,内容从右到左水平流动。下一水平行位于上一行下方。

vertical-rl:对于左对齐(ltr)脚本,内容从上到下垂直流动,下一垂直行位于上一行左侧。对于右对齐(rtr)脚本,内容从下到上垂直流动,下一垂直行位于上一行右侧。

vertical-lr:对于左对齐(ltr)脚本,内容从上到下垂直流动,下一垂直行位于上一行右侧。对于右对齐(rtr)脚本,内容从下到上垂直流动,下一垂直行位于上一行左侧。

资源:MDN

大家都说简历没项目写,我就帮大家找了一个项目,还附赠【搭建教程】

3. font-variant-numeric

font-variant-numeric CSS属性控制数字,分数和序号标记的替代字形的使用。

它采用以下这些值之一: normal | ordinal | slashed-zero | lining-nums | oldstyle-nums | proportional-nums | tabular-nums | diagonal-fractions | stacked-fractions

此属性对于设置数字样式很有用。 根据情况,你可能希望显示老式的数字或带有斜杠的零,对于这些情况,font-feature-settings很有用。

图片描述

请注意,font-variant-numericfont-feature-settings组属性的一部分。 诸如font-variant-capsfont-variant-ligatures之类的属性也属于该组。
还要注意,像所有font-feature-settings属性一样,你的字体需要实现上述功能才能正常工作。 我使用的字体是Fira Sans

资源:MDN

4. user-select

每当我们有不想让用户选择的文本,或者相反,如果发生了双击或上下文单击,希望选择所有文本时,user-select属性将非常有用。

此属性采用以下值之一:none | auto | text | all

none:元素及其子元素的文本不可选中。 请注意这个Selection 对象可以包含这些元素。 从Firefox 21开始, none 表现的像 -moz-none,因此可以使用 -moz-user-select: text 在子元素上重新启用选择。

auto
auto 的具体取值取决于一系列条件,具体如下:

  • ::before::after 伪元素上,采用的属性值是 none
  • 如果元素是可编辑元素,则采用的属性值是 contain
  • 否则,如果此元素的父元素的 user-select 采用的属性值为 all,则该元素采用的属性值也为 all
  • 否则,如果此元素的父元素的 user-select 采用的属性值为 none,则该元素采用的属性值也为 none
  • 否则,采用的属性值为 text

text:用户可以选择文本。
all:在一个HTML编辑器中,当双击子元素或者上下文时,那么包含该子元素的最顶层元素也会被选中。

图片描述

资源:MDN

大家都说简历没项目写,我就帮大家找了一个项目,还附赠【搭建教程】

5. clip-path

clip-path CSS 属性可以创建一个只有元素的部分区域可以显示的剪切区域。区域内的部分显示,区域外的隐藏。剪切区域是被引用内嵌的URL定义的路径或者外部svg的路径,或者作为一个形状例如circle()clip-path属性代替了现在已经弃用的剪切 clip属性。

此属性采用以下值之一:circle() | ellipse() | polygon() | path() | url()

由于这是对该属性的介绍,因此,这里不会深入研究每个值。

我使用最多的两个值是circlepolygoncircle(radius at pair)值有两个参数,第一个参数是圆的半径,第二个参数是表示圆心的点。polygon(pair, pair, pair ...)值取3个或更多的点,表示一个三角形、一个矩形等等。

图片描述

6. shape-outside

shape-outside的CSS 属性定义了一个可以是非矩形的形状,相邻的内联内容应围绕该形状进行包装。 默认情况下,内联内容包围其边距框; shape-outside提供了一种自定义此包装的方法,可以将文本包装在复杂对象周围而不是简单的框中。它采用与clip-path相同的值。

clip-path定义用户如何查看元素,shape-outside定义其他HTML元素如何查看元素。

clipboard.png

资源:MDN

7. background-clip

最后,backgroundclip CSS属性设置元素的背景是否扩展到其borderpaddingcontent 框之下。

此属性采用以下值之一:border-box (default) | padding-box | content-box | text

图片描述

资源:MDN

总结

下图是结合上面 7 个属性实现的布局,让大家加深一下印象。

clipboard.png

如果你还知道一些新奇的属性,欢迎留言。


代码部署后可能存在的BUG没法实时知道,事后为了解决这些BUG,花了大量的时间进行log 调试,这边顺便给大家推荐一个好用的BUG监控工具 Fundebug

原文:https://dev.to/mustapha/7-ama...


交流

文章每周持续更新,可以微信搜索「 大迁世界 」第一时间阅读和催更(比博客早一到两篇哟),本文 GitHub https://github.com/qq449245884/xiaozhi 已经收录,整理了很多我的文档,欢迎Star和完善,大家面试可以参照考点复习,另外关注公众号,后台回复福利,即可看到福利,你懂的。

查看原文

赞 29 收藏 21 评论 0

前端小智 发布了文章 · 2020-11-04

为了面试能通过,我要看完这75道面试题(下)

作者:Mark A
译者:前端小智
来源:dev
点赞再看,微信搜索 【大迁世界】 关注这个没有大厂背景,但有着一股向上积极心态人。本文 GitHubhttps://github.com/qq44924588... 上已经收录,文章的已分类,也整理了很多我的文档,和教程资料。

编程,建网站必备的阿里云服务器竟然免费送了!

51. 什么是 async/await 及其如何工作?

async/await是 JS 中编写异步或非阻塞代码的新方法。它建立在Promises之上,让异步代码的可读性和简洁度都更高。

async/await是 JS 中编写异步或非阻塞代码的新方法。 它建立在Promises之上,相对于 Promise 和回调,它的可读性和简洁度都更高。 但是,在使用此功能之前,我们必须先学习Promises的基础知识,因为正如我之前所说,它是基于Promise构建的,这意味着幕后使用仍然是Promise

使用 Promise

function callApi() {
  return fetch("url/to/api/endpoint")
    .then(resp => resp.json())
    .then(data => {
      //do something with "data"
    }).catch(err => {
      //do something with "err"
    });
}

使用async/await

async/await,我们使用 tru/catch 语法来捕获异常。

async function callApi() {
  try {
    const resp = await fetch("url/to/api/endpoint");
    const data = await resp.json();
    //do something with "data"
  } catch (e) {
    //do something with "err"
  }
}

注意:使用 async关键声明函数会隐式返回一个Promise

const giveMeOne = async () => 1;

giveMeOne()
  .then((num) => {
    console.log(num); // logs 1
  });

注意:await关键字只能在async function中使用。在任何非async function的函数中使用await关键字都会抛出错误。await关键字在执行下一行代码之前等待右侧表达式(可能是一个Promise)返回。

const giveMeOne = async () => 1;

function getOne() {
  try {
    const num = await giveMeOne();
    console.log(num);
  } catch (e) {
    console.log(e);
  }
}

// Uncaught SyntaxError: await is only valid in async function

async function getTwo() {
  try {
    const num1 = await giveMeOne(); // 这行会等待右侧表达式执行完成
    const num2 = await giveMeOne(); 
    return num1 + num2;
  } catch (e) {
    console.log(e);
  }
}

await getTwo(); // 2

52. 展开(spread )运算符和 剩余(Rest) 运算符有什么区别?

展开运算符(spread)是三个点(...),可以将一个数组转为用逗号分隔的参数序列。说的通俗易懂点,有点像化骨绵掌,把一个大元素给打散成一个个单独的小元素。

剩余运算符也是用三个点(...)表示,它的样子看起来和展开操作符一样,但是它是用于解构数组和对象。在某种程度上,剩余元素和展开元素相反,展开元素会“展开”数组变成多个元素,剩余元素会收集多个元素和“压缩”成一个单一的元素。

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

const nums = [5, 6];
const sum = add(...nums);
console.log(sum);

在本例中,我们在调用add函数时使用了展开操作符,对nums数组进行展开。所以参数a的值是5b的值是6,所以sum11

function add(...rest) {
  return rest.reduce((total,current) => total + current);
};

console.log(add(1, 2)); // 3
console.log(add(1, 2, 3, 4, 5)); // 15

在本例中,我们有一个add函数,它接受任意数量的参数,并将它们全部相加,然后返回总数。

const [first, ...others] = [1, 2, 3, 4, 5];
console.log(first); // 1
console.log(others); // [2,3,4,5]

这里,我们使用剩余操作符提取所有剩余的数组值,并将它们放入除第一项之外的其他数组中。

53. 什么是默认参数?

默认参数是在 JS 中定义默认变量的一种新方法,它在ES6或ECMAScript 2015版本中可用。

//ES5 Version
function add(a,b){
  a = a || 0;
  b = b || 0;
  return a + b;
}

//ES6 Version
function add(a = 0, b = 0){
  return a + b;
}
add(1); // returns 1 

我们还可以在默认参数中使用解构。

function getFirst([first, ...rest] = [0, 1]) {
  return first;
}

getFirst();  // 0
getFirst([10,20,30]);  // 10

function getArr({ nums } = { nums: [1, 2, 3, 4] }){
    return nums;
}

getArr(); // [1, 2, 3, 4]
getArr({nums:[5,4,3,2,1]}); // [5,4,3,2,1]

我们还可以使用先定义的参数再定义它们之后的参数。

function doSomethingWithValue(value = "Hello World", callback = () => { console.log(value) }) {
  callback();
}
doSomethingWithValue(); //"Hello World"

54. 什么是包装对象(wrapper object)?

我们现在复习一下JS的数据类型,JS数据类型被分为两大类,基本类型引用类型

基本类型:Undefined,Null,Boolean,Number,String,Symbol,BigInt

引用类型:Object,Array,Date,RegExp等,说白了就是对象。

其中引用类型有方法和属性,但是基本类型是没有的,但我们经常会看到下面的代码:

let name = "marko";

console.log(typeof name); // "string"
console.log(name.toUpperCase()); // "MARKO"

name类型是 string,属于基本类型,所以它没有属性和方法,但是在这个例子中,我们调用了一个toUpperCase()方法,它不会抛出错误,还返回了对象的变量值。

原因是基本类型的值被临时转换或强制转换为对象,因此name变量的行为类似于对象。 除nullundefined之外的每个基本类型都有自己包装对象。也就是:StringNumberBooleanSymbolBigInt。 在这种情况下,name.toUpperCase()在幕后看起来如下:

console.log(new String(name).toUpperCase()); // "MARKO"

在完成访问属性或调用方法之后,新创建的对象将立即被丢弃。

55. 隐式和显式转换有什么区别)?

隐式强制转换是一种将值转换为另一种类型的方法,这个过程是自动完成的,无需我们手动操作。

假设我们下面有一个例子。

console.log(1 + '6'); // 16
console.log(false + true); // 1
console.log(6 * '2'); // 12

第一个console.log语句结果为16。在其他语言中,这会抛出编译时错误,但在 JS 中,1被转换成字符串,然后与+运算符连接。我们没有做任何事情,它是由 JS 自动完成。

第二个console.log语句结果为1,JS 将false转换为boolean 值为 0,,true1,因此结果为1

第三个console.log语句结果12,它将'2'转换为一个数字,然后乘以6 * 2,结果是12。

而显式强制是将值转换为另一种类型的方法,我们需要手动转换。

console.log(1 + parseInt('6'));

在本例中,我们使用parseInt函数将'6'转换为number ,然后使用+运算符将16相加。

56. 什么是NaN? 以及如何检查值是否为NaN?

NaN表示“非数字”是 JS 中的一个值,该值是将数字转换或执行为非数字值的运算结果,因此结果为NaN

let a;

console.log(parseInt('abc')); // NaN
console.log(parseInt(null)); // NaN
console.log(parseInt(undefined)); // NaN
console.log(parseInt(++a)); // NaN
console.log(parseInt({} * 10)); // NaN
console.log(parseInt('abc' - 2)); // NaN
console.log(parseInt(0 / 0)); // NaN
console.log(parseInt('10a' * 10)); // NaN

JS 有一个内置的isNaN方法,用于测试值是否为isNaN值,但是这个函数有一个奇怪的行为。

console.log(isNaN()); // true
console.log(isNaN(undefined)); // true
console.log(isNaN({})); // true
console.log(isNaN(String('a'))); // true
console.log(isNaN(() => { })); // true

所有这些console.log语句都返回true,即使我们传递的值不是NaN

ES6中,建议使用Number.isNaN方法,因为它确实会检查该值(如果确实是NaN),或者我们可以使自己的辅助函数检查此问题,因为在 JS 中,NaN是唯一的值,它不等于自己。

function checkIfNaN(value) {
  return value !== value;
}

57. 如何判断值是否为数组?

我们可以使用Array.isArray方法来检查值是否为数组。 当传递给它的参数是数组时,它返回true,否则返回false

console.log(Array.isArray(5));  // false
console.log(Array.isArray("")); // false
console.log(Array.isArray()); // false
console.log(Array.isArray(null)); // false
console.log(Array.isArray({ length: 5 })); // false

console.log(Array.isArray([])); // true

如果环境不支持此方法,则可以使用polyfill实现。

function isArray(value){
 return Object.prototype.toString.call(value) === "[object Array]"
}

当然还可以使用传统的方法:

let a = []
if (a instanceof Array) {
  console.log('是数组')
} else {
  console.log('非数组')
}

58. 如何在不使用%模运算符的情况下检查一个数字是否是偶数?

我们可以对这个问题使用按位&运算符,&对其操作数进行运算,并将其视为二进制值,然后执行与运算。

function isEven(num) {
  if (num & 1) {
    return false
  } else {
    return true
  }
}

0 二进制数是 000
1 二进制数是 001
2 二进制数是 010
3 二进制数是 011
4 二进制数是 100
5 二进制数是 101
6 二进制数是 110
7 二进制数是 111

以此类推...

与运算的规则如下:

aba & b
000
010
111

因此,当我们执行console.log(5&1)这个表达式时,结果为1。首先,&运算符将两个数字都转换为二进制,因此5变为1011变为001

然后,它使用按位怀运算符比较每个位(01)。 101&001,从表中可以看出,如果a & b1,所以5&1结果为1

101 & 001
101
001
001
  • 首先我们比较最左边的1&0,结果是0
  • 然后我们比较中间的0&0,结果是0
  • 然后我们比较最后1&1,结果是1
  • 最后,得到一个二进制数001,对应的十进制数,即1

    由此我们也可以算出console.log(4 & 1) 结果为0。知道4的最后一位是0,而0 & 1 将是0。如果你很难理解这一点,我们可以使用递归函数来解决此问题。

    function isEven(num) {

     if (num < 0 || num === 1) return false;
     if (num == 0) return true;
     return isEven(num - 2);

    }

59. 如何检查对象中是否存在某个属性?

检查对象中是否存在属性有三种方法。

第一种使用 in 操作符号:

const o = { 
  "prop" : "bwahahah",
  "prop2" : "hweasa"
};

console.log("prop" in o); // true
console.log("prop1" in o); // false

第二种使用 hasOwnProperty 方法,hasOwnProperty() 方法会返回一个布尔值,指示对象自身属性中是否具有指定的属性(也就是,是否有指定的键)。

console.log(o.hasOwnProperty("prop2")); // true
console.log(o.hasOwnProperty("prop1")); // false

第三种使用括号符号obj["prop"]。如果属性存在,它将返回该属性的值,否则将返回undefined

console.log(o["prop"]); // "bwahahah"
console.log(o["prop1"]); // undefined

60. AJAX 是什么?

即异步的 JavaScript 和 XML,是一种用于创建快速动态网页的技术,传统的网页(不使用 AJAX)如果需要更新内容,必需重载整个网页面。使用AJAX则不需要加载更新整个网页,实现部分内容更新

用到AJAX的技术:

  • HTML - 网页结构
  • CSS - 网页的样式
  • JavaScript - 操作网页的行为和更新DOM
  • XMLHttpRequest API - 用于从服务器发送和获取数据
  • PHP,Python,Nodejs - 某些服务器端语言

61. 如何在 JS 中创建对象?

使用对象字面量:

const o = {
  name: "前端小智",
  greeting() {
    return `Hi, 我是${this.name}`;
  }
};

o.greeting(); // "Hi, 我是前端小智"

使用构造函数:

function Person(name) {
   this.name = name;
}

Person.prototype.greeting = function () {
   return `Hi, 我是${this.name}`;
}

const mark = new Person("前端小智");

mark.greeting(); // "Hi, 我是前端小智"

使用 Object.create 方法:

const n = {
   greeting() {
      return `Hi, 我是${this.name}`;
   }
};

const o = Object.create(n); 
o.name = "前端小智";

62. Object.seal 和 Object.freeze 方法之间有什么区别?

Object.freeze()

Object.freeze() 方法可以冻结一个对象。一个被冻结的对象再也不能被修改;冻结了一个对象则不能向这个对象添加新的属性,不能删除已有属性,不能修改该对象已有属性的可枚举性、可配置性、可写性,以及不能修改已有属性的值。此外,冻结一个对象后该对象的原型也不能被修改。freeze() 返回和传入的参数相同的对象。

Object.seal()

Object.seal()方法封闭一个对象,阻止添加新属性并将所有现有属性标记为不可配置。当前属性的值只要可写就可以改变。

方法的相同点:

  1. ES5新增。
  2. 对象不可能扩展,也就是不能再添加新的属性或者方法。
  3. 对象已有属性不允许被删除。
  4. 对象属性特性不可以重新配置。

方法不同点:

  • Object.seal方法生成的密封对象,如果属性是可写的,那么可以修改属性值。

* Object.freeze方法生成的冻结对象,属性都是不可写的,也就是属性值无法更改。

63. in 运算符和 Object.hasOwnProperty 方法有什么区别?

hasOwnPropert方法

hasOwnPropert()方法返回值是一个布尔值,指示对象自身属性中是否具有指定的属性,因此这个方法会忽略掉那些从原型链上继承到的属性。

看下面的例子:

Object.prototype.phone= '15345025546';

let obj = {
    name: '前端小智',
    age: '28'
}
console.log(obj.hasOwnProperty('phone')) // false
console.log(obj.hasOwnProperty('name')) // true

可以看到,如果在函数原型上定义一个变量phonehasOwnProperty方法会直接忽略掉。

in 运算符

如果指定的属性在指定的对象或其原型链中,则in 运算符返回true

还是用上面的例子来演示:

console.log('phone' in obj) // true

可以看到in运算符会检查它或者其原型链是否包含具有指定名称的属性。

64. 有哪些方法可以处理 JS 中的异步代码?

  • 回调
  • Promise
  • async/await
  • 还有一些库: async.js, bluebird, q, co

65. 函数表达式和函数声明之间有什么区别?

看下面的例子:

hoistedFunc();
notHoistedFunc();

function hoistedFunc(){
  console.log("注意:我会被提升");
}

var notHoistedFunc = function(){
  console.log("注意:我没有被提升");
}

notHoistedFunc调用抛出异常:Uncaught TypeError: notHoistedFunc is not a function,而hoistedFunc调用不会,因为hoistedFunc会被提升到作用域的顶部,而notHoistedFunc 不会。

66. 调用函数,可以使用哪些方法?

在 JS 中有4种方法可以调用函数。

作为函数调用——如果一个函数没有作为方法、构造函数、applycall 调用时,此时 this 指向的是 window 对象(非严格模式)

  //Global Scope

  function add(a,b){
    console.log(this);
    return a + b;
  }  

  add(1,5); // 打印 "window" 对象和 6

  const o = {
    method(callback){
      callback();
    }
  }

  o.method(function (){
      console.log(this); // 打印 "window" 对象
  });

作为方法调用——如果一个对象的属性有一个函数的值,我们就称它为方法。调用该方法时,该方法的this值指向该对象。

const details = {
  name : "Marko",
  getName(){
    return this.name;
  }
}

details.getName(); // Marko

作为构造函数的调用-如果在函数之前使用new关键字调用了函数,则该函数称为构造函数。构造函数里面会默认创建一个空对象,并将this指向该对象。

function Employee(name, position, yearHired) {
  // 创建一个空对象 {}
  // 然后将空对象分配给“this”关键字
  // this = {};
  this.name = name;
  this.position = position;
  this.yearHired = yearHired;
  // 如果没有指定 return ,这里会默认返回 this
};

const emp = new Employee("Marko Polo", "Software Developer", 2017);

使用applycall方法调用——如果我们想显式地指定一个函数的this值,我们可以使用这些方法,这些方法对所有函数都可用。

const obj1 = {
 result:0
};

const obj2 = {
 result:0
};


function reduceAdd(){
   let result = 0;
   for(let i = 0, len = arguments.length; i < len; i++){
     result += arguments[i];
   }
   this.result = result;
}


reduceAdd.apply(obj1, [1, 2, 3, 4, 5]);  // reduceAdd 函数中的 this 对象将是 obj1
reduceAdd.call(obj2, 1, 2, 3, 4, 5); // reduceAdd 函数中的 this 对象将是 obj2

67. 什么是缓存及它有什么作用?

缓存是建立一个函数的过程,这个函数能够记住之前计算的结果或值。使用缓存函数是为了避免在最后一次使用相同参数的计算中已经执行的函数的计算。这节省了时间,但也有不利的一面,即我们将消耗更多的内存来保存以前的结果。

68. 手动实现缓存方法]

function memoize(fn) {
  const cache = {};
  return function (param) {
    if (cache[param]) {
      console.log('cached');
      return cache[param];
    } else {
      let result = fn(param);
      cache[param] = result;
      console.log(`not cached`);
      return result;
    }
  }
}

const toUpper = (str ="")=> str.toUpperCase();

const toUpperMemoized = memoize(toUpper);

toUpperMemoized("abcdef");
toUpperMemoized("abcdef");

这个缓存函数适用于接受一个参数。 我们需要改变下,让它接受多个参数。

const slice = Array.prototype.slice;
function memoize(fn) {
  const cache = {};
  return (...args) => {
    const params = slice.call(args);
    console.log(params);
    if (cache[params]) {
      console.log('cached');
      return cache[params];
    } else {
      let result = fn(...args);
      cache[params] = result;
      console.log(`not cached`);
      return result;
    }
  }
}
const makeFullName = (fName, lName) => `${fName} ${lName}`;
const reduceAdd = (numbers, startingValue = 0) => numbers.reduce((total, cur) => total + cur, startingValue);

const memoizedMakeFullName = memoize(makeFullName);
const memoizedReduceAdd = memoize(reduceAdd);

memoizedMakeFullName("Marko", "Polo");
memoizedMakeFullName("Marko", "Polo");

memoizedReduceAdd([1, 2, 3, 4, 5], 5);
memoizedReduceAdd([1, 2, 3, 4, 5], 5);

69. 为什么typeof null 返回 object? 如何检查一个值是否为 null?

typeof null == 'object'总是返回true,因为这是自 JS 诞生以来null的实现。曾经有人提出将typeof null == 'object'修改为typeof null == 'null',但是被拒绝了,因为这将导致更多的bug

我们可以使用严格相等运算符===来检查值是否为null

function isNull(value){
  return value === null;
}

70. new 关键字有什么作用?

new关键字与构造函数一起使用以创建对象:

function Employee(name, position, yearHired) {
  this.name = name;
  this.position = position;
  this.yearHired = yearHired;
};

const emp = new Employee("Marko Polo", "Software Developer", 2017);

new关键字做了4件事:

  • 创建空对象 {}
  • 将空对象分配给 this
  • 将空对象的__proto__指向构造函数的prototype
  • 如果没有使用显式return语句,则返回this

看下面事例:

function Person() {
this.name = '前端小智'
}

根据上面描述的,new Person()做了:

  • 创建一个空对象:var obj = {}
  • 将空对象分配给 this 值:this = obj
  • 将空对象的__proto__指向构造函数的prototype:this.__proto__ = Person().prototype
  • 返回this:return this

71. 什么时候不使用箭头函数? 说出三个或更多的例子?

不应该使用箭头函数一些情况:

  • 当想要函数被提升时(箭头函数是匿名的)
  • 要在函数中使用this/arguments时,由于箭头函数本身不具有this/arguments,因此它们取决于外部上下文
  • 使用命名函数(箭头函数是匿名的)
  • 使用函数作为构造函数时(箭头函数没有构造函数)
  • 当想在对象字面是以将函数作为属性添加并在其中使用对象时,因为咱们无法访问 this 即对象本身。

72. Object.freeze() 和 const 的区别是什么?]

constObject.freeze是两个完全不同的概念。

const 声明一个只读的变量,一旦声明,常量的值就不可改变:

const person = {
    name: "Leonardo"
};
let animal = {
    species: "snake"
};
person = animal; // ERROR "person" is read-only    

Object.freeze适用于值,更具体地说,适用于对象值,它使对象不可变,即不能更改其属性。

let person = {
    name: "Leonardo"
};
let animal = {
    species: "snake"
};
Object.freeze(person);
person.name = "Lima"; //TypeError: Cannot assign to read only property 'name' of object
console.log(person); 

73. 如何在 JS 中“深冻结”对象?

如果咱们想要确保对象被深冻结,就必须创建一个递归函数来冻结对象类型的每个属性:

没有深冻结

let person = {
    name: "Leonardo",
    profession: {
        name: "developer"
    }
};
Object.freeze(person); 
person.profession.name = "doctor";
console.log(person); //output { name: 'Leonardo', profession: { name: 'doctor' } }

深冻结

function deepFreeze(object) {
    let propNames = Object.getOwnPropertyNames(object);
    for (let name of propNames) {
        let value = object[name];
        object[name] = value && typeof value === "object" ?
            deepFreeze(value) : value;
    }
    return Object.freeze(object);
}
let person = {
    name: "Leonardo",
    profession: {
        name: "developer"
    }
};
deepFreeze(person);
person.profession.name = "doctor"; // TypeError: Cannot assign to read only property 'name' of object

74. Iterator是什么,有什么作用?

遍历器(Iterator)就是这样一种机制。它是一种接口,为各种不同的数据结构提供统一的访问机制。任何数据结构只要部署Iterator接口,就可以完成遍历操作(即依次处理该数据结构的所有成员)。

Iterator 的作用有三个:

  1. 为各种数据结构,提供一个统一的、简便的访问接口;
  2. 使得数据结构的成员能够按某种次序排列;
  3. ES6 创造了一种新的遍历命令for...of循环,Iterator 接口主要供for...of消费。

遍历过程:

  1. 创建一个指针对象,指向当前数据结构的起始位置。也就是说,遍历器对象本质上,就是一个指针对象。
  2. 第一次调用指针对象的next方法,可以将指针指向数据结构的第一个成员。
  3. 第二次调用指针对象的next方法,指针就指向数据结构的第二个成员。
  4. 不断调用指针对象的next方法,直到它指向数据结构的结束位置。

每一次调用next方法,都会返回数据结构的当前成员的信息。具体来说,就是返回一个包含valuedone两个属性的对象。其中,value属性是当前成员的值,done属性是一个布尔值,表示遍历是否结束。

//obj就是可遍历的,因为它遵循了Iterator标准,且包含[Symbol.iterator]方法,方法函数也符合标准的Iterator接口规范。
//obj.[Symbol.iterator]() 就是Iterator遍历器
let obj = {
  data: [ 'hello', 'world' ],
  [Symbol.iterator]() {
    const self = this;
    let index = 0;
    return {
      next() {
        if (index < self.data.length) {
          return {
            value: self.data[index++],
            done: false
          };
        } else {
          return { value: undefined, done: true };
        }
      }
    };
  }
};

75. Generator 函数是什么,有什么作用?

如果说 JavaScrip 是 ECMAScript 标准的一种具体实现、Iterator遍历器是Iterator的具体实现,那么Generator函数可以说是Iterator接口的具体实现方式。

执行Generator函数会返回一个遍历器对象,每一次Generator函数里面的yield都相当一次遍历器对象的next()方法,并且可以通过next(value)方法传入自定义的value,来改变Generator函数的行为。

Generator函数可以通过配合Thunk 函数更轻松更优雅的实现异步编程和控制流管理。


代码部署后可能存在的BUG没法实时知道,事后为了解决这些BUG,花了大量的时间进行log 调试,这边顺便给大家推荐一个好用的BUG监控工具 Fundebug

原文:

https://dev.to/macmacky/70-ja...


交流

文章每周持续更新,可以微信搜索「 大迁世界 」第一时间阅读和催更(比博客早一到两篇哟),本文 GitHub https://github.com/qq44924588... 已经收录,整理了很多我的文档,欢迎Star和完善,大家面试可以参照考点复习,另外关注公众号,后台回复福利,即可看到福利,你懂的。

clipboard.png

查看原文

赞 49 收藏 41 评论 2

前端小智 发布了文章 · 2020-11-03

为了面试能通过,我要看完这75道面试题(上)

作者:Mark A
译者:前端小智
来源:dev
点赞再看,微信搜索 【大迁世界】 关注这个没有大厂背景,但有着一股向上积极心态人。本文 GitHubhttps://github.com/qq44924588... 上已经收录,文章的已分类,也整理了很多我的文档,和教程资料。

编程,建网站必备的阿里云服务器竟然免费送了!

服务器如何搭建博客

考题列表

1.undefined 和 null 有什么区别?

在理解undefinednull之间的差异之前,我们先来看看它们的相似类。

它们属于 JavaScript 的 7 种基本类型。

 let primitiveTypes = ['string','number','null','undefined','boolean','symbol', 'bigint'];

它们是属于虚值,可以使用Boolean(value)!!value将其转换为布尔值时,值为false

console.log(!!null); // false
console.log(!!undefined); // false

console.log(Boolean(null)); // false
console.log(Boolean(undefined)); // false

接着来看看它们的区别。

undefined是未指定特定值的变量的默认值,或者没有显式返回值的函数,如:console.log(1),还包括对象中不存在的属性,这些 JS 引擎都会为其分配 undefined 值。

let _thisIsUndefined;
const doNothing = () => {};
const someObj = {
  a : "ay",
  b : "bee",
  c : "si"
};

console.log(_thisIsUndefined); // undefined
console.log(doNothing()); // undefined
console.log(someObj["d"]); // undefined

null“不代表任何值的值”null是已明确定义给变量的值。 在此示例中,当fs.readFile方法未引发错误时,我们将获得null值。

fs.readFile('path/to/file', (e,data) => {
   console.log(e); // 当没有错误发生时,打印 null
   if(e){
     console.log(e);
   }
   console.log(data);
 });

在比较nullundefined时,我们使用==时得到true,使用===时得到false:

 console.log(null == undefined); // true
 console.log(null === undefined); // false

2. && 运算符能做什么

&& 也可以叫逻辑与,在其操作数中找到第一个虚值表达式并返回它,如果没有找到任何虚值表达式,则返回最后一个真值表达式。它采用短路来防止不必要的工作。

console.log(false && 1 && []); // false
console.log(" " && true && 5); // 5

使用if语句

const router: Router = Router();

router.get('/endpoint', (req: Request, res: Response) => {
   let conMobile: PoolConnection;
   try {
      //do some db operations
   } catch (e) {
   if (conMobile) {
    conMobile.release();
   }
  }
});

使用&&操作符

const router: Router = Router();

router.get('/endpoint', (req: Request, res: Response) => {
  let conMobile: PoolConnection;
  try {
     //do some db operations
  } catch (e) {
    conMobile && conMobile.release()
  }
});

3. || 运算符能做什么

||也叫或逻辑或,在其操作数中找到第一个真值表达式并返回它。这也使用了短路来防止不必要的工作。在支持 ES6 默认函数参数之前,它用于初始化函数中的默认参数值。

console.log(null || 1 || undefined); // 1

function logName(name) {
  var n = name || "Mark";
  console.log(n);
}

logName(); // "Mark"

4. 使用 + 或一元加运算符是将字符串转换为数字的最快方法吗?

根据MDN文档+是将字符串转换为数字的最快方法,因为如果值已经是数字,它不会执行任何操作。

5. DOM 是什么?

DOM 代表文档对象模型,是 HTML 和 XML 文档的接口(API)。当浏览器第一次读取(解析)HTML文档时,它会创建一个大对象,一个基于 HTM L文档的非常大的对象,这就是DOM。它是一个从 HTML 文档中建模的树状结构。DOM 用于交互和修改DOM结构或特定元素或节点。

假设我们有这样的 HTML 结构:

<!DOCTYPE html>
<html lang="en">

<head>
   <meta charset="UTF-8">
   <meta name="viewport" content="width=device-width, initial-scale=1.0">
   <meta http-equiv="X-UA-Compatible" content="ie=edge">
   <title>Document Object Model</title>
</head>

<body>
   <div>
      <p>
         <span></span>
      </p>
      <label></label>
      <input>
   </div>
</body>

</html>

等价的DOM是这样的:

clipboard.png

JS 中的document对象表示DOM。它为我们提供了许多方法,我们可以使用这些方法来选择元素来更新元素内容,等等。

6. 什么是事件传播?

事件发生在DOM元素上时,该事件并不完全发生在那个元素上。 在“冒泡阶段”中,事件冒泡或向上传播至父级,祖父母,祖父母或父级,直到到达window为止;而在“捕获阶段”中,事件从window开始向下触发元素 事件或event.target

事件传播有三个阶段:

  1. 捕获阶段–事件从 window 开始,然后向下到每个元素,直到到达目标元素。
  2. 目标阶段–事件已达到目标元素。
  3. 冒泡阶段–事件从目标元素冒泡,然后上升到每个元素,直到到达 window

clipboard.png

7. 什么是事件冒泡?

事件发生在DOM元素上时,该事件并不完全发生在那个元素上。 在冒泡阶段,事件冒泡,或者事件发生在它的父代,祖父母,祖父母的父代,直到到达window为止。

假设有如下的 HTML 结构:

<div class="grandparent">
  <div class="parent">
    <div class="child">1</div>
  </div>
</div>

对应的 JS 代码:

function addEvent(el, event, callback, isCapture = false) {
  if (!el || !event || !callback || typeof callback !== 'function') return;
  if (typeof el === 'string') {
    el = document.querySelector(el);
  };
  el.addEventListener(event, callback, isCapture);
}

addEvent(document, 'DOMContentLoaded', () => {
  const child = document.querySelector('.child');
  const parent = document.querySelector('.parent');
  const grandparent = document.querySelector('.grandparent');

  addEvent(child, 'click', function (e) {
    console.log('child');
  });

  addEvent(parent, 'click', function (e) {
    console.log('parent');
  });

  addEvent(grandparent, 'click', function (e) {
    console.log('grandparent');
  });

  addEvent(document, 'click', function (e) {
    console.log('document');
  });

  addEvent('html', 'click', function (e) {
    console.log('html');
  })

  addEvent(window, 'click', function (e) {
    console.log('window');
  })

});

addEventListener方法具有第三个可选参数useCapture,其默认值为false,事件将在冒泡阶段中发生,如果为true,则事件将在捕获阶段中发生。 如果单击child元素,它将分别在控制台上记录childparentgrandparenthtmldocumentwindow,这就是事件冒泡。

8. 什么是事件捕获?

当事件发生在 DOM 元素上时,该事件并不完全发生在那个元素上。在捕获阶段,事件从window开始,一直到触发事件的元素。

假设有如下的 HTML 结构:

<div class="grandparent">
  <div class="parent">
    <div class="child">1</div>
  </div>
</div>

对应的 JS 代码:

function addEvent(el, event, callback, isCapture = false) {
  if (!el || !event || !callback || typeof callback !== 'function') return;
  if (typeof el === 'string') {
    el = document.querySelector(el);
  };
  el.addEventListener(event, callback, isCapture);
}

addEvent(document, 'DOMContentLoaded', () => {
  const child = document.querySelector('.child');
  const parent = document.querySelector('.parent');
  const grandparent = document.querySelector('.grandparent');

  addEvent(child, 'click', function (e) {
    console.log('child');
  });

  addEvent(parent, 'click', function (e) {
    console.log('parent');
  });

  addEvent(grandparent, 'click', function (e) {
    console.log('grandparent');
  });

  addEvent(document, 'click', function (e) {
    console.log('document');
  });

  addEvent('html', 'click', function (e) {
    console.log('html');
  })

  addEvent(window, 'click', function (e) {
    console.log('window');
  })

});

addEventListener方法具有第三个可选参数useCapture,其默认值为false,事件将在冒泡阶段中发生,如果为true,则事件将在捕获阶段中发生。 如果单击child元素,它将分别在控制台上打印windowdocumenthtmlgrandparentparent,这就是事件捕获

9. event.preventDefault() 和 event.stopPropagation()方法之间有什么区别?

event.preventDefault() 方法可防止元素的默认行为。 如果在表单元素中使用,它将阻止其提交。 如果在锚元素中使用,它将阻止其导航。 如果在上下文菜单中使用,它将阻止其显示或显示。 event.stopPropagation()方法用于阻止捕获和冒泡阶段中当前事件的进一步传播。

10. 如何知道是否在元素中使用了event.preventDefault()方法?

我们可以在事件对象中使用event.defaultPrevented属性。 它返回一个布尔值用来表明是否在特定元素中调用了event.preventDefault()

11. 为什么此代码 obj.someprop.x 会引发错误?

const obj = {};
console.log(obj.someprop.x);

显然,由于我们尝试访问someprop属性中的x属性,而 someprop 并没有在对象中,所以值为 undefined。 记住对象本身不存在的属性,并且其原型的默认值为undefined。因为undefined没有属性x,所以试图访问将会报错。

12. 什么是 event.target ?

简单来说,event.target是发生事件的元素或触发事件的元素。

假设有如下的 HTML 结构:

<div onclick="clickFunc(event)" style="text-align: center;margin:15px;
border:1px solid red;border-radius:3px;">
    <div style="margin: 25px; border:1px solid royalblue;border-radius:3px;">
        <div style="margin:25px;border:1px solid skyblue;border-radius:3px;">
          <button style="margin:10px">
             Button
          </button>
        </div>
    </div>
 </div>

JS 代码如下:

function clickFunc(event) {
  console.log(event.target);
}

如果单击 button,即使我们将事件附加在最外面的div上,它也将打印 button 标签,因此我们可以得出结论event.target是触发事件的元素。

13. 什么是 event.currentTarget??

event.currentTarget是我们在其上显式附加事件处理程序的元素。

假设有如下的 HTML 结构:

<div onclick="clickFunc(event)" style="text-align: center;margin:15px;
border:1px solid red;border-radius:3px;">
    <div style="margin: 25px; border:1px solid royalblue;border-radius:3px;">
        <div style="margin:25px;border:1px solid skyblue;border-radius:3px;">
          <button style="margin:10px">
             Button
          </button>
        </div>
    </div>
 </div>

JS 代码如下:

function clickFunc(event) {
  console.log(event.currentTarget);
}

如果单击 button,即使我们单击该 button,它也会打印最外面的div标签。 在此示例中,我们可以得出结论,event.currentTarget是附加事件处理程序的元素。

14. == 和 === 有什么区别?

==用于一般比较,===用于严格比较,==在比较的时候可以转换数据类型,===严格比较,只要类型不匹配就返回flase

先来看看 == 这兄弟:

强制是将值转换为另一种类型的过程。 在这种情况下,==会执行隐式强制。 在比较两个值之前,==需要执行一些规则。

假设我们要比较x == y的值。

  1. 如果xy的类型相同,则 JS 会换成===操作符进行比较。
  2. 如果xnull, yundefined,则返回true
  3. 如果xundefinedynull,则返回true
  4. 如果x的类型是number, y的类型是string,那么返回x == toNumber(y)
  5. 如果x的类型是string, y的类型是number,那么返回toNumber(x) == y
  6. 如果x为类型是boolean,则返回toNumber(x)== y
  7. 如果y为类型是boolean,则返回x == toNumber(y)
  8. 如果xstringsymbolnumber,而yobject类型,则返回x == toPrimitive(y)
  9. 如果xobjectystringsymbol 则返回toPrimitive(x) == y
  10. 剩下的 返回 false

注意:toPrimitive首先在对象中使用valueOf方法,然后使用toString方法来获取该对象的原始值。

举个例子。

xyx == y
55true
1'1'true
nullundefinedtrue
0falsetrue
'1,2'[1,2]true
'[object Object]'{}true

这些例子都返回true

第一个示例符合条件1,因为xy具有相同的类型和值。

第二个示例符合条件4,在比较之前将y转换为数字。

第三个例子符合条件2

第四个例子符合条件7,因为yboolean类型。

第五个示例符合条件8。 使用toString()方法将数组转换为字符串,该方法返回1,2

最后一个示例符合条件8。 使用toString()方法将对象转换为字符串,该方法返回[object Object]

xyx === y
55true
1'1'false
nullundefinedfalse
0falsefalse
'1,2'[1,2]false
'[object Object]'{}false

如果使用===运算符,则第一个示例以外的所有比较将返回false,因为它们的类型不同,而第一个示例将返回true,因为两者的类型和值相同。

具体更多规则可以对参考我之前的文章:

我对 JS 中相等和全等操作符转化过程一直很迷惑,直到有了这份算法

15. 为什么在 JS 中比较两个相似的对象时返回 false?

先看下面的例子:

let a = { a: 1 };
let b = { a: 1 };
let c = a;

console.log(a === b); // 打印 false,即使它们有相同的属性
console.log(a === c); // true

JS 以不同的方式比较对象和基本类型。在基本类型中,JS 通过值对它们进行比较,而在对象中,JS 通过引用或存储变量的内存中的地址对它们进行比较。这就是为什么第一个console.log语句返回false,而第二个console.log语句返回trueac有相同的引用地址,而ab没有。

16. !! 运算符能做什么?

!!运算符可以将右侧的值强制转换为布尔值,这也是将值转换为布尔值的一种简单方法。

console.log(!!null); // false
console.log(!!undefined); // false
console.log(!!''); // false
console.log(!!0); // false
console.log(!!NaN); // false
console.log(!!' '); // true
console.log(!!{}); // true
console.log(!![]); // true
console.log(!!1); // true
console.log(!![].length); // false

17. 如何在一行中计算多个表达式的值?

可以使用逗号运算符在一行中计算多个表达式。 它从左到右求值,并返回右边最后一个项目或最后一个操作数的值。

let x = 5;

x = (x++ , x = addFive(x), x *= 2, x -= 5, x += 10);

function addFive(num) {
  return num + 5;
}

上面的结果最后得到x的值为27。首先,我们将x的值增加到6,然后调用函数addFive(6)并将6作为参数传递并将结果重新分配给x,此时x的值为11。之后,将x的当前值乘以2并将其分配给xx的更新值为22。然后,将x的当前值减去5并将结果分配给xx更新后的值为17。最后,我们将x的值增加10,然后将更新的值分配给x,最终x的值为27

18. 什么是提升?

提升是用来描述变量和函数移动到其(全局或函数)作用域顶部的术语。

为了理解提升,需要来了解一下执行上下文执行上下文是当前正在执行的“代码环境”。执行上下文有两个阶段:编译执行

编译-在此阶段,JS 引荐获取所有函数声明并将其提升到其作用域的顶部,以便我们稍后可以引用它们并获取所有变量声明(使用var关键字进行声明),还会为它们提供默认值: undefined

执行——在这个阶段中,它将值赋给之前提升的变量,并执行或调用函数(对象中的方法)。

注意:只有使用var声明的变量,或者函数声明才会被提升,相反,函数表达式或箭头函数,letconst声明的变量,这些都不会被提升。

假设在全局使用域,有如下的代码:

console.log(y);
y = 1;
console.log(y);
console.log(greet("Mark"));

function greet(name){
  return 'Hello ' + name + '!';
}

var y;

上面分别打印:undefined,1, Hello Mark!

上面代码在编译阶段其实是这样的:

function greet(name) {
  return 'Hello ' + name + '!';
}

var y; // 默认值 undefined

// 等待“编译”阶段完成,然后开始“执行”阶段

/*
console.log(y);
y = 1;
console.log(y);
console.log(greet("Mark"));
*/

编译阶段完成后,它将启动执行阶段调用方法,并将值分配给变量。

function greet(name) {
  return 'Hello ' + name + '!';
}

var y;

//start "execution" phase

console.log(y);
y = 1;
console.log(y);
console.log(greet("Mark"));

19. 什么是作用域?

JavaScript 中的作用域是我们可以有效访问变量或函数的区域。JS 有三种类型的作用域:全局作用域函数作用域块作用域(ES6)

  • 全局作用域——在全局命名空间中声明的变量或函数位于全局作用域中,因此在代码中的任何地方都可以访问它们。
//global namespace
var g = "global";

function globalFunc(){
  function innerFunc(){
    console.log(g); // can access "g" because "g" is a global variable
  }
 innerFunc();
}  
  • 函数作用域——在函数中声明的变量、函数和参数可以在函数内部访问,但不能在函数外部访问。
function myFavoriteFunc(a) {
  if (true) {
    var b = "Hello " + a;
  }
  return b;
}

myFavoriteFunc("World");

console.log(a); // Throws a ReferenceError "a" is not defined
console.log(b); // does not continue here 
  • 块作用域-在块{}中声明的变量(let,const)只能在其中访问。
 function testBlock(){
   if(true){
     let z = 5;
   }
   return z; 
 }

 testBlock(); // Throws a ReferenceError "z" is not defined

作用域也是一组用于查找变量的规则。 如果变量在当前作用域中不存在,它将向外部作用域中查找并搜索,如果该变量不存在,它将再次查找直到到达全局作用域,如果找到,则可以使用它,否则引发错误,这种查找过程也称为作用域链

   /* 作用域链
     
     内部作用域->外部作用域-> 全局作用域
  */

  // 全局作用域
  var variable1 = "Comrades";   
  var variable2 = "Sayonara";

  function outer(){
  // 外部作用域
    var variable1 = "World";
    function inner(){
    // 内部作用域
      var variable2 = "Hello";
      console.log(variable2 + " " + variable1);
    }
    inner();
  }  
  outer(); // Hello World

clipboard.png

20. 什么是闭包?

这可能是所有问题中最难的一个问题,因为闭包是一个有争议的话题,这里从个人角度来谈谈,如果不妥,多多海涵。

闭包就是一个函数在声明时能够记住当前作用域、父函数作用域、及父函数作用域上的变量和参数的引用,直至通过作用域链上全局作用域,基本上闭包是在声明函数时创建的作用域。

看看小例子:

   // 全局作用域
   var globalVar = "abc";

   function a(){
     console.log(globalVar);
   }

   a(); // "abc" 

在此示例中,当我们声明a函数时,全局作用域是a闭包的一部分。

clipboard.png

变量globalVar在图中没有值的原因是该变量的值可以根据调用函数a的位置和时间而改变。但是在上面的示例中,globalVar变量的值为abc

来看一个更复杂的例子:

var globalVar = "global";
var outerVar = "outer"

function outerFunc(outerParam) {
  function innerFunc(innerParam) {
    console.log(globalVar, outerParam, innerParam);
  }
  return innerFunc;
}

const x = outerFunc(outerVar);
outerVar = "outer-2";
globalVar = "guess"
x("inner");

clipboard.png

上面打印结果是 guess outer inner

当我们调用outerFunc函数并将返回值innerFunc函数分配给变量x时,即使我们为outerVar变量分配了新值outer-2outerParam也继续保留outer值,因为重新分配是在调用outerFunc之后发生的,并且当我们调用outerFunc函数时,它会在作用域链中查找outerVar的值,此时的outerVar的值将为 "outer"

现在,当我们调用引用了innerFuncx变量时,innerParam将具有一个inner值,因为这是我们在调用中传递的值,而globalVar变量值为guess,因为在调用x变量之前,我们将一个新值分配给globalVar

下面这个示例演示没有理解好闭包所犯的错误:

const arrFuncs = [];
for(var i = 0; i < 5; i++){
  arrFuncs.push(function (){
    return i;
  });
}
console.log(i); // i is 5

for (let i = 0; i < arrFuncs.length; i++) {
  console.log(arrFuncs[i]()); // 都打印 5
}

由于闭包,此代码无法正常运行。var关键字创建一个全局变量,当我们 push 一个函数时,这里返回的全局变量i。 因此,当我们在循环后在该数组中调用其中一个函数时,它会打印5,因为我们得到i的当前值为5,我们可以访问它,因为它是全局变量。

因为闭包在创建变量时会保留该变量的引用而不是其值。 我们可以使用IIFES或使用 let 来代替 var 的声明。

21. JavaScript 中的虚值是什么?

 const falsyValues = ['', 0, null, undefined, NaN, false];

简单的来说虚值就是是在转换为布尔值时变为 false 的值。

22. 如何检查值是否虚值?

使用 Boolean 函数或者 !! 运算符。

23. 'use strict' 是干嘛用的?

"use strict"ES5 特性,它使我们的代码在函数或整个脚本中处于严格模式严格模式帮助我们在代码的早期避免 bug,并为其添加限制。

严格模式的一些限制:

  1. 变量必须声明后再使用
  2. 函数的参数不能有同名属性,否则报错
  3. 不能使用with语句
  4. 不能对只读属性赋值,否则报错
  5. 不能使用前缀 0 表示八进制数,否则报错
  6. 不能删除不可删除的属性,否则报错
  7. 不能删除变量delete prop,会报错,只能删除属性delete global[prop]
  8. eval不能在它的外层作用域引入变量
  9. evalarguments不能被重新赋值
  10. arguments不会自动反映函数参数的变化
  11. 不能使用arguments.callee
  12. 不能使用arguments.caller
  13. 禁止this指向全局对象
  14. 不能使用fn.callerfn.arguments获取函数调用的堆栈
  15. 增加了保留字(比如protectedstaticinterface

设立”严格模式”的目的,主要有以下几个:

  1. 消除Javascript语法的一些不合理、不严谨之处,减少一些怪异行为;
  2. 消除代码运行的一些不安全之处,保证代码运行的安全;
  3. 提高编译器效率,增加运行速度;
  4. 为未来新版本的Javascript做好铺垫。

24. JavaScript 中 this 值是什么?

基本上,this指的是当前正在执行或调用该函数的对象的值。this值的变化取决于我们使用它的上下文和我们在哪里使用它。

const carDetails = {
  name: "Ford Mustang",
  yearBought: 2005,
  getName(){
    return this.name;
  },
  isRegistered: true
};

console.log(carDetails.getName()); // Ford Mustang

这通常是我们期望结果的,因为在getName方法中我们返回this.name,在此上下文中,this指向的是carDetails对象,该对象当前是执行函数的“所有者”对象。

接下我们做些奇怪的事情:

var name = "Ford Ranger";
var getCarName = carDetails.getName;

console.log(getCarName()); // Ford Ranger

上面打印Ford Ranger,这很奇怪,因为在第一个console.log语句中打印的是Ford Mustang。这样做的原因是getCarName方法有一个不同的“所有者”对象,即window对象。在全局作用域中使用var关键字声明变量会在window对象中附加与变量名称相同的属性。请记住,当没有使用“use strict”时,在全局作用域中this指的是window对象。

console.log(getCarName === window.getCarName); // true
console.log(getCarName === this.getCarName); // true

本例中的thiswindow引用同一个对象。

解决这个问题的一种方法是在函数中使用applycall方法。

console.log(getCarName.apply(carDetails)); // Ford Mustang
console.log(getCarName.call(carDetails));  // Ford Mustang

applycall方法期望第一个参数是一个对象,该对象是函数内部this的值。

IIFE立即执行的函数表达式,在全局作用域内声明的函数,对象内部方法中的匿名函数和内部函数的this具有默认值,该值指向window对象。

   (function (){
     console.log(this);
   })(); // 打印 "window" 对象

   function iHateThis(){
      console.log(this);
   }

   iHateThis(); // 打印 "window" 对象

   const myFavoriteObj = {
     guessThis(){
        function getName(){
          console.log(this.name);
        }
        getName();
     },
     name: 'Marko Polo',
     thisIsAnnoying(callback){
       callback();
     }
   };


   myFavoriteObj.guessThis(); // 打印 "window" 对象
   myFavoriteObj.thisIsAnnoying(function (){
     console.log(this); // 打印 "window" 对象
   });

如果我们要获取myFavoriteObj对象中的name属性(即Marko Polo)的值,则有两种方法可以解决此问题。

一种是将 this 值保存在变量中。

const myFavoriteObj = {
 guessThis(){
  const self = this; // 把 this 值保存在 self 变量中
  function getName(){
    console.log(self.name);
  }
  getName();
 },
 name: 'Marko Polo',
 thisIsAnnoying(callback){
   callback();
  }
};

第二种方式是使用箭头函数

const myFavoriteObj = {
  guessThis(){
     const getName = () => { 
       console.log(this.name);
     }
     getName();
  },
  name: 'Marko Polo',
  thisIsAnnoying(callback){
   callback();
  }
};

箭头函数没有自己的 this。它复制了这个封闭的词法作用域中this值,在这个例子中,this值在getName内部函数之外,也就是myFavoriteObj对象。

25. 对象的 prototype(原型) 是什么?

简单地说,原型就是对象的蓝图。如果它存在当前对象中,则将其用作属性和方法的回退。它是在对象之间共享属性和功能的方法,这也是JavaScript实现继承的核心。

const o = {};
console.log(o.toString()); // logs [object Object] 

即使o对象中不存在o.toString方法,它也不会引发错误,而是返回字符串[object Object]。 当对象中不存在属性时,它将查看其原型,如果仍然不存在,则将其查找到原型的原型,依此类推,直到在原型链中找到具有相同属性的属性为止。 原型链的末尾是Object.prototype

console.log(o.toString === Object.prototype.toString); // logs true


26. 什么是 IIFE,它的用途是什么?

IIFE或立即调用的函数表达式是在创建或声明后将被调用或执行的函数。 创建IIFE的语法是,将function (){}包裹在在括号()内,然后再用另一个括号()调用它,如:(function(){})()

(function(){
  ...
} ());

(function () {
  ...
})();

(function named(params) {
  ...
})();

(() => {

});

(function (global) {
  ...
})(window);

const utility = (function () {
  return {
    ...
  }
})

这些示例都是有效的IIFE。 倒数第二个救命表明我们可以将参数传递给IIFE函数。 最后一个示例表明,我们可以将IIFE的结果保存到变量中,以便稍后使用。

IIFE的一个主要作用是避免与全局作用域内的其他变量命名冲突或污染全局命名空间,来个例子。

<script data-original="https://cdnurl.com/somelibrary.js"></script>

假设我们引入了一个omelibr.js的链接,它提供了一些我们在代码中使用的全局函数,但是这个库有两个方法我们没有使用:createGraphdrawGraph,因为这些方法都有bug。我们想实现自己的createGraphdrawGraph方法。

解决此问题的一种方法是直接覆盖:

<script data-original="https://cdnurl.com/somelibrary.js"></script>
<script>
   function createGraph() {
      // createGraph logic here
   }
   function drawGraph() {
      // drawGraph logic here
   }
</script>

当我们使用这个解决方案时,我们覆盖了库提供给我们的那两个方法。

另一种方式是我们自己改名称:

<script data-original="https://cdnurl.com/somelibrary.js"></script>
<script>
   function myCreateGraph() {
      // createGraph logic here
   }
   function myDrawGraph() {
      // drawGraph logic here
   }
</script>

当我们使用这个解决方案时,我们把那些函数调用更改为新的函数名。

还有一种方法就是使用IIFE

<script data-original="https://cdnurl.com/somelibrary.js"></script>
<script>
   const graphUtility = (function () {
      function createGraph() {
         // createGraph logic here
      }
      function drawGraph() {
         // drawGraph logic here
      }
      return {
         createGraph,
         drawGraph
      }
   })
</script>

在此解决方案中,我们要声明了graphUtility 变量,用来保存IIFE执行的结果,该函数返回一个包含两个方法createGraphdrawGraph的对象。

IIFE 还可以用来解决一个常见的面试题:

var li = document.querySelectorAll('.list-group > li');
for (var i = 0, len = li.length; i < len; i++) {
   li[i].addEventListener('click', function (e) {
      console.log(i);
   })

假设我们有一个带有list-group类的ul元素,它有5li子元素。 当我们单击单个li元素时,打印对应的下标值。但在此外上述代码不起作用,这里每次点击 li 打印 i 的值都是5,这是由于闭包的原因。

闭包只是函数记住其当前作用域,父函数作用域和全局作用域的变量引用的能力。 当我们在全局作用域内使用var关键字声明变量时,就创建全局变量i。 因此,当我们单击li元素时,它将打印5,因为这是稍后在回调函数中引用它时i的值。

使用 IIFE 可以解决此问题:

var li = document.querySelectorAll('.list-group > li');
for (var i = 0, len = li.length; i < len; i++) {
   (function (currentIndex) {
      li[currentIndex].addEventListener('click', function (e) {
         console.log(currentIndex);
      })
   })(i);
}

该解决方案之所以行的通,是因为IIFE会为每次迭代创建一个新的作用域,我们捕获i的值并将其传递给currentIndex参数,因此调用IIFE时,每次迭代的currentIndex值都是不同的。

27. Function.prototype.apply 方法的用途是什么?

apply() 方法调用一个具有给定this值的函数,以及作为一个数组(或类似数组对象)提供的参数。

const details = {
  message: 'Hello World!'
};

function getMessage(){
  return this.message;
}

getMessage.apply(details); // 'Hello World!'
call()方法的作用和 apply() 方法类似,区别就是call()方法接受的是参数列表,而apply()方法接受的是一个参数数组。
const person = {
  name: "Marko Polo"
};

function greeting(greetingMessage) {
  return `${greetingMessage} ${this.name}`;
}

greeting.apply(person, ['Hello']); // "Hello Marko Polo!"

28. Function.prototype.call 方法的用途是什么?

call() 方法使用一个指定的 this 值和单独给出的一个或多个参数来调用一个函数。

const details = {
  message: 'Hello World!'
};

function getMessage(){
  return this.message;
}

getMessage.call(details); // 'Hello World!'

注意:该方法的语法和作用与 apply() 方法类似,只有一个区别,就是 call() 方法接受的是一个参数列表,而 apply() 方法接受的是一个包含多个参数的数组。

const person = {
  name: "Marko Polo"
};

function greeting(greetingMessage) {
  return `${greetingMessage} ${this.name}`;
}

greeting.call(person, 'Hello'); // "Hello Marko Polo!"

29. Function.prototype.apply 和 Function.prototype.call 之间有什么区别?

apply()方法可以在使用一个指定的 this 值和一个参数数组(或类数组对象)的前提下调用某个函数或方法。call()方法类似于apply(),不同之处仅仅是call()接受的参数是参数列表。

const obj1 = {
 result:0
};

const obj2 = {
 result:0
};

function reduceAdd(){
   let result = 0;
   for(let i = 0, len = arguments.length; i < len; i++){
     result += arguments[i];
   }
   this.result = result;
}

reduceAdd.apply(obj1, [1, 2, 3, 4, 5]); // 15
reduceAdd.call(obj2, 1, 2, 3, 4, 5); // 15

30. Function.prototype.bind 的用途是什么?

bind() 方法创建一个新的函数,在 bind() 被调用时,这个新函数的 this 被指定为 bind() 的第一个参数,而其余参数将作为新函数的参数,供调用时使用。

import React from 'react';

class MyComponent extends React.Component {
     constructor(props){
          super(props); 
          this.state = {
             value : ""
          }  
          this.handleChange = this.handleChange.bind(this); 
          // 将 “handleChange” 方法绑定到 “MyComponent” 组件
     }

     handleChange(e){
       //do something amazing here
     }

     render(){
        return (
              <>
                <input type={this.props.type}
                        value={this.state.value}
                     onChange={this.handleChange}                      
                  />
              </>
        )
     }
}

31. 什么是函数式编程? JavaScript 的哪些特性使其成为函数式语言的候选语言?

函数式编程(通常缩写为FP)是通过编写纯函数,避免共享状态、可变数据、副作用 来构建软件的过程。数式编程是声明式 的而不是命令式 的,应用程序的状态是通过纯函数流动的。与面向对象编程形成对比,面向对象中应用程序的状态通常与对象中的方法共享和共处。

函数式编程是一种编程范式 ,这意味着它是一种基于一些基本的定义原则(如上所列)思考软件构建的方式。当然,编程范示的其他示例也包括面向对象编程和过程编程。

函数式的代码往往比命令式或面向对象的代码更简洁,更可预测,更容易测试 - 但如果不熟悉它以及与之相关的常见模式,函数式的代码也可能看起来更密集杂乱,并且 相关文献对新人来说是不好理解的。

JavaScript支持闭包和高阶函数是函数式编程语言的特点。

32. 什么是高阶函数?

高阶函数只是将函数作为参数或返回值的函数。

function higherOrderFunction(param,callback){
    return callback(param);
}

33. 为什么函数被称为一等公民?

在JavaScript中,函数不仅拥有一切传统函数的使用方式(声明和调用),而且可以做到像简单值一样赋值(var func = function(){})、传参(function func(x,callback){callback();})、返回(function(){return function(){}}),这样的函数也称之为第一级函数(First-class Function)。不仅如此,JavaScript中的函数还充当了类的构造函数的作用,同时又是一个Function类的实例(instance)。这样的多重身份让JavaScript的函数变得非常重要。

34. 手动实现 Array.prototype.map 方法

map() 方法创建一个新数组,其结果是该数组中的每个元素都调用一个提供的函数后返回的结果。

function map(arr, mapCallback) {
  // 首先,检查传递的参数是否正确。
  if (!Array.isArray(arr) || !arr.length || typeof mapCallback !== 'function') { 
    return [];
  } else {
    let result = [];
    // 每次调用此函数时,我们都会创建一个 result 数组
    // 因为我们不想改变原始数组。
    for (let i = 0, len = arr.length; i < len; i++) {
      result.push(mapCallback(arr[i], i, arr)); 
      // 将 mapCallback 返回的结果 push 到 result 数组中
    }
    return result;
  }
}

35. 手动实现Array.prototype.filter方法

filter() 方法创建一个新数组, 其包含通过所提供函数实现的测试的所有元素。

function filter(arr, filterCallback) {
  // 首先,检查传递的参数是否正确。
  if (!Array.isArray(arr) || !arr.length || typeof filterCallback !== 'function') 
  {
    return [];
  } else {
    let result = [];
     // 每次调用此函数时,我们都会创建一个 result 数组
     // 因为我们不想改变原始数组。
    for (let i = 0, len = arr.length; i < len; i++) {
      // 检查 filterCallback 的返回值是否是真值
      if (filterCallback(arr[i], i, arr)) { 
      // 如果条件为真,则将数组元素 push 到 result 中
        result.push(arr[i]);
      }
    }
    return result; // return the result array
  }
}

36. 手动实现Array.prototype.reduce方法

reduce() 方法对数组中的每个元素执行一个由您提供的reducer函数(升序执行),将其结果汇总为单个返回值。

function reduce(arr, reduceCallback, initialValue) {
  // 首先,检查传递的参数是否正确。
  if (!Array.isArray(arr) || !arr.length || typeof reduceCallback !== 'function') 
  {
    return [];
  } else {
    // 如果没有将initialValue传递给该函数,我们将使用第一个数组项作为initialValue
    let hasInitialValue = initialValue !== undefined;
    let value = hasInitialValue ? initialValue : arr[0];
   、

    // 如果有传递 initialValue,则索引从 1 开始,否则从 0 开始
    for (let i = hasInitialValue ? 0 : 1, len = arr.length; i < len; i++) {
      value = reduceCallback(value, arr[i], i, arr); 
    }
    return value;
  }
}

37. arguments 的对象是什么?

arguments对象是函数中传递的参数值的集合。它是一个类似数组的对象,因为它有一个length属性,我们可以使用数组索引表示法arguments[1]来访问单个值,但它没有数组中的内置方法,如:forEachreducefiltermap

我们可以使用Array.prototype.slicearguments对象转换成一个数组。

function one() {
  return Array.prototype.slice.call(arguments);
}

注意:箭头函数中没有arguments对象。

function one() {
  return arguments;
}
const two = function () {
  return arguments;
}
const three = function three() {
  return arguments;
}

const four = () => arguments;

four(); // Throws an error  - arguments is not defined

当我们调用函数four时,它会抛出一个ReferenceError: arguments is not defined error。使用rest语法,可以解决这个问题。

const four = (...args) => args;

这会自动将所有参数值放入数组中。

38. 如何创建一个没有 prototype(原型)的对象?

我们可以使用Object.create方法创建没有原型的对象。

const o1 = {};
console.log(o1.toString()); // [object Object]

const o2 = Object.create(null);
console.log(o2.toString());
// throws an error o2.toString is not a function 

39. 为什么在调用这个函数时,代码中的b会变成一个全局变量?

function myFunc() {
  let a = b = 0;
}

myFunc();

原因是赋值运算符是从右到左的求值的。这意味着当多个赋值运算符出现在一个表达式中时,它们是从右向左求值的。所以上面代码变成了这样:

function myFunc() {
  let a = (b = 0);
}

myFunc();

首先,表达式b = 0求值,在本例中b没有声明。因此,JS引擎在这个函数外创建了一个全局变量b,之后表达式b = 0的返回值为0,并赋给新的局部变量a

我们可以通过在赋值之前先声明变量来解决这个问题。

function myFunc() {
  let a,b;
  a = b = 0;
}
myFunc();


40. ECMAScript 是什么?

ECMAScript 是编写脚本语言的标准,这意味着JavaScript遵循ECMAScript标准中的规范变化,因为它是JavaScript的蓝图。

ECMAScript 和 Javascript,本质上都跟一门语言有关,一个是语言本身的名字,一个是语言的约束条件
只不过发明JavaScript的那个人(Netscape公司),把东西交给了ECMA(European Computer Manufacturers Association),这个人规定一下他的标准,因为当时有java语言了,又想强调这个东西是让ECMA这个人定的规则,所以就这样一个神奇的东西诞生了,这个东西的名称就叫做ECMAScript。

javaScript = ECMAScript + DOM + BOM(自认为是一种广义的JavaScript)

ECMAScript说什么JavaScript就得做什么!

JavaScript(狭义的JavaScript)做什么都要问问ECMAScript我能不能这样干!如果不能我就错了!能我就是对的!

——突然感觉JavaScript好没有尊严,为啥要搞个人出来约束自己,

那个人被创造出来也好委屈,自己被创造出来完全是因为要约束JavaScript。

由于篇幅过长,我将此系列分成上下二篇,下篇我们在见。


代码部署后可能存在的BUG没法实时知道,事后为了解决这些BUG,花了大量的时间进行log 调试,这边顺便给大家推荐一个好用的BUG监控工具 Fundebug

原文:https://dev.to/macmacky/70-ja...


交流

文章每周持续更新,可以微信搜索「 大迁世界 」第一时间阅读和催更(比博客早一到两篇哟),本文 GitHub https://github.com/qq44924588... 已经收录,整理了很多我的文档,欢迎Star和完善,大家面试可以参照考点复习,另外关注公众号,后台回复福利,即可看到福利,你懂的。

clipboard.png

查看原文

赞 57 收藏 45 评论 3

前端小智 发布了文章 · 2020-11-02

28 个提升开发幸福度的 VsCode 插件

点赞再看,微信搜索 【大迁世界】 关注这个没有大厂背景,但有着一股向上积极心态人。本文 GitHubhttps://github.com/qq44924588... 上已经收录,文章的已分类,也整理了很多我的文档,和教程资料。

双 1111 拼团低至 85 元,来一起拼团学习搭建自己的博客吧

服务器如何搭建博客

1. Quokka.js

Quokka.js 是一个用于 JavaScript 和 TypeScript 的实时运行代码平台。这意味着它会实时运行你输入后的代码,并在编辑器中显示各种执行结果,建议亲自尝试一下。

图片描述

安装此扩展后,可以按Ctrl / Cmd(⌘)+ Shift + P显示编辑器的命令选项板,然后键入 Quokka 以查看可用命令的列表。选择并运行 “New JavaScript File”命令。你也可以按(⌘+ K + J)直接打开文件。在此文件中输入的任何内容都会立即执行。

图片描述

Quokka.js类似的扩展 –

  • Code Runner – 支持多种语言,如C,C ++,Java,JavaScript,PHP,Python,Perl,Perl 6等。
  • Runner

2. 括号配对着色(Bracket Pair Colorizer) 和 彩虹缩进(Indent Rainbow)

花括号和圆括号是许多编程语言不可分割的部分,在 JavaScript 等语言中,在一屏代码中花括号和园括号可能有多层嵌套,有些括号不太容易识别哪个对应哪个,然而却没有简单的方法来识别这些括号前后的对应关系。

括号配对着色(Bracket Pair Colorizer)彩虹缩进(Indent Rainbow)。这是两个不同的扩展。然而,他们就像是一对情侣,可以完美的配合使用。这些扩展将为你的编辑器添加一系列颜色,并使代码块易于辨别,一旦你习惯了它们,如果 VSCode 没有它们就会让人觉得很平淡。

不使用括号配对着色(Bracket Pair Colorizer) 和 彩虹缩进(Indent Rainbow)

不使用括号配对着色(Bracket Pair Colorizer) 和 彩虹缩进(Indent Rainbow)

使用括号配对着色(Bracket Pair Colorizer) 和 彩虹缩进(Indent Rainbow)后

使用括号配对着色(Bracket Pair Colorizer) 和 彩虹缩进(Indent Rainbow)后

3. snippets(代码片段)

代码片段是编辑器中的短代码。因此,可以输入 imr 并按Tab 来展开该代码片段,而不是'import React from '。类似地,clg 变成了 console.log。

各种各样的框架和类库都有很多代码片段:Javascript,React,Redux,Angular,Vue,Jest。 我个人认为 Javascript 代码片段非常有用,因为我主要使用 JS 。

一些很好的代码片段扩展 –

4. TODO高亮

通常在进行编码时,你认为可能有更好的方法来执行相同的操作。这时你留下注释// TODO: 需要重构 或其他相关的东西。但是你很容易忘记了这个注释,并将你的代码推送到主版本库(master) 或者生产环境(production)。 但是你如果使用 Todo Highlighter(高亮),它会高亮的显示并让你容易看到这个注释。

它以明亮的颜色突出代码中的 “TODO/FIXME” 或代码任何其他注释,以便始终清晰可见。另外还有一个很好的功能是 List Highlighted annotations ,它会在控制台中列出了所有 TODO。

图片描述

使用 Todo Highlighter(高亮)类似的扩展 –

  • Todo+ —  更强大的 Todo 高亮扩展,具有更多功能。
  • Todo Parser

5. Import Cost

扩展允许您查看导入模块的大小,它对 Webpack 中的 bundlers 有很大帮助,你可以查看是导入整个库还是只导入特定的实用程序。

图片描述

6. REST Client

作为 web 开发人员,我们经常需要使用 REST api。为了检查url和检查响应,使用了 Postman 之类的工具。但是,既然编辑器可以轻松地完成相同的任务,为什么还要使用不同的应用程序呢? REST Client 它允许你发送 HTTP 请求并直接在 Visual Studio 代码中查看响应。

图片描述

7. 自动闭合标记(Auto Close Tag)和自动重命名标记(Auto Rename Tag)

自从React的出现以及它在过去几年获得的吸引力以来,以 JSX 形式出现的类似 html 的语法现在非常流行。我们还必须使用 JavaScript 标签进行编码。任何web开发人员都会告诉你,输入标签是一件痛苦的事情。在大多数情况下,我们需要一个能够快速、轻松地生成标签及其子标签的工具。Emmet 是 VSCode 中一个很好的例子,然而,有时候,你只是想要一些简单明了的东西。例如自动更新标签,它在你输入开始标签时自动生成结束标签。当你更改相同的标签时,关闭标记会自动更改,这两个扩展就是这样做的。

它还适用于JSX和许多其他语言,如XML,PHP,Vue,JavaScript,TypeScript,TSX。

在这里获取这两个扩展 – 自动闭合标记(Auto Close Tag)自动重命名标记(Auto Rename Tag)

Auto Rename Tag

Auto Close Tag

类似的扩展 –

8. GitLens

正如其作者所说,GitLens 增强了 Visual Studio Code 中内置的 Git 功能,它包含了许多强大的功能,例如通过跟踪代码显示的代码作者,提交搜索,历史记录和GitLens资源管理器。你可以在此处阅读这些功能的完整说明。

图片描述

类似的扩展 –

9. Git项目管理器(Git Project Manager,GPM)

Git项目管理器(Git Project Manager,GPM)允许你直接从 VSCode 窗口打开一个针对Git存储库的新窗口。 基本上,你可以打开另一个存储库而无需离开VSCode。

安装此扩展后,您必须将 gitProjectManager.baseProjectsFolders 设置为包含 repos 的URL列表。例如:

{
    "gitProjectManager.baseProjectsFolders": [
        "/home/user/nodeProjects",
        "/home/user/personal/pocs"
    ]
} 

图片描述

类似的扩展 –

Project Manager – 我没有亲自使用它,但它有百万+安装。所以建议你一定要看一下。

10. Indenticator(缩进指示器)

在视觉上突出显示当前的缩进个数,因此,你可以轻松区分在不同级别缩进的各种代码块。

图片描述

11. VSCode Icons

使您的编辑更具吸引力的图标!

图片描述

类似的扩展 –

12. Dracula (Theme)

Dracula 是我最喜欢的主题。

图片描述

我们可以使用快捷键来快速的选择更换主题;

首先:按下 Ctrl + k

然后再按下:Ctrl + t

13. 其它推荐

  • Fira Code — 带编程连体字的等宽字体。 愚人码头注:clone 项目后,找到 ttf 文件夹,然后安装该文件夹中的字体文件。重新启动 VSCode ,选择TOOLS -> Options -> Fonts and Colors ,选择 Fira Code 即可。
  • Live Server — 一个具有静态和动态页面的实时重新加载功能的本地开发服务器。
  • EditorConfig for VS Code – 此插件尝试使用.editorconfig文件中的设置覆盖用户/工作区设置,不需要其他或特定于 vscode 的文件。与任何EditorConfig插件一样,如果未指定root = true,EditorConfig将继续在项目外部查找.editorconfig文件。
  • Prettier for VSCode — 一个代码格式化工具。
  • Bookmarks – 它可以帮助您在代码中导航,轻松快速地在重要位置之间移动。不再需要搜索代码,它还支持一组选择命令,允许您选择书签线和书签线之间的区域,它对日志文件分析非常有用。
  • Path Intellisense — Visual Studio Code插件,可自动填充文件名。
  • Version Lens — 在Visual Studio代码编辑器中显示npm,jspm,bower,dub和dotnet核心的软件包版本信息。

14. Material Theme & Icons

这是 VS Code 主题中的重要角色。 作者认为重要的主题是在编辑器中用笔和纸书写最接近的东西(特别是在使用无对比变体主题时)。 从集成的工具到文本编辑器,你的编辑器看起来几乎是平的和无缝的。

想象一个史诗般的主题加上史诗般的图标。 Material Theme Icons 是替换默认 VSCode 图标的绝佳选择。设计的大型图标目录与主题融为一体,使其更加美观,这有助于你在资源管理器中轻松找到你的文件。

clipboard.png

15. 具有居中布局的禅模式或者勿扰模式 (Zen Mode)

为了让广大苦逼码农能够在 coding/docing 时有清晰的思路,代表最广大码农利益的 VSCode 也加入了“禅模式”。该模式可以在你在页面编辑文件时启用,效果是全屏化你的编辑框,然后带有若隐若现的云雾效果。

打开方式:文件 > 首选项 > 设置 > 用户设置 > 工作台 > 禅模式

clipboard.png

16. 具有连字的字体

文字的风格使阅读变得简单方便,你可以使用好看连字的字体使编辑器看起来更友好。 这里是支持连字的6种最佳字体 (根据www.slant.co)

你可以尝试 Fira Code,它非常棒而且是开源的。 以下是引入 Fira Code 后在 VSCode 辊更改该字体的方法。

"editor.fontFamily": "Fira Code",
"editor.fontLigatures": true

具体使用方法可以参考:

vscode中修改字体,使用 Fira Code

提高visual studio使用逼格的连体字(Fira code)以及多行编辑(MixEdit)

17. 彩虹缩进 (indent-rainbow)

缩进风格,这个扩展为文本前面的缩进着色,在每个步骤中交替使用四种不同的颜色。

当然如果需要自定义自己喜欢的颜色,请将以下代码段复制并粘贴到 settings.json

"indentRainbow.colors": [
"rgba(16,16,16,0.1)",
"rgba(16,16,16,0.2)",
"rgba(16,16,16,0.3)",
"rgba(16,16,16,0.4)",
"rgba(16,16,16,0.5)",
"rgba(16,16,16,0.6)",
"rgba(16,16,16,0.7)",
"rgba(16,16,16,0.8)",
"rgba(16,16,16,0.9)",
"rgba(16,16,16,1.0)"
],

18. 自定义标题栏

这是一个很棒的视觉调整,改变了不同项目的标题栏颜色,以便轻松识别它们。 如果你处理可能具有相同代码或文件名的应用程序(例如react-native 应用程序和 React Web应用程序),这非常有用

设置方式:打开方式:文件 > 首选项 > 设置 > 工作区设置

19. Tag Wrapping

如果你不认识 Emmet,那么你可能是一个喜欢打字的人。Emmet 允许你写入缩写代码并返回的相应标记,目前 VSCode 已经内置,所以不用配置了。

如果你想了解更多的 Emmet 的简写,可以查看 Emmet Cheatsheet

20. 内外平衡

这条建议来自 https://vscodecandothat.com/,作者非常推荐它。

你可以使用 balance inwardbalance outward 的 Emmet 命令在 VS 代码中选择整个标记。 将这些命令绑定到键盘快捷键是有帮助的,例如 Ctrl + Shift + 向上箭头用于平衡向外,而 Ctrl + Shift +向下箭头 用于平衡向内。

21. Turbo Console.log()

没有人喜欢输入非常长的语句,比如 console.log()。这真的很烦人,尤其是当你只想快速输出一些东西,查看它的值,然后继续编码的时候。如果我告诉你,你可以像 Lucky Luke一样快速地控制台记录任何东西呢?

这是通过名为 Turbo Console Log 的扩展来完成的。它支持对下面一行中的任何变量进行日志记录,并在代码结构之后自动添加前缀。你还可以 取消注释/注释 alt+shift+u / alt+shift+c 为所有由这个扩展添加的 console.log()

此外,你也可以通过 alt+shift+d 删除所有:

22. Live server

这是一个非常棒的扩展,可以帮助你启动一个本地开发服务器,为静态和动态页面提供实时重新加载功能,它对 HTTPS、CORS、自定义本地主机地址和端口等主要特性提供了强大的支持。

如果与 VSCode LiveShare 一起使用,它甚至可以让你共享本地主机。

23. 使用多个游标 复制/粘贴

Mac: opt+cmd+up or opt+cmd+down

Windows: ctrl+alt+up or ctrl+alt+down

Linux: alt+shift+up or alt+shift+down

24. Breadcrumbs(面包屑)

编辑器的内容上方现在有一个被称为 Breadcrumbs 的导航栏,它显示你的当前位置,并允许在符号和文件之间快速导航。要使用该功能,可使用 View > Toggle Breadcrumbs 命令或通过 breadcrumbs.enabled 设置启用。要与其交互,请使用 Focus Breadcrumbs 命令或按 Ctrl + Shift +

25. Code CLI

代码有一个强大的命令行界面,允许你控制如何启动编辑器。你可以通过命令行选项打开文件、安装扩展名、更改显示语言和输出诊断信息。

想象一下,你通过 git clone <repo-url> 克隆一个远程库,你想要替换你正在使用的当前 VS Code实例。 通过命令 code . -r 将在不必离开 CLI 界面的情况下完成这一操作 (在此处了解更多信息)。

26. Polacode


你经常会看到带有定制字体和主题的代码截屏,如下所示。这是在VS代码与 x 扩展

我知道 Carbon 也是一种更好,更可定制的替代品。 但是,Polacode 允许你保留在代码编辑器中并使用你可能已购买的任何专用字体,这些字体在 Carbon 中无法使用。

27. Quokka (JS/TS ScratchPad)

Quokka 是J avaScript 和 TypeScript 的快速原型开发平台。在你输入代码时,它将立即运行你的代码,并在代码编辑器中显示各种执行结果。

Quokka 的一个很棒的扩展插件,当你准备技术面试时,你可以输出每个步骤,而不必在调试器中设置断点。它还可以帮助您在实际使用之前研究库的函数,如 Lodash 或 MomentJS,它甚至可以用于异步调用。

28. WakaTime

如果你想记录每天编程所花的时间,WakaTime 是一个扩展,它可以帮助记录和存储有关编程活动的指标和分析。


交流

文章每周持续更新,可以微信搜索「 大迁世界 」第一时间阅读和催更(比博客早一到两篇哟),本文 GitHub https://github.com/qq44924588... 已经收录,整理了很多我的文档,欢迎Star和完善,大家面试可以参照考点复习,另外关注公众号,后台回复福利,即可看到福利,你懂的。

clipboard.png

查看原文

赞 28 收藏 19 评论 2

前端小智 发布了文章 · 2020-10-26

通过编写简易虚拟DOM,来学习虚拟DOM 的原理

作者:deathmood
译者:前端小智
来源:medium

1024程序员节,160就能买到400的书,红宝书 5 折

为了保证的可读性,本文采用意译而非直译。

要构建自己的虚拟DOM,需要知道两件事。你甚至不需要深入 React 的源代码或者深入任何其他虚拟DOM实现的源代码,因为它们是如此庞大和复杂——但实际上,虚拟DOM的主要部分只需不到50行代码。

有两个概念:

  • Virtual DOM 是真实DOM的映射
  • 当虚拟 DOM 树中的某些节点改变时,会得到一个新的虚拟树。算法对这两棵树(新树和旧树)进行比较,找出差异,然后只需要在真实的 DOM 上做出相应的改变。

用JS对象模拟DOM树

首先,我们需要以某种方式将 DOM 树存储在内存中。可以使用普通的 JS 对象来做。假设我们有这样一棵树:

<ul class=”list”>
  <li>item 1</li>
  <li>item 2</li>
</ul>

看起来很简单,对吧? 如何用JS对象来表示呢?

{ type: ‘ul’, props: { ‘class’: ‘list’ }, children: [
  { type: ‘li’, props: {}, children: [‘item 1’] },
  { type: ‘li’, props: {}, children: [‘item 2’] }
] }

这里有两件事需要注意:

  • 用如下对象表示DOM元素
{ type: ‘…’, props: { … }, children: [ … ] }

  • 用普通 JS 字符串表示 DOM 文本节点

但是用这种方式表示内容很多的 Dom 树是相当困难的。这里来写一个辅助函数,这样更容易理解:

function h(type, props, …children) {
  return { type, props, children };
}

用这个方法重新整理一开始代码:

h(‘ul’, { ‘class’: ‘list’ },
  h(‘li’, {}, ‘item 1’),
  h(‘li’, {}, ‘item 2’),
);

这样看起来简洁多了,还可以更进一步。这里使用 JSX,如下:

<ul className=”list”>
  <li>item 1</li>
  <li>item 2</li>
</ul>

编译成:

React.createElement(‘ul’, { className: ‘list’ },
  React.createElement(‘li’, {}, ‘item 1’),
  React.createElement(‘li’, {}, ‘item 2’),
);

是不是看起来有点熟悉?如果能够用我们刚定义的 h(...) 函数代替 React.createElement(…),那么我们也能使用JSX 语法。其实,只需要在源文件头部加上这么一句注释:

/** @jsx h */
<ul className=”list”>
  <li>item 1</li>
  <li>item 2</li>
</ul>

它实际上告诉 Babel ' 嘿,小老弟帮我编译 JSX 语法,用 h(...) 函数代替 React.createElement(…),然后 Babel 就开始编译。'

综上所述,我们将DOM写成这样:

/** @jsx h */
const a = (
  <ul className=”list”>
    <li>item 1</li>
    <li>item 2</li>
  </ul>
);

Babel 会帮我们编译成这样的代码:

const a = (
  h(‘ul’, { className: ‘list’ },
    h(‘li’, {}, ‘item 1’),
    h(‘li’, {}, ‘item 2’),
  );
);

当函数 “h” 执行时,它将返回普通JS对象-即我们的虚拟DOM:

const a = (
  { type: ‘ul’, props: { className: ‘list’ }, children: [
    { type: ‘li’, props: {}, children: [‘item 1’] },
    { type: ‘li’, props: {}, children: [‘item 2’] }
  ] }
);

从Virtual DOM 映射到真实 DOM

好了,现在我们有了 DOM 树,用普通的 JS 对象表示,还有我们自己的结构。这很酷,但我们需要从它创建一个真正的DOM。

首先让我们做一些假设并声明一些术语:

  • 使用以' $ '开头的变量表示真正的DOM节点(元素,文本节点),因此 $parent 将会是一个真实的DOM元素
  • 虚拟 DOM 使用名为 node 的变量表示

* 就像在 React 中一样,只能有一个根节点——所有其他节点都在其中

那么,来编写一个函数 createElement(…),它将获取一个虚拟 DOM 节点并返回一个真实的 DOM 节点。这里先不考虑 propschildren 属性:

function createElement(node) {
  if (typeof node === ‘string’) {
    return document.createTextNode(node);
  }
  return document.createElement(node.type);
}

上述方法我也可以创建有两种节点分别是文本节点和 Dom 元素节点,它们是类型为的 JS 对象:

{ type: ‘…’, props: { … }, children: [ … ] }

因此,可以在函数 createElement 传入虚拟文本节点和虚拟元素节点——这是可行的。

现在让我们考虑子节点——它们中的每一个都是文本节点或元素。所以它们也可以用 createElement(…) 函数创建。是的,这就像递归一样,所以我们可以为每个元素的子元素调用 createElement(…),然后使用 appendChild() 添加到我们的元素中:

function createElement(node) {
  if (typeof node === ‘string’) {
    return document.createTextNode(node);
  }
  const $el = document.createElement(node.type);
  node.children
    .map(createElement)
    .forEach($el.appendChild.bind($el));
  return $el;
}

哇,看起来不错。先把节点 props 属性放到一边。待会再谈。我们不需要它们来理解虚拟DOM的基本概念,因为它们会增加复杂性。

完整代码如下:

/** @jsx h */

function h(type, props, ...children) {
  return { type, props, children };
}

function createElement(node) {
  if (typeof node === 'string') {
    return document.createTextNode(node);
  }
  const $el = document.createElement(node.type);
  node.children
    .map(createElement)
    .forEach($el.appendChild.bind($el));
  return $el;
}

const a = (
  <ul class="list">
    <li>item 1</li>
    <li>item 2</li>
  </ul>
);

const $root = document.getElementById('root');
$root.appendChild(createElement(a));

比较两棵虚拟DOM树的差异

现在我们可以将虚拟 DOM 转换为真实的 DOM,这就需要考虑比较两棵 DOM 树的差异。基本的,我们需要一个算法来比较新的树和旧的树,它能够让我们知道什么地方改变了,然后相应的去改变真实的 DOM。

怎么比较 DOM 树?需要处理下面的情况:

  • 添加新节点,使用 appendChild(…) 方法添加节点

图片描述

  • 移除老节点,使用 removeChild(…) 方法移除老的节点

图片描述

  • 节点的替换,使用 replaceChild(…) 方法

图片描述

如果节点相同的——就需要需要深度比较子节点

图片描述

编写一个名为 updateElement(…) 的函数,它接受三个参数—— $parentnewNodeoldNode,其中 $parent 是虚拟节点的一个实际 DOM 元素的父元素。现在来看看如何处理上面描述的所有情况。

添加新节点

function updateElement($parent, newNode, oldNode) {
  if (!oldNode) {
    $parent.appendChild(
      createElement(newNode)
    );
  }
}

移除老节点

这里遇到了一个问题——如果在新虚拟树的当前位置没有节点——我们应该从实际的 DOM 中删除它—— 这要如何做呢?

如果我们已知父元素(通过参数传递),我们就能调用 $parent.removeChild(…) 方法把变化映射到真实的 DOM 上。但前提是我们得知道我们的节点在父元素上的索引,我们才能通过 $parent.childNodes[index] 得到该节点的引用。

好的,让我们假设这个索引将被传递给 updateElement 函数(它确实会被传递——稍后将看到)。代码如下:

function updateElement($parent, newNode, oldNode, index = 0) {
  if (!oldNode) {
    $parent.appendChild(
      createElement(newNode)
    );
  } else if (!newNode) {
    $parent.removeChild(
      $parent.childNodes[index]
    );
  }
}

节点的替换

首先,需要编写一个函数来比较两个节点(旧节点和新节点),并告诉节点是否真的发生了变化。还有需要考虑这个节点可以是元素或是文本节点:

function changed(node1, node2) {
  return typeof node1 !== typeof node2 ||
         typeof node1 === ‘string’ && node1 !== node2 ||
         node1.type !== node2.type
}

现在,当前的节点有了 index 属性,就可以很简单的用新节点替换它:

function updateElement($parent, newNode, oldNode, index = 0) {
  if (!oldNode) {
    $parent.appendChild(
      createElement(newNode)
    );
  } else if (!newNode) {
    $parent.removeChild(
      $parent.childNodes[index]
    );
  } else if (changed(newNode, oldNode)) {
    $parent.replaceChild(
      createElement(newNode),
      $parent.childNodes[index]
    );
  }
}

比较子节点

最后,但并非最不重要的是——我们应该遍历这两个节点的每一个子节点并比较它们——实际上为每个节点调用updateElement(…)方法,同样需要用到递归。

  • 当节点是 DOM 元素时我们才需要比较( 文本节点没有子节点 )
  • 我们需要传递当前的节点的引用作为父节点
  • 我们应该一个一个的比较所有的子节点,即使它是 undefined 也没有关系,我们的函数也会正确处理它。
  • 最后是 index,它是子数组中子节点的 index
function updateElement($parent, newNode, oldNode, index = 0) {
  if (!oldNode) {
    $parent.appendChild(
      createElement(newNode)
    );
  } else if (!newNode) {
    $parent.removeChild(
      $parent.childNodes[index]
    );
  } else if (changed(newNode, oldNode)) {
    $parent.replaceChild(
      createElement(newNode),
      $parent.childNodes[index]
    );
  } else if (newNode.type) {
    const newLength = newNode.children.length;
    const oldLength = oldNode.children.length;
    for (let i = 0; i < newLength || i < oldLength; i++) {
      updateElement(
        $parent.childNodes[index],
        newNode.children[i],
        oldNode.children[i],
        i
      );
    }
  }
}

完整的代码

Babel+JSX
/* @jsx h /

function h(type, props, ...children) {
  return { type, props, children };
}

function createElement(node) {
  if (typeof node === 'string') {
    return document.createTextNode(node);
  }
  const $el = document.createElement(node.type);
  node.children
    .map(createElement)
    .forEach($el.appendChild.bind($el));
  return $el;
}

function changed(node1, node2) {
  return typeof node1 !== typeof node2 ||
         typeof node1 === 'string' && node1 !== node2 ||
         node1.type !== node2.type
}

function updateElement($parent, newNode, oldNode, index = 0) {
  if (!oldNode) {
    $parent.appendChild(
      createElement(newNode)
    );
  } else if (!newNode) {
    $parent.removeChild(
      $parent.childNodes[index]
    );
  } else if (changed(newNode, oldNode)) {
    $parent.replaceChild(
      createElement(newNode),
      $parent.childNodes[index]
    );
  } else if (newNode.type) {
    const newLength = newNode.children.length;
    const oldLength = oldNode.children.length;
    for (let i = 0; i < newLength || i < oldLength; i++) {
      updateElement(
        $parent.childNodes[index],
        newNode.children[i],
        oldNode.children[i],
        i
      );
    }
  }
}

// ---------------------------------------------------------------------

const a = (
  <ul>
    <li>item 1</li>
    <li>item 2</li>
  </ul>
);

const b = (
  <ul>
    <li>item 1</li>
    <li>hello!</li>
  </ul>
);

const $root = document.getElementById('root');
const $reload = document.getElementById('reload');

updateElement($root, a);
$reload.addEventListener('click', () => {
  updateElement($root, b, a);
});

HTML

<button id="reload">RELOAD</button>
<div id="root"></div>

CSS

#root {
  border: 1px solid black;
  padding: 10px;
  margin: 30px 0 0 0;
}

打开开发者工具,并观察当按下“Reload”按钮时应用的更改。

图片描述

总结

现在我们已经编写了虚拟 DOM 实现及了解它的工作原理。作者希望,在阅读了本文之后,对理解虚拟 DOM 如何工作的基本概念以及在幕后如何进行响应有一定的了解。

然而,这里有一些东西没有突出显示(将在以后的文章中介绍它们):

  • 设置元素属性(props)并进行 diffing/updating
  • 处理事件——向元素中添加事件监听
  • 让虚拟 DOM 与组件一起工作,比如React
  • 获取对实际DOM节点的引用
  • 使用带有库的虚拟 DOM,这些库可以直接改变真实的 DOM,比如 jQuery 及其插件

原文:
https://medium.com/@deathmood...

代码部署后可能存在的BUG没法实时知道,事后为了解决这些BUG,花了大量的时间进行log 调试,这边顺便给大家推荐一个好用的BUG监控工具 Fundebug


交流

文章每周持续更新,可以微信搜索「 大迁世界 」第一时间阅读和催更(比博客早一到两篇哟),本文 GitHub https://github.com/qq449245884/xiaozhi 已经收录,整理了很多我的文档,欢迎Star和完善,大家面试可以参照考点复习,另外关注公众号,后台回复福利,即可看到福利,你懂的。

clipboard.png

查看原文

赞 17 收藏 11 评论 1