在JS中关于函数的问题还是蛮多的,对象,原型链,模块化都有它的身影,不愧是“一等公民”。
JavaScript在设计时借鉴了Scheme语言,也正是如此,函数在JS中会有如此高的等级。
创建函数
虽说函数是一等公民,但其本身依然是个对象,作为引用类型的一员,变量本身是一个指针。
let afunc = function(){
let name = 'afunc'
console.log('function name is', name)
}
function bfunc(){
let name = 'bfunc'
console.log('function name is', name)
}
所以上述两种声明方法都是OK的,唯一不同的是使用 function bfunc(){...}
会有函数声明提升(在执行函数之前会将该函数内所有的function都优先声明一遍)。所以使用function声明函数时可以在声明前调用。
也正是因为指针这件事,让函数重写这件事变得不可能,因为重写只会覆盖之前定义的函数。
但我们也可以将函数作为变量进行传递,例如下面这个对数组对象的排序:
let array = [{name:'ton', age: 21}, {name:'jack', age:20}]
function sordObj(attr){
return function(a, b){
var value1 = a[attr]
var value2 = b[attr]
if(value1 < value2) {
return -1
} else {
return 0
}
}
}
array.sort(sordObj('age'))
array.sort方法支持传入一个回调函数作为参数,但该回调函数的参数是固定的,也就是需要比较的两个值,直接传入对象显然是错误的。因此通过return的方式比较对象内部的属性。
还有一种不太推荐的书写方式:用构造函数的方式书写
let afunc = new Function('num1', 'num2', "return num1+num2")
console.log(afunc)
//ƒ anonymous(num1,num2
//) {
//return num1+num2
//}
但这样的写法并不好,首先代码会执行两次,一次是普通的赋值代码。第二次是成为一个函数;其次是不方便写函数内部的语句。
函数的属性
输出一下刚刚的函数,
作为一个“对象”,那就一定有属性,构造函数,原型这些东西。我们也可以看到有arguments,caller,length,name,prototype几个属性,其中prototype是函数所特有的。
更进一步也可以看到原型上有call(), bind()等方法。
如果我们使用sortObj.constructor
还可以看到function的构造函数Function的信息:ƒ Function() { [native code] }
,可以看到Function中是底层C++的代码的内容(在chromium内核中)。
在function除了prototype外还有个scopes属性,代表着执行该函数时的作用域。
匿名函数和立即执行函数
匿名函数和立即执行函数相差不大,本质上都是一个东西。当一个函数没有指定名字时就叫匿名函数,立即执行它就是立即执行函数。匿名函数是没办法主动调用的,因为并没有变量指向它。
(function(){...}) //匿名函数
(function(){...})() //立即执行函数
(function(){...}()) //立即执行函数
立即执行函数在jQuery时代还是挺常见的,因为当时没有块级作用域。
(function($){...})(jQuery)
箭头函数
箭头函数是ES6推出的新语法,可以让代码变得非常简洁
document.bindEvent('click', (a,b) => {...})
document.bindEvent('click', a => {...}) // 一个参数时可以不加()
document.bindEvent('click', a => a*2) // 只有一个语句时可以不加{},且该语句会被return出去
但箭头函数也有缺陷,最主要的几个是 this指向,不能使用new,super这些继承构造相关的关键字(也没有prototype),也不能使用arguement。
异步函数
在ES8之后新增了 async 和 await关键字,这两个关键字让异步函数在JS中成为可能,async关键字也可以加在箭头函数前面,具体的细节可以参考promise和异步函数相关的内容。
不过异步函数也是不能使用new 关键字的,代码会直接报错。
这些都算函数的基础内容,但除此之外,函数的运行过程等内容也是需要掌握的。