函数表达式
来源:互联网 发布:cctv13网络电视 编辑:程序博客网 时间:2024/03/29 04:45
下述内容主要讲述了《JavaScript高级程序设计(第3版)》第7章关于“函数表达式”。
一、回顾
定义函数的方式有两种:第一种是“函数声明”,另一种就是“函数表达式”。
“函数声明”会被提升,意味着把函数声明放在调用它的语句后面。
示例1:
a(); // ab(); // TypeError: b is not a functionfunction a() { console.log("a");}var b = function() { console.log("b");};
声明本身会被提升,而包含函数表达式在内的赋值并不会被提升。
函数提升的关键,就是理解函数声明与函数表达式之间的区别。
了解更多的变量提升问题,请查看JavaScript提升(你不知道的JavaScript)
示例2:
if(true) { function sayHi() { console.log("Hi, Jerry!") }} else { function sayHi() { console.log("Hi, Tang!"); }}sayHi();// 在chrome、firefox下输出:Hi, Jerry!// 在Safari下输出:Hi, Tang!
示例3:
var sayHi;if(true) { sayHi = function() { console.log("Hi, Jerry!") }} else { sayHi = function() { console.log("Hi, Tang!"); }}sayHi();// 全部输出:Hi, Jerry!
示例4:
function sayHi() { console.log("Hi, Jerry!")}function sayHi() { console.log("Hi, Tang!");}sayHi(); // 全部输出:Hi, Tang!
说明:后面的函数声明可以覆盖前面的。
二、递归
示例5:
function factorial(num) { if(num <= 1) { return 1; // 书写递归函数,尽量要先写结束条件 } else { return num * factorial(num-1); // num-- }}factorial(4); // 24
var anotherFactorial = factorial;factorial = null;anotherFactorial(3); // TypeError: factorial is not a function
原因:在调用anotherFactorial()时,由于必须执行factorial(),而factorial已经不再是函数,所以就会导致错误。
function factorial(num) { if(num <= 1) { return 1; } else { return num * arguments.callee(num-1); // num-- }}
注意:在严格模式下,不允许使用arguments.callee
示例6 – 具名函数:
var factorial = function fn(num) { if(num <= 1) { return 1; } else { return num * fn(num-1); // num-- }}var anotherFactorial = factorial;factorial = null;anotherFactorial(3); // 6
三、闭包
形式:在一个函数内部创建另外一个函数。
定义:指有权访问另一个函数作用域中的变量的函数。
当函数可以记住并访问所在的词法作用域,即使函数是在当前词法作用域之外执行,这时就产生了闭包。
JavaScript作用域闭包(你不知道的JavaScript)
示例7:
function createComparisonFunction(propertyName) { return function(obj1, obj2) { var value1 = obj1[propertyName], value2 = obj2[propertyName]; return value1 - value2; }}var p1 = { age: 25 };var p2 = { age: 26 };var compareAge = createComparisonFunction("age");var result = compareAge(p1, p2);if(result === 0) { console.log("一样大!")}else if(result > 0) { console.log("p1大!")}else { console.log("p2大!");}compareAge = null; // 释放内存
作用:一般来讲,函数执行完毕后,局部活动对象就会被销毁,内存中仅保存全局作用域。
上述createComparisonFunction执行完毕,其作用域会被销毁,但其活动对象仍然保存在内存中,直到匿名函数被销毁后。
解释:每个执行环境都有一个表示变量的对象–变量对象。作用域链本质是一个指向变量对象的指针列表,它只引用但不实际包含变量对象!!!
建议:过多的闭包可能会导致内存占用过多,建议只在绝对必要时使用闭包。
示例8–经典面试题(为每个li绑定click事件,并输入对应的顺序号。):
<ul> <li>第一条</li> <li>第二条</li> <li>第三条</li> <li>第四条</li></ul>
每个函数的作用域中都保存着活动对象,所以它们引用的都是一个变量i。
var list = document.getElementsByTagName("li");for(var i = 0, len = list.length; i < len; i++) { list[i].onclick = function() { console.log(i); // 4 }}
通过创建另一个匿名函数强制让闭包的行为符合预期。
for(var i = 0, len = list.length; i < len; i++) { list[i].addEventListener("click", (function(i) { return function() { // function(i) console.log(i); } })(i))}
更巧妙的方法
list.onclick = function() { console.log($(this).prevAll().length);}
四、this对象
this对象在运行时基于函数的执行环境绑定。
在全局函数中,this等于window,当函数作为某个对象的方法调用时,this等于当前对象。
JavaScript中的this(你不知道的JavaScript)
示例9:
var name = "window";var object = { name: "current object", getName: function() { return this.name; }};object.getName(); // current object
var name = "window";var object = { name: "current object", getName: function() { return function() { return this.name; } }};object.getName()(); // window// var getNameByWindow = object.getName(); // getNameByWindow();
var name = "window";var object = { name: "current object", getName: function() { var that = this; return function() { return that.name; } }};object.getName()(); // current object
五、内存溢出
JavaScript对象和Dom对象循环引用,导致内存不能释放,内存溢出!
示例10:
var element = document.getElementById("id"); // Dom对象var id = element.id; // JavaScript对象element.onclick = function() { console.log(id);};element = null;
必须记住:闭包会引用包含函数的整个活动对象,而其中包含着element。即使闭包不直接引用element,包含函数的活动对象中也仍然会保存一个引用。因此有必要把element设置为null,解除对DOM对象的引用,顺利减少其引用数,收回占用内存。
六、块级作用域
JavaScript词法作用域(你不知道的JavaScript)
示例11:
for(var i = 0; i < 10; i++) {}var i; // 被忽略,变量提示console.log(i); // 10
(function() { for(var i = 0; i < 10; i++) {}})();var i;console.log(i); // undefined
七、私有变量
JavaScript函数作用域,使得函数中定义的变量,都可以被认为是私有变量。
示例12 –构造函数模式:
function Person(name) { this.getName = function() { return name; };}var p1 = new Person("Jerry");var p2 = new Person("Tang");p1.getName();p2.getName();
每个实例都会创建上述同样的方法
示例13 – 静态私有变量:
(function() { var name = ""; Person = function(value) { name = value; }; Person.prototype.getName = function() { return name; }})();var p1 = new Person("Jerry");var p2 = new Person("Tang");p1.getName();p2.getName();
代码得到了复用,但是所有实例返回相同值
模块模式:创建的每个单例都是Object的实例。
var getSingle = function(fn) { var result; return function() { return result || (result = fn.apply(this, arguments)); };};// 测试function testSingle(){}getSingle(testSingle)() === getSingle(testSingle)(); // true
所以,要视情况而定,选择何种方式创建私有变量!
- 函数表达式
- 函数表达式
- 函数表达式
- 函数表达式
- 函数表达式
- 函数表达式
- 函数声明&函数表达式
- 函数声明与函数表达式
- 函数声明和函数表达式
- 函数表达式 和函数声明
- 函数声明与函数表达式
- 函数声明与函数表达式
- 函数声明与函数表达式
- 函数声明与函数表达式
- 函数声明和函数表达式
- 函数表达式和函数声明
- 函数声明和函数表达式
- 函数声明与函数表达式
- Web服务器 Apache URL重写
- android启动界面(SplashActivity)
- 习题3-2 分子量 UVa1586
- POJ - 3279 Fliptile
- shell中if条件字符串、数字比对,[[ ]]和[ ]区别
- 函数表达式
- HDU5727 Necklace
- linux ftp命令入门学习(新手适用)
- Java之内部类语法详解(附源码)
- 使用QProxyStyle定制QMenu (二)
- Nyist201 作业题
- css样式表
- Codeforces 682B贪心
- Android 图片的异步加载(有缓存机制)