JavaScript的一等公民 — 函数

原文地址:

https://software.intel.com/zh-cn/articles/javascript-first-class-citizen-function/

此文是对博客的笔记和扩充,参考了JavaScript高级程序设计。

1、简单的JavaScript函数

1)常见的函数声明方式有以下两种:

function myfunc(args) { ... }

var myfunc = function(args) { ... }

两种函数有一些区别:第一种声明方式,可以把函数声明放到调用它的语句后面。但第二种,则不可以。原因是第二种声明方式严格意义上并不是函数的声明而是一个函数表达式。

myfunc1(); // 能够正常调用,因为myfunc1采用直接声明的方式

function myfunc1() {

}

myfunc2(); // 出错 TypeError: undefined is not a function

var myfunc2 = function() {

};

2)JavaScript函数也直接或间接地支持递归,比如斐波那契函数:

function fib(n) {

if (n == 1 || n == 2) {

return 1;

} else {

return fib(n - 2) + fib(n - 1);

}

}

3)在JavaScript的函数可以处理变长参数,在函数内部都拥有一个名为arguments的局部变量,它是一个类数组(array-liked)的对象,里面包含了所有调用时传入的参数,有length属性表示参数的个数。例如:

function test() {

alert(arguments.length);

}

test(1); // 1

test(1, ‘a‘); // 2

test(true, [], {}); // 3

2、函数进阶

1)匿名函数和嵌套函数

没有名称的函数成为匿名函数(Anonymouse Function)。也叫λ函数,匿名函数的name属性是空字符串。

函数内部声明函数,成为嵌套函数(Nested Function),潜逃函数作用域为整个父函数。

var myfunc = function(args) { ... }这种声明方式也算是一种匿名函数。

匿名函数常被用来防止全局环境污染。

例:

(function() { // 匿名函数

function log(msg) {

console.log(msg);

}

// 其他代码

}()); // 立即执行

如果需要暴露一些接口的话有如下几种方法:

var mylib = (function(global) {

function log(msg) {

console.log(msg);

}

log1 = log;  // 法一:利用没有var的变量声明的默认行为,在log1成为全局变量(不推荐)

global.log2 = log;  // 法二:直接在全局对象上添加log2属性,赋值为log函数(推荐)

return {  // 法三:通过匿名函数返回值得到一系列接口函数集合对象,赋值给全局变量mylib(推荐)

log: log

};

}(window));

2)高阶函数

a.函数作为参数:

function negative(n) {

return -n; // 取n的相反值

}

function square(n) {

return n*n; // n的平方

}

function process(nums, callback) {

var result = [];

for(var i = 0, length = nums.length; i < length; i++) {

result[i] = callback(nums[i]); // 对数组nums中的所有元素传递给callback进行处理,将返回值作为结果保存

}

return result;

}

var nums = [-3, -2, -1, 0, 1, 2, 3, 4];

var n_neg = process(nums, negative);

// n_neg = [3, 2, 1, 0, -1, -2, -3, -4];

var n_square = process(nums, square);

// n_square = [9, 4, 1, 0, 1, 4, 9, 16];

在process中,起先并不知道callback是什么,直到传递进去参数后,才直到到底要进行何种操作。

b.函数作为返回值

function generator() {

var i = 0;

return function() {

return i++;

};

}

var gen1 = generator(); // 得到一个自然数生成器

var gen2 = generator(); // 得到另一个自然数生成器

var r1 = gen1(); // r1 = 0

var r2 = gen1(); // r2 = 1

var r3 = gen2(); // r3 = 0

var r4 = gen2(); // r4 = 1

以上其实是一个闭包。

3)一个常用的类比来解释闭包和类(Class)的关系:类是带函数的数据,闭包是带数据的函数。

闭包中使用的变量有一个特性,就是它们不在父函数返回时释放,而是随着闭包生命周期的结束而结束。比如像上一节中generator的例子,gen1和gen2分别使用了相互独立的变量i(在gen1的i自增1的时候,gen2的i并不受影响,反之亦然),只要gen1或gen2这两个变量没有被JavaScript引擎垃圾回收,他们各自的变量i就不会被释放。

