存档

文章标签 ‘javascript精萃’

《javascript精粹》读书笔记 – 数组

2011年1月23日 没有评论

1. 数组length属性的值是这个数组的最大整数属性名加上1.它不一定等于数组里属性的个数:
[js]
var myArray = [];
alert(myArray.length); // 0

myArray[1000000] = true;
alert(myArray.length); //1000001
[/js]

2. 在数组尾部添加一个新元素:
[js]
var numbers = ['zero', 'one', 'two'];
numbers[numbers.length] = ‘three’;
numbers.push(‘four’);
[/js]

3. 删除数组元素。
js中的数组实际上就是对象,所以delete运算符就可以用来从数组中移除元素:
[js]
var numbers = ['one', 'two', 'three', 'four'];
delete numbers[2]; // ['one', 'two', undefined, 'four']
[/js]
不幸的是,那样会在数组中遗留一个空洞。 通常可以使用splice方法来删除元素:
[js]
var numbers = ['one', 'two', 'three', 'four'];
numbers.splice(2, 1); //['one', 'two', 'four']
[/js]
这个对大型数组来说可能会效率不高。

4. 当属性名是小而连续的整数时,应该使用数组;否则使用对象。

5. 判断是否是数组:
[js]
var is_array = function(value){
return value &&
typeof value === ‘object’ &&
value.constructor === Array;
};
[/js]
不幸的是,它在识别从不同的窗口(window)或帧(frame)里构造的数组时会失败。完整的测试方法如下:
[js]
var is_array = function(value){
return value &&
typeof value === ‘object’ &&
typeof value.length === ‘number’ &&
typeof value.splice === ‘function’ &&
!(value.propertyIsEnumberable(‘length’));
};
[/js]

《javascript精粹》读书笔记 – 函数

2011年1月14日 没有评论

1. 当实际参数的个数与形式参数的个数不匹配时,不会导致错误。
如果实际参数值过多了,超出的参数值将被忽略;如果实际的参数值过少,缺失的值将被替换为undefined。

2. 在javascript中一共有四种调用模式: 方法调用模式、函数调用模式、构造器调用模式和apply调用模式。这些模式在如何初始化关键参数this上存在差异。
a.方法调用模式:
当一个函数被保存为对象的一个属性时,我们称它为一个方法。方法调用模式可以使用this去访问对象,所以它能够从对象中取值或修改对象。
b.函数调用模式:
当一个函数并非一个对象的属性时,它被当作一个函数来调用。当函数以此模式调用时,this被绑定到全局变量,这是语言设计上的一个错误。(正确的情况:当内部函数被调用时,this应该仍然被绑定到外部函数的this变量。)解决方法:如果该方法定义一个变量并给他赋值为this,那么内部函数就可以通过那个变量访问到this,按照约定,给那个变量命名为 that:
[js]
//给 myObject增加一个double方法
myObject.double = function()
{
var that = this; //解决方法

var helper = function()
{
that.value = add(that.value, that.value);
}

helper(); //以函数的形式调用helper。
}

//以方法的形式调用double
myObject.double();
[/js]
c.构造器调用模式:
在一个函数前面带上new来调用,那么将创建一个隐藏连接到该函数的prototype成员的新对象,同时this将会被绑定到那个新对象上。
[js]
//创建一个名为Quo的构造函数。它构造一个带有status属性的对象
var Quo = function(String)
{
this.status = String;
}

//给Quo的所有实例提供一个名为get_status的公共方法
Quo.prototype.get_status = function()
{
return this.status;
}

//构造一个Quo实例
var myQuo = new Quo("confused"); //构造器模式调用
document.writeln(myQuo.get_status());
[/js]
按照约定,构造器函数保存在以大写格式命名的变量里。
d.Apply调用模式:
apply方法接受两个参数,第一个是将被绑定给this的值,第二个就是参数数组。它让我们构建一个参数数组并用其去调用函数。
[js]
//构造一个Quo实例
var myQuo = new Quo("confused"); //构造器模式调用
document.writeln(myQuo.get_status());

