《JavaScript高级程序设计》读书笔记

第三章 基本概念

数据类型:

  • 5种基本类型: Undefined,Null,Boolean,Number,String
  • 1种复杂类型:Object

isNaN() 方法

数字类型转换

Number() 适用于任何数据类型的转换
parseInt()parseFloat() 专门用于把字符串转换为数值

parseInt & parseFloat

parseInt(012, 10) 第二个参数表示转换的数字本身为十进制数
parseFloat() 只能转换十进制数

++操作符

1
2
3
4
5
6
var s1 = “2”;
s1++;
console.log(s1) // 3
var s2 = “2”;
s2 = s2 + 1;
console.log(s2) // “21”

比较字符串

比较字符串大小时,比较的是字符编码值的大小

for的作用域

1
for(var i = 0; i < 10; i++) {}

等价于

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
var i;
for(i = 0; i < 10; i++) {}
```
由于不存在块级作用域, 所以在外面可以访问到
### 函数的参数
用`arguments`对象访问参数数组
```javascript
function howManyArgs() {
console.log(arguments.length);
}
howManyArgs(12, 34) // => 2
howManyArgs() // => 0

没有重载

可以通过检查传入函数的参数作出不同的反应,来模仿方法的重载

第四章 变量、作用域和内存问题

传递参数

访问变量有按值和按引用两种方式,而参数只能按值传递

1
2
3
4
5
6
7
8
9
function setName(obj) {
obj.name = "Nicholas";
obj = new Object();
obj.name = "Greg";
}
var person = new Object();
setName(person);
console.log(person.name); // => "Nicholas"

证明参数为按值引用(可以理解为局部变量)

没有块级作用域

1
2
3
4
for (var i = 0; i < 10; i++) {
console.log(i);
}
console.log(i); // 10

第五章 引用类型

常见引用类型

  • Object
  • Array
  • Date
  • RegExp
  • Function
  • 基本包装类型
    • Boolen
    • Number
    • String
  • 单体内置对象
    • Global
    • Math

检测数组

Array.isArray()

支持的浏览器:IE9+、Firefox 4+、Safari 5+、Opera 10.5+和Chrome

数组的迭代方法 ES5

  • every
  • filter
  • forEach
  • map
  • some

以上方法均不改变原数组的值

数组的归并方法 ES5

  • reduce
  • reduceRight

函数声明与函数表达式

1
2
3
4
5
// 函数声明
console.log(sum(10, 20)); // 30
function sum(num1, num2) {
return num1 + num2;
}
1
2
3
4
5
// 函数表达式
console.log(sum(10, 20)); // Unexpected identifier
var sum = function (num1, num2) {
return num1 + num2;
}

arguments.callee

1
2
3
4
5
6
7
8
9
function factorial (num) {
if (num <= 0) return 1;
return num * factorial(num - 1);
}
function factorial (num) {
if (num <= 0) return 1;
return num * arguments.callee(num - 1);
}

第二种方式解除了函数与函数名的耦合

函数的属性

1
2
3
4
function sum (num1, num2) {
return num1 + num2;
}
console.log(sum.length) // 2 即希望收到的命名参数的个数

apply() 方法和 call() 方法

区别:

1
2
3
4
5
sum.apply(context, arguments);
// arguments 可以是数组,也可以是参数的枚举
sum.call(context, arguments);
// arguments 必须是参数的枚举

作用:扩充作用域

1
2
3
4
5
6
7
8
9
10
11
window.color = 'red';
var o = { color: 'blue' };
function sayColor () {
console.log(this.color);
}
sayColor(); // red
sayColor.call(this); // red
sayColor.call(window); // red
sayColor.call(o); // blue

bind() 方法

1
2
var objectSayColor = sayColor.bind(o);
objectSayColor(); // blue

toPrecision() 方法

1
2
3
4
var num = 99;
console.log(num.toPrecision(1)); // 1e+2
console.log(num.toPrecision(2)); // 99
console.log(num.toPrecision(3)); // 99.0

charAt()charCodeAt() 方法

1
2
3
var stringValue = 'hello world';
console.log(stringValue.charAt(1)); // e
console.log(stringValue.charCodeAt(1)) // 101

slice()substr()substring() 方法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
console.log(stringValue.slice(3)); // lo world
console.log(stringValue.substring(3)); // lo world
console.log(stringValue.substr(3)); // lo world
console.log(stringValue.slice(3, 7)); // lo w
console.log(stringValue.substring(3, 7)); // lo w
console.log(stringValue.substr(3, 7)); // lo worl
// substr 第二个参数表示返回字符的个数
console.log(stringValue.slice(-3)); // rld
console.log(stringValue.substring(-3)); // hello world
console.log(stringValue.substr(-3)); // rld
console.log(stringValue.slice(3, -4)); // lo w
console.log(stringValue.substring(3, -4)); // hel
console.log(stringValue.substr(3, -4)); //