在JavaScript编程中,不知不觉就会使用到闭包,闭包的这个特性在带来易用的同时,也容易带来类似内存泄露的问题。例如:

var elem = document.getElementById(‘test‘);

elem.addEventListener(‘click‘, function() {

alert(‘You clicked ‘ + elem.tagName);

});

这段代码的作用是点击一个结点时显示它的标签名称,它把一个匿名函数注册为一个DOM结点的click事件处理函数,函数内引用了一个DOM对象elem,就形成了闭包。这就会产生一个循环引用,即:DOM->闭包->DOM->闭包...DOM对象在闭包释放之前不会被释放;而闭包作为DOM对象的事件处理函数存在,所以在DOM对象释放前闭包不会释放,即使DOM对象在DOM tree中删除,由于这个循环引用的存在,DOM对象和闭包都不会被释放。可以用下面的方法可以避免这种内存泄露:

var elem = document.getElementById(‘test‘);

elem.addEventListener(‘click‘, function() {

alert(‘You clicked ‘ + this.tagName); // 不再直接引用elem变量

});

上面这段代码中用this代替elem(在DOM事件处理函数中this指针指向DOM元素本身),让JS运行时不再认为这个函数中使用了父类的变量,因此不再形成闭包。

4)类构造函数

参考:http://zjzhome.sinaapp.com/article-content.php?id=29

3、JavaScript的妖怪

1)Function类

在JavaScript运行时中有一个内建的类叫做Function,用function关键字声明一个函数其实是创建Function类对象的一种简写形式,所有的函数都拥有Function类所有的方法,例如call、apply、bind等等,可以通过instanceof关键字来验证这个说法。

既然Function是一个类,那么它的构造函数就是Function(它本身也是Function类的对象),应该可以通过new关键字来生成一个函数对象。第一个妖怪来了,那就是如何用Function类构造一个函数。Function的语法如下:

new Function ([arg1[, arg2[, ... argN]],] functionBody)

其中arg1, arg2, ... argN是字符串,代表参数名称,functionBody也是字符串,表示函数体,前面的参数名称是可多可少的,Function的构造函数会把最后一个参数当做函数体,前面的都当做参数处理。

var func1 = new Function(‘name‘, ‘return "Hello, " + name + "!";‘);

func1(‘Ghostheaven‘); // Hello, Ghostheaven!

以上方法就通过Function构造了一个函数,这个函数跟其他用function关键字声明的函数一模一样。

看到这儿,很多人可能会问为什么需要这样一个妖怪呢?“存在的即是合理的”,Function类有它独特的用途,你可以利用它动态地生成各种函数逻辑,或者代替eval函数的功能,而且能保持当前环境不会被污染。

2)自更新函数(Self-update Function)

在很多语言中,函数一旦声明过就不能再次声明同名函数,否则会产生语法错误,而在JavaScript中的函数不仅可以重复声明,而且还可以自己更新自己。自己吃自己的妖怪来了!

function selfUpdate() {

window.selfUpdate = function() {

alert(‘second run!‘);

};

alert(‘first run!‘);

}

selfUpdate(); // first run!

selfUpdate(); // second run!

这种函数可以用于只运行一次的逻辑,在第一次运行之后就整个替换成一段新的逻辑。

JavaScript的一等公民 — 函数

时间: 2024-10-08 13:58:37

JavaScript的一等公民 — 函数的相关文章

JavaScript世界的一等公民 - 函数

http://www.cnblogs.com/ciangcic/p/3526957.html 简介在很多传统语言(C/C++/Java/C#等)中,函数都是作为一个二等公民存在,你只能用语言的关键字声明一个函数然后调用它,如果需要把函数作为参数传给另一个函数,或是赋值给一个本地变量,又或是作为返回值,就需要通过函数指针(function pointer).代理(delegate)等特殊的方式周折一番.而在JavaScript世界中函数却是一等公民,它不仅拥有一切传统函数的使用方式(声明和调用),