//构建一个包含两个数字的数组,并将他们相加
add = function(a, b)
{
return a+b;
}

var anArray = [3, 4];
var sum = add.apply(null, anArray); //7

//构建一个包含status成员的对象
var statusObject = {
status: "A-OK"
};

//statusObject并没有继承自Quo.prototype,但我们可以再statusObject上调用
//get_status方法,尽管statusObject并没有一个名为get_status的方法。
var status = Quo.prototype.get_status.apply(statusObject);
[/js]

3. 参数:
当一个函数被调用时,会有一个默认的参数,就是 arguments “数组”。
[js]
//构造一个将很多值相加的函数

//注意该函数内部定义的变量sum不会与函数外部定义的sum产生冲突。
//该函数只能看到内部的那个变量。
var sum = function()
{
var i, sum=0;
for(i = 0; i < arguments.length; i+=1)
{
sum += arguments[i];
}
return sum;
};

document.writeln(sum(4, 8, 15, 16, 23, 42)); //108
[/js]
注:arguments并不是一个真正的数组。它知识一个“类似数组”的对象。arguments用友一个length属性,但它缺少所有的数组方法。

4. 返回:
一个函数总是会返回一个值。如果没有置顶返回值,则返回undefined。
如果函数以构造器方式被调用,且返回值不是一个对象,则返回this(该新对象)。

5.给类型增加方法:
[js]
Function.prototype.method = function(name, func)
{
this.prototype[name] = func;
return this;
}

//给Number添加一个integer方法,来提取数字钟的整数部分
//根据正负来判断是使用Math.ceil还是Math.floor
Number.method(‘integer’, function(){
return Math[this < 0 ? 'ceil' : 'floor'](this);
});

document.writeln((-10/3).integer()); //-3

//去掉字符串两端空白的方法
String.method(‘trim’, function()
{
return this.replace(/^\s+|\s+$/g, ”);
});
[/js]
基本类型的原型是公共结构,所以在类库混用时务必小心。一个保险的做法就是只在确定没有该方法时再添加:
[js]
//有条件的增加一个方法

Function.prototype.method = function(name, func)
{
if(!this.prototype[name])
{
this.prototype[name] = func;
return this;
}
};
[/js]

6.闭包
理解内部函数能访问外部函数的实际变量而无需复制
[js]
//构造一个函数,用错误的方式给一个数组中的借点设置事件处理程序
//当点击一个节点时,按照预想应该弹出一个对话框显示节点的序号
//但他总是会显示节点的数目

var add_the_handlers = function(nodes) {
var i;
for (i = 0; i < nodes.length; i += 1) {
nodes[i].onclick = function(e) {
alert(i);
}
}
}
[/js]
add_the_handlers函数的目的是给每个事件处理器一个唯一值(i)。它未能达到目的的原因是事件处理器函数绑定了变量i,而不是函数在构造时的变量i的值
正确的写法:
[js]
//点击一个节点,将会弹出一个对话框显示节点的序号
var add_the_handlers = function (nodes) {
var i;
for (i = 0; i < nodes.length; i +=1 ) {
nodes[i].onclick = function (i) {
return funtion (e) {
return alert(e);
} ;
}(i);
}
}
[/js]
现在,定义了一个函数并立即传递i进去执行, 而不是把一个函数赋值给onclick。那个函数将返回一个事件处理器函数。这个事件处理器函数绑定的是传递进去的i的值,而不只定义在add_the_handlers函数里的i的值。那个被返回的函数被复制给onclick。

7.javascript的单例模式
Javascript的单例就是用对象字面量表示法创建的对象,对象的属性值可以是数值或函数,并且属性值在该对象的生命周期中不会发生变化。他通常作为工具为程序其他部分提供功能支持

