javascript权威指南笔记(01)

来源:互联网 发布:大数据信用查询平台 编辑:程序博客网 时间:2024/06/05 17:47

第一、二章

  • JavaScript的5种原始类型:undefined、null、布尔值、数字和字符串。

  • JavaScript中两个非常重要的数据类型是对象和数组。

  • 字符区分大小写

  • 通过方括号定义数组元素和通过花括号定义对象属性名和属性值之间的映射关系。

  • 贷款计算器demo

第三章

3.1 数字

-9007199254740992~9007199254740992

3.1.1 整型直接量

  • JavaScript不区分整数值和浮点数值,JavaScript中的所有数字均用浮点数值表示。

  • 支持十进制、十六进制、不能使用以0为前缀的整型直接量(八进制),es6禁止

3.1.2 浮点型直接量

6.02e23 //6.02*10^23
6.02e-23 //6.02*10^-23 也可是E

3.1.3

  • JavaScript中的算术运算在溢出(overflow)、下溢(underflow)或被零整除时不会报错。当数字运算结果超过了JavaScript所能表示的数字上限(溢出),结果为一个特殊的无穷大(infinity)值,在JavaScript中以Infinity表示。同样地,当负数的值超过了JavaScript所能表示的负数范围,结果为负无穷大,在JavaScript中以-Infinity表示。无穷大值的行为特性和我们所期望的是一致的:基于它们的加、减、乘和除结果还是无穷大值(当然还保留它们的正负号)。

  • 下溢(underflow)是当运算结果无限接近于零并比JavaScript能表示的最小值还小的时候发生的一种情形。这种情况下,JavaScript将会返回0。当一个负数发生下溢时,JavaScript返回一个特殊的值“负零”。这个值(负零)几乎和正常的零完全一样,JavaScript程序员很少用到负零。

  • 被零整除在JavaScript并不报错:它只是简单的返回无穷大(Infinity)或负无穷大(-Infinity)。

  • 零除以零是没有意义的,这种整除运算结果也是一个非数字(non-a-number)值,用NaN表示。无穷大除以无穷大,给任意负数作开方运算或者算术运算符与不是数字或无法转换为数字的操作数一起使用时都将返回NaN。

  • NAN与任何值都不相等,包括她自身,所以当x为NAN时,X!=X(true)
    isFinite():在参数不是NAN,Infinity或者-Inifinity的时候返回true

1/0正无穷大 1/-0负无穷大

3.1.4 二进制浮点数和四舍五入错误

JavaScript采用IEEE-745浮点数表示法(几乎所有现代编程语言所采用),这是一种二进制表示法,可以精确地表示分数,比如 1/2 、 1/8 和 1/1024 。 遗憾的是,我们常用的分数(特别是在金融计算方面)都是十进制分数 1/10 、 1/100 等。二进制浮点数表示法并不能精确表示类似0.1这样简单的数字。

JavaScript中的数字具有足够的精度,并可以及其近似于0.1。但事实是,数字不能精确表述的确带来了一些问题。看下这段代码:

var x = .3 - .2;

var y = .2 - .1;

x == y; // ==false: 两值不相等!

x == .1; // ==false;

y == .1; // ==true;

由于舍入误差,0.3和0.2之间的近似差值实际上并不等于0.2和0.1之间的近似差值。这个问题并不只在JavaScript中才会出现,理解这一点非常重要:在任何使用二进制浮点数的编程语言中都会有这个问题。

3.1.5 日期和时间

var date = new Date();

getFullYear();
getMonth();
getDate();
getDay();
getHours();

3.2 文本

字符串(string)是一组由16位值组成的不可变的有序序列。JavaScript中并没有表示单个字符的“字符型”。要表示一个16位值,只需将其赋值给字符串变量即可,这个字符串长度为1。

类似replace()和toUpperCase()方法都返回新字符串,原字符串本身并没有发生改变。

在 ECMAScript 5 中,字符串可以当做只读数组,除了使用charAt()方法,也可以使用方括号来访问字符串中的单个字符(16位值):

s = “hello, world”;
s[0]; // ==”h”
基于Mozilla的Web浏览器(比如Firefox)很久之前就支持这种方式的字符串索引,多数现代浏览器(IE除外)也紧跟Mozilla的脚步,在 ECMAScript 5 成型之前就支持了这一特性。

3.2.4 模式匹配

  • JavaScript定义了RegExp()构造函数,用来创建表示文本匹配模式的对象。

  • 尽管RegExp并不是语言中的基本数据类型,但是它们依然具有直接量写法,可以直接在JavaScript程序中使用。在两条斜线之间的文本构成了一个正则表达式。第二条斜线之后也可以跟随一个或多个字母,用来修饰匹配模式的含义。

3.3 布尔值

下面这些值会被转换成false:

undefined

null

0

-0

NaN

“”

所有其他值,包括所有对象(数组)都会转换成true。

3.4 null undefined

null:没有值
undefinded:不存在

3.6 包装对象

我们看到字符串也同样具有属性和方法:

var s = “hello world!”;

