JavaScript 标准参考教程-阅读总结(一)

参考:http://javascript.ruanyifeng.com/

1、字符串

1)由于 HTML 语言的属性值使用双引号,所以很多项目约定 JavaScript 语言的字符串只使用单引号,本教程遵守这个约定。当然,只使用双引号也完全可以。重要的是坚持使用一种风格,不要一会使用单引号表示字符串,一会又使用双引号表示。

2)反斜杠(\)在字符串内有特殊含义,用来表示一些特殊字符,所以又称为转义符。

如果字符串的正常内容之中,需要包含反斜杠,则反斜杠前面需要再加一个反斜杠,用来对自身转义。【"Prev \\ Next"=》"Prev \ Next"

3)字符串可以被视为字符数组,因此可以使用数组的方括号运算符,用来返回某个位置的字符(位置编号从0开始)。但是,字符串与数组的相似性仅此而已。实际上,无法改变字符串之中的单个字符

var str = 'hello';
console.log(str[0],str[5]); // h undefined
str[5] = '!';
console.log(str); // hello

4)length属性返回字符串的长度,该属性也是无法改变的。

var str = 'hello';
console.log(str.length); // 5
str.length = 3;
console.log(str.length); // 5

2、对象

var obj = { p:1, q:2 };
var props = [];
for(var p in obj) {
    props.push(p);
}
console.log(props); // ['p','q']
console.log(Object.keys(obj)); // ['p','q']

3、数组

1)数组的length属性,返回数组的成员数量。length属性是可写的。如果人为设置一个小于当前成员个数的值,该数组的成员会自动减少到length设置的值。

var arr = [ 'a', 'b', 'c' ];
arr.length // 3
arr.length = 2;
arr // ["a", "b"]

清空数组的一个有效方法,就是将length属性设为0。

var arr = [ 'a', 'b', 'c' ];
arr.length = 0; // 等价于arr = []
arr // []

 2)数组的遍历可以考虑使用for循环或while循环;数组的forEach方法,也可以用来遍历数组。

var colors = ['red', 'green', 'blue'];
colors.forEach(function (color) {
  console.log(color);
});
// red
// green
// blue

4、函数

1)函数参数如果是原始类型的值(数值、字符串、布尔值),传递方式是传值。这意味着,在函数体内修改参数值,不会影响到函数外部。

var p = 2;
function f(p) {
  p = 3;
}
f(p);
p // 2

如果函数参数是复合类型的值(数组、对象、其他函数),传递方式是传址。也就是说,传入函数的原始值的地址,因此在函数内部修改参数,将会影响到原始值。

var obj = { p: 1 };
function f(o) {
  o.p = 2;
}
f(obj);
obj.p // 2

注意,如果函数内部修改的,不是参数对象的某个属性,而是替换掉整个参数,这时不会影响到原始值。

var obj = [1, 2, 3];
function f(o) {
  o = [2, 3, 4];
}
f(obj);
obj // [1, 2, 3]
/*在函数f内部,参数对象obj被整个替换成另一个值。这时不会影响到原始值。这是因为,形式参数(o)的值实际是参数obj的地址,重新对o赋值导致o指向另一个地址,
保存在原地址上的值当然不受影响。*/

2)arguments 对象

由于 JavaScript 允许函数有不定数目的参数,所以需要一种机制,可以在函数体内部读取所有参数。这就是arguments对象的由来。arguments对象包含了函数运行时的所有参数arguments[0]就是第一个参数,arguments[1]就是第二个参数,以此类推。这个对象只有在函数体内部,才可以使用。

function f(a) {
  console.log(arguments)
}
f(1,2,3) // [1, 2, 3]

通过arguments对象的length属性,可以判断函数调用时到底带几个参数。

3)闭包

由于在 JavaScript 语言中,只有函数内部的子函数才能读取内部变量,因此可以把闭包简单理解成“定义在一个函数内部的函数”。

闭包的最大用处有两个,一个是可以读取函数内部的变量,另一个就是让这些变量始终保持在内存中,即闭包可以使得它诞生环境一直存在。注意,外层函数每次运行,都会生成一个新的闭包,而这个闭包又会保留外层函数的内部变量,所以内存消耗很大。因此不能滥用闭包,否则会造成网页的性能问题。