8.模块
模块模式的一般形式是:一个定义了私有变量和函数的函数;利用闭包创建可以访问私有变量和函数的特权函数,或者把他们保存到一个可访问的地方。
[js]
String.method(‘deentityify’, function (){
var entity = {
quot: ‘"’,
lt: ‘<’,
gt: ‘>’
}

return function () {
return this.replace(/&([^&;]+);/g,
function (a, b) {
var r = entity[b];
return typeof r === ‘string’ ? r : a;
});
};
}());
[/js]
模块模式利用了函数作用域和闭包来创建绑定对象与私有成员关联,摒弃了全局变量的使用,从而缓解这个JavaScript的最为糟糕的特性之一所带来的影响

《javascript精粹》读书笔记 – 对象

2011年1月9日 没有评论

《javascript精粹》中和对象相关的内容:

1. 当尝试检索对象的一个并不存在的成员元素的值,将返回一个undefined值。可以通过“||”运算符来填充默认值:
[js]
stooge["middle-name"] //undefined
var middle = stooge["middle-name"] || "tom";
[/js]

2. 当尝试检索一个undefined值将会导致TypeError异常。可以通过“&&”运算符来避免错误:
[js]
flight.equipment //undefined
flight.equipment.model //throw "TypeError"
flight.equipment && flight.equipment.model //undefined
[/js]

3. 对象通过引用来传递。他们永远不会被拷贝
[js]
var x = stooge;
x.nickname = ‘Curly’;
var nick = stooge.nickname;
//因为x和stooge是指向同一个对象的引用,所以nick的值为’Curly’

var a={}, b={}, c={};
//a, b和c每个都引用一个<strong>不同</strong>的空对象

a=b=c={}
//a,b和c都引用<strong>同一个</strong>空对象
[/js]

4. 原型连接
通过beget方法创建一个使用原对象作为其原型的新对象
[js]
if (typeof Object.beget !== ‘function’)
{
Object.beget = function (o)
{
var F = function () {};
F.prototype = o;
return new F();
}
}

var another_stooge = Object.beget(stooge);
[/js]
注:
a. 原型连接在更新时是不起作用的
当我们对某个对象作出改变时,不会触及到该对象的原型。
b. 原型连接只有在检索值的时候才被用到
如果尝试去获取对象的某个属性值,且该对象没有此属性名,那么从它的圆形对象中获取属性值。 (如果那个原型对象也没有该属性值,那么再从它的原型中寻找,依次类推,知道该过程左后的重点Object.prototype。如果想要的属性完全不存在于原型链中,那么结果就是undefined值。这个过程称为委托
c. 原型关系是一种动态关系。
如果我们添加一个新的属性到原型中,该属性会立即对所有基于该原形创建的对象可见。

5. 反射
在一些时候下,我们需要能够在对对象完全不了解的情况下进行处理,并且在处理之前发现他们的属性和方法,这个过程称作反射(reflection)。
在判断对象是否有某一属性时,可以使用”hasOwnProperty“方法,如果对象拥有独有的属性,它将返回true。
注:hasOwnProperty方法不会检查原型链。

6. 枚举
for in 语句可以用来遍历一个对象中的所有属性名。
属性名出现的顺序是不确定的,因此要对任何可能出现的顺序有所准备。如果你想要确保属性以特定的顺序出现,最好的办法就是完全避免使用for in语句,而是创建一个数组,在其中以正确的顺序包含属性名:
[js]
//使用for in语句:
var name;
for (name in another_stooge)
{
if(typeof another_stooge[name] !== ‘function’)
{
document.writeln(name + ‘: ‘ + another_stooge[name]);
}
}

//使用for语句
var i;
var properties = [
'first-name',
'middle-name',
'last-name',
'profession'
]
for (i=0; i<properties.length; i +=1)
{
document.writeln(properties[i] + ‘: ‘ +
another_stooge[properties[i]]);
}
[/js]