var word = s.substring(s.indexOf(” “)+1, s.length);

字符串既然不是对象,为什么它会有属性呢? 只要引用了字符串s的属性,JavaScript就会将字符串值通过调用new String(s)的方式转换成对象,这个对象继承了字符串的方法,并被用来处理属性的引用。一旦属性引用结束,这个新创建的对象就会销毁(其实在表现上并不一定创建或销毁这个临时对象,然而整个过程看起来是这样)。

同字符串一样,数字和布尔值也具有各自的方法:通过Number()和Boolean()构造函数创建一个临时对象,这些方法的调用均是来自于这个临时对象。null和undefined没有包装对象:访问它们的属性会造成一个类型错误。

var s = “test”;

s.len = 4;

var t = s.len;

当运行这段代码时,t的值是undefined。第二行代码创建一个临时字符串对象,并给其len属性赋值为4,随即销毁这个对象。第三行通过原始的(没有被修改过的)字符串值创建一个新字符串对象,尝试读取其len属性,这个属性自然不存在,表达式求值结果是undefined。这段代码说明了在读取字符串、数字和布尔值的属性值(或方法)的时候,表现得像对象一样。但如果你试图给其属性赋值,则会忽略这个操作:修改只是发生在临时对象身上,而这个临时对象并未继续保留下来。

存取字符串、数字或布尔值的属性时创建的临时对象称做包装对象,它只是偶尔用来区分字符串值和字符串对象、数字和数值对象以及布尔值和布尔对象。你需要明白字符串、数字和布尔值是有别于对象的。

需要注意的是,可通过String(),Number()或Boolean()构造函数来显式创建包装对象:

var s = “test”;

var S = new String(s);

typeof(s); // ==”string”

typeof(S); // ==”object”

typeof可以检测给定变量的数据类型,可能的返回值有:

  1. ‘undefined’ — 这个值未定义;

  2. ‘boolean’ — 这个值是布尔值;

  3. ‘string’ — 这个值是字符串;

  4. ‘number’ — 这个值是数值;

  5. ‘object’ — 这个值是对象或null;

  6. ‘function’ — 这个值是函数;

3.7 不可变的原始值和可变的对象引用

JavaScript中的原始值(undefined、null、布尔值、数字和字符串)与对象有着根本区别。

原始值是不可更改的:任何方法都无法更改一个原始值。

对象和原始值不同,首先,对象是可变的,它们的值是可修改的;其次,对象的比较并非值的比较:即使两个对象包含相同的属性及相同的值,他们也是不相等的。

我们通常将对象称为引用类型(reference type),以此来和JavaScript的基本类型区分开来。依照术语的叫法,对象值都是引用(reference),对象的比较均是引用的比较:当且仅当它们引用同一基对象时,它们才相等。

3.8 类型转换

var n = 1 - “x”; // ==NaN:字符串”x”无法转换为数字

n + ” objects” // ==”NaN objects”:NaN转换为字符串”NaN”

“==”等于运算符在判断两个值是否相等时会做类型转换,”===”恒等运算符在判断相等时并未做任何类型转换。

3.8.2 显式类型转换

做显式类型转换最简单的方法就是使用Boolean()、Number()、String()或Object()函数。当不通过new运算符调用这些函数时,它们会作为类型转换函数进行类型转换。

Number类定义的toString()方法可以接收表示转换基数(radix)的可选参数,如果不指定此参数,转换规则将是基于十进制。同样,亦可以将数字转换为其他进制数,例如

var n = 17;

binary_string = n.toString(2); // 转换为 “10001”

octal_string = “0” + n.toString(8); // 转换为 “021”

hex_string = “0x” + n.toString(16); // 转换为 “0x11”

Number类为数字到字符串的类型转换场景定义了三个方法:

  • toFixed()根据小数点后的指定位数将数字转换为字符串,它从不使用指数计数法;
  • toExponential()使用指数计数法将数字转换为指数形式的字符串;
  • toPrecision()根据指定的有效数字位数将数字转换为字符串。如果有效数字的位数少于数字整数部分的位数,则转换成指数形式。
  • 如果通过Number()转换函数传入一个字符串,它会试图将其转换为一个整数或浮点直接量,这个方法只能基于十进制进行转换,并且不能出现非法的尾随字符。ParseInt()只解析整数,而ParseFloat()则可以解析整数和浮点数。如果字符串前缀是”0x”或者”0X”,parseInt()将其解释为十六进制数,parseInt()和parseFloat()都会跳过任意数量的前导空格,尽可能解析更多数值字符,并忽略后面的内容。如果第一个非空格字符是非法的数字直接量,将最终返回NaN;

parseInt(“3 blind mice”); // ==3

parseInt(“0xFF”); // ==255

parseIntFloat(“.1”); // ==NaN:整数不能以”.”开始

parseInt()可以接收第二个可选参数,这个参数指定数字转换的基数,合法的取值范围是2~36:

parseInt(“11”, 2); // ==3

parseInt(“077”, 10); // ==77

3.9 变量声明

