整合营销服务商

电脑端+手机端+微信端=数据同步管理

免费咨询热线:

如何成为一名前端开发之HTML入门

如何成为一名前端开发之HTML入门

末有福利

前端开发,入门简单,有一台可以运行多款浏览器的电脑,能联网查询资料即可。深入的部分,需要更多的理论知识、肯钻研的精神。 前端开发,需要入门了解的屈指可数,主要就是如下几个大方面:

背景知识

软件安装

文本编辑器

  • vscode(推荐使用,功能强大)
  • Sublime Text
  • NodePad++

浏览器

Firefox,Chrome , Opera, Safari, Internet Explorer and Microsoft Edge

版本控制

Git GitHub

构建工具

  • Webpack
  • Gulp

Web standards

Web standards ,主要定义浏览器端,功能接口标准的,标准的具体实现,由不同的浏览器厂商完成。

当下使用的web技术

主流浏览器

Firefox, Chrome , Opera, Safari, Internet Explorer and Microsoft Edge

协议

协议,主要用于通信。前后端不是独立的,彼此通过协议,互换信息,web系统才能运行正常

  • http
  • https
  • socket

HTML, CSS, and JavaScript

开发工具

  • 各浏览的DevTool,便于调试
  • Linters插件
  • Minify工具
  • CDN等
  • 测试工具
  • js库和前端框架(站点构建的更快、更高效)

服务端语言

Python, NodeJS, Deno, Go, Rust 

面临的挑战

浏览器兼容

Web standards,各实现厂商不同,支持力度不同,浏览器兼容问题自然存在

响应式设计

厂商的不同,展示场景的不同,意味着不能全篇一律敲定,需要动态变化展示内容

性能

天下站点,只有快,才能留住用户

易用性

站点的访问人群不同,要具备普适性, 都能轻松使用。

国际化

国际语言众多,需要尽可能的多支持

安全性

用户隐私数据保护

HTML

html基础概念

html元素

当然,也有例外,不是这种格式的

# Empty elements, or (void elements.) 
<img src="https://raw.githubusercontent.com/mdn/beginner-html-site/gh-pages/images/firefox-icon.png">
复制代码

html元素嵌套

<p>My cat is <strong>very</strong> grumpy.</p>
复制代码

块元素和行内元素

块元素,独占一行;行内元素,按先后顺序,排列 这些与css的display不同,不影响元素能包含哪些元素,能被哪些元素包含

元素属性

其中,也存在一种特殊的属性:Boolean attributes

<input type="text" disabled>
# 等效于
<input type="text" disabled="disabled">
复制代码

另外,也会存在一些特殊的写法。推荐都是key="value"形式

<a href=https://www.mozilla.org/>favorite website</a>
复制代码

html文档结构

不管多少连续的空白,浏览器都会解析会一个空格

<!DOCTYPE html> # 定义解析格式
<html> # 文档的root 
  <head> #定义源数据地方 
    <meta charset="utf-8">
    <title>My test page</title>
  </head>
  <body> # 文档的可见内容部分
    <p>This is my page</p>
  </body>
</html>
复制代码

元数据等请移步参考

html特殊字符

<, >,",' and &, 这是html自身使用的,如果用户需要展示,那么需要转义



html注释

<!-- 
 <p>I am!</p> 
-->
复制代码

html 多媒体

image

  • 普通的图片

alt描述图片

# 推荐添加alt,而不是text子元素节点添加
<img src="images/dinosaur.jpg"
    alt="The head and torso of a dinosaur skeleton;
         it has a large head with long sharp teeth">
复制代码

caption添加标题

<figure>
  <img src="images/dinosaur.jpg"
       alt="The head and torso of a dinosaur skeleton;
            it has a large head with long sharp teeth"
       width="400"
       height="341">
 <figcaption>A T-Rex on display in the Manchester University Museum.</figcaption>
