JS的函数

JS函数的主要特点

JS的函数与Java迥异。这里把它的特殊之处归纳整理一下:

1)JS函数既是函数,又是对象,还相当于函数指针/委托。

2)JS函数可以被new,这时它变成了对象构造函数。

3)作为对象方法的JS函数是对象的属性,可以用名称文本作关键字访问,也可以被替换掉。这点类似函数指针/委托。

4)JS函数可以与其定义文本相互转化。可以打印出函数的完整定义,也可以把定义文本转成函数。这个是动态语言的优点。

5)JS函数支持嵌套。且内嵌函数可以通过变量返回被外部访问。

6)JS支持内嵌函数访问上一层作用域的变量,使得这种变量不被销毁,变成类似C语言中以static声明的静态局部变量。这个叫JS闭包。

JS函数的几种形式

我惊讶地发现,没有一种形式与Java相同。连箭头函数都不同。箭头函数倒是与C#相似。

简单形式的全局函数

JAVA/C#是静态语言,声明函数都是这样:

// 返回值类型 函数名称 (形参){函数体}
void func(String[] arguments) {
     ...
}

JS由于是动态语言,故省去了形参和返回值的类型描述,但是要用function关键字:

// function关键字 函数名称 (无须声明类型的形参){函数体}
function myFunction(a, b) {
    return a * b; // 
}

与其它语言一样,JS支持默认参数:

function myFunction(x, y = 10) {
    // y is 10 if not passed or undefined
    return x + y;
}
 
console.log(myFunction(0, 2)); // 输出 2
console.log(myFunction(5)); // 输出 15, y 参数的默认值

参数个数可变的全局函数

C#用params关键字指示可变参数,JAVA用...指示可变参数。JavaScript呢?什么都不用指示,直接用arguments对象访问。arguments对象是函数内自动产生的。例如:

function func() {
    for(let i=0; i<arguments.length; i++)
    console.log(`第${i}个参数=`, arguments[i]);
}
func();// 无输出
func(1); // 输出1行:第0个参数= 1
func(1,2);// 输出2行:第0个参数= 1;第1个参数= 2
func(1,2,3);// 输出3行

其实任何函数都相当于是可变参数的函数。声明几个形参,就从arguments中取前几个传过去:

function func(a, b) {
    console.log(a+b, arguments.length);
}
func();// 输出NaN 0,因为a,b都是undefined
func(1);// 输出NaN 1,因为b是undefined
func(1,1);// 输出2 2
func(1,1,1);// 输出2 3

声明的形参叫显示参数。其它叫隐式参数。

用Function() 构造函数定义的函数

//Function的最后一个参数是函数体,前面的参数都是形参
var myFunction = new Function("a", "b", "return a * b");
var x = myFunction(4, 3);

这个是动态语言的优点,可以把字符串化成代码。

匿名函数

函数可以作为一个对象被赋值给一个变量:

var func = function abc() {
    console.log("函数赋值给变量");
};
func();//非常像函数指针/委托

进一步,把函数名称省了,就产生了匿名函数。例如:

var func = (function () {
    console.log("匿名函数");
});
func();

自调用函数

更进一步,对于仅在一处调用的函数,不但连函数名称省了,连赋值给变量也省了,声明函数的时候就把它调用了:

(function () {
    console.log("我将调用自己!");
})();
(function (a, b) {
    console.log(a + b);
})(1, 2);

在js前台打包形成的单文件中,其主体框架就是一个自调用函数。

箭头函数

在C#和Java中称为lamda表达式。形式与C#相同。ES5标准不支持,ES6标准增加。例如:

var x=3, y=4;
const ff = (x, y) => x * y;
console.log(ff(5,5));// 输出25

形式上等价于一个匿名函数:

var x=3, y=4;
const ff = function(x, y) {
     return x * y;
}
console.log(ff(5,5));

作为对象方法的函数

这样的函数不是全局函数,是对象的一个属性值。例如:

var obj = {
    a:1,
    b:2,
    func: function () {
        return this.a + this.b;
    }
}
console.log(obj.func());         // 输出3
console.log(obj.func); // 输出 [Function: func]
console.log(obj.func.toString()); /* 输出3行: 
function () {
        return this.a + this.b;
    }
*/

