您的位置:首页 > Web前端 > JavaScript

《javascript语言精粹》学习笔记2

2013-03-27 00:00 134 查看
来到第四章。

函数:前面说过,函数就是对象,特点就是还能够被调用。

1.函数对象

因为函数是对象,所以它们可以像任何其他的值一样被使用。函数可以保存在变量,对象和数组中。函数可以被当作参数传递给其他函数,函数也可以返回函数,也可以拥有方法。

2.函数字面量

<!-- lang: js -->
var add = function (a,b) {
return a + b;
}

包括保留字function,函数名(可省略,无名称为匿名函数),包围在括号中的参数和包围在花括号中的函数主体。

3.调用

除了接受形式参数外,每个函数默认接受两个附加的参数,this和arguments。js有四种调用模式:方法调用模式,函数调用模式,构造器调用模式和apply调用模式。这些模式在如何初始化关键参数this上存在差异。
实际参数个数超过形式参数个数时,超出的参数值会省略,少时缺失的值会替换为undefined。这个方面js的容忍度很高,就是不会提示。
4.方法调用模式

当一个函数为对象的一个属性时,我们称之为方法。当方法被调用时,this绑定到该对象。

<!-- lang: js -->
var myObject = {
value : 0,
increment : function (inc) {
this.value += typeof inc === 'number' ? inc : 1;
}
};

myObject.increment();  // 1
document.writeln(myObject.value);

myObject.increment(2);  // 3
document.writeln(myObject.value);

5.函数调用模式

当一个函数并非一个对象的属性时,那么它就是被当作一个函数来调用。它仅仅是一个函数而已。

<!-- lang: js -->
var sum = add(3,4);

注意:这样调用函数,this 被绑定到全局对象。一个解决方案是:如果该方法定义一个变量并把它赋值为this,那么内部函数就可以通过那个变量访问到this,按照约定,那个变量命名为that。

<!-- lang: js -->
myObject.double = function () {
var that = this;
var helper = function () {
that.value = add(that.value. that.value);
};

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

//以方法的形式调用double
myObject.double();
document.writeln(myObject.value);

6.构造器调用模式

如果在一个函数前面加上new来调用,那么背地里会创建一个链接到该函数的prototype成员的新对象,同时this会绑定到那个新对象上。

<!-- lang: 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());

原文不推荐,后面有更好的替代方式。
7.Apply调用模式

apply方法让我们构建一个参数数组传递给调用函数。接收两个参数,第一个是要绑定给this的值,第二个就是一个参数数组。

实际就是把一个原本不是属于自己的方法或函数用在自己身上,这就是apply的意思。

<!-- lang: js -->
var array = [3,4];
var sum = add.apply(null,array);//7,用了上面定义的add函数,用数组传递参数
//构造一个包含status成员的对象
var statusObject = {
status : "A-OK"
};
//statusObject 并没有继承自Quo.prototype,但我们可以在statusObject上调用get_status方法,尽管statusObject并没有
var status = Quo.prototype.get_status.apply(statusObject);//A-OK

8.参数

当函数被调用时,会得到一个"免费"配送的参数,那就是arguments数组。数组包括传递给形参的参数,也包括没有分配形式参数的多余参数。也就是说传递给形参的参数可以通过两种途径在函数中取得。

<!-- lang: js -->
var sum = function (){
var sum = 0;
for(var i = 0; i < arguments.length; i += 1){
sum += arguments[i];

}
return sum;
}
document.writeln(sum(1,2,3,4));//10

注意:arguments只是一个类似数组的对象,不是一个真正的数组,只是拥有一个length的属性,但它没有任何数组的方法。书说后面会有它的后果,我们拭目以待。

9.返回

一个函数总会返回一个值,如果没有指定则为undefined。

**如果函数调用时在前面加上了new前缀,且返回值不是一个对象,则返回this(该新对象)。**这个对应构造器调用模式。

10.异常

(感觉自己很没注意差错控制检验这方面的事情TT)
这一部分跟C++差不多,有throw就要有对应的catch。catch到的error要对应throw出来的东西。例子跑出的是一个对象,则catch的参数对应一个对象,然后把对应的信息输出来。

书上例子先理解下。

<!-- lang: js -->
var add = function (a,b) {
if (typeof a !== 'number' || typeof b !== 'number') {
throw {
name : 'TypeError',
message : 'add needs number'
};
}
return a + b;
}

var try_it = function () {
try {
add("seven");
} catch (e) {
document.writeln(e.name + ': ' + e.message);
}
}

try_it();

11.扩充类型的功能

js能够给语言的基本类型扩充功能。第三章对象的时候,我们可以通过给Object.prototype添加方法,可以让该方法对所有对象都可用。这样的方式对函数,数组,字符串,数字,正则表达式和布尔值都适用。

下面这个扩充在后面会经常用到的喔!

<!-- lang: js -->
Function.prototype.method = function (name, func) {
this.prototype[name] = func;
return this;
}

通过给Function.prototype增加方法来是的该方法对所有函数可用。我们下次给对象增加方法时就不用键入prototype这几个字符,省掉一点麻烦(不是很懂。。其实是不是看起来帅点,优雅一点呢)