</figure>
复制代码
  • 响应式图片

根据展示设备尺寸的不同,加载不同的图片

<img srcset="elva-fairy-480w.jpg 480w,
            elva-fairy-800w.jpg 800w"
    sizes="(max-width: 600px) 480px,
           800px"
    src="elva-fairy-800w.jpg"
    alt="Elva dressed as a fairy">
复制代码

设备尺寸相同,但是分辨率不同

<img srcset="elva-fairy-320w.jpg,
            elva-fairy-480w.jpg 1.5x,
            elva-fairy-640w.jpg 2x"
    src="elva-fairy-640w.jpg"
    alt="Elva dressed as a fairy">
复制代码

利用picture,不同设选择加载不同的图片

<picture>
  <source media="(max-width: 799px)" srcset="elva-480w-close-portrait.jpg">
  <source media="(min-width: 800px)" srcset="elva-800w.jpg">
  <img src="elva-800w.jpg" alt="Chris standing up holding his daughter Elva">
</picture>
复制代码

picture+svg

<picture>
  <source type="image/svg+xml" srcset="pyramid.svg">
  <source type="image/webp" srcset="pyramid.webp"> 
  <img src="pyramid.png" alt="regular pyramid built from four equilateral triangles">
</picture>
复制代码

audio

单一url 浏览器厂商,针对音频的支持格式不是不同的,譬如: MP3, MP4 and WebM

<video src="rabbit320.webm" controls>
  <p>Your browser doesn't support HTML5 video. Here is a <a href="rabbit320.webm">link to the video</a> instead.</p> 
</video>
复制代码

浏览器适配问题

<audio controls>
  <source src="viper.mp3" type="audio/mp3">
  <source src="viper.ogg" type="audio/ogg">
  <p>Your browser doesn't support HTML5 audio. Here is a <a href="viper.mp3">link to the audio</a> instead.</p>
</audio>
复制代码

video

单一url 浏览器厂商,针对视频的支持格式不是不同的

<video src="rabbit320.webm" controls>
  <p>Your browser doesn't support HTML5 video. Here is a <a href="rabbit320.webm">link to the video</a> instead.</p> 
</video>
复制代码

浏览器适配问题

<video controls width="400" height="400"
       autoplay loop muted preload="auto" 
       poster="poster.png">
  <source src="rabbit320.mp4" type="video/mp4">
  <source src="rabbit320.webm" type="video/webm">
  <p>Your browser doesn't support HTML video. Here is a <a href="rabbit320.mp4">link to the video</a> instead.</p>
</video>
复制代码

svg

image引用svg

<img 
    src="equilateral.svg" 
    alt="triangle with all three sides equal"
    height="87"
    width="100" />
复制代码

html引用svg

<svg width="300" height="200">
    <rect width="100%" height="100%" fill="green" />
</svg>
复制代码

iframe等中引用svg

<iframe src="triangle.svg" width="500" height="500" sandbox>
    <img src="triangle.png" alt="Triangle with three unequal sides" />
</iframe>
复制代码

canvas

canvas与svg不同,canvas基于像素,svg基于矢量图

#html
<canvas id="my-canvas" width="600" height="400"></canvas>
复制代码

嵌入元素

iframe, embed and object

iframe

<iframe src="https://developer.mozilla.org/en-US/docs/Glossary"
        width="100%" height="500" frameborder="0"
        allowfullscreen sandbox>
  <p> 
    <a href="https://developer.mozilla.org/en-US/docs/Glossary">
       Fallback link for browsers that don't support iframes
    </a>
  </p>
</iframe>
复制代码

html table

样式指定

<table>
  <tr>
    <th>Data 1</th>
    <th style="background-color: yellow">Data 2</th>
  </tr>
  <tr>
    <td>Calcutta</td>
    <td style="background-color: yellow">Orange</td>
  </tr>
  <tr>
    <td>Robots</td>
    <td style="background-color: yellow">Jazz</td>
  </tr>
