《JavaScript高级程序设计》(第3版)读书笔记 第3章

  • 函数名不能使用关键字(typeof不行但typeOf能够,区分大小写)
  • 标识符就是指变量、函数、属性的名字,或者函数的参数。javascript

    1. 第一个字符必须是一个字母、下划线(_)或者一个美圆符号($)
    2. 其余字符能够是字母、下划线、美圆符号或者数字
  • 严格模式 在严格模式下ECMAScript3中的一些不肯定的行为将获得处理,并且某些不安全的操做也会抛出错误。
// 在整个脚本顶部添加
"use strict";

// 在函数内部上方使用
function deSmoething() {
    "use strict";
    // 函数体
}
  • 关键字和保留字

break,do,instanceof,typeof,case,else,new,var,catch,finally,return,viod,continue,for,switch,while,debugger*,function,this,with,default,if,throw,delete,in,tryjava

  • 在函数中不用var声明变量会变成全局变量,有效但不推荐
function test() {
    message = 'hi'; //全局变量
}
test()
alert(message);     // 'hi'
  • 数据类型 ECMAScript中有5种简单数据类型(也称为基本数据类型):Undefined,Null,Boolean,Number,String 还有1种复杂数据类型Object。本质上Object是由一组无序名值对组成的。
  • typeof返回值"undefined","boolean","string","number","object","function" (typeof null 返回结果是"object") (typeof 是操做符不是函数所以最好不要用括号)

Undefined

// 在使用bar声明变量但未对其加以初始化,这个变量的值就是undefined
var message;
alert(message == undefined); // true
alert(message === undefined); // true

// 下面这个变量并无声明
// var age
alert(age); // 报错

// 对于还没有声明过的变量,只能执行一项操做,即便用typeof操做符检测其数据类型
// 对未经声明的变量使用delete不会致使错误,但这样没什么意义,并且在严格模式下确实会致使错误
var message;
// var age
alert(typeof message);     // "undefined"
alert(typeof age);        // "undefined"

// 二者都返回了undefined这个结果有其逻辑上的合理性。
// 由于虽然这两种变量从技术角度看有本质区别,但实际上不管对那种变量也不可能执行真正的操做

Null 只有一个值的数据类型(null)

// 从逻辑角度来看,null值表示一个空对象指针,而这也正是使用typeof 操做符检测null值时会返回"object"的缘由
var car = null;
alert(typeof car);         //    "object"

//  若是定义变量准备在未来用于保存对象,那么最好将该变量初始化为null而不是其余值
if (car != null) {
   ...
}

// 实际上 undefined值时派生自null值得,所以ECMA-262规定他们的相等性测试要返回true
alert(null == undefined)     // true
  • Boolean 类型
// 虽然Boolean类型的字面值只有2个,但ECMAScript中全部类型的值都有与这两个Boolean值等价的值。
// 任何非零的数字值(包括无穷大)返回true   0 和 NaN 返回false
var message = "Hello world!";
Boolean(message);  // true
Boolean(0);        // false
Boolean(NaN);      // false
Boolean(Infinity)  // true
Boolean({})        // true
Boolean(null)      // false

Number 类型

// 八进制若是字面值中的数值超出了范围,那么前导零将被忽略,后面的数值将被当作十进制数值解析
var num1 = 070;         // 八进制的56
var num2 = 079;         // 无效的八进制数值 解析为79
var num3 = 08;          // 无效的八进制数值 解析为8

// 八进制字面量在严格模式下是无效的,会致使支持该模式的JavaScript引擎抛出错误

// 十六进制字面值的前两位必须是0x 后跟任何十六进制数字 (0~9 和 A~F)其中字母不区分大小写
// 在进行算术计算时,全部八进制和十六进制表示的数值最终都将被转换成十进制数值

// 鉴于JavaScript中数值的保存方式,能够保存正零和负零,二者被认为相等
0 === -0  // true

浮点数。

  • 因为保存浮点数值须要的内存空间是保存证书值得两倍,所以ECMAScript会不失时机的将浮点数值转换为整数值。
  • 浮点数值的最高精度是17位小数,但在进行计算时其精度远远不如证书。例如 0.1 + 0.2 = 0.30000000000000004
  • 这是使用基于IEEE754数值的浮点计算的通病,ECMASscript并不是独此一家

