0. JS的组成部分
- 核心(ECMAScript): 核心语言功能.
- 文档对象模型(DOM): 访问和操作网页内容的方法和接口.
- 浏览器对象模型(BOM): 与浏览器交互的方法和接口.
1. 语法
-
/ * */
注释块是不安全的.- 由于
/*
也会出现在正在表达式的字面量中, 所以是不安全的. - 尽量使用
//
来代替/*
.
- 由于
- 数字类型
- 只有一种数字类型, 在内部被表示为64位的浮点数.
- 并未分离出证书类型. 所以
1 === 1.0
- e�代表指数部分:` 100 === 1e2.
- 字符串
- 没有char 类型.
-
\u
用来指定数字字符编码-
'A' === '\u0041'
.
-
2. 对象
- 对象是可悲的键控集合( keyed collections).
- 数字,函数,正则表达式都是对象.
- 在获取对象的成员的值时, 使用
||
来填充默认值.-
var status = flight.status || 'unknown'
.
-
- 原型链
- 原型连接只在检索时才被用到, 并且是动态的关系.
- 添加新属性后, 立即对创建的对象可见.
- 删除和更新属性都不会触及原型链, 而只会影响当前对象.
- delete 可能会暴露出原本被覆盖掉的原型链上的属性.
- 使用hasOwnProperty 来禁止原型链检查.
- 原型连接只在检索时才被用到, 并且是动态的关系.
- 减少全局变量污染
- 创建一个唯一的全局变量,
var myMap = {};
- 然后以此变量作为应用的容器. 其它变量都是其属性.
3. 函数
- 3.1 一个JS函数也是一个对象.
- 所有的对象从技术上讲也只是函数.
- 函数会连接到Function.prototype 原型上.
- 函数创建时的隐藏属性:
- 函数的上下文和实现函数行为的代码.
- 一个”调用”属性(调用函数时,调用的是该属性).
- 闭包: 连接到外部上下文的连接.
- 3.2 函数的调用模式. 导致this参数的差异.
- 方法调用模式.
- 保存为对象属性的函数,称为方法.
- 在方法被调用时,this被绑定到该对象上.
- 函数调用模式.
- 并非对象的属性的函数. this被绑定到了全局对象上.
- 当函数作为内部函数时, 由于this为全局对象,所以必须使用闭包特性(定义一个外部变量值保存this)来在函数内部使用外部对象的属性.
- 构造器调用模式.
- 在函数调用时加上new. 将会创建一个连接到该函数的prototype成员的新对象. 且this被绑定到新对象上.
- 构造器函数约定为以大写字母开头. 但是调用它时如果没有加new,不会有异常和警告发生,所以应替代之.
- Apply调用模式.
- 函数默认拥有apply(this,参数数组)方法.
- 方法调用模式.
- 3.3 参数
- 调用时,会生成一个arguments伪数组(用于length,但是没有其他数组方法),利用它实现可变参数特性.
- 3.4 返回
- 总是会有返回值,在未指定时,返回undefined. new调用时,返回this.
- 3.5 异常
- 一个try只能有一个catch,若要处理特定类型的异常,检查异常对象的name来确定其属性.
- 3.6 扩充类型的功能.
- 给Object/Function/Number.prototype添加方法.
- 由于原型是公用结构,保险的做法是在没有该方法时才添加它.
- 3.7 尾递归优化
- 函数的末尾是返回自身递归调用的结果, 由于再创建新的调用栈纯属无用,会重用当前调用栈,而调用过程会成为一个循环调用.
- JS没有进行尾递归优化, 所以深度递归的函数可能会栈溢出而失败.
- 3.8 作用域
- JS并没有块级作用域的概念,所以应该在函数的顶部声明所有的变量. JS只有函数作用域.
- 3.9 闭包
- 内部函数拥有比它的外部函数更长的生命周期.
- 内部函数无需复制就能访问外部函数的实际变量.
- 3.10 模块
- 使用函数产生模块,来摒弃全局变量的使用.
- 模块模式: 定义了私有变量和函数的函数; 利用闭包创建可以访问到两者的特权函数,最后返回该特权函数.
- 可用来产生安全的对象,如用以产生序列号的对象.
- 3.11 柯里化
- 把多参函数转换为一系列单参数函数并调用的技术.
- 把函数与传递给它的参数相结合,从而产生出新的函数.
- 3.12 记忆
- 将先前操作的结果记录在某对象里,从而避免无谓的重复计算. 使用数组+闭包来实现.
- 设计产生另一函数的函数,来减少工作量.
4. 继承
- JS是弱类型的,不需要类型转换.
- **重要的是对象能做什么,而不是从哪里来. **
- 对象可以直接从其他对象继承.
- 对象从其它对象继承时的间接层: 通过构造器函数产生对象.
5. 数组
- 类数组特征的对象,并非真正的数组.将下标作为属性使用.
- 混合类型的元素, 元素可以为各种类型.
- 长度. 不会越界,length为最大下标属性+1,而非元素个数.
- 增大length不会分配更多空间,减少length会删除后续元素.
-
numbers[numbers.length]=2; 等同于: numbers.push(2)
.
- 删除. 数组就是对象,使用delete删除元素时会留下空洞. 使用splice()来删除一些元素并替换为其它的元素.
- 混淆. 当属性名是小而连续的整数时,使用数组,否则使用对象.
- 判断对象是否为数组:
function (value) { return Object.prototype.toString.apply(value) === ‘[object Array]’;}
- 判断对象是否为数组:
- 没有多维数组,自行创建元素为数组的数组.
- array的方法
- join, pop, reverse.
- shift,(移出首元素),unshift(插到首位)
- sort(将元素以字符串形式排序,排列数字时可传递自定义比较函数).
6. 正则表达式
- 所有部分必须紧密排列在一起,不支持注释和空白.
7. 精简的JS
- 函数是顶级对象. 函数是有词法作用域的闭包.
- 基于原型继承的动态对象. 对象是无类别的.
- 对象字面量和数组字面量. 列出对象的组成部分,就能创建出对象. JSON
8. JS 糟粕
- 全局变量. 由于没有链接器,JS依赖于全局变量进行连接. 所有编译单元的顶级变量被撮合到全局对象的公共命名空间中.
- 函数外的var声明; 全局对象(window)上添加的属性; 直接使用未经声明的变量(隐式的全局变量).
- 作用域. 代码块({})不会创建新的作用域,所以变量应定义在函数头部.
- js 中只有全局作用域和函数作用域, 而并没有局部作用域.
- 容易导致错误的自动插入分号机制.
- 含有大量语言并未使用的保留字.
- 16位的Unicode. 会把(表示单个字符的)一对字符认为是两个不同的字符.
- typeof 辨别不出null, 也区分不出数组和对象.
- parseInt,会遇到非数字时停止(并返回之前解析出的数字). 首字符为0时按八进制求值,最好加上基数参数.
- 浮点数, 二进制浮点数不能精确地处理十进制小数(
0.1+0.2!=0.3
). 通过将其转化为整数来避免该问题. - NaN.
-
typeof NaN = ‘number’, NaN !== NaN.
使用isNaN.
- 使用isFinite判断值是否可做数字, 而为了避免它的试图转换. 首先判断值的typeof是否为number.
-
- 对象. 永远不会是真的空对象. 因为可从原型链中取得成员属性.
- == 会试图去强制转换类型, 应始终使用===.
- 避免使用with, 其原意是快捷地访问对象的属性.
-
function语句会被提升, 无论其在哪里定义,都会被移动到定义所在作用域的顶层,从而放宽了先定义后使用的要求.
- 应避免使用function语句,而使用function表达式.
避免: function foo(){} ==> 而使用: var foo = function foo(){}.
- 避免使用类型的包装对象.
- �例如:
new Boolean, new Object, new Array
, 应使用{}, []
代替.
- �例如:
- void是运算符.
- 接收一个运算数并返回undefined, 应避免使用它.
- new 操作符: 基于该函数的原型创建一个新对象,
- 在忘记使用new时带来风险(不会创建对象,且this 被绑定到全局对象上). 应避免使用new.