4)立即调用的函数表达式

有时,我们需要在定义函数之后,立即调用该函数。这时,你不能在函数的定义之后加上圆括号,这会产生语法错误。为了避免解析上的歧义,JavaScript 引擎规定,如果function关键字出现在行首,一律解释成语句。因此,JavaScript引擎看到行首是function关键字之后,认为这一段都是函数的定义,不应该以圆括号结尾,所以就报错了。解决方法就是不要让function出现在行首,让引擎将其理解成一个表达式。最简单的处理,就是将其放在一个圆括号里面

(function(){ /* code */ }());
// 或者
(function(){ /* code */ })();

两种写法都是以圆括号开头,引擎就会认为后面跟的是一个表示式,而不是函数定义语句,所以就避免了错误。这就叫做“立即调用的函数表达式”(Immediately-Invoked Function Expression),简称 IIFE。注意,上面两种写法最后的分号都是必须的。如果省略分号,遇到连着两个 IIFE,就会报错。

通常情况下,只对匿名函数使用这种“立即执行的函数表达式”。它的目的有两个:一是不必为函数命名,避免了污染全局变量;二是 IIFE 内部形成了一个单独的作用域,可以封装一些外部无法读取的私有变量。

// 写法一
var tmp = newData;
processData(tmp);
storeData(tmp);
// 写法二
(function () {
  var tmp = newData;
  processData(tmp);
  storeData(tmp);
}());
// 写法二比写法一更好,因为完全避免了污染全局变量

 5、运算符

1)任何值(包括NaN本身)与NaN比较,返回的都是false

NaN == NaN // false

2)(严格)相等运算符

两个复合类型(对象、数组、函数)的数据比较时,不是比较它们的值是否相等,而是比较它们是否指向同一个地址。

{} === {} // false
[] === [] // false
(function () {} === function () {}) // false

如果两个变量引用同一个对象,则它们相等。

var v1 = {};
var v2 = v1;
v1 === v2 // true

3)布尔运算符

3.1)取反运算符(!)

如果对一个值连续做两次取反运算,等于将其转为对应的布尔值,与Boolean函数的作用相同。这是一种常用的类型转换的写法。

!!x // 等同于Boolean(x)

3.2)且运算符(&&)

't' && '' // ""
't' && 'f' // "f"
't' && (1 + 2) // 3
'' && 'f' // ""

var x = 1;
(1 - 1) && ( x += 1) // 0
x // 1

上面代码的最后一个例子,由于且运算符的第一个运算子的布尔值为false,则直接返回它的值0,而不再对第二个运算子求值,所以变量x的值没变。

这种跳过第二个运算子的机制,被称为“短路”。有些程序员喜欢用它取代if结构

if (i) { doSomething(); } // 等价于i && doSomething();

上面代码的两种写法是等价的,但是后一种不容易看出目的,也不容易出错,建议谨慎使用

3.3)或运算符(||)

't' || '' // "t"
't' || 'f' // "t"
'' || 'f' // "f"
'' || '' // ""
var x = 1;
true || (x = 2) // true
x // 1

短路规则对这个运算符也适用。上面代码中,且运算符的第一个运算子为true,所以直接返回true,不再运行第二个运算子。所以,x的值没有改变。这种只通过第一个表达式的值,控制是否运行第二个表达式的机制,就称为“短路”

或运算符常用于为一个变量设置默认值

function saveText(text) {
  text = text || '';
} // 如果函数调用时,没有提供参数,则该参数默认设置为空字符串

3.4)圆括号的作用

圆括号(())可以用来提高运算的优先级,因为它的优先级是最高的。

4 + 5 * 6 // 34
(4 + 5) * 6 // 54
// 由于使用了圆括号,加法会先于乘法执行

运算符的优先级别十分繁杂,且都是硬性规定,因此建议总是使用圆括号,保证运算顺序清晰可读,这对代码的维护和除错至关重要

6、数据类型转换

1)强制转换

1.1)Number函数将字符串转为数值,要比parseInt函数严格很多。基本上,只要有一个字符无法转成数值,整个字符串就会被转为NaN

parseInt('42 cats') // 42
Number('42 cats') // NaN

1.2)String函数可以将任意类型的值转化成字符串

