关于事件传播的三个阶段

事件传播分为三个阶段:事件捕获,目标对象本身的事件程序,事件冒泡。

事件传播的捕获阶段像反向的冒泡阶段。最先调用Window对象的捕获处理程序,接着是body对象的,再然后是 DOM 树向下,以此类推,直到调用事件目标的父元素的捕获事件处理程序。在目标对象本身上注册的捕获事件处理程序不会被调用。

addEventListener()把一个布尔值作为其第三个参数。如果这个参数是true,那么事件处理程序被注册为捕获事件处理程序,它会在事件传播的第一个阶段调用。事件冒泡得到广泛的支持,它能用在包括 IE 在内的所有浏览器中,且无论事件处理程序以哪种方式注册(除非它们被注册为捕获事件处理程序)。而事件捕获只能用于以addEventListener()注册且第三个参数是true的事件处理程序中。这意味着事件捕获无法在 IE9 之前的 IE 中使用。

查看全文

apply() 与 call() 用法浅析

apply()call() 作为所有函数的方法,定义为:在指定this值与参数的情况下调用某个函数或方法。两者的区别是:call()方法接受的是若干个参数的列表,而apply()方法接受的是一个包含多个参数的数组

这两个方法的关键点在于指定this与参数,所以我们以此为切入点,看看在实际的工作中有哪些用法。

使用apply()支持可变参数

所有的语言,有一些我们经常要做的事情,似乎都被语言的开发人员莫名其妙的忽略了,JavaScript 也不例外。比如查找数组中的最小值与最大值。在 JavaScript 中似乎没有这两种功能,但如果随意探索的话,可能会发现Math对象有两个名为min()max()的方法。

一开始我们认为,这些方法可能就是我们想要的答案,但测试以后,我们发现第个方法都需要可变长度的参数列表,而不是数组。这根本不能满足我们的需求,也就是说,Math.max()的调用,需要像下面这样:

查看全文

JavaScript 中三种转换为数字类型的方式

JavaScript有三种将其它类型转换为数字类型的方式:parseInt()parseFloat()Number()parseInt()parseFloat()只能在 String 类型上调用。而Number()不仅限于 String 类型,比如Date类型上也可以使用。

parseInt()

在判断字符串是否是数字值前,parseInt()parseFloat()都会仔细分析该字符串。

parseInt()方法首先查看位置 0 处的字符,判断它是否是个有效数字;如果不是,该方法将返回NaN,不再继续执行其他操作。但如果该字符是有效数字,该方法将查看位置 1 处的字符,进行同样的测试。这一过程将持续到发现非有效数字的字符为止,此时parseInt()将把该字符之前的字符串转换成数字。

例如,如果要把字符串 “12345red” 转换成整数,那么parseInt()将返回 12345,因为当它检查到字符 r 时,就会停止检测过程。

字符串中包含的数字字面量会被正确转换为数字,比如 “0xA” 会被正确转换为数字 10。不过,字符串 “22.5” 将被转换成 22,因为对于整数来说,小数点是无效字符。

查看全文

函数的递归调用

递归(recursion)是经常遇到的一个概念。当函数调用自身,或调用另外一个函数,但这个函数的调用树中的某个地方又调用了自己时,递归就发生了。

对任何类型的程序来说,递归都是一个非常有用的技术——很多数学公式在本质上都是递归。而且,对树进行遍历时,递归也是非常有用的,这是一个可能会在 Web 程序中出现的构造。我们还可以使用递归深入理解函数在 JavaScript 中是如何工作的。

从最简单的开始。

普通命名函数中的递归

有很多常见的递归函数示例。其中一个是用于检测回文——相当于递归技术的“Hello World”。

回文的非递归定义是“一个短语,不管从哪个方向读都是相同的”,我们可以用它来实现一个函数,用于创建反向字符串并和字符串本身进行比较。但是复制字符串从多方面来产不是简洁的解决方案,其中一个原因就是需要分配并创建新的字符串。