JavaScript世界的一等公民—— 函数

简介 在很多传统语言(C/C++/Java/C#等)中,函数都是作为一个二等公民存在,你只能用语言的关键字声明一个函数然后调用它,如果需要把函数作为参数传给另一个函数,或是赋值给一个本地变量,又或是作为返回值,就需要通过函数指针(function pointer).代理(delegate)等特殊的方式周折一番. 而在JavaScript世界中函数却是一等公民,它不仅拥有一切传统函数的使用方式(声明和调用),而且可以做到像简单值一样赋值.传参.返回,这样的函数也称之为第一级函数(First-cla

scala 学习笔记(07) 一等公民的函数

在scala中一切皆对象,一切皆函数,函数跟Int,String.Class等其它类型是处于同等的地位,换句话说,使用函数跟使用普通的类型一样,没什么区别,因此: 1.函数可以赋值给变量,可以当参数传递 def helloWorld(msg: String): Unit = { println("Hello," + msg) } def foo = { println("foo") } def main(args: Array[String]) { //函数赋值给变

我是世界第一等:函数

1995年5月,美国程序员Brendan Eich只用了10天,完成了javascript的设计,函数是这门语言的第一等公民.一经推出,网景公司迅速统治了整个浏览器市场. 1997年12月,中国歌手刘德华表示不服,于是写了一首用闽南语唱的歌,叫做<我是世界第一等>.可惜的是,刘德华现在还是连个Hello World都不会写. 心疼华仔…… 不过还是要回到主题,为啥我们常说,函数是javascript世界的第一等呢? 一般的编程语言,第一类对象都具备如下几个特点: 可以通过字面量创建 可被赋于变

JavaScript笔记(二):函数只是一种对象

上一篇随笔提到JS中有两种数据类型:原始类型和对象类型,但是我们还没有提到函数.实际上函数也是一种对象,准确地说函数应该叫做函数对象.下面从对象开始说起. 1. 对象和函数 最简单的对象,是这样的: var obj = {}; 我们创建了一个空对象.说它空是因为它没有任何自定义属性,但是实际上它还是有一些默认属性的,这些属性是从它的原型对象继承来的,比如constructor.toString和valueOf等.除此之外,ECMA-262中还定义了对象的一些内置属性,这些属性对JS语言来说是不可

编程语言中,何谓“一等公民”

http://blog.csdn.net/hikaliv/article/details/4588163 一等公民,First-Class Citizen,此处 Citizen 可换为 object / value / entity 等词.所谓"一等公民"即指在程序中可无限使用的对像(相比其它对像)."一等公民"者可以: 表示为匿名字面值 存储于变量中 存储于数据结构中 作为函数的参数传递 作为函数的返回值 在运行时构造

Javascript读书笔记:函数定义和函数调用

定义函数 使用function关键字来定义函数,分为两种形式: 声明式函数定义: function add(m,n) { alert(m+n); } 这种方式等同于构造一个Function类的实例的方式: var add = new Function("m", "n", "alert(m+n);"); Function类构造方法的最后一个参数为函数体:"alert(m+n);",前面的都是函数的形参,参数必须是字符串形式的:&

JavaScript高级程序设计之函数

函数实际上是对象,每个函数都是Function类型的实例. 函数是引用类型. 函数名实际上是一个指向函数对象的指针,不会与某个函数绑定. // 这种写法更能表达函数的本质 var sum = function(num1, num2) { return num1 + num2; }; var anotherSum = sum; sum = null; console.log(anotherSum(10, 20)); // 30 console.log(sum(10, 20)); // typeer

JavaScript中的回调函数

在学习JavaScript的过程中遇到了很多,使用到回调函数的例子,出现了许多疑问,就由一个栗子开始吧: 在JavaScript中接触的第一个回调函数是在setInterval()和setTimeout()中出现的: 1 var num = 10; 2 3 var interValId = setInterval(function (){ 4 console.log(num); 5 num--; 6 if(num==0){ 7 clearInterval(interValId); 8 } 9 }