String(123) // "123"
String('abc') // "abc"
String(true) // "true"
String(undefined) // "undefined"
String(null) // "null"

String方法的参数如果是对象,返回一个类型字符串;如果是数组,返回该数组的字符串形式。

String({a: 1}) // "[object Object]"
String([1, 2, 3]) // "1,2,3"

1.3)Boolean函数可以将任意类型的值转为布尔值。

所有对象的布尔值都是true,这是因为 JavaScript 语言设计的时候,出于性能的考虑,如果对象需要计算才能得到布尔值,对于obj1 && obj2这样的场景,可能会需要较多的计算。为了保证性能,就统一规定,对象的布尔值为true

Boolean({}) // true
Boolean([]) // true

2)自动转换

由于自动转换具有不确定性,而且不易除错,建议在预期为布尔值、数值、字符串的地方,全部使用BooleanNumberString函数进行显式转换

7、错误处理机制

一旦发生错误,程序就中止执行了。JavaScript 提供了try...catch结构,允许对错误进行处理,选择是否往下执行。如果你不确定某些代码是否会报错,就可以把它们放在try...catch代码块之中,便于进一步对错误进行处理catch代码块捕获错误之后,程序不会中断,会按照正常流程继续执行下去

// a //Uncaught ReferenceError: a is not defined
// 上一行报错,程序中止执行了
try {
    a
} catch(e) {
    console.log(e); // ReferenceError: a is not defined
}
console.log('try catch'); // try catch

8、编程风格

1)全局变量

JavaScript 最大的语法缺点,可能就是全局变量对于任何一个代码块,都是可读可写。这对代码的模块化和重复使用,非常不利。

因此,建议避免使用全局变量。如果不得不使用,可以考虑用大写字母表示变量名,这样更容易看出这是全局变量,比如UPPER_CASE

2)变量声明

JavaScript 会自动将变量声明”提升“到代码块的头部。

if (!x) {
  var x = {};
}
// 等同于
var x;
if (!x) {
  x = {};
}

这意味着,变量xif代码块之前就存在了。为了避免可能出现的问题,最好把变量声明都放在代码块的头部

for (var i = 0; i < 10; i++) {
  // ...
}
// 写成如下,容易看出存在一个全局的循环变量i
var i;
for (i = 0; i < 10; i++) {
  // ...
}

另外,所有函数都应该在使用之前定义。函数内部的变量声明,都应该放在函数的头部

9、包装对象

原始类型的值,可以自动当作对象调用,即调用各种对象的方法和参数。这时,JavaScript 引擎会自动将原始类型的值转为包装对象实例,在使用后立刻销毁实例。比如,字符串可以调用length属性,返回字符串的长度

'abc'.length // 3
/* abc是一个字符串,本身不是对象,不能调用length属性。JavaScript 引擎自动将其转为包装对象,在这个对象上调用length属性。
调用结束后,这个临时对象就会被销毁。这就叫原始类型与实例对象的自动转换。*/

自动转换生成的包装对象是只读的,无法修改。所以,字符串无法添加新属性。

var s = 'Hello World';
s.x = 123;
s.x // undefined
/* 调用结束后,包装对象实例会自动销毁。这意味着,下一次调用字符串的属性时,实际是调用一个新生成的对象,
而不是上一次调用时生成的那个对象,所以取不到赋值在上一个对象的属性。
*/

10、JSON对象

JSON对象是 JavaScript 的原生对象,用来处理 JSON 格式数据。它有两个静态方法:JSON.stringify()JSON.parse()

JSON.stringify方法用于将一个值转为 JSON 字符串。该字符串符合 JSON 格式,并且可以被JSON.parse方法还原。如果对象的属性是undefined、函数或 XML 对象,该属性会被JSON.stringify过滤如果数组的成员是undefined、函数或 XML 对象,则这些值被转成null

var obj = {
  a: undefined, b: function () {}
};
JSON.stringify(obj) // "{}"

var arr = [undefined, function () {}];
JSON.stringify(arr) // "[null,null]"

JSON.parse方法用于将 JSON 字符串转换成对应的值。如果传入的字符串不是有效的 JSON 格式,JSON.parse方法将报错

posted @ 2018-04-19 16:26  Colorful_coco  阅读(174)  评论(0编辑  收藏  举报