replace() 方法

第二个参数为字符串

1
2
3
4
5
6
var text = 'cat, bat, sat, fat';
var result = text.replace('at', 'ond');
console.log(result); // 'cond, bat, sat, fat'
result = text.replace(/at/g, 'ond');
console.log(result); // 'cond, bond, sond, fond'

第二个参数为函数时,传入函数的参数依次为:模式的匹配项匹配项在字符串中的位置原始字符串

split() 方法

分隔符可以是字符串,也可以是一个RegExp对象

第二个参数为返回数组的长度

单体内置对象

不需要显示地实例化内置对象,就可以直接使用其实例

Math

1
2
3
4
5
6
7
var max = Math.max(3, 54, 32, 16);
console.log(max); // 54
// 找到数组中的最小值
var values = [3, 54, 32, 14];
var min = Math.min.apply(Math, values);
console.log(min); // 3

第六章 面向对象的程序设计

主要分为两部分,见如下两篇文章

创建对象的几种方法

继承

第七章 函数表达式

函数声明:
特征: 函数声明提升

1
2
3
function a() {
}

函数表达式:
特征: 相当于赋值

1
var a = function() {}

闭包

概念:指有权访问另一个函数作用域中的变量的函数

1
2
3
4
5
6
7
8
9
10
11
12
13
14
function createComparisonFunction(propertyName) {
return function(obj1, boj2) {
var value1 = obj1[propertyName];
var value2 = obj2[propertyName];
if(value1 < value2) {
return -1;
} else if (value1 > value2) {
return 1;
} else {
return 0;
}
};
}

在另一个函数(createComparisonFunction)内部定义的函数(匿名函数)会将外部函数的活动对象添加到它的作用域链中。

因此,内部匿名函数的作用域链中,会包含外部函数的活动对象

1
2
3
4
5
6
7
var compareNames = createComparisonFunction('name');
// createComparisonFunction 的活动对象依然没有被销毁,因为其在被内部函数的作用域链引用
var result = compareNames({name: 'a'}, {name: 'b'});
compareNames = null;
//解除对匿名函数的引用(释放内存)

闭包与变量

闭包所保存的是整个变量对象,而不是某个特殊的变量

1
2
3
4
5
6
7
8
9
function createFunctions() {
var result = new Array();
for(var i = 0; i < 10; i++) {
result[i] = function() {
return i;
}
}
return result;
}

最后 result 中的每个值都是 10

可以通过创建另一个匿名函数强制让闭包的行为符合预期

1
2
3
4
5
6
7
8
9
10
11
function createFunctions() {
var result = new Array();
for (var i = 0; i < 10; i++) {
result[i] = function(num) {
return function() {
return num;
};
}(i);
}
return result;
}

this对象

匿名函数的执行环境具有全局性,因此其this通常指向window

1
2
3
4
5
6
7
8
9
10
11
12
var name = 'The window';
var object = {
name: 'My Object',
getNameFunc: function() {
return function() {
return this.name;
};
}
};
console.log(object.getNameFunc()()); // The window

通过把外部作用域中的this对象保存在一个闭包能够访问到的变量里,就可以让闭包访问该对象了,如下:

1
2
3
4
5
6
7
8
9
10
var name = 'The window';
var object = {
name: 'My Object';
getNameFunc: function() {
var that = this;
return function() {
return that.name;
};
}
};

内存泄漏

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
function assignHandler() {
var element = document.getElementById('abc');
// method 1: element 无法被清除
element.onclick = function() {
alert(element.id);
};
// method 2: element 最后被回收
var id = element.id;
element.onclick = function() {
alert(id);
}
element = null;
}

模仿块级作用域

1
2
3
(function() {
// 块级作用域
})();

多人协作时,可以通过创建私有作用域,来使用自己的变量,而不会扰乱全局作用域

私有变量

私有变量包括:

  • 函数的参数
  • 局部变量
  • 在函数内部定义的其他函数

如:

1
2
3
4
function add(num1, num2) {
var sum = num1 + num2;
return sum;
}

在函数内部有三个私有变量: num1/num2/sum
在函数内部可以访问他们,但是在函数外部则不能访问他们
如果在这个函数内部创建一个闭包,则可以创建用于访问私有变量的公有方法(称为特权方法)

1.在构造函数中定义特权方法

1
2
3
4
5
6
7
8
9
10
function MyObject() {
var privateVariable = 10;
function privateFunction() {
return false;
}
this.publicMethod = function() {
privateVariable++;
return privateFunction();
}
}