数值范围。

  • 因为内存限制,ECMAScript可以表示的最小值保存在Number.MIN_VALUE 在大多数浏览器中这个值时 5e-324
  • 最大值在Number.MAX_VALUE中在大多数浏览器中,这个值是1.7976931348623157e+308;
  • 若是计算中超过了范围将会自动转换成Infinity或-Infinity
  • Infinity没法参加计算
  • 想要肯定某个值是否是有穷的(在范围内)使用isFinite()函数
  • 访问Number.NEGATIVE_INFINITY 和 Number.POSITIVE_INFINITY 也能够获得负和正Infinity的值
Infinity + -Infinity // NaN

var res = Number.MAX_VALUE + Number.MAX_VALUE;
alert(isFinite(res));     //    false

NaN 即非数值(Not a Number)

  • 是一个特殊的数值,这个数值用于表示一个原本要返回数值的操做数值未返回数值的状况
  • 任何涉及NaN的操做都会返回NaN
  • NaN与任何值都不相等,包括NaN自己
  • isNaN() 在接受到一个值以后,会尝试将其转换为数值,而任何不能转换为数值的值都会致使整个函数返回true
isNaN(NaN);     // true
isNaN(10);     // false
isNaN('10');     // false
isNaN('blue'); // ture
isNaN(true);     // false 能够被转换成数值1

// isNaN()也适用于对象,在基于对象调用isNaN()函数时,会首先调用对象的valueOf()方法,而后肯定该方法返回的值是否能够转换为数值
// 若是不能,则基于整个返回值再调用toString()方法,再测试返回值
// 整个过程也是ECMAScript中内置函数和操做符的通常执行流程
var o = {
    valueOf: function() {
        return '10';
    }
}
isNaN(o)    // false 先返回'10' 再转换成数值10

var o2 = {
    valueOf: function() {
        return 'blue';
    }
}
isNaN(o2)    // ture 先返回'blue' 不能转换成数值
  • 数值转换。Number() parseInt() parseFloat()
Number(true) // 1
Number(false) // 0
Number(null) // 0
Number(undefined) // NaN
Number('') // 0

// 若是字符串中包含有效的十六进制格式,例如"0xf" 则将其转换为相同大小的十进制数值
// Number()也适用于对象,在基于对象调用isNaN()函数时,会首先调用对象的valueOf()方法,而后肯定该方法返回的值是否能够转换为数值
// 若是不能,则基于整个返回值再调用toString()方法,再测试返回值

// 因为Number()函数在转换字符串时比较复杂并且不够合理,所以在处理整数和浮点数的时候更一般的作法是用parseInt() 和 parseFloat()函数
Number('    0.2') // 0.2
Number('   02blue') // NaN
parseInt('   02blue') // 2
Number('    0.2blue;') // NaN
parseFloat('    0.2blue;') // 0.2

// 在使用parseInt() 解析八进制字面量的字符串时 ECMAScript 3 和 5 存在分歧
// ECMAScript 3 认为56(八进制), ECMAScript 5 认为是70(十进制)
var num = parseInt("070")
// ECMAScript 5 中 parseInt()已经不具备解析八进制值得能力 即便在非严格模式下也是如此
// 能够为这个函数提供第二个参数避免歧义
var num = parseInt("0xAF", 16);  // 175
// 实际上,若是有第二个参数,字符串能够不带“0x”
var num1 = parseInt("AF", 16);   // 175
var num2 = parseInt("AF");   // NaN

// parseFloat() 从第一个字符开始解析,直到末尾,除非解析到碰见一个无效的浮点数为止
// 第一个小数点有效以后就无效
// parseFloat() 只能解析十进制值,所以它没有用的哥参数指定基数的用法,非十进制数值都返回0
// 若是字符串包含的是一个可解析为整数的数,会返回一个整数
parseFloat("1234blue"); // 1234
parseFloat("0xA"); // 0
parseFloat("22.24.5")  // 22.24
parseFloat("0980.5"); // 980.5
parseFloat("3.125e"); // 31250000

String 类型

  • 单引号和双引号均可以用,单首尾必须匹配