相当于把函数赋值给一个变量,类似函数指针/委托。

作为对象构造函数的函数

es5及前的标准没有用class定义法的创新之举?权宜之计?反正es6标准迫于形式加上了。

常常会在各种node.js包的CJS模块中看到这么用。例如:

function Student(name, score) {
    this.name = name;
    this.score  = score;
    return this.name;
}

var x = new Student("John", 3);
console.log(x.name, "=", x.score); // 输出John = 3
console.log(Student("Rose", 5)); // 输出Rose

Student在这里既作为对象构造函数使用,也作为普通的全局函数使用。非常混乱!匪夷所思。那么Student指的是什么?对象还是函数,还是构造函数?你要log打印的话,是普通函数,不信可以用console.log(Student.toString()) 试试。

再加上this的混乱,让这个问题更加复杂。

对于下面的代码:

this.op='b';
console.log(this === globalThis);

function Student(name, score) {
    this.name = name;
    this.score  = score;
    console.log(this.op);
    return name;
}

var x = new Student("John", 3);
console.log(x.name, "=", x.score); 

console.log(Student("Rose", 5));
console.log(this.op);

Chrome的输出结果是:

Node.JS的输出结果是:

一个函数,它是作为对象构造函数还是普通的全局函数?主要看是不是在前面加了关键字new。

搞一个不伦不类的对象构造函数是难以理解的。怎么看似乎都没必要,直接用{}定义对象不行吗?例如:

function Person(name, age) {
  this.name = name;
  this.age = age;
}
var v = new Person("John", 50);
console.log(v);

var v2={name:"John", age:50};
console.log(v2);

你会看到v和v2的定义是一样的,原型链是一样的。既然如此,干嘛不用后面这种正常的对象定义式呢?不但如此,ECMAScript标准还创造了多种对象构造方式,百花齐放。

JS嵌套函数

函数内可以嵌套函数。初看难以理解,细想C#/JAVA中的委托、匿名函数不是也可以声明在一个函数内部吗,所以没什么奇怪的。例如:

function mylog() {
    var func = function() {
        console.log("abc");
    }
    func();  
}
mylog();

变成下面这种形式,从C#/JAVA的视角看起来就匪夷所思了,但是它是可以工作的:

function mylog() {
    function func() {
        console.log("abc");
    }
    func();  
}
mylog();

JS闭包

嵌套函数可以访问上一层作用域的变量。例如:

function add() {
    var counter = 0;
    function plus() {counter += 1;}
    plus();    
    return counter; 
}
var v = add();// v=1

除了访问上一层作用域的变量这一点,上面的代码换一种形式就比较容易理解了:

function add() {
    var counter = 0;
    var func = function plus() {counter += 1;}
    func();    
    return counter; 
}
var v = add();// v=1

内嵌函数可以赋值给变量,从而可以被外部访问。例如:

var add = (function () {
    var counter = 0;
    return function () {
        console.log(counter);
        return counter += 1;
	}
})();
console.log(add.toString());
add();
add();
add();

这段代码能把人绕晕,输出结果是:

重点是那个匿名函数中的变量counter居然一直存在着,它相当于C语言中以static声明的静态局部变量。由于内嵌函数可以被外部访问

JS函数与原型的关系

其它

虽然全局函数/变量是全局对象的属性,但是由于它们默认不可枚举,所以无法按属性访问,也无法修改。例如:

function func(){};
console.log(this["func"]);//输出undefined

多次用同一个名称定义函数,会导致问题。下面的代码将输出2然后输出3,而不仅仅是输出3:

function myFunction(arg1, arg2) {
    this.firstName = arg1;
    this.lastName  = arg2;
} 
var x = new myFunction("John","Doe");
function myFunction(a, b) {
    console.log(arguments.length);
}
myFunction(1, 2, 3);

在对象的方法中,this 表示该方法所属的对象。在函数中,this 表示全局对象,但在严格模式下是undefined。

js中的this是个复杂而混乱的问题,在浏览器和node.js中的表现不完全相同!

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值