末有福利
前端开发,入门简单,有一台可以运行多款浏览器的电脑,能联网查询资料即可。深入的部分,需要更多的理论知识、肯钻研的精神。 前端开发,需要入门了解的屈指可数,主要就是如下几个大方面:
Firefox,Chrome , Opera, Safari, Internet Explorer and Microsoft Edge
Git GitHub
Web standards ,主要定义浏览器端,功能接口标准的,标准的具体实现,由不同的浏览器厂商完成。
Firefox, Chrome , Opera, Safari, Internet Explorer and Microsoft Edge
协议,主要用于通信。前后端不是独立的,彼此通过协议,互换信息,web系统才能运行正常
Python, NodeJS, Deno, Go, Rust
Web standards,各实现厂商不同,支持力度不同,浏览器兼容问题自然存在
厂商的不同,展示场景的不同,意味着不能全篇一律敲定,需要动态变化展示内容
天下站点,只有快,才能留住用户
站点的访问人群不同,要具备普适性, 都能轻松使用。
国际语言众多,需要尽可能的多支持
用户隐私数据保护
当然,也有例外,不是这种格式的
# Empty elements, or (void elements.)
<img src="https://raw.githubusercontent.com/mdn/beginner-html-site/gh-pages/images/firefox-icon.png">
复制代码
<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>
复制代码
不管多少连续的空白,浏览器都会解析会一个空格
<!DOCTYPE html> # 定义解析格式
<html> # 文档的root
<head> #定义源数据地方
<meta charset="utf-8">
<title>My test page</title>
</head>
<body> # 文档的可见内容部分
<p>This is my page</p>
</body>
</html>
复制代码
元数据等请移步参考
<, >,",' and &, 这是html自身使用的,如果用户需要展示,那么需要转义
<!--
<p>I am!</p>
-->
复制代码
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>
复制代码
单一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>
复制代码
单一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>
复制代码
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与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>
复制代码
<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 版本针对调试器和代码分析器的改进,值得期待
关注订阅号「程序员小乐」,收看更多精彩内容
嘿,你在看吗?
数创建与定义的过程
不会销毁的函数执行空间
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()
}
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) 内部会输出第二个形参
*
*/
*请认真填写需求信息,我们会在24小时内与您取得联系。