字符字面量

  • \n 换行
  • \t 制表
  • \b 退格
  • \r 回车
  • \f 进纸
  • \ 斜杠
  • \' 单引号
  • \" 双引号
  • \xnn 以十六进制代码nn表示的一个字符(其中n为0~F) 例如 \x41表示"A"
  • \unnnn 以十六进制代码nnnn表示一个Unicode字符 (其中n为0~F) 例如 \u03a3 表示希腊字符 sigma 字符串length为1

字符串的特色

  • ECMAScript 中的字符串时不可变的,字符串一旦建立,它们的值就不能改变
  • 要改变某个变量保存的字符串,首先要销毁原来的字符串,而后再用另外一个包含新值得字符串填充该变量
var lang = "Java";
lang = lang + "Script";
/*
 * 实现这个操做的过程
 * 首先建立一个能容纳10个字符的新字符串,而后再这个字符串中填充"Java" 和 "Script"
 * 最后销毁原来的字符串"Java"和字符串"Script",由于这个两个字符串都没用了
 * 这个过程是在后台发生的,而这也是某些旧版本的浏览器 (1.0如下的Firefox IE6等)拼接字符串速度很慢的缘由所在
 */

转换为字符串

  • 使用几乎每一个值都有的toString()方法 数值、布尔值、对象和字符串值(没错,字符串也有)
  • 在不知道要转换的值是否是null或undefined的状况下,还可使用转型函数String()
// toSring()能够传一个参数:输出数值的基数 默认十进制
var num = 10;
num.toString();   // "10"
num.toString(2);  // "1010"
num.toString(8);  // "12"
num.toString(10); // "10"
num.toSring(16);  // "a" 

null.toString();  // 报错
undefined.toString() // 报错 

String(null); // "null"
String(undefined); // "undefined"

Object 类型

// 三种写法等价 但不建议省略小括号
var o = {};
var o = new Object();
var o = new Object;
o.constructor // ƒ Object() { [native code] }  就是Object()
  • 从技术角度讲,ECMA-262中对象的行为不必定适用于JavaScript中的其余对象。浏览器环境中的对象,好比BOM DOM中的对象都属于宿主中的对象

Object 的每一个实例都具备下列属性和方法es6

  1. constructor: 保存着用于建立当前对象的函数
  2. hasOwnProperty(propertyName): 用于检查给定的属性在当前对象实例中(而不是在实例的原型中)是否存在。其中propertyName必须为字符串
  3. isPrototypeOf(object): 用于检查传入的对象是不是当前对象的原型
  4. propertyIsEnumerable(propertyName): 用于检查给定的属性是否能使用for-in语句来枚举
  5. toLocaleString(): 返回对象的字符串表示,该字符串与执行环境的地区对应
  6. toString(): 返回对象的字符串
  7. valueOf(): 返回对象的字符串、数值或布尔值表示。一般与toString()方法的返回值相同
// Baz.prototype, Bar.prototype, Foo.prototype 和 Object.prototype 在 baz 对象的原型链上:
function Foo() {}
function Bar() {}
function Baz() {}

Bar.prototype = Object.create(Foo.prototype);
Baz.prototype = Object.create(Bar.prototype);

var baz = new Baz();

console.log(Baz.prototype.isPrototypeOf(baz)); // true
console.log(Bar.prototype.isPrototypeOf(baz)); // true
console.log(Foo.prototype.isPrototypeOf(baz)); // true
console.log(Object.prototype.isPrototypeOf(baz)); // true

操做符

  • 包括算数操做符、位操做符、关系操做符和相等操做符

递增和递减操做符

  • 前置型 在赋值前改变
  • 后置型 在赋值后改变
var num1 = 2;
var num2 = 20;
var num3 = --num1 + num2;         // 21
var num4 = num1 + num2;           // 21

var num1 = 2;
var num2 = 20;
var num3 = num1-- + num2;         // 22
var num4 = num1 + num2;           // 21
  • 在应用于一个包含有效数字字符串时,先将其转换为数值
  • 在应用于一个包含有效数字的字符串时,将变量的值设置为NaN,字符串变量变成数值变量
  • false,先将其转换为0
  • true,先将其转换为1
  • 浮点数,正常加减1
  • 对象,先调用vlueOf()方法,若是结果是NaN则调用toString()方法后再应用前述规则