</table>
复制代码

更好的样式指定

col 一次指定即可

<table>
  <colgroup>
    <col> # 定义在colgroup中,与th个数对应
    <col style="background-color: yellow">
  </colgroup>
  <tr>
    <th>Data 1</th>
    <th>Data 2</th>
  </tr>
  <tr>
    <td>Calcutta</td>
    <td>Orange</td>
  </tr>
  <tr>
    <td>Robots</td>
    <td>Jazz</td>
  </tr>
</table>
复制代码

全部设置

<colgroup>
  <col style="background-color: yellow" span="2">
</colgroup>
复制代码

如果你现在也想学习前端开发技术,在学习前端的过程当中有遇见任何关于学习方法,学习路线,学习效率等方面的问题,你都可以申请加入我的Q群:前114中6649后671,里面有许多前端学习资料 大厂面试真题免费获取,希望能够对你们有所帮助。

击上方 "程序员小乐"关注, 星标或置顶一起成长

每天凌晨00点00分, 第一时间与你相约


每日英文

Someday, you will find the one, who will watch every sunrise with you until the sunset of your life.

总有一天,你会遇上那个人,陪你看每一次日出,直到你的人生落幕。


每日掏心话

每个人的心里,都有一个禁地,不愿再触及,也不愿再想起,却永远无法忘记。既然忘不了,就不必费神忘记,做人本就很累,何必再自寻烦恼。

来自:等你归去来 | 责编:乐乐

链接:cnblogs.com/yougewe/p/10327217.html

程序员小乐(ID:study_tech)第 835 次推文 图片来自百度


往日回顾:手把手教你如何解决代码中 if…else 过多的问题


正文


谈到线上环境,一般开发同学,不太容易接触到。即使接触到,也只是其中的冰山一角!

所以,其实说起线上环境的部署,咱们好像都有点懂,但是又都不一定完全懂!网上的知识无穷无尽,但往往都是各司一职,对于普通同学,很难窥其全貌!

所以,我今天就来说说,一些普通的线上环境的部署步骤,和一些脚本小技巧吧。只希望通过这篇文章,能够让大家有一个运维的全局观!

我将会分几条线来整理咱们的运维思路!

一、从理论上讲,我们应该怎么做?

1.针对的是什么样的用户群体,体量大概会有多少?

这是一个部署规划的前题。为啥呢?

一、如果你针对的是后台管理员,人数也不多,那么你可能只需要一个服务器就可以了,前后端也都可以部署在同一台服务器上;如果稍微考虑下单点故障问题,则顶多两台服务器搞定!

二、如果针对的是前端普通用户,那么,往往就会考虑多机部署,前后端分离,单点问题,负载均衡了;至于具体要部署多少台,则要根据你的用户情况来定了,当然,前期一般没必要部署很多台服务器!更多的考虑是横向扩展的能力。只要能支持横向扩展,则短期内,往往不用担心性能和架构问题!

2.为支持预估的用户量,大概需要多少的带宽?
  
有访问就会有流量产生,而预估的用户量,则是一个带宽资源需求的一个决断依据!

一般针对前期用户不太确定的场景,可以先买个 10M 左右的共享带宽,基本能够应付;经过一段时间的观察后,再进行带宽的变更也可以;

当然,考虑带宽,自然也会存在一个公网IP的问题,因为流量是从IP进来的。而在IP之前,则是域名的访问。域名问题则又涉及到DNS,不必细说!

公网IP可以是直接指向机器的,也可以是指向负载均衡器的。如果想要支持横向扩展,则IP的指向一定是一个负载均衡器。因为只有这样,当遇到流量突增,或者做活动的时候,才能更快速的进行扩容!

3.数据库规划如何?

数据在当下时代,算是重中之重了。机器没了可以再买,代码没了可以再写,但是数据没了就完蛋了!