2.使用静态私有变量来实现特权方法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
(function() {
var privateVariable = 10;
function privateFunction() {
return false;
}
MyObject = function() {
};
MyObject.prototype.publicMethod = function() {
privateVariable++;
return privateFunction();
};
})();

模块模式

为单例创建私有变量和特权方法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
var singleton = function() {
var privateVariable = 10;
function privateFunction() {
return false;
}
return {
publicProperty: true,
publicMethod: function() {
privateVariable++;
return privateFunction();
}
}
}();

增强的模块模式

适合单例必须是某种类型的实例,同时还必须添加某些属性和方法对其加以增强的情况

1
2
3
4
5
6
7
8
9
10
11
12
13
14
var singleton = function() {
var privateVariable = 10;
function privateFunction() {
return false;
}
var object = new CustomType();
object.publicProperty = true;
object.publicMethod = function() {
privateVariable++;
return privateFunction();
}
return object;
}

第八章 BOM

部分内容选自A tale of two viewports

Screen Size

screen size 范围图片(需要科学上网)

screen.widthscreen.height分别获得设备宽高(设备像素)

Window Size

window size 范围图片

window.innerWidthwindow.innerHeight获得浏览器可视区域的css像素测量

window size 范围图片(Opera中)

但在Opera浏览器中
window.innerWidthwindow.innerHeight获得浏览器可视区域的设备像素测量

这意味着当我们放大或者缩小(用zomm的方式而不是拉伸)页面内容的时候
在其他浏览器中innerWidthinnerHeight是变化的,因为固定可视区域容纳的像素点变化了;
而在Opera浏览器中,由于上面两个属性代表设备的像素值,因此固定可视区域所容纳的设备像素点是不变的;

(两者都包含滚动条)

Scrolling offset

scrolling offset 范围图片

window.pageXOffsetwindow.pageYOffset包含了文档水平和垂直滚动的偏移量
(CSS像素)

Viewport

viewport 范围

document.documentElement.clientWidthdocument.documentElement.clientHeight表示视口宽高
(不包括滚动条)

element

<html> 元素范围

document.documentElement.offsetWidthdocument.documentElement.offsetHeight表示html元素尺寸

Event coordinates

pageX/Y 示意图

clientX/Y 示意图

间歇调用和超时调用

JavaScript是一个单线程序的解释器,因此一定时间内只能执行一段代码。为了控制要执行的代码,就有一个JavaScript任务队列。这些任务会按照将它们添加到队列的顺序执行。setTimeout()的第二个参数告诉JavaScript再过多长时间把当前任务添加到队列中

如果队列是空的,那么添加的代码会立即执行;如果队列不是空的,那么它就要等前面的代码执行完了以后再执行

调用setTimeout()后,该方法会返回一个数值ID,表示超时调用。这个超时调用ID是计划执行代码的唯一标识符,可以通过它来取消超时调用

一般认为,使用超时调用来模拟间歇调用是一种最佳模式

location对象

属性名 例子
hash “#contents”
host “www.wrox.com:80”
hostname “www.wrox.com”
href “http:/www.wrox.com”
pathname “/WileyCDA/“
port “8080”
protocol “http:”
search “?q=javascript”

decodeURIComponent()用来解码URL中的query值

位置操作

可以通过 location.href = "http://www.baidu.com" 改变当前加载的页面

通过修改location的其他属性也可以改变页面,并且会在历史记录中生成一条新记录

如果要避免生成记录,则可以用location.replace()方法

location.reload() // 重新加载(可能从缓存中加载)
location.reload(true) // 重新加载(从服务器重新加载)

history对象

1
2
3
4
5
6
7
8
9
10
11
12
history.go(-1);
history.go(1);
history.go(2);
history.go("baidu.com") // 跳转到最近的 baidu.com 页面
history.back();
history.forward();
if (history.length == 0) {
// 这是用户打开窗口后的第一个页面
}

第九章 客户端检测

  • 能力检测
  • 怪癖检测
  • 用户代理检测

第十章 DOM

因为NodeList是动态的,所以每一次操作都需要重新查询一遍,十分消耗性能,因此要尽量减少DOM操作

第十二章 DOM2 和 DOM3

元素大小

  1. 偏移量
    • offsetHeight
    • offsetWidth
    • offsetTop
    • offsetLeft
    • offsetParent
  2. 客户区大小
    • clientWidth
    • clientHeight
  3. 滚动大小
    • scrollHeight
    • scrollWidth
    • scrollLeft
    • scrollTop
  4. 确定元素大小
    • getBoundingClientRect()

第十三章 事件

事件流

从页面接收事件的顺序

  • 事件冒泡*
  • 事件捕获
坚持原创技术分享,您的支持将鼓励我继续创作!