var s1 = "2";
var s2 = "z";
var b = false;
bar f = 1.1;
var o = {
    varluOf: function() {
        return -1;
    }
};

s1++;        // 3
s2++;         // NaN
b++;        // 1
f--;         // 0.10000000000000009 浮点运算舍入错误致使
o--;        // -2

一元加减操做符

  • +号会对值自动转换成数值
  • -号会变成负数

位操做符

  • ECMAscript中的全部数值都以IEEE-754 64位格式存储,但位操做符并不直接操做64位值。而是先将64位的值转成32位的整数,而后执行操做 ,最后再将结果转回64位。
  • 32位中的前31位表示整数的值,第32位表示数值的符号,0为正数 1为负数,即为符号位
  • 正数以纯二进制格式存储,没有用到的位用0填充
  • 负数一样二进制,但使用的格式是二进制补码express

    • 求数值绝对值的二进制码
    • 求二进制反码 将0换成1 1换成0
    • 获得的二进制反码加1
  • NaN 和 Infinity 应用位操做符时,会被当作0处理,因为64位转存32位致使
  • 按位非(NOT) ~
  • 按位与(AND) &
  • 按位或(OR) |
  • 按位异或(XOR) ^ 两个数值对应位上只有一个1时才返回1,其余状况返回0
  • 左移 << 右侧以0填充
  • 有符号的右移 >> 数值向右移动,保留符号位,左侧以符号位数值填充
  • 无符号的右移 >>> 数值向右移动,不保留符号位 ,左侧以0来填充
var oldvalue = 64;
var newvalue = oldvalue >> 5;        // 二进制10

var oldvalue = 64;
var newvalue = oldvalue >>> 5;      // 二进制 10

var oldvalue = -64;
var newvalue = oldvalue >>> 5;         // 等于十进制的134217726

布尔操做符

  • 逻辑非 !
!"blue"       // false
!0            // true
!NaN          // true
!null         // true
!undefined    // true
!""           // true
!12345        // false
  • 逻辑与 && 逻辑与是短路操做,若是第一个操做数可以决定结果,就不会对第二个操做数求值数组

    var found = true;
    var result = (found && someUndefinedVariable);     // 抛出错误
    alert(result);         //    这里不会执行
    
    var found = false;
    var result = (found && someUndefindedVariable);     // 不会发生错误
    alert(result);         // false
    • 若是第一个操做数是对象,则返回第二个操做数
    • 若是第二个操做数是对象,则只有在第一个求值结果为true的时候才会返回该对象
    • 若是两个都是对象,则返回第二个操做数
    • 若是有一个操做数是null 则返回null
    • 若是有一个操做数是NaN 则返回NaN
    • 若是有一个操做数是undefined 则返回undefined
    • 若是求值的操做数没有声明会抛出错误
  • 逻辑或 || 短路操做浏览器

    var found = true;
    var result = (found || someUndefinedVariable);     // 不会抛出错误
    alert(result);         // true   
    
    var found = false;
    var result = (found || someUndefindedVariable);     // 发生错误
    alert(result);         // 这里不会执行
    • 若是第一个操做数是对象,则返回第一个操做数
    • 若是第一个操做数求值为false,则返回第二个
    • 若是两个都是对象,则返回第一个操做数
    • 若是两个操做数都是null 则返回null
    • 若是两个操做数都是NaN 则返回NaN
    • 若是两个操做数都是undefined 则返回undefined
    • 若是求值的操做数没有声明会抛出错误

乘性操做符

  • 若是参与计算的某个操做数不是数值,后台会先试用Number()转型函数
  • 若是有一个操做数是NaN 则结果是NaN
  • 若是Infinity 与 0 相乘 则结果是NaN
  • 若是Infinity 与 非0相乘 则结果是Infinity 或 -Infinity
  • Infinity * Infinity = Infinity
  • -Infinity * Infinity = -Infinity
  • Infinity / Infinity = NaN
  • 0 / 0 = NaN
  • 非0的有限数 / 0 = Infinity 或 - Infinity
  • 0 / 非0的有限数 = Infinity 或 - Infinity
  • 有限数值 % 0 = NaN
  • Infinity % Infinity = NaN
  • Infinity % 有限数值 = NaN
  • Infinity + -Infinity = NaN
  • +0 + -0 = +0
  • 两个操做数都是字符串 结果是拼接字符串
  • 若是只有一个操做数是字符串,则将另外一个操做数转换为字符串
  • +0 - -0 = +0