数据库一般要遵从几个基本原则: 一、带宽要大;二、运算速度要快;三、要能承受足够大的运算空间;(即:带宽足够大/cpu核数够多/内存容量够大/最大并发连接数/…)

所以,一般不要在数据库上省钱,能多点就多点!

另外,也不要什么样的数据都往数据库(关系型数据库)存,搞清楚各类型数据库的强项与弱项,做出明智的选择。否则会带来很多不必要的麻烦!

4.应用要基于操作系统来部署还是基于容器来部署?

这是个决策性的问题!基于操作系统的部署,是一种比较传统和常见的部署方式。优点是,很多系统工具都是完善的,只要你大概知道要部署什么,部署下来一般不会有太多问题,因为这是个完整的系统。

但是,由于系统与系统之间可能不能完全一致,有各种各样的差异,所以,你在这个机器上运行成功的东西,在另外的机器上则不一定能成功。因此,基于系统的部署将会使我们的问题排查难度大大增加,而且移值性会很差。比如你在机器A上安装了10个软件,你可能配置了n个选项,但是,当你在安装B机器的时候,你并不能很好的利用原有的配置,你还得从头一个个地来!

因此,有另一个部署方案,基于容器的部署(我这里是基于docker容器的部署)。docker就类似于一个个的虚拟机,但是它更加轻量级,当一个docker部署好后,你可以任意复制到其他机器上运行,看起来很诱人吧。

不过,docker只是入门级容器,对于大量集群容器的管理,还是显得力不从心,当然你很容易找到另一个方案: Kubernetes (K8s); 你只要花上少许的时间了解下,你就可以应用了!

当然了,使用容器的方案,有没有什么缺点呢?应该是有的,比如本来可以基于系统的监控方案,因为接入容器后,监控指标则不一定适用了,当然现成的方案还是有的,不过得另外再花点时间研究了。再比如:如果容器出了问题,是否能排查出来,这也是另一个问题!

5.都有些什么样的基础设施或者中间件?

想要运行应用程序,自然是先考虑运行环境的。比如:应用需要 nginx 来做http服务器,用 tomcat 来做java web应用服务器,用redis来做缓存中间件,用zk来做应用协调中间件,用rabbitmq来做消息中间件,等等!

因此,要在代码跑起来之前,先要把这些环境给准备好咯。

准备这些中间件或基础设施之前,也要问下当下的形势,是否有高性能高可用应用需求?比如:是否需要集群部署,或者单机部署?往往集群部署又会依赖其他的中间件!也更复杂!

当然,这些都不是事。事儿是在出问题之后,能够有意识,能够猜测到问题发生的点!

6.应用代码应该怎样部署?

当基础环境就绪后,就应该让主角上场了。应用代码怎么部署?

最简单的: 通过ftp上传代码到服务器上后,一个个部署!这种方案是最原始的,也是在没有办法搞更好的方案的时候使用的,不应长期使用;

稍微好点的: 使用集成工具(如jenkins)进行打包,然后上传一个私有yum镜像服务器(yum 源)。然后在线进行yum 安装;这种方式,借助了集成工具,几个好处:


  • 可以检测代码合法性如:单元测试、代码规范(可能需要插件);

  • 对任何的改动有简单留档,可以备查的同时,也为代码的回滚提供了可能;

  • 减少了手动上传导致的包破坏的可能性;

  • 适合大规模应用;


再成熟点的: 再往后面,手动 yum 安装也已经太累了,所以急需一个部署平台,实现自动化部署;(这里的自动化部署可能就是基于CI集成部署的一种升级版)。总之,大大减小了人工参与程序,提升了效率,同时也保证了质量!当然,这种部署平台已经经过了严格的测试,出错的可能性也比较小了!

7.服务器的安全性?