因为基本类型的原型是公用结构,所以在类库混用时务必小心。一个保险的做法是指在确定没有该方法时才添加它。

<!-- lang: js -->
Function.prototype.method = function (name, func) {
if (!this.prototype[name]) {
this.prototype[name] = func;
}
return this;
}

12.递归

递归就是那意思,调用函数自身去解决它的子问题。
不多说,觉得里面一个遍历DOM树的函数写的不错。

<!-- lang: js -->
var walk_the_DOM = function walk(node, func) {
func(node);
node = node.firstChild;
while (node) {
walk(node,func);
node = node.nextSibling;
}
}

书里还提到了一个概念叫尾递归优化,意思是如果一个函数返回自身递归调用的结果,那么调用的过程会被替换为一个循环,它可以显著提高速度。然后又说js没有提供尾递归优化(坑爹呢~~)。

13.作用域

这一方面好像之前看过的几道前端面试题目就有这个的内容,基础要掌握啊!!

重新找回以前的一篇博文来看,才发现以前都没怎么理解,这次看了几个钟,有些收获。墙裂建议看看:http://blog.csdn.net/rommel1/article/details/7804973

总结一下就是,像javascript这样的解释型语言,基本分为两个阶段,编译期与运行期。在预编译阶段,它是用函数来划分作用域,然后逐层为其以 var 声明的变量与函数定义开辟内存空间,再然后对var变量进行特殊处理,统统赋初始值为undefined。
在研究上文的过程中,又看到类属性和实例属性的概念,又找了一篇文章,感觉说的还比较全面。最重要的一点:动态共有属性优于原型属性。即:如果二者都定义了同一属性,则最终的属性值以动态公有属性为准。附链接:http://evanwukong.blog.163.com/blog/static/134836495201141752910780/

看了这些,书里的例子已是浮云。
其他语言是块级作用域,但js不支持,js有的是函数作用域,所以要在顶部声明函数中可能用到的所有变量,而且产生了下面的闭包。
14.闭包

闭包的好处是内部函数可以访问定义他们的外部函数的参数和变量(除了this和arguments)
之前的Quo构造器没什么意义,可以直接访问到的属性为什么要用一个getter方法去访问呢?
所以下面的方式比较推荐。

<!-- lang: js -->
var quo = function (status) {
return {
get_status : function () {
return status;
}
};
};
//构造一个quo实例
var myQuo = quo("amazed");
document.writeln(myQuo.get_status());

这个quo无需在前面加new,即使quo已经返回,但get_status仍可以访问到quo对象的status属性。

下面是一个设置DOM节点为黄色,然后把它渐变为白色的函数。

<!-- lang: js -->
var fade = function (node) {
var level = 1;
var step = function () {
var hex = level.toString(16);
node.style.backgroundColor = '#FFFF' + hex + hex;
if (level < 15) {
level += 1;
setTimeout(step, 100);
}
};
//书中是setTimeout(step, 100);我觉得下面也可以
step();
};
fade(document.body);

一个糟糕的例子,

<!-- lang: js -->
var add_the_handleers = function (nodes) {
var i;
for (i = 0; i < nodes.length; i += 1) {
alert(i);
}
};

这个例子的本意是想每一个节点alert出不同的值,,但是由于绑定的数是变量i本身,而不是函数在构造时变量i的值。
改良一下,

<!-- lang: js -->
var add_the_handlers = function (nodes) {
var helper = function (i) {
return function (e) {
alert(i);
};
};
var i;
for (var i = 0; i < nodes.length; i += 1) {
nodes[i].onclick = helper(i);
};
}

上面的程序把i通过形参i传递进去,用到的不是外面的变量i本身,所以解决了以上那个问题。

15.回调

说的是概念吧。大家知道ajax的概念就差不多我觉得。
16.模块

利用函数包装成模块,提供接口却隐藏状态与实现的函数或对象。
我们想给String增加deentityify方法,用来寻找字符窜中的HTML字符实体并把他们替换为对应的字符。这就需要保存字符实体及其对应字符。我们不能在全局变量中保存,保存在函数内部又会带来运行时的内耗,以为每次执行该函数时该字面量都会被求值一次。理想方法就是放进闭包里。

<!-- lang: js -->
String.method('deentityify', function () {
var entity = {
quot : '"',
lt : '<',
gt : '>'
};
return function () {
return this.replace(/&([^&;]+);/g,
function (a,b) {
var r = entity;
return typeof r === 'string' ? r : a;
}
);
};
}());
document.writeln('<">'.deentityify());//<">

[b]注意
最后一行的括号,用()运算法立刻调用我们刚刚构造出来的函数。因为返回的是函数,要让调用deentityfy函数执行代码,所以要加括号。
模块模式的一般形式是:一个定义了私有变量和函数的函数,利用闭包创建可以访问私有变量和函数的特权函数,最后返回这个特权函数,或者把他们保存到一个可访问到的地方。

17.级联

让函数返回this就可以实现优雅的级联。

18.柯里化

就是说一个原本的函数加上一个传递给它的参数,变成一个新的函数,不是很懂有什么用?
19.记忆

这个要根据具体情况来做,比如把一些后面需要用到的结果保存在一个数组,后面用到直接取值,就不用再去计算了。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息