var num1 = 5;
var num2 = 10;
var message = "the sum of 5 and 10 is " + num1 + num2;
alert(message);    // "the sum of 5 and 10 is 510"
var message = "the sum of 5 and 10 is " + (num1 + num2);
alert(message);    // "the sum of 5 and 10 is 15"

var res6 = 5 - null;  // 5 null转为0

关系操做符

  • 若是两个操做数都是字符串,则比较两个字符串对应的字符编码值
  • 若是一个操做数是数值,则将另外一个操做数转换为一个数值进行比较
  • 若是一个操做数是对象,调用valueOf()方法,若是没有则调用toString()
  • 若是一个操做数是布尔值,则先将其转换为数值
  • 任何操做数与NaN进行关系比较,结果都是false
// 字母B的字符编码为66 而字母a的字符编码是97
// 若是要真正按照字母顺序比较字符串,就要相同大小
var result = "Brick" < "alphabet";         // true
var result = "Brick".toLowerCase() < "alphabet".toLowerCase();     // false

// "2" 字符编码是50,而"3"的字符编码是51
var result = "23" < "3";    // true
var result = "23" > 3;    // false

var result = "a" < 3;     // false 由于"a"被转换成了NaN

var result = NaN < 3;    // false
var result = NaN >= 3;    // false

相等操做符

  • 相等和不相等 == , !=安全

    • 这两个操做符都会先转换操做数(一般称为强制转型),而后再比较他们的相等性
    • 若是有一个操做数是布尔值,先转为1或0再比较
    • 若是一个是字符串另外一个是数值,则转换为数值比较
    • 对象先调用valueOf(),用获得的基本类型值按照前面规则进行比较
    • null 和 undefined 相等
    • 比较以前不能讲null和undefined转换成其余任何值
    • 若是有一个操做数是NaN,则相等操做符返回false, NaN也不等于NaN
    • 若是两个操做数都是对象,则比较他们是否是同一个对象,若是都指向同一个对象,则相等操做符返回true
  • 全等和不全等 ===, !==函数

    • 除了比较以前不转换操做数类型,全等和不全等与相等和不相等操做符没有扫码区别。
null == undefined     // true
null === undefined    // false null 和 undefined是两个基本类型

条件操做符(条件运算符、三元表达式)

variable = boolean_expression ? true_value : false_value;

赋值操做符

  • = 把右侧的值赋给左侧的变量
  • *= /= %= += -=
  • <<= 左移赋值
  • '>>=' 有符号右移赋值
  • '>>>=' 无符号右移赋值
  • 主要目的就是简化赋值操做,使用它们不能带来任何性能的提高

逗号操做符

  • 使用逗号操做符能够在一条语句中执行多个操做
  • 逗号操做符总会返回表达式中的最后一项
var num1=1, num2=2, num3=3;
var num = (5,3,2,4,0);     // num的值为0

语句

if语句

  • 推荐代码块写法

do-while 语句

  • 后测试循环语句,只有在循环体中的代码执行以后,才会测试出口条件
var i = 2;
do {
    i += 2;
    console.log(i);
} while (i < 2);

// 打印 4

while 语句

  • 前测试循环语句,在循环体内的代码被执行以前,就会对出口条件求值
var i = 2;
while (i < 2 ) {
    i += 2;
    console.log(i);
}
// 没有打印

for 语句

  • 前测试循环语句,在循环体内的代码被执行以前,就会对出口条件求值
  • 使用while循环作不到的,使用for循环一样作不到,也就是说for循环只是把循环有关的代码集中在了一个位置
  • 因为ECMAScript中不存在块级做用域,所以在循环内部定义的变量也能够在外部访问到
// 无限循环
for (;;) {
    ...
}