不考虑服务器的安全性的应用,无异于自暴自弃。黑客无处不在,不过幸好现在系统也是越来越完善,只要稍加控制,即不那么容易被攻破了。但是如果放弃安全防护,则随便来一个菜鸟程序员就把你搞死了,那时的损失就大了。推荐:如何设计一个安全的对外接口

网络安全是个很专业的领域,我不敢造次去谈它。不过我们可以简单的做下防护: 如防火墙、授权操作、病毒库等等。当然,如果使用xx云服务,则轻松方便多了,在后台点点设置几下搞定!

8.服务的可监控性?

无监控,不上线!

这是一个警示,如果线上服务没有监控,则所有线上的东西,都成了盲区,这对程序员GG们来说,简直太糟糕了,虽然他们很自信!

监控分两个方面:一是系统级别的监控;二是应用级别的监控;(一般忽略其他监控: 如网络)

系统级别的监控一般可以安装第三方的软件来解决: 如 zabbix, grafana …

而应用级别的监控,则需要自己拥有一套监控代码了,而这对初期项目,则往往比较吃力。当然,如果引入一些开源的解决方案也是可以的,比如 ELK, 做到分布式日志中心的作用的同时,也可以根据日志做相应的应用报错监控!然而这又涉及另外的机器费用和人力成本问题,也显得不那么简单了。

而如果使用xx云服务,则往往都会自带服务器监控的,可以很方便地查看到服务器情况,站在高层次预估应用是否存在潜藏的问题!

如上,就是一些个人觉得的在部署一整套线上环境的时候,需要考虑的事项!从理论上讲解了下个人见解,不对之处,请赐教!

二、接下来,我将给到一些实际的操作捷径或提示?(linux)

1.免密登录服务器?

在n服务器之间跳转,如果每次都要求输入密码,那确实太烦了。尤其在密码一般还很不容易记住的情况下!

所以,可以将一台服务器作为跳板机,在这台服务器上,可以免密地登录到允许的n台子服务器;

操作步骤有二:

# 1. 先使用 ssh-keygen 生成本机的key
ssh-keygen -t rsa # 如果已生成不要重复生成
# 2. 使用 ssh-copy-id 将本机的 key 发送到需要免密登录的服务器,首次copy时会要求输入密码,后续则免密了
ssh-copy-id -i ~/.ssh/id_rsa.pub root@172.1.2.111

2.服务器之间文件(夹)拷贝?

拷贝文件的目的有很多,比如:代码同步,文件同步,资源同步,甚至是会话同步….

# 1. 使用scp 拷贝文件
scp /home/ol-web.war root@xxx.com:/www/tomcat/wepapps/ # 从本机拷贝到远程
scp /home/ol-web.war root@xxx.com:/www/tomcat/wepapps/ # 从远程拷贝到本机
scp -r /www/nginx/html/ root@.2.3.2:/www/nginx/html/ # 从本机拷贝文件夹到远程
# 2. 使用 rsync 同步文件,(可能需要安装 rsync 服务)
rsync -av --delete /www/nginx/html/ root@.2.3.1:/www/nginx/html/ # 同步所有属性,本地删除的文件也同步远程删除

其中,scp一般是系统自带的命令,而rsync则需要自行安装服务。

scp复制你可以认为是增量复制,所以远程文件往往会越来越大,垃圾文件越来越多。

而rsync则是保持两端完全一致,可能会符合应用场景!但是,别忘了把rsync服务加入到开机启动项中!

3.快捷使用 ssh 等等命令,使用 tab 键进行信息补全?

当使用 ssh / scp 等等命令操作的时候,其操作对象往往 1.2.3.x 这样的ip显示,如果不能友好点,那确实太累了!我们可以如下操作,以实现 ssh 也能更好的记忆:

# 在文件 /root/.bashrc 中,添加脚本如下,使自动补全添加 ssh
# auto complete ...
complete -W "$(echo $(grep -v '^$|#' .ssh/config | sort -u | sed 's/^ssh //'))" ssh
# 在文件 /root/.ssh/config 中,添加需要自动补全的服务器,
Host 172.2.3.5 server-api-01
Host 172.2.3.6 server-api-02
# 以上服务器名字需要在 /etc/hosts 文件中添加相应解析
# 而登录 server时,只需, ssh server-api-01 即可

如上补全工作,无需在所有服务器上进行操作,只需在相应的跳板机上提供功能即可!

4.简要 saltstack 搭建指南?

salt 是个方便易用的集群管理工具,比如你可以用于批量重启服务,全局搜索日志等等;

# 1. 安装, 仅需到相应机器上安装即可
yum install salt-master salt-minion
# 2. 配置 /etc/salt/master /etc/salt/minion, 最简单的,只需修改 minion 配置,指向 master 的ip即可;
#指定master,冒号后有一个空格, minion
master: 172.1.2.22
id: server-api-01
user: root
# 3. 启动所有节点, status, restart
systemctl start salt-master # 162机器可用
systemctl start salt-minion
/etc/init.d/salt-master start # 155机器可用
/etc/init.d/salt-minion start
# 4. 将所有salt-minion 添加到 master 集群管理
salt-key -A
# 5. 登录跳板机 api_01, 运行salt 操作,执行集群管理工作
salt server-api-02 cmd.run 'lsof -i:80'
salt '*' test.ping

5.简要集群复制shell脚本?

有时,你可能需要将你的应用发布到n台服务中,你可以直接改如下shell,也可以依赖于salt这样的高级工具进行发布!shell 参考如下:

#!/bin/bash
# find out my ip to exclude...
MY_MERCHINE_IP=`ifconfig eth0 |awk -F "[: ]+" '/inet addr/{print }'`;
MERCHINE_IP_LIST="172.1.2.7 172.1.3.4";
for m_ip in $MERCHINE_IP_LIST;
do
if [[ $m_ip !=$MY_MERCHINE_IP ]]; then
echo "- Installing apps to mechine@${m_ip} ...";
# install api apps
scp /www/test/hello-1.0.0-SNAPSHOT.jar root@${m_ip}:/www/test/
rsync -av --delete /www/html/ root@${m_ip}:/www/html/
echo "- Install apps to merchine@${m_ip} done.";
fi;
done;

6.简要docker搭建指南?

docker 作为一个容器化的基石,一出世就被追棒。包括现在的 k8s ,也是基于docker的。docker 可以让你在一处搭建,处处运行,从而避免每次新买机器就要搞很久的尴尬局面;其搭建也是很简单的(简单应用):

为方便任意发挥,我们可以基于centos这种系统级别的镜像进行创建自己的image;

# docker 安装:
yum install docker
service docker start
# 拉取 centos6 的 docker 镜像
docker pull centos:6
docker images
# 构建一个 image, 创建空目录,编辑 Dockerfile
vim Dockerfile # 内容可变
FROM centos:6
MAINTAINER oom <w@163.com>
# move all configuration files into container

# RUN yum install -y lsof
# EXPOSE 80
# CMD ["sh","-c","service httpd start;bash"]
# 创建镜像
docker build -t tmp_image:1.0 .
# 创建并运行容器
docker run -h tmp_container -itd --name tmp_container -v /opt/docker/webapps:/www/webapp tmp_image:1.0
# 进入容器,相当于进入 centos 操作系统
docker exec -it tmp_container bash
# 保存容器修改到images
  docker commit -m 'web final.' 49d79fc19eaa tmp_image:1.2
# 备份容器修改后的docker镜像
  docker save > /opt/images/images_final/tmp_image.final.tar tmp_image:1.2
# 恢复你的备份镜像,即全网发布
# 可以在任何装 docker 的地方加载保存的镜像
  docker load -i /opt/images/images_final/tmp_image.final.tar

7.定制你的登录欢迎语?

