《javascript语言精粹》学习笔记2
2013-03-27 00:00
134 查看
来到第四章。
函数:前面说过,函数就是对象,特点就是还能够被调用。
因为函数是对象,所以它们可以像任何其他的值一样被使用。函数可以保存在变量,对象和数组中。函数可以被当作参数传递给其他函数,函数也可以返回函数,也可以拥有方法。
包括保留字function,函数名(可省略,无名称为匿名函数),包围在括号中的参数和包围在花括号中的函数主体。
除了接受形式参数外,每个函数默认接受两个附加的参数,this和arguments。js有四种调用模式:方法调用模式,函数调用模式,构造器调用模式和apply调用模式。这些模式在如何初始化关键参数this上存在差异。
当一个函数为对象的一个属性时,我们称之为方法。当方法被调用时,this绑定到该对象。
注意:这样调用函数,this 被绑定到全局对象。一个解决方案是:如果该方法定义一个变量并把它赋值为this,那么内部函数就可以通过那个变量访问到this,按照约定,那个变量命名为that。
原文不推荐,后面有更好的替代方式。
apply方法让我们构建一个参数数组传递给调用函数。接收两个参数,第一个是要绑定给this的值,第二个就是一个参数数组。
实际就是把一个原本不是属于自己的方法或函数用在自己身上,这就是apply的意思。
注意:arguments只是一个类似数组的对象,不是一个真正的数组,只是拥有一个length的属性,但它没有任何数组的方法。书说后面会有它的后果,我们拭目以待。
一个函数总会返回一个值,如果没有指定则为undefined。
(感觉自己很没注意差错控制检验这方面的事情TT)
这一部分跟C++差不多,有throw就要有对应的catch。catch到的error要对应throw出来的东西。例子跑出的是一个对象,则catch的参数对应一个对象,然后把对应的信息输出来。
书上例子先理解下。
下面这个扩充在后面会经常用到的喔!
通过给Function.prototype增加方法来是的该方法对所有函数可用。我们下次给对象增加方法时就不用键入prototype这几个字符,省掉一点麻烦(不是很懂。。其实是不是看起来帅点,优雅一点呢)
因为基本类型的原型是公用结构,所以在类库混用时务必小心。一个保险的做法是指在确定没有该方法时才添加它。
不多说,觉得里面一个遍历DOM树的函数写的不错。
书里还提到了一个概念叫尾递归优化,意思是如果一个函数返回自身递归调用的结果,那么调用的过程会被替换为一个循环,它可以显著提高速度。然后又说js没有提供尾递归优化(坑爹呢~~)。
这一方面好像之前看过的几道前端面试题目就有这个的内容,基础要掌握啊!!
重新找回以前的一篇博文来看,才发现以前都没怎么理解,这次看了几个钟,有些收获。墙裂建议看看:http://blog.csdn.net/rommel1/article/details/7804973
总结一下就是,像javascript这样的解释型语言,基本分为两个阶段,编译期与运行期。在预编译阶段,它是用函数来划分作用域,然后逐层为其以 var 声明的变量与函数定义开辟内存空间,再然后对var变量进行特殊处理,统统赋初始值为undefined。
在研究上文的过程中,又看到类属性和实例属性的概念,又找了一篇文章,感觉说的还比较全面。最重要的一点:动态共有属性优于原型属性。即:如果二者都定义了同一属性,则最终的属性值以动态公有属性为准。附链接:http://evanwukong.blog.163.com/blog/static/134836495201141752910780/
看了这些,书里的例子已是浮云。
闭包的好处是内部函数可以访问定义他们的外部函数的参数和变量(除了this和arguments)
之前的Quo构造器没什么意义,可以直接访问到的属性为什么要用一个getter方法去访问呢?
所以下面的方式比较推荐。
这个quo无需在前面加new,即使quo已经返回,但get_status仍可以访问到quo对象的status属性。
下面是一个设置DOM节点为黄色,然后把它渐变为白色的函数。
一个糟糕的例子,
这个例子的本意是想每一个节点alert出不同的值,,但是由于绑定的数是变量i本身,而不是函数在构造时变量i的值。
改良一下,
上面的程序把i通过形参i传递进去,用到的不是外面的变量i本身,所以解决了以上那个问题。
说的是概念吧。大家知道ajax的概念就差不多我觉得。
利用函数包装成模块,提供接口却隐藏状态与实现的函数或对象。
我们想给String增加deentityify方法,用来寻找字符窜中的HTML字符实体并把他们替换为对应的字符。这就需要保存字符实体及其对应字符。我们不能在全局变量中保存,保存在函数内部又会带来运行时的内耗,以为每次执行该函数时该字面量都会被求值一次。理想方法就是放进闭包里。
[b]注意最后一行的括号,用()运算法立刻调用我们刚刚构造出来的函数。因为返回的是函数,要让调用deentityfy函数执行代码,所以要加括号。
模块模式的一般形式是:一个定义了私有变量和函数的函数,利用闭包创建可以访问私有变量和函数的特权函数,最后返回这个特权函数,或者把他们保存到一个可访问到的地方。
让函数返回this就可以实现优雅的级联。
就是说一个原本的函数加上一个传递给它的参数,变成一个新的函数,不是很懂有什么用?
这个要根据具体情况来做,比如把一些后面需要用到的结果保存在一个数组,后面用到直接取值,就不用再去计算了。
函数:前面说过,函数就是对象,特点就是还能够被调用。
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.记忆
这个要根据具体情况来做,比如把一些后面需要用到的结果保存在一个数组,后面用到直接取值,就不用再去计算了。
相关文章推荐
- 《JavaScript语言精粹》学习笔记(函数(2))
- 《JavaScript语言精粹》学习笔记
- 《javascript语言精粹》学习笔记(一)
- 《JavaScript语言精粹》学习笔记——8.方法
- 《JavaScript语言精粹》学习笔记
- 《JavaScript语言精粹》学习笔记(对象)
- 《JavaScript语言精粹》学习笔记——5.继承
- 《JavaScript语言精粹》学习笔记
- 《JavaScript语言精粹》学习笔记
- 《JavaScript语言精粹》学习笔记
- 《JavaScript语言精粹》学习笔记——2.语法
- 《JavaScript语言精粹》学习笔记——附录A.毒瘤
- 《JavaScript语言精粹》学习笔记——附录B.糟粕
- 《JavaScript语言精粹》学习笔记(函数(1))
- 《JavaScript语言精粹》学习笔记——3.对象
- 《JavaScript语言精粹》学习笔记
- 《JavaScript语言精粹》学习笔记(函数(3))
- 《javascript语言精粹》学习笔记3
- 《JavaScript语言精粹》学习笔记——6.数组
- 《javascript语言精粹》学习笔记1