0%
这是一片思考的空间 -- arthinking
Spring 重构&代码整洁之道 软件设计 JVM 并发编程 数据结构与算法 分布式 存储 网络 微服务 设计模式
Java技术栈 - 涉及Java技术体系

Javascript笔记 - javascript中的函数

C语言中函数是顶级的实体,而Java中函数时被对象包装起来的。

在Javascript中,函数本身也是一个对象。

在Javascript中,函数的使用方式如下:

① 赋值给一个变量 ② 作为一个对象的属性 ③ 作为函数的一个参数 ④ 作为函数的返回结果 ⑤ 用字面量来创建

1、函数对象:

1.1、创建函数的方法:

1.1.1、通过Function构造器创建函数:

var funcName = new Function( [argname1, [... argnameN,]] body );

参数列表中可以任意多个参数,参数列表后面是函数体。

var add = new Function("a","b","return(a+b)");
alert(add(1,2));

1.1.2、通过function字面量来创建函数:

var add = function(a, b){
return a + b
}

其实这里的function关键字实际上会调用Function来创建一个对象,跟第一种创建方式本质上是一样的,只是书写方式方便了。

在Javascript中创建方法的本质是,举上面的例子来说,就是为全局对象添加额一个属性,该属性的名字为add,而属性的值是一个对象。

本质上很创建字符串是没什么区别的,都是给全局对象动态的增加一个新的 属性而已:

var message ="Hello World!!!";

下面通过一个给函数添加属性的例子来说明函数也是一个独立的对象:

var add = function(a, b){
return a + b
}
var message ="Hello World!!!";
//函数既对象
function add(a, b){
return a + b;
}
add.message = "add method!";
alert(add);
alert(add.message);
alert(add(1,2));

1.2、函数的参数:

在Javascript中,你可以传递给任意多个参数传递给一个函数,既使这个函数声明时并没有制定形式参数:

function addMethod(a, b, c){
alert(c); //输出为undefined
return a + b + c;
}
alert(addMethod(1 ,2)); //输出为NaN

上面的例子中,形式参数为3个,而我们传递了2个参数,结果可见,第三个参数为undefined。事实上,Javascript在处理函数的参数时,与其他的编译型语言不一样,解释器传递的是一个类似于数组的内部值arguments,这个在函数对象生成的时候就被初始化为了,数组的每个元素都初始化为undefined。这样才可以在函数中处理那些没有被传进来的参数(都初始化为了undefined),从而实现可以处理任意个数的参数。

现在我们在上面的addMethod(a, b, c);方法中传入多余的参数,也是可以的:

alert(addMethod(1 ,2 ,3,4)); //输出为6

下面是一个统计的例子,在函数中通过arguments获得传入进来的参数数组:

//函数的参数的例子
function sum(){
var result = 0;
for(var i=0; i < arguments.length; i++){
if(isNaN(arguments[i])){
alert("输入了非法的参数!");
return;
} else {
result += arguments[i];
}
}
return result;
}
alert(sum("a",2,3));

1.3、函数的作用域:

Javascript中的变量的作用于是函数体内有效而无块作用域,这点要与其他的面向对象的语言区分开来。

下面展示了在块中定义的一个变量在块之外还可以访问的例子:

function printInfo(){
for(var i=0; i<10; i++){

}
alert(i);

}
printInfo();

在for循环执行后,外部仍然可以访问for循环中的i变量,只因为它们都在同一个函数中。

Javascript的函数是在局部作用域内运行的,在函数的局部作用域内可以访问函数体外部的变量和函数。

Javascript的作用域为词法作用域:在定义时(词法分析时)就确定下来了,而不是在执行时确定的,如下例:

//词法作用域
var testCon = "arthinking";
function testScope(){
alert(testCon); //输出undefined
var testCon = "Jason";
alert(testCon); //输出Jason
}
testScope();

在上面的例子中,词法分析结束后,在构造作用域链时,会将函数内定义的var变量testCon放入该作用域链,所以在testScope()函数内testCon变量都是可见的。由于在函数作用域中,第一行时testCon是未定义的,所以输出了undefined。

如果不在testScope中重新定义testCon变量,则第一个输出为"arthinking":

var testCon = "arthinking";
function testScope(){
alert(testCon); //输出arthinking
testCon = "Jason";
alert(testCon); //输出Jason
}
testScope();

1.4、函数上下文:

与传统的面向对象编程不一样,一般的面向对象编程如Java,方法只能依赖于某个存在的类,所以方法的上下文总是在某个类中的。

而在Javascript中,函数也是一种对象。理解这一点对于理解函数式编程尤为重要,即在函数式编程中,函数被认为是处于第一级位置的。

而在Javascript等函数式编程中,函数内的this指针式可以变化的。函数可以作为一个对象的方法,也可以作为另一个对象的方法,也就是函数本身是独立的。

可以使用Function对象的call或者apply函数来修改函数的上下文:

使用call和apply修改函数上下文:

下面是一个例子:

//函数的上下文
var user1 = {
name : "arthinking"
}
var user2 = {
name : "Jason"
}
function getUsername(){
return this.name;
}
alert(getUsername.call(user1));
alert(getUsername.apply(user2));

传递多个参数的例子:

如果有需要改变this的函数需要传入多个参数,call方法需要在传入的对象之后跟若干个参数,以逗号分隔。apply方法则是在传入的对象之后跟一个参数数组,下面是使用例子:

var user1 = {
name : "arthinking"
}
var user2 = {
name : "Jason"
}
function getUsername(){
return this.name;
}
function setUsername(firstName, lastName){
this.name = firstName + " " + lastName;
}
setUsername.apply(user1, ["arthinking", "Peng"]);
setUsername.call(user2, "Jason", "Peng");
alert(getUsername.call(user1));
alert(getUsername.apply(user2));

2、使用函数:

在Javascript中,函数的使用方式如下:

2.1、赋值给一个变量

//赋值给一个变量
function add(a, b){
return a + b;
}

var a = add;
var result = a(1, 2);
alert(result);

这里a引用了add,也就是a的值是一个函数对象(一个可执行的代码块)。

2.2、作为一个对象的属性

//赋值给一个函数属性
function add(a, b){
return a + b;
}
var obj = {
name : "An Object"
}
obj.func = add;
alert(obj.func(1, 2));

该例子和上一个例子实际上是一样的,第一个例子中的变量a其实也就是全局对象window中的一个属性。

2.3、 作为函数的一个参数

//作为函数的参数来传递
function processor1(str){
return "arthinking: " + str;
}
function processor2(str){
return "Jason: " + str;
}
function speek(str, handler){
alert(handler(str));
}

speek("Good Job!", processor1);
speek("Good Job!!!", processor2);

可见,可以通过函数名来传递函数。

2.4、作为函数的返回结果

//作为函数的返回结果
function sing(){
alert("想唱就唱,要唱的响亮。");
}
function getLRC(){
return function(){
alert("想唱就唱,要唱的响亮。");
}
}
getLRC()();

注意,这里由于使用了匿名函数,返回的只是这个函数的字面值,如果想要返回该匿名函数的执行结果,则需要按照上面的形式书写:getLRC()(); 第一个括号表示调用getLRC本身,此时返回值为函数,第二个括号表示调用这个返回的函数。相当于这样:

//作为函数的返回结果
function sing(){
alert("想唱就唱,要唱的响亮。");
}
function getLRC(){
return sing;
}
getLRC()();

当然,如果不是匿名函数,也可以在返回的时候写括号:

//作为函数的返回结果
function sing(){
alert("想唱就唱,要唱的响亮。");
}
function getLRC(){
return sing();
}
getLRC();

欢迎关注我的其它发布渠道

订阅IT宅
内功修炼
Java技术栈
Java架构杂谈是IT宅精品文章公众号,欢迎订阅:
📄 网络基础知识:两万字长文50+张趣图带你领悟网络编程的内功心法 📄 HTTP发展史:三万长文50+趣图带你领悟web编程的内功心法 📄 HTTP/1.1:可扩展,可靠性,请求应答,无状态,明文传输 📄 HTTP/1.1报文详解:Method,URI,URL,消息头,消息体,状态行 📄 HTTP常用请求头大揭秘 📄 HTTPS:网络安全攻坚战 📄 HTTP/2:网络安全传输的快车道 📄 HTTP/3:让传输效率再一次起飞 📄 高性能网络编程:图解Socket核心内幕以及五大IO模型 📄 高性能网络编程:三分钟短文快速了解信号驱动式IO 📄 高性能网络编程:彻底弄懂IO复用 - IO处理杀手锏,带您深入了解select,poll,epoll 📄 高性能网络编程:异步IO:新时代的IO处理利器 📄 高性能网络编程:网络编程范式 - 高性能服务器就这么回事 📄 高性能网络编程:性能追击 - 万字长文30+图揭秘8大主流服务器程序线程模型
📄 Java内存模型:如果有人给你撕逼Java内存模型,就把这些问题甩给他 📄 一文带你彻底理解同步和锁的本质(干货) 📄 AQS与并发包中锁的通用实现 📄 ReentrantLock介绍与使用 📄 ReentrantReadWriteLock介绍与使用 📄 ReentrantLock的Condition原理解析 📄 如何优雅的中断线程 📄 如何优雅的挂起线程 📄 图解几个好玩的并发辅助工具类 📄 图解BlockingQueue阻塞队列
📄 消息队列那么多,为什么建议深入了解下RabbitMQ? 📄 高并发异步解耦利器:RocketMQ究竟强在哪里? 📄 Kafka必知必会18问:30+图带您看透Kafka
📄 洞悉MySQL底层架构:游走在缓冲与磁盘之间 📄 SQL运行内幕:从执行原理看调优的本质 📄 洞悉Redis技术内幕:缓存,数据结构,并发,集群与算法