由于可能存在线上环境与测试环境共存的情况,一不小心的切换错误,就可能导致不可挽回的损失。所以,如果我们能在登录的时候,做一个简单的提示,那么就会少一点出错的可能性。所以,订制你的登录欢迎语吧!

# 修改登录欢迎语 vim /etc/motd
*****************************************************************
!!! WARNING: 欢迎来到线上机器: service-api-01 ,请谨慎操作哦 !!!
*****************************************************************

这样,用户登录后,就会清楚的知道自己是在操作生产环境了!

8.更方便的查看nginx的访问日志?

对于后端的日志而言,往往都是主动打印到某个固定位置,从而开发人员可以直接使用 tail -f xxx.log 进行日志的查看!

然而对于前端的代码而言,则往往没有相应的开发日志,唯一可以借助的就是 http 服务器的日志来排查问题了!

所以,比如使用 nginx 作为 http 服务器,那么就应该把尽可能多的有用日志打印出来。那么,如何快速查看 nginx 日志,则是有必要的!比如我们可以这样:

# vim /usr/bin/log_nginx_host , 使用 log_nginx_host 直接查看所有 nginx 日志
tail -f /var/log/nginx/access.log /var/log/nginx/error.log

如上,将会把访问日志与错误日志一起打印出来,从而快速定位问题!


欢迎在留言区留下你的观点,一起讨论提高。如果今天的文章让你有新的启发,学习能力的提升上有新的认识,欢迎转发分享给更多人。


猜你还想看


阿里、腾讯、百度、华为、京东最新面试题汇集

从上帝视角看Java如何运行

一次项目代码重构:使用Spring容器干掉条件判断

IDEA-2020.1 版本针对调试器和代码分析器的改进,值得期待

关注订阅号「程序员小乐」,收看更多精彩内容
嘿,你在看吗?

数创建与定义的过程

  • 函数定义阶段 在堆内存中开辟一段空间 把函数体内的代码一模一样的存储在这段空间内 把空间赋值给栈内存的变量中
  • 函数调用阶段 按照变量名内的存储地址找到堆内存中对应的存储空间 在调用栈中开辟一个新的函数执行空间 在执行空间中进行形参赋值 在执行空间中进行预解析 在执行空间中完整执行一遍函数内的代码 销毁在调用栈创建的执行空间

不会销毁的函数执行空间

  1. 当函数内返回一个引用数据类型
  2. 并且函数外部有变量接收这个引用数据类型
  3. 函数执行完毕的执行空间不会销毁
  4. 如果后续不需要这个空间了,只要让变量指向别的位置即可
function fn() {
    const obj={
        a: 1,
        b: 2
    }
    return obj
}

const res=fn()
console.log(res)

// 如果后续不需要这个空间了, 只需要让 res 指向别的位置即可
res=100


闭包

  • 需要一个不会被销毁的执行空间
  • 需要直接或间接返回一个函数
  • 内部函数使用外部函数的私有变量
  • 概念 : 函数里的函数
  • 优点: 可以在函数外访问函数内的变量 延长了变量的生命周期
  • 缺点: 闭包函数不会销毁空间,大量使用会造成内存溢出
function outer () {
    let a=100
    let b=200

    // 我们说 inner 是 outer 的闭包函数
    function inner () {
        /**
         * 我使用了一个 a 变量, 但是 inner 自己没有
         * 所以我用的是 外部函数 outer 内部的变量 a
        */
        // console.log(a)
        return a
    }
    return inner
}

// 我们说 res 是 outer 的闭包函数
let res=outer()

let outerA=res()
console.log(outerA)


沙箱模式

  • 利用了函数内间接返回了一个函数
  • 外部函数返回一个对象,对象内书写多个函数
function outer () {
    let a=100
    let b=200

    // 创建一个 沙箱, "间接的返回一个函数"
    const obj={
        getA: function () {
            return a
        },
        getB: function () {
            return b
        },
        setA: function (val) {
            a=val
        }
    }
    return obj
}