通过利用回文的更多数学定义,我们要可以想出一个更简洁的解决方案,这些定义如下所示:

  1. 单个与零个字符都是一个回文。
  2. 如果字符串的第一个字符和最后一个字符相同。将这前后两个排除后,其它的字符串仍是一个回文的话,我们称原字符串是一个回文。

查看全文

闭包与即时函数

有一个重要的构造是在 JavaScript 高级函数式编程中经常使用的,这种构造依赖于对闭包的充分利用,比如:(function(){})()
这种模式的代码,毫无疑问可能用在很多地方,它给 JavaScript 语言带来了出乎意料的能力。让我们分解这段代码,探究其内部到底发生了什么。

首先,先忽略第一组括号的内容,再回头来看代码:(...)()

众所周知,可以通过函数名加圆括号:functionName(),的语法方式调用任意一个函数。但是在这里,我们可以使用任意一个引用函数实例的表达式作为函数的名称。就像下面的代码,使用变量名调用该变量所引用的函数:

与其它表达式在一起使用,我们需要将一个操作符——函数调用操作符 ( ) ,应用在整个表达式上,所以需要用圆括号将该表达式括起来。

也就是说,在 (...)() 中,第一组圆括号仅仅是用于划定表达式的范围,而第二个圆括号则是一个操作符。如下代码,将函数引用通过圆括号括起来是完全合法的:

查看全文

使用闭包实现缓存记忆(memoization)

缓存记忆让函数能够记录它之前被调用时所产生的结果。这样,我们第二次获取结果值时,优先从函数的缓存记录中查找结果,避免重复计算结果。

首先要实例一个无闭包的版本:

查看全文

闭包是如何工作的?

简单来说,闭包(closure)是函数在声明时访问函数自身之外的变量时所创建的作用域

让我们将这句话分解来看:一、闭包是作用域,二、函数在声明时访问外部变量所创建的作用域。另外:声明的函数在后续的任何时刻都可以被调用,即使是声明时的作用域消失之后。

从最简单的代码开始理解:

在这个最简单的例子中,outerFunction能够访问到外部的outerValue变量。这样的代码,我们可能已经编写了成千上万次,但是却没有意识到其实我们已经创建了一个闭包。

一点都不特别是吧?因为外部变量与外部函数都是在全局作用域内声明的,该作用域(也是闭包)从未消失过(因为页面已经被加载)。所以该函数可以访问到外部变量也就不足为奇了,因为它仍在作用域内并且是可用的。这种情况下,即使有闭包的存在,我们也不清楚他的好处。

查看全文

定时器(interval)与延时器(timeout)

定时器与延时器提供了一种让一段代码在一定毫秒之后,再异步执行的能力。由于 JavaScript 单线程的特性,定时器与延时器提供了一种跳出这种限制的方法,以一种不太直观的方式来执行代码。

有一个需要理解的重要概念,就是 JavaScript 中定时器与延时器的执行时间是不能保证的,原因就是因为 JavaScript 是单线程的。JavaScript 在同一时间只能执行一个代码块,这些代码块的执行就阻塞了异步事件的处理。这意味着,当一个异步事件发生时(如鼠标单击,ajax 回调事件),它就会排队,并且在线程空闲时才进行执行。

很容易验证浏览器对定时器或延时器的阻塞,使用alert()即可,代码很简单:

查看全文

利用参数个数以及闭包进行函数重载

基于传入的参数,有很多种方法可以判断并进行函数重载。一种通用的方法是,根据传入参数的类型执行不同的操作;另一种方法是,可以通过判断某些特定的参数是否存在来执行不同的操作;还有一种方法是判断传入参数的个数来执行不同的操作。本文介绍的就是最后一种方法。

对于一个函数,在参数方面,我们可以确定两件事情:

  • 通过函数的length属性,可以知道声明了多少命名参数(形参个数)
  • 通过arguments.length,可以知道在调用时传入了多少参数(实参个数)

查看全文