什么是幂等?分布式锁如何实现业务幂等?
|
现如今很多系统都会基于分布式或微服务思想完成对系统的架构设计。那么在这一个系统中,就会存在若干个微服务,而且服务间也会产生相互通信调用。那么既然产生了服务调用,就必然会存在服务调用延迟或失败的问题。当出现这种问题,服务端会进行重试等操作或客户端有可能会进行多次点击提交。如果这样请求多次的话,那最终处理的数据结果就一定要保证统一,如支付场景。此时就需要通过保证业务幂等性方案来完成。
|什么是幂等
幂等本身是一个数学概念。即 f(n) = 1^n ,无论n为多少,f(n)的值永远为1。在编程开发中,对于幂等的定义为:无论对某一个资源操作了多少次,其影响都应是相同的。 换句话说就是:在接口重复调用的情况下,对系统产生的影响是一样的,但是返回值允许不同,如查询。 幂等性不仅仅只是一次或多次操作对资源没有产生影响,还包括第一次操作产生影响后,以后多次操作不会再产生影响。并且幂等关注的是是否对资源产生影响,而不关注结果。
以SQL为例:
幂等性设计主要从两个维度进行考虑:空间、时间。空间:定义了幂等的范围,如生成订单的话,不允许出现重复下单。时间:定义幂等的有效期。有些业务需要永久性保证幂等,如下单、支付等。而部分业务只要保证一段时间幂等即可。同时对于幂等的使用一般都会伴随着出现锁的概念,用于解决并发安全问题。
|接口幂等
对于幂等的考虑,主要解决两点前后端交互与服务间交互。这两点有时都要考虑幂等性的实现。从前端的思路解决的话,主要有三种:前端防重、PRG模式、Token机制。2.1)前端防重 通过前端防重保证幂等是最简单的实现方式,前端相关属性和JS代码即可完成设置。可靠性并不好,有经验的人员可以通过工具跳过页面仍能重复提交。主要适用于表单重复提交或按钮重复点击。2.2)PRG模式 PRG模式即POST-REDIRECT-GET。当用户进行表单提交时,会重定向到另外一个提交成功页面,而不是停留在原先的表单页面。这样就避免了用户刷新导致重复提交。同时防止了通过浏览器按钮前进/后退导致表单重复提交。是一种比较常见的前端防重策略。2.3)token机制 2.3.1)方案介绍 通过token机制来保证幂等是一种非常常见的解决方案,同时也适合绝大部分场景。该方案需要前后端进行一定程度的交互来完成。
1)服务端提供获取token接口,供客户端进行使用。服务端生成token后,如果当前为分布式架构,将token存放于redis中,如果是单体架构,可以保存在jvm缓存中。
2)当客户端获取到token后,会携带着token发起请求。
3)服务端接收到客户端请求后,首先会判断该token在redis中是否存在。如果存在,则完成进行业务处理,业务处理完成后,再删除token。如果不存在,代表当前请求是重复请求,直接向客户端返回对应标识。
但是现在有一个问题,当前是先执行业务再删除token。在高并发下,很有可能出现第一次访问时token存在,完成具体业务操作。但在还没有删除token时,客户端又携带token发起请求,此时,因为token还存在,第二次请求也会验证通过,执行具体业务操作。
对于这个问题的解决方案的思想就是并行变串行。会造成一定性能损耗与吞吐量降低。第一种方案:对于业务代码执行和删除token整体加线程锁。当后续线程再来访问时,则阻塞排队。第二种方案:借助redis单线程和incr是原子性的特点。当第一次获取token时,以token作为key,对其进行自增。然后将token进行返回,当客户端携带token访问执行业务代码时,对于判断token是否存在不用删除,而是对其继续incr。如果incr后的返回值为2。则是一个合法请求允许执行,如果是其他值,则代表是非法请求,直接返回。
那如果先删除token再执行业务呢?其实也会存在问题,假设具体业务代码执行超时或失败,没有向客户端返回明确结果,那客户端就很有可能会进行重试,但此时之前的token已经被删除了,则会被认为是重复请求,不再进行业务处理。
这种方案无需进行额外处理,一个token只能代表一次请求。一旦业务执行出现异常,则让客户端重新获取令牌,重新发起一次访问即可。推荐使用先删除token方案 但是无论先删token还是后删token,都会有一个相同的问题。每次业务请求都回产生一个额外的请求去获取token。但是,业务失败或超时,在生产环境下,一万个里最多也就十个左右会失败,那为了这十来个请求,让其他九千九百多个请求都产生额外请求,就有一些得不偿失了。虽然redis性能好,但是这也是一种资源的浪费。
|服务幂等|防重表
对于防止数据重复提交,还有一种解决方案就是通过防重表实现。防重表的实现思路也非常简单。首先创建一张表作为防重表,同时在该表中建立一个或多个字段的唯一索引作为防重字段,用于保证并发情况下,数据只有一条。在向业务表中插入数据之前先向防重表插入,如果插入失败则表示是重复数据。
对于防重表的解决方案,可能有人会说为什么不使用悲观锁。悲观锁在使用的过程中也是会发生死锁的。悲观锁是通过锁表的方式实现的。 假设现在一个用户A访问表A(锁住了表A),然后试图访问表B; 另一个用户B访问表B(锁住了表B),然后试图访问表A。 这时对于用户A来说,由于表B已经被用户B锁住了,所以用户A必须等到用户B释放表B才能访问。 同时对于用户B来说,由于表A已经被用户A锁住了,所以用户B必须等到用户A释放表A才能访问。此时死锁就已经产生了。
|Mysql乐观锁保证幂等
MySQL乐观锁是基于数据库完成分布式锁的一种实现,实现的方式有两种:基于版本号、基于条件。但是实现思想都是基于MySQL的行锁思想来实现的。
通过版本号控制是一种非常常见的方式,适合于大多数场景。但现在库存扣减的场景来说,通过版本号控制就是多人并发访问购买时,查询时显示可以购买,但最终只有一个人能成功,这也是不可以的。其实最终只要商品库存不发生超卖就可以。那此时就可以通过条件来进行控制。
mysql乐观锁更适用于一些需要计数的表上,而且在竞争不激烈,出现并发冲突几率较小时,推荐使用乐观锁。虽然通过MySQL乐观锁可以完成并发控制,但锁的操作是直接作用于数据库上,这样就会在一定程度上对数据库性能产生影响。并且mysql的连接数量是有限的,如果出现大量锁操作占用连接时,也会造成MySQL的性能瓶颈。
|zookeeper分布式锁实现思想
对于分布式锁的实现,zookeeper天然携带的一些特性能够很完美的实现分布式锁。其内部主要是利用znode节点特性和watch机制完成。
在zookeeper中节点会分为四类,分别是:持久节点:一旦创建,则永久存在于zookeeper中,除非手动删除。持久有序节点:一旦创建,则永久存在于zookeeper中,除非手动删除。同时每个节点都会默认存在节点序号,每个节点的序号都是有序递增的。如demo000001、demo000002…..demo00000N。临时节点:当节点创建后,一旦服务器重启或宕机,则被自动删除。临时有序节点:当节点创建后,一旦服务器重启或宕机,则被自动删除。同时每个节点都会默认存在节点序号,每个节点的序号都是有序递增的。如demo000001、demo000002…..demo00000N。
watch监听机制
watch监听机制主要用于监听节点状态变更,用于后续事件触发,假设当B节点监听A节点时,一旦A节点发生修改、删除、子节点列表发生变更等事件,B节点则会收到A节点改变的通知,接着完成其他额外事情。
实现原理
其实现思想是当某个线程要对方法加锁时,首先会在zookeeper中创建一个与当前方法对应的父节点,接着每个要获取当前方法的锁的线程,都会在父节点下创建一个临时有序节点,因为节点序号是递增的,所以后续要获取锁的线程在zookeeper中的序号也是逐次递增的。根据这个特性,当前序号最小的节点一定是首先要获取锁的线程,因此可以规定序号最小的节点获得锁。所以,每个线程再要获取锁时,可以判断自己的节点序号是否是最小的,如果是则获取到锁。当释放锁时,只需将自己的临时有序节点删除即可。
在并发下,每个线程都会在对应方法节点下创建属于自己的临时节点,且每个节点都是临时且有序的。那么zookeeper又是如何有序的将锁分配给不同线程呢? 这里就应用到了watch监听机制。每当添加一个新的临时节点时,其都会基于watcher机制监听着它本身的前一个节点等待前一个节点的通知,当前一个节点删除时,就轮到它来持有锁了。然后依次类推。
优缺点
1)zookeeper是基于cp模式,能够保证数据强一致性。
2)基于watch机制实现锁释放的自动监听,锁操作性能较好。
3)频繁创建节点,对于zk服务器压力较大,吞吐量没有redis强。
原理剖析
低效锁思想
在通过zookeeper实现分布式锁时,有另外一种实现的写法,这种也是非常常见的,但是它的效率并不高,此处可以先对这种实现方式进行探讨。
此种实现方式,只会存在一个锁节点。当创建锁节点时,如果锁节点不存在,则创建成功,代表当前线程获取到锁,如果创建锁节点失败,代表已经有其他线程获取到锁,则该线程会监听锁节点的释放。当锁节点释放后,则继续尝试创建锁节点加锁。
这种方案的低效点就在于,只有一个锁节点,其他线程都会监听同一个锁节点,一旦锁节点释放后,其他线程都会收到通知,然后竞争获取锁节点。这种大量的通知操作会严重降低zookeeper性能,对于这种由于一个被watch的znode节点的变化,而造成大量的通知操作,叫做羊群效应。
高效锁思想
为了避免羊群效应的出现,业界内普遍的解决方案就是,让获取锁的线程产生排队,后一个监听前一个,依次排序。推荐使用这种方式实现分布式锁
按照上述流程会在根节点下为每一个等待获取锁的线程创建一个对应的临时有序节点,序号最小的节点会持有锁,并且后一个节点只监听其前面的一个节点,从而可以让获取锁的过程有序且高效。
代码实现
|Redis分布式锁原理&实现
分布式锁的一个很重要的特性就是互斥性,同一时间内多个调用方加锁竞争,只能有一个调用方加锁成功。而redis是基于单线程模型的,可以利用这个特性让调用方的请求排队,对于并发请求,只会有一个请求能获取到锁。redis实现分布式锁也很简单,基于客户端的几个API就可以完成,主要涉及三个核心API:setNx():向redis中存key-value,只有当key不存在时才会设置成功,否则返回0。用于体现互斥性。expire():设置key的过期时间,用于避免死锁出现。delete():删除key,用于释放锁。1)编写工具类实现加锁通过jedis.set进行加锁,如果返回值是OK,代表加锁成功如果加锁失败,则自旋不断尝试获取锁,同时在一定时间内如果仍没有获取到锁,则退出自旋,不再尝试获取锁。requestId:用于标识当前每个线程自己持有的锁标记
解锁时,要避免当前线程将别人的锁释放掉。假设线程A加锁成功,当过了一段时间线程A来解锁,但线程A的锁已经过期了,在这个时间节点,线程B也来加锁,因为线程A的锁已经过期,所以线程B时可以加锁成功的。此时,就会出现问题,线程A将线程B的锁给释放了。对于这个问题,就需要使用到加锁时的requestId。当解锁时要判断当前锁键的value与传入的value是否相同,相同的话,则代表是同一个人,可以解锁。否则不能解锁。但是对于这个操作,有非常多的人,会先查询做对比,接着相同则删除。虽然思路是对的,但是忽略了一个问题,原子性。判断与删除分成两步执行,则无法保证原子性,一样会出现问题。所以解锁时不仅要保证加锁和解锁是同一个人还要保证解锁的原子性。因此结合lua脚本完成查询&删除操作。
测试类
此时可以发现,多线程会竞争同一把锁,且没有获取获取到锁的线程会自旋不断尝试去获取锁。每当一个线程将锁释放后,则会有另外一个线程持有锁。依次类推。
存在的问题
锁续期
当对业务进行加锁时,锁的过期时间,绝对不能想当然的设置一个值。假设线程A在执行某个业务时加锁成功并设置锁过期时间。但该业务执行时间过长,业务的执行时间超过了锁过期时间,那么在业务还没执行完时,锁就自动释放了。接着后续线程就可以获取到锁,又来执行该业务。就会造成线程A还没执行完,后续线程又来执行,导致同一个业务逻辑被重复执行。因此对于锁的超时时间,需要结合着业务执行时间来判断,让锁的过期时间大于业务执行时间。上面的方案是一个基础解决方案,但是仍然是有问题的。业务执行时间的影响因素太多了,无法确定一个准确值,只能是一个估值。无法百分百保证业务执行期间,锁只能被一个线程占有。如想保证的话,可以在创建锁的同时创建一个守护线程,同时定义一个定时任务每隔一段时间去为未释放的锁增加过期时间。当业务执行完,释放锁后,再关闭守护线程。 这种实现思想可以用来解决锁续期。
服务单点&集群问题
在单点redis虽然可以完成锁操作,可一旦redis服务节点挂掉了,则无法提供锁操作。在生产环境下,为了保证redis高可用,会采用异步复制方法进行主从部署。当主节点写入数据成功,会异步的将数据复制给从节点,并且当主节点宕机,从节点会被提升为主节点继续工作。假设主节点写入数据成功,在没有将数据复制给从节点时,主节点宕机。则会造成提升为主节点的从节点中是没有锁信息的,其他线程则又可以继续加锁,导致互斥失效。
|Redisson分布式锁
redisson是redis官网推荐实现分布式锁的一个第三方类库。其内部完成的功能非常强大,对各种锁都有实现,同时对于使用者来说非常简单,让使用者能够将更多的关注点放在业务逻辑上。此处重点利用Redisson解决单机Redis锁产生的两个问题。
单机Redisson实现
依赖
配置文件
启动类
锁工具
测试类
根据执行效果可知,多线程并发获取所时,当一个线程获取到锁,其他线程则获取不到,并且其内部会不断尝试获取锁,当持有锁的线程将锁释放后,其他线程则会继续去竞争锁。
源码分析
lock()源码分析
当获取到RLock对象后,调用其内部的lock()执行加锁操作。根据源码描述,当线程获取锁时,如果没有获取到锁,则会让其进入自旋,直到获取到锁。 如果获取到锁,则会一直保留到调用unLock()手动释放或根据传入的leaseTime时间自动释放。当前传入两个参数值:锁超时时间,时间单位。主要用于避免死锁的出现,假设持有锁的redis节点宕机,到期后锁可以自动释放。
lock()方法中还会调用lock()的另外一个重载方法,需要传入三个参数:过期时间、时间单位、是否中断。
在三个参数的lock()重载方法中,首先会获取当前线程id,接着调用tryAcquire()方法尝试获取锁,如果返回值为null,代表获取到锁。 如果返回值不是null,则根据当前线程id创建异步任务并放入线程池中,接着进入自旋,在自旋过程中,尝试调用tryAcquire()获取锁,如果获取到则退出自旋。否则会不断的尝试获取锁。
在lock()方法中,最核心的是tryAcquire()。其内部核心实现会调用tryAcquireAsync(),并传入过期时间、时间单位和当前线程id,进行锁的获取。如果leaseTime不为-1,代表设置了有效时间,接着调用tryAcquireAsync()去获取锁。如果是-1的话,则默认把永不过期改为30秒过期,并且创建异步任务,如果没有获取到锁,则什么都不做。如果获取到了锁,则调用scheduleExpirationRenewal()对当前线程id的锁进行延时。
最终的tryLockInnerAsync()则是获取锁的具体实现。可以看到,其内部是基于lua脚本语言完成锁获取的。因为获取锁的过程涉及到了多步,为了保证执行过程的原子性,所以使用了lua,最核心的就是要理解这段lua脚本的执行过程。
对于这款lua脚本来说,KEYS[1]代表需要加锁的key,ARGV[1]代表锁的超时时间,ARGV[2]代表锁的唯一标识。对于这段lua脚本,简单来说:1)检查锁key是否被占用了,如果没有则设置锁key和唯一标识,初始值为1,并且设置锁key的过期时间。2)如果锁key存在,并且value也匹配,表示是当前线程持有的锁,那么重入次数加1,并且设置失效时间。3)返回锁key的失效时间毫秒数。
unLock()源码分析
在释放锁时,unlock()内部会调用unlockAsync()对当前线程持有的锁进行释放。其内部最终会执行unlockInnerAsync()方法完成锁释放并返回结果。
在unlockInnerAsync()中仍然是结合lua脚本完成释放锁操作。相关参数:KEYS[1]:当前锁key。KEYS[2]:redis消息的ChannelName,每个锁对应唯一的一个 channelName。ARGV[1]:redis消息体,用于标记redis的key已经解锁,用于通知其他线程申请锁。ARGV[2]:锁超时时间。ARGV[3]:锁的唯一标识。
1)判断锁key和锁的唯一标识是否匹配,如果不匹配,表示锁已经被占用,那么直接返回。
2)如果是当前线程持有锁,则value值-1,用于重入操作。
3)如果-1后的值大于0,则对锁设置过期时间。
4)如果-1后的值为0,则删除锁key,并发布消息,该锁已被释放。用于通知其他线程申请锁。
锁续期
对于锁续期问题,在单点redis实现分布式锁时已经介绍过了,用于防止业务执行超时或宕机而引起的业务被重复执行。根据对lock方法的解析,可以发现,当设置完过期时间后,当前锁的过期时间就已经被设定了,不会发生改变,锁到期后则会被自动释放,因此在业务执行中,通过lock()方法加锁会造成隐患。
红锁
当在单点redis中实现redis锁时,一旦redis服务器宕机,则无法进行锁操作。因此会考虑将redis配置为主从结构,但在主从结构中,数据复制是异步实现的。假设在主从结构中,master会异步将数据复制到slave中,一旦某个线程持有了锁,在还没有将数据复制到slave时,master宕机。则slave会被提升为master,但被提升为slave的master中并没有之前线程的锁信息,那么其他线程则又可以重新加锁
redlock算法redlock是一种基于多节点redis实现分布式锁的算法,可以有效解决redis单点故障的问题。官方建议搭建五台redis服务器对redlock算法进行实现。在redis官网中,对于redlock算法的实现思想也做了详细的介绍。地址:https://redis.io/topics/distlock。整个实
现过程分为五步:1)记录获取锁前的当前时间2)使用相同的key,value获取所有redis实例中的锁,并且设置获取锁的时间要远远小于锁自动释放的时间。假设锁自动释放时间是10秒,则获取时间应在5-50毫秒之间。通过这种方式避免客户端长时间等待一个已经关闭的实例,如果一个实例不可用了,则尝试获取下一个实例。3)客户端通过获取所有实例的锁后的时间减去第一步的时间,得到的差值要小于锁自动释放时间,避免拿到一个已经过期的锁。并且要有超过半数的redis实例成功获取到锁,才算最终获取锁成功。如果不是超过半数,有可能出现多个客户端重复获取到锁,导致锁失效。4)当已经获取到锁,那么它的真正失效时间应该为:过期时间-第三步的差值。5)如果客户端获取锁失败,则在所有redis实例中释放掉锁。为了保证更高效的获取锁,还可以设置重试策略,在一定时间后重新尝试获取锁,但不能是无休止的,要设置重试次数。
虽然通过redlock能够更加有效的防止redis单点问题,但是仍然是存在隐患的。假设redis没有开启持久化,clientA获取锁后,所有redis故障重启,则会导致clientA锁记录消失,clientB仍然能够获取到锁。这种情况虽然发生几率极低,但并不能保证肯定不会发生。
保证的方案就是开始AOF持久化,但是要注意同步的策略,使用每秒同步,如果在一秒内重启,仍然数据丢失。使用always又会造成性能急剧下降。
官方推荐使用默认的AOF策略即每秒同步,且在redis停掉后,要在ttl时间后再重启。 缺点就是ttl时间内redis无法对外提供服务。
实现
redisson对于红锁的实现已经非常完善,通过其内部提供的api既可以完成红锁的操作。
测试类
redissonRedLock加锁源码分析
作者:有梦想的老王
原文链接:https://www.cnblogs.com/whgk/p/14389642.html
JavaScript基础知识点总结
//小憨憨
/*第一章
*HTML引用js方法:
*1,外部引用:HTML外部引用js:<script src=\”js/day1.js\”></script>
*2,内部引用:<script> alert(\”小憨憨\”);</script>
*3,元素事件引用:<input type=\”button\” value=\”button\” onclick=\”alert(\’welcome\’)\” />
*/
/*第二章
* 变量定义:
* 1,变量由数字,字母,下划线,$组成,且不能以数字开头
* 2,变量不能使用系统关键词
* 3变量定义语法:var 变量1=变量值,变量2=变量值,…;
*/
//举例:
var a = 10;
document.write(a);//在页面输出一个内容
/*
* 数据类型:
* 1,基本数据类型:数字,字符串,布尔值,未定义值(undefined),空值(null)
* 2,引用数据类型:数组,对象
* 其中:数字不区分整型和浮点型
*/
/*
* 运算符:
* 1,算术运算符:+ – * / % ++ —
* 加法运算规则:数字+数字=数字; 数字+字符串=字符串; 字符串+字符串=字符串
* 2,
*/
//举例:
var a = 10;
var str = \”小憨憨\”;
document.write(str + a, typeof(str + a));
/*
* 赋值运算符:= += -= *= /=
* 比较运算符:> < >= <= == !=
* 逻辑运算符:&& || !
* 条件运算符:var b=条件?表达式1:表达式2;//相当于C语言中三目运算符
*/
/*
* 表达式语句:一个分号对应一条语句
* 类型转换:
* 1,隐式类型转换(js自动完成的)
* 2,显式类型转换
* (1)字符串转数字:Number(),parseInt(),parseFloat()(字符串必须是数字字符串)
* (2)数字转字符串:toString
* (3)转义字符:\\\’ \\\” \\n等
* 3,注释: 单行注释 和 多行注释 用法:与C语言注释一样
*/
//举例:
document.write(\”Number(\\\”123\\\”):\” +Number(\”123\”) + \”<br/>\”);
document.write(parseInt(\”+123.456px\”));//第一个字符为+或-也进行转换,从左往右取整数
document.write(parseFloat(\”123.456px\”));
var num = 123;
document.write(num.toString());
/*第三章
* 流程控制:
* 1,顺序结构:程序代码从上到下,从左到右依次执行
* 2,选择结构:
* (1)if语句:(单重if)
* <1>:if(条件){语句块}
* <2>:if(条件){语句块} else{语句块}
* <3>:if(条件){语句块} else if(条件){语句块} else{语句块}
* (2)switch语句:
* switch(判断值){ case 取值1:语句块1;break;
* case 取值2:语句块2;break;
* …
* default:语句块n;break;}
* 3,循环结构:
* (1)while循环:
* while(条件){语句块}
* (2)do…while循环:
* do{语句块}while(条件);
* (3)for循环:
* for(初始化表达式;条件表达式;循环后操作表达式){语句块}
*/
//举例:计算1+2+3+…+100
var n = 1, sum = 0;
while(n <= 100)
{
sum += n;
n++;
}
document.write(sum);
//找出100-1000中的水仙花数
var str1 = \”\”;
for(var i = 100; i < 1000; i++)
{
var a = i / 100; //取百位上的数字
a = parseInt(a);
var b = i % 100 / 10; //取十位上的数字
b = parseInt(b);
var c = i % 10; //取个位上的数字
c = parseInt(c);
if(i == (a * a * a + b * b * b + c*c*c))
{
str1 = str1 + i + \”、\”;
}
}
document.write(\”水仙花数有:\” + str1);
//判断一个数是不是整数
window.onload = function()
{
var n = 3.14159;
if(parseInt(n) == parseFloat(n))
{
document.write(n + \”是整数\”);
}
else
{
document.write(n + \”不是整数\”);
}
}
/*第四章
* 函数:
* 1,函数的定义:函数名命名规则遵循变量命名规则
* (1)没有返回值的函数定义:function(参数1,参数2,…){语句块}
* (2)有返回值的函数定义:function(参数1,参数2,…){语句块; return 返回值}
* 2,变量的作用域:全局变量,局部变量
* 3,函数的调用:
* (1)直接调用:函数名(实参1,实参2,…);
* (2)在表达式中调用:例如:var sum = 函数名(实参1,…)
* (3)在超链接中调用:<a href=\”javascript:函数名\”> </a>
* (4)在事件中调用
* 4,函数嵌套
* 5,内置函数:比如:parseInt()
*/
//举例:
function add_sum(e, f){
var sum = e + f;
document.write(sum);
}
add_sum(10, 20);
function test(){alert(\”doubi\”);}//供超链接调用测试的函数
/*第五章
* 1,js中对象分为:自定义对象和内置对象
* 2,常用的内置对象:字符串对象,数组对象,日期对象,数值对象
* 3,字符串对象相关知识点:
* 3.1 获取字符串长度:语法: 字符串名.length
* 3.2大小写转换: 字符串名.toLowerCase(),字符串名.toUpperCase()
* 3.3获取一个字符:字符串名.charAt(n)
* 3.4获取字符串: 字符串名.substring(start, end)
* 3.5替换字符串: 字符串.replace(原字符串或正则表达式, 替换字符串)
* 3.6分割字符串: 字符串.split(\”分隔符\”)
* 3.7检索字符串的位置: 字符串.indexOf(指定字符串),字符串.lastIndexOf(指定字符串)
* indexOf:返回首次出现的位置 lastIndexOf:返回最后一次出现的位置 没找到返回-1
* 3.8删除字符串中的一个字符:
*/
//举例
var str = \”This is doubixiaohanhan\”;
document.write(\”字符串长度为:\” + str.length);//空格也计算在内
document.write(\”转为大写字母:\” + str.toUpperCase());
document.write(\”转为小写字母:\” + str.toLowerCase());
document.write(\”获取第3个字符: \” + str.charAt(3));//字符串下标从0开始计算
document.write(str.substring(8, 23));
document.write(str.replace(\”doubixiaohanhan\”, \”hahahahahaha\”));
var str1 = str.split(\” \”);//以空格作为分隔符
for(var i = 0; i < 3; i++)
document.write(str1[i]);
document.write(str.indexOf(\”bi\”));
document.write(str.lastIndexOf(\”han\”));
//找出字符串中所有字符b,不区分大小写
var n = 0,str1 = \”doubixiaohanhan\”;
document.write(\”源字符串:\” + str1);
for(var j = 0; j < str1.length; j++)
{
var char = str1.charAt(j);
if(\’h\’ == char.toLowerCase())
{
document.write(char);
n = n + 1;
}
}
document.write(\”字符串中有\” + n + \”个字符h\”);
//统计字符串中数字的个数
function get_number(str){
var num = 0, i = 0;
while(i < str.length){
var char = str.charAt(i);
if((char != \” \”) && (!isNaN(char))){//isNaN:不是数字则返回true
num++;
}
i++;
}
alert(\”执行完毕\”);
return num;
}
var ret = get_number(\”dou120k53KDDD6656\”);
document.write(ret);
/*第六章:数组对象
* 1,数组的创建:(数组 中可以存储不同类型的数据)
* (1)完整形式:var 数组名=new Array(元素1,元素2,..)
* (2)简写形式:var 数组名=[元素1,元素2,…]
* 2,数组的获取:使用下标获取,下标从0开始
* 3,数组的赋值:arr[i]=值;
* 4,获取数组的长度: 数组名.length
* 5,截取数组: 数组名.slice(start, end)
* 6,为数组添加元素:
* (1)在数组开头添加元素: 数组名.unshift(元素1,元素2,…)
* (2)在数组末尾添加元素: 数组名.push(元素1,元素2,…)
* (3)在数组首部删除元素: 数组名.shift()
* (4)在数组末尾删除元素: 数组名.pop()
* (5)数组排序: 升序:数组名.sort(up) 降序:数组名.sort(down)
* 其中:function up(a,b){return a-b;} function down(a,b){return b-a;}
* (6)数组颠倒顺序: 数组名.reverse()
* (7)将数组元素连接成字符串: 数组名.join(\”连接符\”)
* (8)
*/
//举例
var arr = [\”js\”,\”jquery\”];
document.write(arr + \’\\n\’);
arr.unshift(\”db\”);
arr.push(\”ab\”);
document.write(arr);
arr.shift();
arr.pop();
document.write(arr);
var arr1 = [3, 6, 2, 5, 8, 1];
document.write(arr1);
function up(a,b){return a–b;}
arr1.sort(up);
document.write(arr1);
function down(a,b){return b–a}
arr1.sort(down);
document.write(arr1);
var arr = [\”js\”,\”jquery\”,\”abcd\”];
var re = arr.join(\”\”);
document.write(re);
document.write(typeof(re));
//例题:将字符串所有字符颠倒顺序
function test(str){
var arr = str.split(\”\”);
document.write(typeof(arr));
arr.reverse();
var re = arr.join(\”\”);
document.write(typeof(re));
return re;
}
document.write(\”javascript颠倒后: \” + test(\”javascript\”));
/*第七章:时间对象
* 创建一个日期对象必续使用new关键字:语法: var 日期对象名 = new Date();
* Date对象的方法有很多,主要分为两大类:获取时间:getXxx() 和 设置时间:setXxx()
*
* getFullYear() 获取年份:取值4位数字
* getMonth() 获取月份:取值0(一月)-11(十二月)整数
* getDate() 获取日数:取值0-31整数
* getHours() 获取小时:取值0-23整数
* getMinutes() 获取分钟:取值0-59整数
* getSeconds() 获取秒数:取值0-59整数
* getMilliseconds() 获取毫秒
* getDay() 获取星期几:0表示星期天
* setFullYear(year,month,day) 设置年月日
* setMonth(month,day) 设置月日
* setDate(day) 设置日 : 1-31整数
* setHours(hour,min,sec,millsec) 设置时分秒毫秒
* setMinutes(min,sec,millsec) 设置分秒毫秒
* setSeconds(sec,millsec) 设置秒毫秒
*/
//举例
var d = new Date();
var myyear = d.getFullYear();
var mymonth = d.getMonth();
var myday = d.getDate();
var myday1 = d.getDay();
var weekend = [\”星期天\”,\”星期一\”,\”星期二\”,\”星期三\”,\”星期四\”,\”星期五\”,\”星期六\”];
document.write(myyear + \”年\”+(mymonth+1)+\”月\”+myday+\”日\”+weekend[myday1]);
/*第八章:数学对象
* 数学对象不需要使用new关键字来创造,而是直接使用它的属性和方法
* 语法: Math.属性 Math.方法
* 注:属性往往都是常量,比如:圆周率:PI (常用格式:度数*Math.PI/180)
* Math中方法常用有:
* max min sin cos tan asin acos atan floor ceil random atan2
* Math中方法不常用有:
* abs sqrt log pow exp
* random():用于生成0-1之间的随机数,即: [0,1)
* 随机生成某个范围的任意数:
* 1,Math.random()*m:生成0~m之间的随机数
* 2,Math.random()*m+n:生成n~m+n之间的随机数
* 3,Math.random()*m-n:生成-n~m-n之间的随机数
* 4,Math.random()*m-m:生成-m~0之间的随机数
*/
//举例
var a = Math.max(3,9,10,2,4,6,12,67,9);
document.write(a);
var b = 0.6;
document.write(Math.floor(b));//floor向下取整
document.write(Math.ceil(b)); //ceil向上取整
document.write(Math.random()*10);
//例题:生成随机验证码
function random_validate(str){
var arr = str.split(\”\”);
var result = \”\”;
for(var i = 0;i < 4; i++){
var n = Math.floor(Math.random()*arr.length);
result += arr[n];
}
return result;
}
document.write(random_validate(\”asjcbakavbavakvhakjbvkvJASSDKABKAVAVJ24123435\”));
/*第九章:DOM基础
* DOM:document object model文档对象模型(W3C定义的一个标准)
* DOM操作:理解:元素操作;DOM采用树形结构
* 重点:每一个元素就是一个节点,每个节点就是一个对象。操作元素时其实就是把这个元素看成一个对象,
* 然后使用其对象的属性和方法进行操作。节点包括元素,二者实际不是同一概念
* DOM节点有12种类型,其中常用的三种:元素节点,属性节点,文本节点
* 不同节点的nodeType属性值:
* 元素节点:1
* 属性节点:2
* 文本节点:3
*
* 一,获取元素(实际获取元素节点),js种有以下方式:
* 1,getElemnetById()
* 2,getElemnetByTagName():返回一个类数组(伪数组):只能使用length属性和下标形式
* 3,getElemnetByClassName()
* 4,getElemnetByName():只用于表单元素,一般用于单选按钮和复选框
* 5,querySelector()和querySelectorAll()
* 6,document.title()和document.body()
* 二,创建元素:(动态DOM操作)
* 创建元素节点:createElement()
* 创建文本节点:createTextNode()
* 总结:创建一个元素的步骤:
* (1)创建元素节点:createElement()
* (2)创建文本节点:createTextNode()
* (3)把文本节点插入元素节点:appendChild()
* (4)把组装好的元素插入到已有的元素中:appendChild()
* 三,插入元素
* 1,appenChild() :把一个元素插到父元素的内部子元素的末尾
* 2,insertBefore():把一个元素插到父元素的内部某个子元素的之前
*
* 四,删除元素:removeChild()
* 五,赋值元素:obj.cloneNode(bool)
* obj:被复制的对象
* bool:参数 true:复制元素本身及其子元素 false:仅仅复制本身
* 六,替换元素:replaceChild(new,old)
*/
//创建简单元素
window.onload =function(){
var oDiv = document.getElementById(\”content\”);
var oStrong = document.createElement(\”strong\”);
var oTxt = document.createTextNode(\”小憨憨\”);
oStrong.appendChild(oTxt);
oDiv.appendChild(oStrong);
}
//创建带属性的元素
window.onload =function(){
var oInput = document.createElement(\”input\”);
oInput.id = \”sumit\”;
oInput.type = \”button\”;
oInput.value = \”提交\”;
document.body.appendChild(oInput);
}
//创建动态图片
window.onload =function(){
var oImg = document.createElement(\”img\”);
oImg.className = \”doubi\”;
oImg.src = \”img/doubi.jpg\”;
oImg.style.border = \”1px solid silver\”;
document.body.appendChild(oImg);
}
//创建多个元素
window.onload =function(){
var oTable = document.createElement(\”table\”);
for(var i = 0; i < 3;i++){
var oTr = document.createElement(\”tr\”);
for(var j = 0; j < 3; j++){
var oTd = document.createElement(\”td\”);
oTr.appendChild(oTd);
}
oTable.appendChild(oTr);
}
document.body.appendChild(oTable);
}
//子元素插到末尾
window.onload =function(){
var oBtn = document.getElementById(\”btn\”);
oBtn.onclick = function(){
var oU1 = document.getElementById(\”list\”);
var oTxt = document.getElementById(\”txt\”);
var textNode = document.createTextNode(oTxt.value);
var oLi = document.createElement(\”li\”);
oLi.appendChild(textNode);
oU1.appendChild(oLi);
}
}
//子元素插到某个子元素之前
window.onload =function(){
var oBtn = document.getElementById(\”btn\”);
oBtn.onclick = function(){
var oU1 = document.getElementById(\”list\”);
var oTxt = document.getElementById(\”txt\”);
var textNode = document.createTextNode(oTxt.value);
var oLi = document.createElement(\”li\”);
oLi.appendChild(textNode);
oU1.insertBefore(oLi, oU1.firstElementChild);
}
}
//删除子元素
window.onload =function(){
var oBtn = document.getElementById(\”btn\”);
oBtn.onclick = function(){
var oU1 = document.getElementById(\”list\”);
oU1.removeChild(oU1.lastElementChild);
}
}
//复制元素
window.onload =function(){
var oBtn = document.getElementById(\”btn\”);
oBtn.onclick = function(){
var oU1 = document.getElementById(\”list\”);
document.body.appendChild(oU1.cloneNode(1));
}
}
//替换元素
window.onload =function(){
var oBtn = document.getElementById(\”btn\”);
oBtn.onclick = function(){
var oFirst = document.querySelector(\”body *:first-child\”);
var oTag = document.getElementById(\”tag\”);
var oTxt = document.getElementById(\”txt\”);
var oNewTag = document.createElement(oTag.value);
var oNewTxt = document.createTextNode(oTxt.value);
oNewTag.appendChild(oNewTxt);
document.body.replaceChild(oNewTag, oFirst);
}
}
/*第十章:DOM进阶
* 操作HTML元素属性的方式:对象属性和对象方法
* 不管是用那种方式,都需要涉及两个操作:获取HTML属性值,设置HTML属性值
* 一,获取HTML属性值:
* 语法: obj.attr (obj是元素名,是一个DOM对象,指的是getElementById()
* 等方法获取到的元素节点)
* 二,设置HTML属性值: obj.attr = \”值\”;
* 三,HTML属性操作(对象方法)
* 1,getAttribute():获取元素的某个属性值
* 2,setAttribute():设置元素的某个属性值,参数1:属性名 参数2:属性值
* 3,removeAttribute():删除某个属性
* 4,hasAttribute():判断元素是否含有某个属性
* 四,总结:
* 1,“对象属性方式”和“对象方法方式”都可以操作静态HTML的属性和动态DOM属性
* 2,只有“对象方法方式”才可以操作自定义属性
*
* 五,CSS属性操作:是指js操作一个元素的CSS样式
* 1,获取CSS属性值:getComputeStyle(obj).attr <==> getComputeStyle(obj)[\”attr\”]
* obj:DOM对象 attr:CSS属性名
* 2,设置CSS属性值
* (1)style对象:行内样式 语法:obj.style.attr=\”值\”
* (2)cssText方法
* 六,DOM遍历
* 1,查找父元素:obj.parentNode obj:DOM对象
* 2,查找子元素:
* (1)childNodes,firstChild,lastChild
* (2)children,firstElementChild,lastElementChild
* 注:childNodes:获取所有节点包括文本节点 children:获取所有元素节点,不包括文本节点
* 3,查找兄弟元素:
* (1)previousSibling:查找前一个兄弟节点
* (2)nextSibling:查找后一个兄弟节点
* (3)previousElementSibling:查找前一个兄弟元素节点
* (4)nextElementSibling:查找后一个元素节点
* 七,innerHTML与innerText
*/
//获取静态HTML中的属性值
window.onload = function(){
var oBtn = document.getElementById(\”btn\”);
oBtn.onclick = function(){
alert(oBtn.id);
}
}
//获取动态HTML中的属性值
window.onload = function(){
var oInput = document.createElement(\”input\”);
oInput.id = \”submit\”;
oInput.type = \”button\”;
oInput.value = \”提交\”;
document.body.appendChild(oInput);
oInput.onclick = function(){
alert(oInput.id);
}
}
//获取单行文本框的值
window.onload = function(){
var oBtn = document.getElementById(\”btn\”);
oBtn.onclick = function(){
var oTxt = document.getElementById(\”txt\”);
alert(oTxt.value);
//document.write(oTxt.value);
}
}
//获取单选框的值
window.onload = function(){
var oBtn = document.getElementById(\”btn\”);
var oFruit = document.getElementsByName(\”fruit\”);
oBtn.onclick = function(){
for(var i = 0; i < oFruit.length; i++){
if(oFruit[i].checked){
alert(oFruit[i].value);
}
}
}
}
//获取复选框的值
window.onload = function(){
var oBtn = document.getElementById(\”btn\”);
var oFruit = document.getElementsByName(\”fruit\”);
var str = \”\”;
oBtn.onclick = function(){
for(var i = 0; i < oFruit.length; i++){
if(oFruit[i].checked){
str +=oFruit[i].value;
}
}
alert(str);
}
}
//获取下拉列表的值
window.onload = function(){
var oBtn = document.getElementById(\”btn\”);
var oSelect = document.getElementById(\”select\”);
oBtn.onclick = function(){
alert(oSelect.value);
}
}
//设置属性值
window.onload = function(){
var oBtn = document.getElementById(\”btn\”);
oBtn.onclick = function(){
oBtn.value = \”button\”;
document.write(oBtn.value);
}
}
//获取固有属性值
window.onload = function(){
var oBtn = document.getElementById(\”btn\”);
oBtn.onclick = function(){
alert(oBtn.getAttribute(\”id\”));
}
}
//获取自定义属性值
window.onload = function(){
var oBtn = document.getElementById(\”btn\”);
oBtn.onclick = function(){
//alert(oBtn.data);//oBtn.data是不能获取自定义属性值
alert(oBtn.getAttribute(\”data\”));
}
}
//获取自定义属性值
window.onload = function(){
var oTd = document.getElementsByTagName(\”td\”);
for(var i = 0;i < oTd.length; i++){
oTd[i].onclick = function(){
var oParent = this.parentNode;
oParent.style.color = \”white\”;
oParent.style.backgroundColor = \”red\”;
};
}
}
//childNodes与children的比较
window.onload = function(){
var oU1 = document.getElementById(\”list\”);
var childNodes = oU1.childNodes.length;
var children = oU1.children.length;
alert(\”childNodes的长度:\”+childNodes + \”\\n\” +\”children的长度:\”+children);
}
/*第十一章:事件基础
* 一,在js中一个事件分为三部分:
* 1,事件主角:是按钮还是div元素或是其它?
* 2,事件类型:是点击还是移动或其它?
* 3,事件过程:这个事件都发生了些什么?
* 二,js中常用的事件:
* 1,鼠标事件
* 2,键盘事件
* 3,表单事件
* 4,编辑事件
* 5,页面事件
* 三,js中事件调用方式:
* 1,在script标签中调用:指的是在<script><script/>标签内调用事件
* 语法: obj.事件名 = function(){};
* 2,在元素中调用:指的是直接HTML属性中调用事件
* 四,鼠标事件
* onclick 鼠标单击事件
* onmouseover 鼠标移入事件
* onmouseout 鼠标移出事件
* onmousedown 鼠标按下事件
* onmouseup 鼠标松开事件
* onmousemove 鼠标移动事件
* 五,键盘事件
* 1,键盘按下:onkeydown
* 2,键盘松开:onkeyup
* 注:指按下或松开的一瞬间,先有按下后有松开
* 键盘事件一般用途:表单操作和动画控制
* 六,表单事件
* 1,onfocus:获取焦点时触发的事件 onblur:获取失去焦点时触发的事件
* 注:只有超链接和表单元素(单选框,多选框,单行文本框,多行文本框,下拉列表)才有上述功能
* 备注:判断一个元素是否有焦点的方法:
* 打开一个页面后按tab键,能够选中的就是具有焦点特性的元素
* 焦点事件(onfocus,onblur)一般用于单行文本框和多行文本框
* 2,onselect:当我们选中单行/多行文本框中内容时就会触发该事件
* 3,onchange:常用于具有多个选项的表单元素:
* (1)单选框选择某一项时触发
* (2)多选框选择某一项时触发
* (3)下拉列表选择某一项时触发
* 4,submit(一般结合后端技术使用,此处暂时不管)
* 七,编辑事件(一般用于保护版权)
* 1,oncopy:可以用于页面内容被复制
* 2,onselectstart:可以用于防止页面内容被选取:本质上是防止页面内容被复制
* 3,oncontexmenu
* 八,页面事件
* 1,onload:表示文档加载完成后再执行的一个事件
* window.onload:一般用于在想要获取页面中某一个元素的时候才会用到
* 2,onbeforeunload:表示离开页面之前触发的一个事件
*/
//鼠标移入移出
window.onload = function(){
var oP = document.getElementById(\”content\”);
oP.onmouseover = function(){
this.style.color = \”red\”;
}
oP.onmouseout = function(){
this.style.color = \”yellow\”;
}
}
//鼠标按下松开
window.onload = function(){
var oDiv = document.getElementById(\”title\”);
var oBtn = document.getElementById(\”btn\”);
oBtn.onmousedown = function(){
oDiv.style.color = \”red\”;
}
oBtn.onmouseup = function(){
oDiv.style.color = \”blue\”;
}
oBtn.onmousemove = function(){
oDiv.style.color = \”green\”;
}
}
//统计输入字符的长度(键盘按下松开实验)
window.onload = function(){
var oTxt = document.getElementById(\”txt\”);
var oNum = document.getElementById(\”num\”);
oTxt.onkeyup = function(){
var str = oTxt.value;
oNum.innerHTML = str.length;
}
}
//统计输入字符的长度(键盘按下松开实验)
window.onload = function(){
var oTxt = document.getElementById(\”txt\”);
var oDiv = document.getElementById(\”content\”);
var myregex = /^[0-9]*$/;
oTxt.onkeyup = function(){
if(myregex.test(oTxt.value)){
oDiv.innerHTML = \”输入正确\”;
}
else{
oDiv.innerHTML = \”必续输入数字\”;
}
}
}
//搜索框(焦点的应用)
window.onload = function(){
var oSearch = document.getElementById(\”search\”);
oSearch.onfocus = function(){
if(this.value == \”小憨憨\”){
this.value = \”\”;
}
};
oSearch.onblur = function(){
if(this.value == \”\”){
this.value = \”小憨憨\”;
}
};
}
//focus方法:onfocus是一个属性
window.onload = function(){
var oTxt = document.getElementById(\”txt\”);
oTxt.focus();
}
//onselect事件
window.onload = function(){
var oTxt1 = document.getElementById(\”txt1\”);
var oTxt2 = document.getElementById(\”txt2\”);
oTxt1.onselect = function(){
alert(\”你选中了单行文本框中的内容\”);
}
oTxt2.onselect = function(){
alert(\”你选中了多行文本框中的内容\”);
}
}
//select方法:当使用搜索框时,每次点击搜索框,它就会帮我们自动选中文本框中的所有内容
window.onload = function(){
var oSearch = document.getElementById(\”search\”);
oSearch.onclick = function(){
this.select();
};
}
//onchange事件用于单选框
window.onload = function(){
var oFruit = document.getElementsByName(\”fruit\”);
var oP = document.getElementById(\”content\”);
for(var i = 0;i < oFruit.length; i++){
oFruit[i].onchange = function(){
if(this.checked){
oP.innerHTML = \”你选择的是: \”+this.value;
}
}
};
}
//onchange事件用于复选框
window.onload = function(){
var oFruit = document.getElementsByName(\”fruit\”);
var oSel = document.getElementById(\”sel\”);
for(var i = 0; i < oFruit.length; i++)
alert(oFruit[i].value);
oSel.onchange = function(){
if(this.checked){
for(var i = 0; i < oFruit.length; i++){
oFruit[i].checked = true;
}
}
else{
for(var i = 0; i < oFruit.length; i++){
oFruit[i].checked = false;
}
}
};
}
//onchange事件用于下拉列表
//选择下拉列表的某一选项时,触发的是onchange事件,而不是onselect事件;
onselect事件仅仅当选择文本框中内容时才会触发
window.onload = function(){
var oList = document.getElementById(\”list\”);
oList.onchange = function(){
var link = this.options[this.selectedIndex].value;
window.open(link);
};
}
//oncopy事件的应用
window.onload = function(){
document.body.oncopy =function(){
return false;
}
}
//onselectstart事件的应用
window.onload = function(){
document.body.onselectstart =function(){
return false;
}
}
//oncontexmenu事件的应用
window.onload = function(){
document.body.oncontextmenu =function(){
return false;
}
}
//onload,onbeforeunload事件的应用
window.onload = function(){
alert(\”doubi\”);
}
window.onbeforeunload = function(e){
e.returnValue = \”你准备离开页面\”;
}
/*第十二章:事件进阶
* 在js中想要给元素添加一个事件,有两种方式:
* 1,事件处理器:缺点:无法为一个元素添加多个相同事件
* 2,事件监听器:优点:事件处理器的缺点;(可以添加多个相同事件)
* 指定是使用addEventListener()方法为一个元素加事件(又称绑定事件)
* 语法:obj.addEventListener(type,fn,false)
* obj:DOM对象 type:是一个字符串,指的是事件类型,不需要加上on前缀
* fn:是一个函数名或一个匿名函数 false:表示事件冒泡阶段调用
* 3,解绑事件:obj.removeEventListener(type,fn,false)
* 4,event对象:当一个事件发生的时候,这个事件有关信息都会临时保存到一个指定的地方,这个
* 地方就是event对象。对于每一个事件,都有一个对应的event对象。
* event对象的常用属性:
* type: 事件类型
* keyCode: 键码值
* shiftKey: 是否按下shift键
* ctrlKey: 是否按下ctrl键
* altKey: 是否按下alt键
*
* keyCode:获取按下的哪个键;语法:event.keyCode
* 返回一个数值,常用的键值对照表:
* W(上) 87
* S(上) 83
* A(上) 65
* D(上) 68
* 上箭头 38
* 下箭头 40
* 左箭头 37
* 右箭头 39
*
* this:
* this是极其复杂,在事件操作中,可以理解:哪个DOM对象(元素节点)调用了
* this所在函数,那么this指向的就是哪个DOM对象
*/
//例题
window.onload = function(){
var oBtn = document.getElementById(\”btn\”);
oBtn.addEventListener(\”click\”, alterMes, false);
function alterMes(){
alert(\”javascript\”);
}
}
//上述等价于下面程序
window.onload = function(){
var oBtn = document.getElementById(\”btn\”);
oBtn.addEventListener(\”click\”, function(){
alert(\”javascript\”);}, false);
}
//获取事件的类型
window.onload = function(){
var oBtn = document.getElementById(\”btn\”);
oBtn.onclick = function(e){//e是一个变量名存储一个event对象
//实际上每次调用一个事件的时候,js都会默认给这个事件函数加上一个隐藏的参数
//这个参数就是event对象,一般来说,event事件是作为事件函数的第一个参数传入
alert(e.type);
}
}
//禁止shift,alt,ctrl键
window.onload = function(){
document.onkeydown = function(e){
if(e.shiftKey || e.altKey || e.ctrlKey){
alert(\”禁止使用shift,alt,ctrl键\”)
}
}
}
//获取上下左右方向键
window.onload = function(){
var oSpan = document.getElementsByTagName(\”span\”)[0];
window.addEventListener(\”keydown\”,doubi,false);
function doubi(e){
switch(e.keyCode)
{
case 38:
case 87:oSpan.innerHTML = \”上\”;break;
case 39:
case 68:oSpan.innerHTML = \”右\”;break;
case 40:
case 83:oSpan.innerHTML = \”下\”;break;
case 65:
case 37:oSpan.innerHTML = \”左\”;break;
default:oSpan.innerHTML = \”\”;break;
}
}
}
/*第十三章:window对象
* 在js中一个浏览器的窗口就是一个window对象。实际上每次打开一个页面时,浏览器都会自动为这个页面
* 创建一个window对象。window对象存放了这个页面的所有信息。
* window对象下的子对象:
* document 文档对象,用于操作页面元素
* title
* body
* forms
* images
* links
* location 地址对象,用于操作URL地址
* navigator 浏览器对象,用于获取浏览器版本信息
* history 历史对象,用于操作浏览历史
* screen 屏幕对象,用于操作屏幕宽度高度
* 注:location等对象又称为BOM(browser object module浏览器对象模型)
* 上述子对象可以看成window对象的属性
*
* window对象常用的方法:
* alert() 提示对话框
* confirm() 判断对话框
* prompt() 输入对话框
* open() 打开窗口
* close() 关闭窗口
* setTimeout() 开启一次性定时器
* clearTimeout() 关闭一次性定时器
* setInterval() 开启重复性定时器
* clearInterval() 关闭重复性定时器
* 注:对于window对象来说,不管它的属性还是方法,都可以省略window前缀
*
* 相关操作:
* 1,窗口操作
* (1)打开窗口:语法:window.open(url,target)
* (2)关闭窗口:语法:window.close()
* 2,对话框:
* (1)alert(\”提示文字\”):一般仅仅用于提示文字,在其换行使用:\”\\n\”;无返回值
* (2)confirm(\”提示文字\”):用于不仅提示文字,还供确认;返回布尔值
* (3)prompt(\”提示文字\”):不仅提示文字,还能返回一个字符串
* 3,定时器
* (1)setTimeout(code,time)
* 其中code:一段代码或一个函数或一个函数名 time:时间,单位ms,表示要过多长时间才执行code中的代码
* (2)clearTimeout()
* (3)setInterval(code,time)
* (4)clearInterval()
* 4,location对象
* location对象的属性
* href 当前页面地址 作用:获取或设置当前页面的地址
* search 当前页面地址?后面的内容 作用:获取或设置当前页面的地址?后面的内容
* 地址?后面的内容也叫做查询字符串(querystring),一般用于数据库查询用的,而且大量用到
* hash 当前页面地址#后面的内容 作用:获取或设置当前页面的地址#后面的内容
* #一般用于锚点链接
* 5,navigator对象:用于获取浏览器的类型
* 语法:window.navigator.userAgent
*/
//举例
window.onload = function(){
var oBtn = document.getElementById(\”btn\”);
oBtn.onclick = function(){
window.open(\”http://www.baidu.com\”,\”_blank\”);//在新窗口打开
window.open(\”http://www.baidu.com\”,\”_self\”);//在当前窗口打开
}
}
//操作空白窗口中的元素
window.onload = function(){
var oBtn = document.getElementsByTagName(\”input\”);
var opener = null;
oBtn[0].onclick = function(){
opener = window.open();
var strHtml = \'<!DOCTYPE html>\\
<html>\\
<head>\\
<title></title>\\
</head>\\
<body>\\
<div>小憨憨</div>\\
</body>\\
</html>\’;
opener.document.write(strHtml);
};
oBtn[1].onclick = function(){
var oDiv = opener.document.getElementsByTagName(\”div\”)[0];
oDiv.style.fontWeight = \”bold\”;
oDiv.style.color = \”hotpink\”;
};
}
//confirm对话框的使用
window.onload = function(){
var oBtn = document.getElementById(\”btn1\”);
oBtn.onclick = function(){
if(confirm(\”确定要跳转到首页吗?\”)){
window.location.href = \”http://www.baidu.com\”;
}else{
document.write(\”取消\”);
}
};
}
//prompt对话框的使用
window.onload = function(){
var oBtn = document.getElementById(\”btn1\”);
oBtn.onclick = function(){
var name = window.prompt(\”请输入您的名字:\”);
document.write(\”欢迎来到<strong>\”+name+\”</strong>\”);
};
}
//setTimeout()的使用
window.onload = function(){
setTimeout(\’alert(\”doubi\”)\’, 2000);
}
//setTimeout()的使用,其中code是一个函数
window.onload = function(){
setTimeout(\’alert(\”doubi\”)\’, 2000);
}
////setTimeout()的使用,其中code是一个函数名
window.onload = function(){
setTimeout(alertMes, 2000);
function alertMes(){
alert(\”doubixiaohanhan\”);
}
}
//clearTimeout()的使用
window.onload = function(){
var oBtn = document.getElementsByTagName(\”input\”);
var timer = null;
oBtn[0].onclick = function(){
//alert(\”你已点击开始定时按钮\”);
timer = setTimeout(alertMes, 5000);
function alertMes(){
alert(\”doubixiaohanhan\”);
}
};
oBtn[1].onclick = function(){
clearTimeout(timer);
}
}
//setInterval()的使用
var n = 10;
window.onload = function(){
var t = setInterval(countdown,1000);
};
function countdown(){
if(n > 0){
n—;
document.getElementById(\”num\”).innerHTML = n;
}
}
//setInterval()的使用,在图片轮播开发中特别有用
window.onload = function(){
var oBtn = document.getElementsByTagName(\”input\”);
var oDiv = document.getElementsByTagName(\”div\”)[0];
var colors = [\”red\”,\”yellow\”,\”blue\”,\”green\”,\”purple\”,\”orange\”];
var time = null;
var i = 0;
oBtn[0].onclick = function(){
clearTimeout(time);
time = setInterval(function(){
oDiv.style.backgroundColor = colors[i];
i++;
i = i%colors.length;
},1000);
};
oBtn[1].onclick = function(){
clearInterval(time);
};
}
//href的使用
window.onload = function(){
var url = window.location.href;
document.write(\”当前页面地址:\”+ url);
window.setTimeout(code,3000);
function code(){
url = window.location.href = \”http://www.baidu.com\”;
window.open(url);
};
}
//navigator的使用
window.onload = function(){
//indexOf(用于查找某个字符串在字符串中首次出现的位置,如果找不到返回-1)
if(window.navigator.userAgent.indexOf(\”MSIE\”) != –1){
alert(\”这是IE浏览器\”);
}
else if(window.navigator.userAgent.indexOf(\”Chrome\”) != –1){
alert(\”这是谷歌浏览器\”);
}
else if(window.navigator.userAgent.indexOf(\”Firefox\”) != –1){
alert(\”这是火狐浏览器\”);
}
else;
}
/*第十四章:document对象
* 浏览器会为每个窗口内的HTML页面自动创建一个document对象
* 一,document对象常用的属性
* document.title 获取文档的title
* document.body 获取文档的body
* document.forms 获取文档的forms
* document.images 获取文档的images
* document.links 获取文档的links
* document.cookie 获取文档的cookie
* document.URL 获取文档的URL
* document.referrer 获取文档的referrer:获取用户在访问当前页面
* 之前所在的页面地址,可以统计用户都是通过什么方式来访问页面的
* 其中:使用document.getElementByTagName(\”forms/img/a\”)操作
* forms、images、links
* 二,document对象常用的方法
* document.getElementById() 通过id获取元素
* document.getElementByTagName() 通过标签名获取元素
* document.getElementByClassName() 通过class获取元素
* document.getElementByName() 通过name获取元素
* document.querySelector() 通过选择器获取元素,只获取第一个
* document.querySelectorAll() 通过选择器获取元素,获取所有
* document.createElement() 创建元素节点
* document.createTextNode() 创建文本节点
* document.write() 输出内容
* document.writeln() 输出内容并换行
*/
//获取url
window.onload = function(){
var url = document.URL;
document.write(url);
}
//输出内容并换行
window.onload = function(){
var url = document.URL;
document.writeln(url);
document.writeln(\”doubixiaohanhan\”);
}
本文作者及来源:Renderbus瑞云渲染农场https://www.renderbus.com
文章为作者独立观点不代本网立场,未经允许不得转载。