// 得到一个沙箱
const res1=outer()

console.log(res1.getA())    // 100
console.log(res1.getB())    // 200

res1.setA(999)
console.log(res1.getA())    // 999

// 重新得到一个沙箱
const res2=outer()
console.log(res2.getA())    // 100


沙箱小案例

<button class="sub">-</button>
<input class="inp" type="text" value="1">
<button class="add">+</button>
<br>
<button class="sub1">-</button>
<input class="inp1" type="text" value="1">
<button class="add1">+</button>


// 准备一个沙箱
function outer() {
    let a=1
    return {
        getA() {
            return a
        },
        setA(val) {
            a=val
        }
    }
}

// 0. 获取元素
const subBtn=document.querySelector('.sub')
const addBtn=document.querySelector('.add')
const inp=document.querySelector('.inp')

// 0. 准备变量
// let count=1
let res=outer()
subBtn.onclick=function () {
    let count=res.getA()
    res.setA(count - 1)
    inp.value=res.getA()
}
addBtn.onclick=function () {
    // count++
    let count=res.getA()
    res.setA(count + 1)
    inp.value=res.getA()
}

// 0. 获取元素
const subBtn1=document.querySelector('.sub1')
const addBtn1=document.querySelector('.add1')
const inp1=document.querySelector('.inp1')

// 0. 准备变量
let res1=outer()
subBtn1.onclick=function () {
    let count=res1.getA()
    res1.setA(count - 1)
    inp1.value=res1.getA()
}
addBtn1.onclick=function () {
    let count=res1.getA()
    res1.setA(count + 1)
    inp1.value=res1.getA()
}


沙箱语法糖

  • 尽可能的简化沙箱模式的语法
  • 利用 get 和 set 进行操作数据
  • 语法糖: 在不影响功能的情况下提供一点更适合操作的语法
function outer() {
    let a=100
    let b=200

    return {
        get a() { return a },
        get b() { return b },
        set a(val) { a=val }
    }
}

let res=outer()
console.log(res.a)
console.log(res.b)
res.a=999
console.log(res.a)   // 999 


闭包面试题!!!!

function fun(n, o) {
    console.log(o)

    const obj={
        fun: function (m) {
            return fun(m, n)
        }
    }

    return obj
}

var a=fun(0)    // undefined
a.fun(1)    // 0
a.fun(2)    // 0
a.fun(3)    // 0

/**
 *  var a=fun(0)
 *  a.fun(1)
 *  a.fun(2)
 *  a.fun(3)
 *
 *  1. var a=fun(0)
 *          调用 fun(QF001) 函数(QF001) 传递一个 参数 0
 *              全局函数 fun (QF001) 的 形参 n==0     形参 o==undefined
 *          调用 fun 函数后, 会返回一个对象 存储在 变量 a 中, 这个对象内部有一个属性叫做 fun, 属性值为 一个函数(QF002),
 *              所以我们可以通过 a.fun()   去调用这个函数
 *
 *  2. a.fun(1)
 *      2.1 调用这个函数 会 return 一个函数 fun (为全局函数 QF001) 的调用结果,   
 *      2.2 调用全局函数 fun(m, n)       m 此时 传递的是 1, n 传递的是 0
 *      2.3 执行全局函数 fun(m, n) 内部会输出第二个形参
 *
 *  3. a.fun(2)
 *      2.1 调用这个函数 会 return 一个函数 fun(为全局函数 QF001) 的调用结果
 *      2.2 调用全局函数 fun(m, n)  m 此时传递的是 2, n 传递的是 0
 *      2.3 执行全局函数 fun(m, n) 内部会输出第二个形参
 *
*/


防抖与节流

防抖

  • 解释:在短时间内触发一件事,每次都用上一次的时间替代,也就是只执行最后一次