如果未在var声明语句中给变量指定初始值,那么虽然声明了这个变量,但在给它存入一个值之前,它的初始值就是undefined;

应当始终使用var来声明变量。

3.10.1 函数作用域和声明提前

在一些类似C语言的编程语言中,花括号内的每一段代码都具有各自的作用域,而且变量在声明它们的代码段之外是不可见的,我们称之为块级作用域(block scope),而JavaScript中没有块级作用域。JavaScript取而代之地使用了函数作用域(function scope):变量在声明它们的函数体以及这个函数体嵌套的任意函数体内都是有定义的。

JavaScript的函数作用域是指在函数内声明的所有变量在函数体内始终是可见的。有意思的是,这意味着变量在声明之前甚至已经可用。JavaScript的这个特性被非正式地称为声明提前(hoisting),即JavaScript函数里声明的所有变量(但不涉及到赋值)都被“提前”至函数体的顶部,看一下如下代码:

var scope = “global”;

function f() {

console.log(scope); // 输出 “undefined”, 而不是”global”

var scope = “local”; // 变量在这里赋初始值,但变量本身在函数体内任何地方均是有定义的

console.log(scope); // 输出 “local”

}

你可能会误以为函数中的第一行会输出”global”,因为代码还没有执行到var语句声明局部变量的地方。其实不然,由于函数作用域的特性,局部变量在整个函数体始终是有定义的,也就是说,在函数体内局部变量覆盖了同名全局变量。尽管如此,只有在程序执行到var语句的时候,局部变量才会被真正赋值。因此,上述过程等价于:将函数内的变量声明“提前”至函数体顶部,同时变量初始化留在原来的位置:

function f() {

var scope; // 在函数顶部声明了局部变量

console.log(scope); // 变量存在,但其值是”undefined”

scope = “local”; // 这里将其初始化并赋值

console.log(scope); // 这里它具有了我们所期望的值

}

由于JavaScript没有块级作用域,因此一些程序员特意将变量声明放在函数体顶部。这种做法使得他们的源代码非常清晰地反映了真实的变量作用域。

3.10.2 作为属性的变量

当声明一个JavaScript全局变量时,实际上是定义了全局对象的一个属性。当使用var声明一个变量时,创建的这个属性是不可配置的,也就是说这个变量无法通过delete运算符删除。如果你没有使用严格模式并给一个未声明的变量赋值的话,JavaScript会自动创建一个全局变量。以这种方式创建的变量是全局对象的正常的可配置属性,并可以删除它们:

var truevar = 1; // 声明一个不可删除的全局变量

fakevar = 2; // 创建全局对象的一个可删除的属性

this.fakevar2 = 3; // 同上

delete truevar; // ==false:变量并没有被删除

delete fakevar; // ==true: 变量被删除

delete this.fakevar2; // ==true: 变量被删除

JavaScript可以允许使用this关键字来引用全局对象,却没有方法可以引用局部变量中存放的对象。这种存放局部变量的对象的特有性质,是一种对我们不可见的内部实现。

3.10.3 作用域链

JavaScript是基于词法作用域的语言:全局变量在程序中始终都是有定义的。局部变量在声明它的函数体内以及其所嵌套的函数内始终是有定义的。

如果将一个局部变量看做是自定义实现的对象的类型的话,那么可以换个角度来解读变量作用域。每一段JavaScript代码(全局代码或函数)都有一个与之相关的作用域链(scope chain)。这个作用域链是一个对象列表或者链表,这组对象定义了这段代码“作用域中”的变量。当JavaScript需要查找变量x的值的时候(这个过程称作“变量解析”(variable resolution)),它会从链中的第一个对象开始查找,如果这个对象有一个名为x的属性,则会直接使用这个属性的值,如果第一个对象中不存在名为x的属性,JavaScript会继续查找链上的下一个对象。如果第二个对象依然没有名为x的属性,则会继续查找下一个对象,以此类推。如果作用域链上没有任何一个对象含有属性x,那么认为这段代码的作用域链上不存在x,并最终抛出一个引用错误(ReferenceError)异常。

在JavaScript的最顶层代码中(也就是不包含在任何函数定义内的代码),作用域链由一个全局对象组成。在不包含嵌套的函数体内,作用域链由两个对象,第一个是定义函数参数和局部变量的对象,第二个是全局对象。在一个嵌套的函数体内,作用域链上至少有三个对象。理解对象链的创建规则是十分重要的。当定义一个函数时,它实际上保存一个作用域链。当调用这个函数时,它创建一个新的对象来存储它的局部变量,并将这个对象添加至保存的那个作用域链上,同时创建一个新的更长的表示函数调用作用域的“链”。对于嵌套函数来讲,事情变得更加有趣,每次调用外部函数时,内部函数又会重新定义一遍。因为每次调用外部函数的时候,作用域链都是不同的。内部函数在每次定义的时候都有微妙的差别——在每次调用外部函数时,内部函数的代码都是相同的,而且关联这段代码的作用域链也不相同。

作用域链的概念对于理解with语句是非常有帮助的,同样对理解闭包的概念也至关重要。

原创粉丝点击