函数声明
匿名函数
var fn = function() {
return 1
}
var fn2 = fn
fn.name // fn
fn2.name //fn
具名函数
// fn3在全局作用域下
function fn3() {
return 3
}
//fn4作用域在函数体内
var fn5 = function fn4() {
return 4
}
console.log(fn4) //not defined
箭头函数
var fn6 = () => {
return 6
}
词法作用域
我们有如下代码
var global1 = 1
function fn1(param1){
var local1 = 'local1'
var local2 = 'local2'
function fn2(param2){
var local2 = 'inner local2'
console.log(local1)
console.log(local2)
}
function fn3(){
var local2 = 'fn3 local2'
fn2(local2)
}
}
对于上面的代码, 浏览器先把代码变成抽象语法树
词法树
假设我们要在fn2中调用local1变量
- 解析器会先在fn2节点中找, 上图中我们发现, fn2节点内没有local1
- 在fn2节点的上层节点中继续找, 也就是在fn1节点中找, 发现了local1
- 调用fn1中的local1
- 假设fn1中还没有, 那就继续往上找, 知道咋window下还找不到, 就报错
- 词法书只是分析调用哪个变量, 并不是分析变量的值
对于上面的第五点, 我们具体说明一下什么叫分析变量, 并不分析变量的值
假如我们有如下代码, 调用b()之后函数的值为1
var a = 1
function b() {
console.log(a)
}
b() //1
我们稍在函数中给a赋值, 这时候打印出来的就是2
var a = 1
function b() {
a = 2
console.log(a)
}
b() //2
这就说明了词法树只会分析函数调用时候调用的是哪个变量, 并不关心变量的值, 变量的值决定于你给它赋值是多少
Call Stack
这里还是提一下loupe, 这个很清晰的描述了调用栈
this & aguments
- 重要:this 就是 call 第第一个参数!call 的其他参数统称为 arguments
- this是隐藏的第一个参数, 且必须是对象
function f(){
console.log(this)
console.log(arguments)
}
f.call() // window
f.call({name:'frank'}) // {name: 'frank'}, []
f.call({name:'frank'},1) // {name: 'frank'}, [1]
f.call({name:'frank'},1,2) // {name: 'frank'}, [1,2]
从上面的代码就能看出, f()这种方式的调用, 就是隐藏了this的简化版的call()
- this 为什么必须是对象
因为 this 就是函数与对象之间的羁绊
var person = {
name: 'frank',
sayHi: function(person){
console.log('Hi, I am' + person.name)
},
sayBye: function(person){
console.log('Bye, I am' + person.name)
},
say: function(person, word){
console.log(word + ', I am' + person.name)
}
}
person.sayHi(person)
person.sayBye(person)
person.say(person, 'How are you')
// 能不能变成
person.sayHi()
person.sayBye()
person.say('How are you')
// 那么源代码就要改了
var person = {
name: 'frank',
sayHi: function(){
console.log('Hi, I am' + this.name)
},
sayBye: function(){
console.log('Bye, I am' + this.name)
},
say: function(word){
console.log(word + ', I am' + this.name)
}
}
// 如果你不想吃语法糖
person.sayHi.call(person)
person.sayBye.call(person)
person.say.call(person, 'How are you')
// 还是回到那句话:this 是 call 的第一个参数
// this 是参数,所以,只有在调用的时候才能确定
person.sayHi.call({name:'haha'}) // 这时 sayHi 里面的 this 就不是 person 了
// this 真的很不靠谱
// 新手疑惑的两种写法
var fn = person.sayHi
person.sayHi() // this === person
fn() // this === window
call / apply
fn.call(asThis, p1,p2) 是函数的正常调用方式
当你不确定参数的个数时,就使用 apply
fn.apply(asThis, params)
bind
call 和 apply 是直接调用函数,而 bind 则是返回一个新函数(并没有调用原来的函数),这个新函数会 call 原来的函数,call 的参数由你指定。
return
每个函数都有 return
如果你不写 return,就相当于写了 return undefined
柯里化和高阶函数
- 柯里化:将 f(x,y) 变成 f(x=1)(y) 或 f(y=1)x
//柯里化之前
function sum(x,y){
return x+y
}
//柯里化之后
function addOne(y){
return sum(1, y)
}
//柯里化之前
function Handlebar(template, data){
return template.replace('{{name}}', data.name)
}
//柯里化之后
function Handlebar(template){
return function(data){
return template.replace('{{name}}', data.name)
}
}
柯里化可以将真实计算拖延到最后再做
关于柯里化的高级文章:
-
高阶函数
在数学和计算机科学中,高阶函数是至少满足下列一个条件的函数:接受一个或多个函数作为输入:forEach sort map filter reduce
输出一个函数:lodash.curry
不过它也可以同时满足两个条件:Function.prototype.bind
下面两个例子就是高阶函数在前端的应用例子
// 筛选出数组中的偶数并求和
reduce(filter(array, function(n){ n % 2 === 0}), function(prev, next){return preve+next},0)
//筛选出数组中的基数并排序
sort(filter(array, function(n){n % 2 === 1}),function(a,b){return a-b})
在前端, 高阶函数和函数式编程在react中有大量应用
回调
回调
分为两部分
- 函数被当做另一个函数参数
- 在函数中调用这个函数参数
回调跟异步没有任何关系
网友评论