第一章 让自己习惯Javascript
1、了解你使用的JS版本
严格模式 "use strict"
2、JS的浮点数
JS只有一种number类型,所有的数字都是双精度浮点数
运算的时候是先隐适的转换为32位整数后进行运算
3、当心隐式的强制转换
3 + true = 4
“+”既重载了数字相加,又重载了字符串连接 1 + 2 + “3” = “33”
NaN不等于其本身,JS中唯一一个不等于自身;isNaN也不可靠,会在判断前将参数转换为数字isNaN(isNaN) == true;正确的判断方式(x!==x)
对象会被转换为字符串;ValueOf与toString JS会盲目的选择ValueOf 用ValueOf强制转换为数字;用toString方法强制转换为字符串
7种假值 false 0 -0 "" NaN null undefined
测试是否未定义 typeof x === "undefined" 或者 x === undefined
4、原始类型优于封装对象
5个类型 布尔值、数字、字符串、null、undefined
5、避免对混合类型使用 == 运算符
"1.0e0" == {valueOf: function(){return true}}
显示的转换if(+form.month.value == today.getDate())
P15 ==运算符强制转换规则
严格模式 "use strict"
2、JS的浮点数
JS只有一种number类型,所有的数字都是双精度浮点数
运算的时候是先隐适的转换为32位整数后进行运算
0.1+0.2 = 0.30000000000000000004 当心精度陷阱
3 + true = 4
“+”既重载了数字相加,又重载了字符串连接 1 + 2 + “3” = “33”
NaN不等于其本身,JS中唯一一个不等于自身;isNaN也不可靠,会在判断前将参数转换为数字isNaN(isNaN) == true;正确的判断方式(x!==x)
对象会被转换为字符串;ValueOf与toString JS会盲目的选择ValueOf 用ValueOf强制转换为数字;用toString方法强制转换为字符串
7种假值 false 0 -0 "" NaN null undefined
测试是否未定义 typeof x === "undefined" 或者 x === undefined
4、原始类型优于封装对象
5个类型 布尔值、数字、字符串、null、undefined
var s = new String("hello") typeof s //object typeof "hello" //string 封装存在的理由是其实用方法,结合隐式强制转换 String有toUpperCase "hello".pro = 17 当调用的时候 "hello".pro//undefined 获取和设置原始类型值的属性会隐式的创建封装对象
"1.0e0" == {valueOf: function(){return true}}
显示的转换if(+form.month.value == today.getDate())
P15 ==运算符强制转换规则
| 参数类型1 | 参数类型2 | 强制转换 |
| null | undefined | 不转换总返回true |
| null或undefined | 其他任何非null或undefined类型 |
不转换总返回false
|
| 原始类型string number或boolean | Date对象 | 将原始类型转换为数字;将date对象转换为原始类型(有限尝试 toString,再valueOf) |
|
原始类型string number或boolean
|
非Date对象 |
将原始类型转换为数字;将非date对象转换为原始类型(有限尝试valueOf,再 toString)
|
|
原始类型string number或boolean
|
原始类型string number或boolean
|
将原始类型转换为数字
|
6、了解分号插入的局限
在return throw break continue ++或--的参数之前不能换行
7、视字符串为16位的代码单元序列
unicode每个字符单位分配了一个唯一的整数,介于0和1114 111之间,称为代码点
unicode允许多个不同的二进制的代码点 流行的标准:UTF-8 UTF-16 UTF-32
每个代码点可以容纳一个16位的数字所以简单的方法就是将代码点与其编码元素一对一的映射起来,称为一个代码单元
两个代码单元表示2^16及其以上的unicode代码点,这两个代码单元称为代理对
UTF-16 一对代码单元共同编码一个等于或大于2^16的代码点
length charAt charCodeAt基于代码单元层级而不是代码点层级,每当字符串包含辅助平面中的代码点时,JS将每个代码点表示为两个元素
第二章 变量作用域
8、少用全局对象
使用全局对象做平台特性检测
全局变量会污染共享的公共命名空间
9、始终声明局部变量
忘记声明局部变量,会被隐式的转变为全局变量
10、避免使用with
JS从最内层的作用域开始向外查找变量。一种是我们希望的with对象属性的变量,另外一种是希望引用外部变量绑定的变量
11、熟练掌握闭包
JS允许你使用在当前函数以外定义的变量
即使外部函数已经返回,当前函数仍然可以引用在外部函数所定义的变量。JS的函数值包含了比调用他们时执行所需要的代码还要多的信息,
还在内部存储他们可能会引用的定义在其封闭作用域的变量,那些在其所涵盖的作用域内跟踪变量的函数称为闭包
闭包可以更新外部变量的值
12、变量声明提升
JS不支持块级作用域,作用域不是离其最近的封闭语句或代码块而是他们的函数
声明与赋值两部分
try...catch是例外,作用域只是catch语句块
13、使用立即调用的函数表达式创建局部作用域
JS为每一个绑定到该作用域的变量在内存中分配一个“槽”(slot)
解决办法是创建一个嵌套函数并立即调用他来强制创建一个局部作用域,立即调用的函数(IIFE)
这是一个函数声明,也可是一个命名函数表达式
函数表达式作用域会被Object.prototype污染
JS环境中会提升命名函数表达式声明,并导致命名函数表达式的重复存储 var f = function g(){return 17;}
15、当心局部块函数声明笨拙的作用域
避免使用eval函数创建的变量污染调用者的作用域
17、间接调用eval函数优于直接调用
eval函数具有访问调用他那时的整个作用域的能力
直接调用 eval("x") 编译器要确保被执行的程序具有完全访问调用者局部作用域的权限
间接调用 (0,eval)(src) 数字字面量0被求值但其被忽略掉了,括号表示的序列表达式的结果是evel
使用全局对象做平台特性检测
全局变量会污染共享的公共命名空间
9、始终声明局部变量
忘记声明局部变量,会被隐式的转变为全局变量
10、避免使用with
JS从最内层的作用域开始向外查找变量。一种是我们希望的with对象属性的变量,另外一种是希望引用外部变量绑定的变量
11、熟练掌握闭包
JS允许你使用在当前函数以外定义的变量
即使外部函数已经返回,当前函数仍然可以引用在外部函数所定义的变量。JS的函数值包含了比调用他们时执行所需要的代码还要多的信息,
还在内部存储他们可能会引用的定义在其封闭作用域的变量,那些在其所涵盖的作用域内跟踪变量的函数称为闭包
闭包可以更新外部变量的值
12、变量声明提升
JS不支持块级作用域,作用域不是离其最近的封闭语句或代码块而是他们的函数
声明与赋值两部分
try...catch是例外,作用域只是catch语句块
13、使用立即调用的函数表达式创建局部作用域
JS为每一个绑定到该作用域的变量在内存中分配一个“槽”(slot)
解决办法是创建一个嵌套函数并立即调用他来强制创建一个局部作用域,立即调用的函数(IIFE)
for(var i=0;i<n;i++){ (function(j){ result[i]=function(){return a[j];} })(i); }
14、当心命名函数表达式笨拙的作用域
function double(x){return x*2;}这是一个函数声明,也可是一个命名函数表达式
函数表达式作用域会被Object.prototype污染
JS环境中会提升命名函数表达式声明,并导致命名函数表达式的重复存储 var f = function g(){return 17;}
15、当心局部块函数声明笨拙的作用域
function f(){return 'global';} function test() { var result = []; if(x) { function f(){return 'local';} result.push(f()); } result.push(f()); return result; } test(true); //[local, local] 你懂得 声明的提升
16、避免使用eval创建局部变量
function test(x){eval("var y=x;");return y;}
17、间接调用eval函数优于直接调用
eval函数具有访问调用他那时的整个作用域的能力
直接调用 eval("x") 编译器要确保被执行的程序具有完全访问调用者局部作用域的权限
间接调用 (0,eval)(src) 数字字面量0被求值但其被忽略掉了,括号表示的序列表达式的结果是evel
第三章 使用函数
18、理解函数调用、方法调用及构造函数调用之间的不同
函数调用:function hello(){return '123';}
方法调用:var obj = {
hello: function{return '123';} //绑定到this变量的对象被称为调用接收者(receiver)
}
构造函数:function user(name) {
this.name = name;
}
u = new user('xx');
19、熟练掌握高阶函数
高阶函数:将函数作为参数或返回值的函数 [1,3,5].sort(function(){})
20、使用call自定义接收者来调用方法
随意给对象添加属性是一种不好的实践
call方法可以调用在给定的对象中不存在的方法
call方法定义高阶函数允许使用者给回调函数指定接收者
21、使用apply方法通过不同数量的参数调用函数
apply方法指定一个可计算的参数数组来调用可变参数的函数
22、使用arguments创建可变参数的函数
使用隐式的arguments对象实现可变参数的函数
23、永远不要修改arguments对象
arguments对象不是标准Array类型
因为obj是arguments[0]的别名 method是arguments[1]的别名 17[25].apply()
复制arguments var args = [].slice.call(arguments)
24、使用变量保存arguments的引用
当返回arguments[i++]的时候访问的是it.next()的参数
当引用arguments的时候要当心函数嵌套层级
绑定一个明确作用域的引用到arguments变量
25、使用bind方法提取具有确定接收者的方法
绑定一个函数用来实现绑定其接收者到一个指定的对象是非常常见的
buffer.add.bind(buffer)与buffer.add是不同的函数
26、使用bind方法实现函数柯里化
paths.map(simpleURL.bind(null, "http", siteDomain))
将函数与其参数的一个子集绑定的技术称为柯里化 预先将函数的某些参数传入,得到一个简单的函数
函数调用:function hello(){return '123';}
方法调用:var obj = {
hello: function{return '123';} //绑定到this变量的对象被称为调用接收者(receiver)
}
构造函数:function user(name) {
this.name = name;
}
u = new user('xx');
19、熟练掌握高阶函数
高阶函数:将函数作为参数或返回值的函数 [1,3,5].sort(function(){})
20、使用call自定义接收者来调用方法
随意给对象添加属性是一种不好的实践
call方法可以调用在给定的对象中不存在的方法
call方法定义高阶函数允许使用者给回调函数指定接收者
21、使用apply方法通过不同数量的参数调用函数
apply方法指定一个可计算的参数数组来调用可变参数的函数
22、使用arguments创建可变参数的函数
使用隐式的arguments对象实现可变参数的函数
23、永远不要修改arguments对象
arguments对象不是标准Array类型
function callMethod(obj, method) { var shift = [].shift; shift.call(arguments); shift.call(arguments); return obj[method].apply(obj, arguments); } callMethod(obj, 'add', 17, 25);//cannot read apply of undefined
复制arguments var args = [].slice.call(arguments)
24、使用变量保存arguments的引用
当返回arguments[i++]的时候访问的是it.next()的参数
当引用arguments的时候要当心函数嵌套层级
绑定一个明确作用域的引用到arguments变量
25、使用bind方法提取具有确定接收者的方法
绑定一个函数用来实现绑定其接收者到一个指定的对象是非常常见的
buffer.add.bind(buffer)与buffer.add是不同的函数
26、使用bind方法实现函数柯里化
paths.map(simpleURL.bind(null, "http", siteDomain))
将函数与其参数的一个子集绑定的技术称为柯里化 预先将函数的某些参数传入,得到一个简单的函数
var adder = function(num){ return function(y){ return num + y; } } print(adder(2)(100));//102
27、使用闭包而不是字符串来封装代码
eval函数会将出现的字符串中的所有变量作为全局变量来解释,通常一些高性能的引擎很难优化字符串中的代码
28、不要信赖函数对象的toString方法
JS有一个非凡的特性,将源代码重现为字符串的能力
由宿主环境的内置库提供的函数将解析失败
浏览器引擎会改变toString的输出
toSring方法生成的源代码并不展示闭包中保存的与内部变量引用相关的值
29、避免使用非标准的栈检查属性
arguments.callee:该arguments对象被调用的函数
arguments.caller:调用该arguments对象的函数,出于安全考虑大多数环境已经移除此特性,非标准但普遍适用的caller,指向函数最近的调用者
eval函数会将出现的字符串中的所有变量作为全局变量来解释,通常一些高性能的引擎很难优化字符串中的代码
28、不要信赖函数对象的toString方法
JS有一个非凡的特性,将源代码重现为字符串的能力
由宿主环境的内置库提供的函数将解析失败
浏览器引擎会改变toString的输出
toSring方法生成的源代码并不展示闭包中保存的与内部变量引用相关的值
29、避免使用非标准的栈检查属性
arguments.callee:该arguments对象被调用的函数
arguments.caller:调用该arguments对象的函数,出于安全考虑大多数环境已经移除此特性,非标准但普遍适用的caller,指向函数最近的调用者
英文版书籍下载:
posted on
浙公网安备 33010602011771号