前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >函数表达式

函数表达式

作者头像
奋飛
发布2019-08-15 09:52:12
4620
发布2019-08-15 09:52:12
举报
文章被收录于专栏:Super 前端Super 前端

下述内容主要讲述了《JavaScript高级程序设计(第3版)》第7章关于“函数表达式”。

一、回顾

定义函数的方式有两种:第一种是“函数声明”,另一种就是“函数表达式”。 “函数声明”会被提升,意味着把函数声明放在调用它的语句后面。 示例1:

代码语言:javascript
复制
a();    // a
b();    // TypeError: b is not a function
function a() {
    console.log("a");
}
var b = function() {
    console.log("b");
};

声明本身会被提升,而包含函数表达式在内的赋值并不会被提升。 函数提升的关键,就是理解函数声明与函数表达式之间的区别。 了解更多的变量提升问题,请查看JavaScript提升(你不知道的JavaScript) 示例2:

代码语言:javascript
复制
if(true) {
    function sayHi() {
        console.log("Hi, Jerry!")
    }
} else {
    function sayHi() {
        console.log("Hi, Tang!");
    }
}
sayHi();
// 在chrome、firefox下输出:Hi, Jerry!
// 在Safari下输出:Hi, Tang!

示例3:

代码语言:javascript
复制
var sayHi;
if(true) {
    sayHi = function() {
        console.log("Hi, Jerry!")
    }
} else {
    sayHi = function() {
        console.log("Hi, Tang!");
    }
}
sayHi();
// 全部输出:Hi, Jerry!

示例4:

代码语言:javascript
复制
function sayHi() {
    console.log("Hi, Jerry!")
}
function sayHi() {
    console.log("Hi, Tang!");
}
sayHi(); // 全部输出:Hi, Tang!

说明:后面的函数声明可以覆盖前面的。

二、递归

示例5:

代码语言:javascript
复制
function factorial(num) {
    if(num <= 1) {
        return 1;   // 书写递归函数,尽量要先写结束条件
    } else {
        return num * factorial(num-1);  // num--
    }
}
factorial(4);   // 24
代码语言:javascript
复制
var anotherFactorial = factorial;
factorial = null;
anotherFactorial(3);    // TypeError: factorial is not a function

原因:在调用anotherFactorial()时,由于必须执行factorial(),而factorial已经不再是函数,所以就会导致错误。

代码语言:javascript
复制
function factorial(num) {
    if(num <= 1) {
        return 1;
    } else {
        return num * arguments.callee(num-1);   // num--
    }
}

注意:在严格模式下,不允许使用arguments.callee

示例6 – 具名函数:

代码语言:javascript
复制
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:

代码语言:javascript
复制
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事件,并输入对应的顺序号。):

代码语言:javascript
复制
<ul>
    <li>第一条</li>
    <li>第二条</li>
    <li>第三条</li>
    <li>第四条</li>
</ul>

每个函数的作用域中都保存着活动对象,所以它们引用的都是一个变量i。

代码语言:javascript
复制
var list = document.getElementsByTagName("li");

for(var i = 0, len = list.length; i < len; i++) {
    list[i].onclick = function() {
        console.log(i); // 4
    }
}

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

代码语言:javascript
复制
for(var i = 0, len = list.length; i < len; i++) {
    list[i].addEventListener("click", (function(i) {
        return function() {     // function(i)
            console.log(i);
        }
    })(i))
}

更巧妙的方法

代码语言:javascript
复制
list.onclick = function() {
    console.log($(this).prevAll().length);
}

四、this对象

this对象在运行时基于函数的执行环境绑定。 在全局函数中,this等于window,当函数作为某个对象的方法调用时,this等于当前对象。 JavaScript中的this(你不知道的JavaScript)

示例9:

代码语言:javascript
复制
var name = "window";
var object = {
    name: "current object",
    getName: function() {
        return this.name;
    }
};
object.getName();   // current object
代码语言:javascript
复制
var name = "window";
var object = {
    name: "current object",
    getName: function() {
        return function() {
            return this.name;
        }
    }
};
object.getName()();  // window
// var getNameByWindow = object.getName(); 
// getNameByWindow();
代码语言:javascript
复制
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:

代码语言:javascript
复制
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:

代码语言:javascript
复制
for(var i = 0; i < 10; i++) {}
var i;  // 被忽略,变量提示
console.log(i); // 10
代码语言:javascript
复制
(function() {
    for(var i = 0; i < 10; i++) {}
})();
var i;
console.log(i);  // undefined

七、私有变量

JavaScript函数作用域,使得函数中定义的变量,都可以被认为是私有变量。 示例12 –构造函数模式:

代码语言:javascript
复制
function Person(name) {
    this.getName = function() {
        return name;
    };
}

var p1 = new Person("Jerry");
var p2 = new Person("Tang");
p1.getName();
p2.getName();

每个实例都会创建上述同样的方法

示例13 – 静态私有变量:

代码语言:javascript
复制
(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的实例。

代码语言:javascript
复制
var getSingle = function(fn) {
    var result;
    return function() {
        return result || (result = fn.apply(this, arguments));
    };
};

// 测试
function testSingle(){}
getSingle(testSingle)() === getSingle(testSingle)();    // true

所以,要视情况而定,选择何种方式创建私有变量!

本文参与 腾讯云自媒体分享计划,分享自作者个人站点/博客。
原始发表:2016年07月23日,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 一、回顾
  • 二、递归
  • 三、闭包
  • 四、this对象
  • 五、内存溢出
  • 六、块级作用域
  • 七、私有变量
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档