for-in 语句

  • for-in语句是一种精准迭代语句,能够用来枚举对象的属性
  • 循环输出的属性名的顺序是不可预测的,根据浏览器而异
  • 迭代对象为null或undefined会抛出错误, ECMAScript 5 更正了这一行为,再也不抛出错误,而是不执行语句

label语句

  • 加标签的语句通常都要与for语句等循环语句配合使用
var i, j;

loop1:
for (i = 0; i < 3; i++) {      //The first for statement is labeled "loop1"
   loop2:
   for (j = 0; j < 3; j++) {   //The second for statement is labeled "loop2"
      if (i == 1 && j == 1) {
         continue loop1;
      }
      console.log("i = " + i + ", j = " + j);
   }
}

// Output is:
//   "i = 0, j = 0"
//   "i = 0, j = 1"
//   "i = 0, j = 2"
//   "i = 1, j = 0"
//   "i = 2, j = 0"
//   "i = 2, j = 1"
//   "i = 2, j = 2"
// Notice how it skips both "i = 1, j = 1" and "i = 1, j = 2"
var itemsPassed = 0;
var i, j;

top:
for (i = 0; i < items.length; i++){
  for (j = 0; j < tests.length; j++){
    if (!tests[j].pass(items[i])){
      continue top;
    }
  }

  itemsPassed++;
}
var i, j;

loop1:
for (i = 0; i < 3; i++) {      //The first for statement is labeled "loop1"
   loop2:
   for (j = 0; j < 3; j++) {   //The second for statement is labeled "loop2"
      if (i == 1 && j == 1) {
         break loop1;
      }
      console.log("i = " + i + ", j = " + j);
   }
}

// Output is:
//   "i = 0, j = 0"
//   "i = 0, j = 1"
//   "i = 0, j = 2"
//   "i = 1, j = 0"
// Notice the difference with the previous continue example

break和continue语句

  • break会当即退出循环
  • continue虽然也会当即退出循环,但会从循环顶部继续执行

with语句 (不推荐使用)

  • 严格模式下不能使用
  • 定义with语句的目的主要是为了简化屡次编写同一个对象的工做
  • 大量使用with语句会致使性能降低,同时也会给调式代码形成困难
var qs = location.search.substring(1);
var hostName = location.hostname;
var url = location.href;

with(location) {
    var qs = search.substring(1);
    var hostName = hostname;
    var url = href;
}

switch语句

  • 经过为每一个case后面都添加一个break语句,就能够避免同时执行多个case
  • 假如确实须要混合集中情形,不要忘了添加注释,说明有意省略了break关键字
  • switch语句在比较值时使用的是全等操做,所以"10"不等于10
switch (i) {
    case 25:
        // 合并两种状况
    case 35:
        alert("25 or 35");
        break;
    case 45:
        alert("45");
        break;
    default:
        alert("Other");
}

函数

  • return语句能够不带任何返回值,函数在中止执行后返回undefined
  • 推荐的作法是要么让函数始终都返回一个值要么永远都不要返回值,不然会给调试带来麻烦
  • 严格模式对函数有一些限制,不然会抛出错误oop

    • 不能把函数命名为eval或arguments
    • 不能把参数命名为eval或arguments
    • 不能出现两个命名参数同名的状况

理解参数

  • ECMAScript函数不介意传递进来多少个参数,也不在意数据类型,即使定义接受两个参数,调用时也未必要传两个
  • 缘由是ECMAScript中的参数在内部是用一个数组来表示的,在函数体内能够经过arguments对象来访问这个参数数组
  • arguments对象只是与数组相似,它并非Array的实例,能够用方括号语法访问每一个元素,也有length属性
  • 函数体内部能够arguments[0],arguments[1]...不显式地使用参数,说明ECMAScript函数的一个重要特色:命名的参数只提供便利性,但不是必需的,解析器不会验证参数名
  • 函数体内能够直接使用arguments[1] = 10 来给参数赋值,但严格模式下会报错不能使用

没有重载

  • ECMAScript函数不能像传统意义上那样实现重载,而在其余语言中能够为一个函数编写两个定义,只要这两个定义的签名(接受的参数的类型和数量)不一样便可
  • 若是在ECMAscript中定义了两个名字相同的函数,则该名字只属于后定义的函数
相关文章
相关标签/搜索