注:没有标注书名只有页数的参考来源均指代《Javascript权威指南第六版》,其他参考均有书名或网址标注。
一、易产生误区知识点记录
1、[] == ! []
所有对象都转化为true(P52),数组是对象,因此空数组[]转化为布尔值为真。而空数组转化为数字时为0.因此左式Number([])=0。
“!”运算符首先将操作数转换为布尔值,然后再对布尔值求反(P81),因此右式为!Boolean([])=!true=false
对于==,如果一个值是false,则将其转化为0再进行比较(p75)
因此以上等式为0==0,结果为true
2、为什么说要避免使用eval()
eval()
是一个危险的函数, 他执行的代码拥有着执行者的权利。如果你用 eval() 运行的字符串代码被恶意方(不怀好意的人)操控修改,您最终可能会在您的网页/扩展程序的权限下,在用户计算机上运行恶意代码。更重要的是,第三方代码可以看到某一个eval()
被调用时的作用域,这也有可能导致一些不同方式的攻击。相似的 Function 就不容易被攻击。
eval()
通常比替代方法慢,因为它必须调用 JS 解释器,而许多其他结构则由现代 JS 引擎进行优化。
3、数组索引既可以是数字也可以是字符
对象是javascript的基本数据类型。对象是一种复合值。它将很多值(原始值 或者其他对象)聚合在一起。可通过名字访问这些值。对象也可以看做是属性的无序集合,每个属性都有一个名/值。属性名是字符串,因此我们可以把对象看成是从字符串到值的映射。 ( P118).数组是对象的页数形式。使用方括号访问数组元素就像用方括号访问对象的属性一样。JavaScript将指定的数字索引值换成字符串——索引值1变成“1”(P146)
4、javascript:void(0)生效原理
void是一级运算符,它出现在操作数之前,操作数可以是任意类型。操作数会照常计算,但忽略计算结果并返回undefined(P89)
既然void会忽略计算结果返回undefined,那干嘛不直接就写undefined呢?答案很简单,undefined 并不是保留词(reserved word),它只是全局对象的一个属性,在低版本 IE 中能被重写。就算不在IE,也可以在局部作用域中被重写。
在url后面跟一个javascript:协议限定符,是另一种嵌入JavaScript代码到客户端的方式。这种特殊的协议类型指定URL内容为任意字符串,这个字符串是会被JavaScript解释器运行的JavaScript代码,它被当成做单独的一行代码对待,这意味着语句之间必须用分号隔开,而注释//必须用/* */替代。javascript:URL能识别的‘资源’是转换成字符串执行代码的返回值。如果代码返回undefined,那么这个资源是没有内容的。javascript:URL可以用在可以使用常规URL的任意地方(P318)
5、如果数据属性是不可配置的,则不能将他的可写性从false改成true,但可以从true修改为false
6、注意,一旦将对象转换为不可拓展的,就无法再将其转换回可拓展的了。
对于那些已经封闭起来的对象是不能解封的。(P140)
7、判断对象是否为一个数组,及为什么instanceof操作符不能视为一个可靠的的数组检测方法的原因。
①、Object.prototype.toString.call(someval).slice(8,-1)判断对象类型
对象的类属性是一个字符串,用以表示对象的类型信息。
[object class]
因此想要获得对象的类,可以调用对象的toString()方法,然后提取已返回字符串的第8个到倒数第二个位置的字符串。不过让人感到棘手的是,很多对象继承的toString()方法重写了,为了能调用正确的toString()版本,必须间接的调用Function.call()方法。(P139)
②在ECMAScript5中,可以使用Array.isArray()函数来检测是否为数组(P160)
③使用instanceof的问题是在Web浏览器中可能有多个窗口或窗体存在。每个窗口都有自己的javascript环境,有自己的全局对象。并且,每个全局对象有自己的一组构造函数。因此一个窗体中的对象将不可能是另外窗体中的构造函数的实例。窗体之间的混淆补偿发生,但这个问题已经足以证明instanceof操作符不能视为一个可靠的的数组检测方法。(P161)
8、数组直接量的语法允许有可选的结尾的逗号,故[,]只有两个元素而非三个
9、函数递归
如果一个函数定义表达式包含名称,函数的局部作用域将会包含一个绑定到函数对象的名称,实际上,函数的名称将成为函数内部的一个局部变量。通常而言,以表达式方式定义的函数都不需要名称,这会让定义它们的代码更为紧凑。(P167)
如果函数声明式不包含名称,则使用参数对象的callee属性来调用自身。在非严格模式下,ECMAScript标准规范规定callee属性指代当前正在执行的函数。
10、根据ECMAScript3和非严格的ECMAScript5对函数调用的规定,调用上下文(this)的值是全局对象。然而,在严格模式下,调用上下文是undefied。以函数形式调用的函数通常不使用this关键字。不过,this关键字可以用来判断当前是严格模式。(P169)
方法调用和函数调用的一个重要的区别,即:调用上下文。属性访问表达式由两个部分组成:一个对象和属性名称。在像这样的函数表达式中(o.m),对象o成为函数调用上下文。函数体可以使用关键字this调用该对象。(P170)
11、方法链:当方法的返回值是一个对象,这个对象还可以在调用它的方法。这种方法调用序列中(通常称之为’‘链’或者’级联’)。每次的调用结果都是另外一个表达式的组成部分。当方法不需要返回值时,最好直接返回this,如果在设计的API中一直采用这种方式(每个方法都返回this),使用API就可以进行’链式调用’风格的编程,在这种编程风格中,只要指定一次要调用的对象即可,余下的方法都可以基于此进行调用。(P171)
12、闭包原理
每次调用JavaScript函数的时候,都会为之创建一个新的对象用来保存局部变量,把这个对象添加至作用域链中。当函数返回的时候,就从作用域链中将这个绑定变量的对象删除。如果不存在嵌套的函数,也没有其他引用指向这个绑定对象时,它就会被当做垃圾回收掉。如果定义了嵌套函数,每个嵌套函数都对应了一个作用域链,并且这个作用域链指向一个变量绑定对象。但如果这些嵌套的函数对象在外部被保存下来,那么它们也会和所指向的变量绑定对象一样当做垃圾回收。但是如果这个函数定义了嵌套的函数,并将它作为返回值返回或者存储在某处的属性里,这时就会有一个外部引用自己指向这个嵌套的函数。它就不会被当做垃圾回收,并且它所指向的变量绑定对象也不会被当做垃圾回收。(P184)
**作用域链:**作用域链是一个对象列表或者链表,这组对象定义了这段代码’作用关于中’的变量。当javascript需要查找变量x的值得时候(变量解析),他会从链中的第一个对象开始查找,如果这个对象有一个名为x的属性,则会直接使用这个属性的值,如果第一个对象中不存在名为x的属性,javascript会继续查找链上的下一个对象。如果第二个对象依然没有名为x的属性,则会继续查找下一个对象,以此类推。如果作用域链上没有任何一个对象含有属性x,那么久认为这段代码的作用域链上不存在x,并最终爆出一个引用错误异常。(P59)
13、理解原型
任何javascript函数都可以作为构造函数(ECMAScript5中的Function.bind()返回的函数除外)都自动拥有一个prototype属性。这个属性的值是一个对象,这个对象包含唯一一个不可枚举的属性constructor。constructor属性的值是一个函数对象。(P205)
无论什么时候,只要创建了一个新的函数,就会根据一组特定的规则为该函数创建一个prototype属性,这个属性指向函数的原型对象。在默认情况下,所有原型对象都会自动获得一个constructor属性,这个属性是一个prototype属性所在函数的指针。就拿前面的例子来说Person.prototype.construtor指向Person.而通过这个构造函数,我们还可以添加其他属性和方法。
创建了自定义的构造函数之后,其原型对向默认只会取得constructor属性;至于其他方法,则都是从Object继承而来的。当调用构造函数创建一个新的实例后,该实例的内部将包含一个指针(内部属性),指向构造函数的原型对象。ECMA-262第5版总管这个指针叫[[Prototype]].虽然在脚本中没有标准的方式访问[[Prototype]],但Firfox、Safair和Chrome在每个对象上的都支持一个属性_—_proto_
;而在其他市县中,这个属性对脚本则是完全不可见的。不过,要明确的真正重要的一点就是,这个连接存在于实例与构造函数的原型对象之间,而不是存在于实例与构造函数之间。(JavaScript高级程序设计第三版P148)
14、ECMAScript3规范规定,一个正则表达式直接量会在执行到它的时候转换为一个RegRxp对象,同一段代码所表示正则表达式直接量的每次运算都返回同一个对象
ECMAScript5规范则做了相反的规定,同一段代码多表示的正则表达式直接量的每次运算都返回新对象。(P254)
15、更改const声明的简单类型数据会报错,而更改const声明的符合数据类型可以成功。
const实际上保证的,并不是变量的值不得改动,而是变量指向的那个内存地址不得改动。对于简单类型的数据(数值、字符串、布尔值),值就保存在变量指向的那个内存地址,因此等同于常量。但对于复合类型的数据(主要是对象和数组),变量指向的内存地址,保存的只是一个指针,const只能保证这个指针是固定的,至于它指向的数据结构是不是可变的,就完全不能控制了。
如果真的想将对象冻结,应该使用Object.freeze方法。(《ECMAScript 6 入门》作者:阮一峰)
二、要点总结
1、正则表达式
①字符类:将直接量字符串单独放进方括号中就组成了字符类。一个字符类可以匹配他所包含的任意字符,因此,正则表达式/[a,b,c]/就就和字母’a’,‘b’,‘c’中的任意一个都匹配。另外,可以通过一个’‘字符来定义否定字符类,他匹配所有不包含在方括号内的字符,定义否定字符类时,将一个’‘符号作为左方括号的第一个字符,正则表达式/[^abc]/匹配的是’a’,‘b’,'c’以外所有字符。(P256)
②重复:正则表达式的重复字符语法
字符 | 匹配 |
---|---|
{n,m} | 匹配前一项至少n次,但不能超过m次 |
{n,} | 匹配前一项n次或者更多次 |
{n} | 匹配前一项n次 |
? | 匹配前一项0次或者一次 |
+ | 匹配前一项一次或者多次 |
* | 匹配前一项0次或者多次 |
③选择:字符’|‘用于分割供选择的字符。例如,/ab|cd|ef/可以匹配’ab’,‘cd’,‘ef’。注意选择项的尝试匹配顺序是从左到右。
④分组:正则表达式中圆括号有多中作用,一个作用是把单独的项组合成子表达式,以便可以像处理独立单元那样用’|‘,’?‘,’*‘,’+‘等对单元内的项进行处理。
在正则表达式中,圆括号的另一个作用是在完整的模式中定义自模块。当一个正则表达式成功地和目标字符串相匹配时,可以从目标穿中抽出和圆括号中的子模式相匹配的部分。
③引用:带圆括号的表达式的另一个用途是允许在同一正则表达式的后部引用前面的子表达式。这是通过在字符’'后加一位或者多位数字实现的。这个数字指定了带圆括号的字表达式,\3引用的是第三个带圆括号的字表达式。注意,因为字表达式可以嵌套另一个字表达式,所以它的位置是参与计数的左括号的位置。
④指定匹配位置
⑤修饰符
字符 | 含义 |
---|---|
i | 执行不区分大小写的匹配 |
g | 执行一个全局匹配,即找到所有匹配,而不是找到第一个之后就停止 |
m | 多行匹配模式,^匹配一行的开头和字符串的开头,$匹配行的结束和字符串的结束 |
2、事件
①事件分类:
- 依赖于设备的输入事件:有些事件和特定的输入设备直接相关,比如鼠标和键盘。
- 独立与设备的输入事件:有些输入事件没有直接相关的特定输入设备。比如click事件表示激活了连接、按钮或其他文档元素,者通常是用过鼠标单击实现,但也能通过键盘或触摸感知设备上的手势来实现
- 用户界面事件:用户界面事件是比较高级的事件,通常出现在定义web应用用户界面的HTML表单元素上。包括文本输入与获取键盘焦点的focus时间、用户改变表单元素显示值的change时间和用户单击表单中的‘提交’按钮的submit事件。
- 状态变化事件:有些时间不是有用户活动而是有网络或浏览器互动触发,用来表示某种生命周期或相关状态的变化。当文档完全加载时,在Window对象上会发生load事件,这可能是这类事件中最常用的。
- 特定api事件:HTML5及相关的规范定义的大量Web API都有自己的事件类型。拖放API。
- 计时器和错误处理程序
②注册事件处理程序
- 设置JavaScript对象属性为事件处理程序:按照约定,事件处理程序属性的名字由’on’后面跟着事件名组成。如onclick,onchange,onload,onmouseover等,注意这些属性名是区分大小写的,所有都是小写,即是事件类型是多个词组成(比如readstatechange)
- 设置HTML标签为事件处理程序:用于设置的文档元素事件处理程序属性也能换成对应的HTML标签的属性,若如此做,属性值应该是Javascript代码字符串。这段代码应该是处理程序的函数主体,而非完整的函数声明。
- addEventListener():addEventLisener()接受三个参数,第一个是要注册处理程序的事件类型,这个事件类型是字符串,但它不应该包含用于设置事件处理程序属性的前缀’on’。第二个参数是指定类型的事件发生时应该调用的函数。最后一个参数是布尔值,通常情况下,会给这个参数传递false。如果穿了truw,那么函数将注册为补货事件处理程 序,并在事件不同的调度阶段调用。
- attachEvent():
1>、因为IE事件模型不支持事件捕获,所以attachEvent()和detachEvent()要求只有两个参数:事件类型和处理程序函数
2>、IE方法的第一个参数使用了带’on’前缀的事件处理程序属性名,而非没有前缀的事件类型。
3>、attachEvent()允许相同的事件处理程序函数注册很多次。当特定的事件发生时,注册函数的调用次数和注册次数一样
3、XMLHttpRequest
①浏览器在XMLHttpRequest类上定义了它们的API。这个类的每个实力都表示一个独立的请求/响应对,并且这个对象的属性和方法允许指定请求细节和提取响应数据。
②你也许能重用已存在的XMLHttpRequest,但注意这将会终止之前通过该对象挂起的任何请求。(P487)
③一个HTTP请求由4部分组成:
- HTTP请求方法或’动作’
- 正在请求的URL
- 一个可选的请求头集合
- 一个可选的请求主体
④服务器返回的HTTP响应包含三部分:
- 一个数字和文本组成的状态码
- 一个响应头集合
- 响应主体
⑤顺序: HTTP请求的各部分有指定顺序,请求方法和URL首先到达,然后是请求头,最后是请求主体。
⑥取得响应- staus和stausText属性以数字以和文本的形式返回HTTP状态码。这些属性保存标准HTTP值。
- 使用getResponseHeader()和getAllResponseHeader()能查询响应头。
- 相应主体可以从responseText属性中得到文本形式的,从responseXML属性中得到Document形式的。
⑦为了响应就绪得到通知,必须j监听XMLHttpRequest对象上的readystatechange事件。为了理解这个事件类型,你必须理解readystate属性。readyState属性是一个整数,它指定了HTTP的状态
- 0:open()尚未调用
- 1:open()已调用
- 2:接收到头信息
- 3:接收到相应主体
- 4:响应完成
4、Web存储: localStorage和sessionStorage的区别在于存储的有效期和作用域不同。
- ①localStorage存储的数据时永久性的。sessionStorage的有效期则不同,一旦窗口或者标签页被永久关闭,那么所有通过sessionStorage存储的数据也都被删除了
- ②localStorage的作用域是限定在文档源级别的。与localStorage一样sessionStorage的作用域也是限定在文档源中,不仅如此,sessionStorage的作用域还被限定在窗口中,如果同源的文档渲染在不同的浏览器标签页中,那么他们之间拥有的是各自的sessionStorage数据,无法共享。
5、HTMLAPI
①地理位置:navigator.geolocation
②历史记录管理
- 哈希路由:利用location.hash和hashchange事件。hash属性设置url的片段标识符,通常是用于指定滚动到的文档中某一部分的ID。但是location.hash不一定要设置为一个元素的ID:它可以设置为任何字符串。如果能够将应用状态编码成字符串,就可以使用该字符串作为片段标识符。(p663)
- history路由:利用history和pushState()方法和popstate事件。HTML5引入了 history.pushState() 和 history.replaceState() 方法,它们分别可以添加和修改历史记录条目。这些方法通常与window.onpopstate 配合使用。
每当处于激活状态的历史记录条目发生变化时,popstate事件就会在对应window对象上触发. 如果当前处于激活状态的历史记录条目是由history.pushState()方法创建,或者由history.replaceState()方法修改过的, 则popstate事件对象的state属性包含了这个历史记录条目的state对象的一个拷贝.
调用history.pushState()或者history.replaceState()不会触发popstate事件.
当网页加载时,各浏览器对popstate事件是否触发有不同的表现,Chrome 和 Safari会触发popstate事件, 而Firefox不会.
参考:https://developer.mozilla.org/zh-CN/docs/Web/API/History_API
③跨域消息传递
④Worker对象
⑤类型化数组和ArrayBuffer
⑥Blob
⑦文件系统API
三、学习心得
1、关于原型构造器的部分,感觉红宝书比犀牛书的讲解更加细致,但是基础部分的话犀牛书的解读是非常清晰且容易理解的。
2、关于书中第18.3节(P508)的基于服务器推送事件的Comet事件,实践后发现实际上是每过3s对服务器进行一次数据请求
3、在实践postMessage时发现一个问题:
window.open()方法的返回打开窗口的引用,但是必须成功打开才有引用返回,如果被浏览器拦截(出于安全性拦截非用户点击的窗口弹出)则返回null。