1.定义
1.1 匿名函数
// 匿名函数,声明未引用会报错
function () {
return 1
}
// fn引用了这个匿名函数,fn存的是地址,函数存在堆内存中
var fn = function (){
return 1
}
// 把函数的地址复制给fn2,fn2现在指向这个函数
// 匿名函数也有名字
var fn2 = fn
fn.name // fn
fn2.name // fn
1.2 具名函数
// fn3是具名函数,作用域为all
function fn3() {
return 1
}
// fn4作用域只是本身,不在顶级全局,只能通过fn5访问
var fn5 = function fn4() {
return 1
}
console.log(fn3) // fn3(){}
console.log(fn4) // fn4 is not defined
1.3 箭头函数
- 箭头函数不接受this指定(指定无效)
- 箭头函数的this按照作用域向上找,自己没有this
- 箭头函数无法call指定,只能向上找
// 单个参数,单句返回
var fn6 = i => i+1
fn6(7) // 8
fn6.name // fn6 箭头函数也有name
// 多个参数,函数体多句
var fn7 = (i, j) => {
console.log(i, j);
rentun i + j;
}
2.词法作用域(静态作用域)
- 一个函数能访问哪些变量,在词法分析时已经确定,与执行顺序,调用顺序没有关系,与具体值无关(看下一个例子),只分析语义。
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)
}
}
var a = 1
function b() {
console.log(a)
// 此时如果访问的a是全局的a,自己没有声明a
}
// 但是b未执行,不能确定a的具体值
a = 2;
b();
// 2, 访问的是全局的a,但是值需要具体执行时确定
3.call stack
function a(){
console.log('a')
return 'a'
}
function b(){
console.log('b')
return 'b'
}
function c(){
console.log('c')
return 'c'
}
a()
b()
c()
// a()先入栈,执行a()
// console.log('a')入栈,执行,出栈,
// 执行return a,返回到a()的位置,a()c出栈
// b和c以此类推
4.this & arguments
- fn()调用是阉割版的call,不考虑传入的this,浏览器用call调用,undefined会根据具体代码自动被填充
- this语法糖存在的意义(让函数有可依托的对象)
- 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]
- this必须是对象,就算传入10,也会new Number(10)转换
- 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')
// 没有this需要把自己传入,繁琐
// 想改造,能不能变成
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
5.call / apply
- fn.call(asThis, p1,p2) 是函数的正常调用方式,call的参数是一个个的
- 当你不确定参数的个数时,使用apply,params传入数组,不确定个数的arguments可以直接遍历
- fn.apply(asThis, params) // params是数组
6.bind
- call 和 apply 是直接调用函数,而 bind 则是返回一个新函数(并没有调用原来的函数),这个新函数会 call 原来的函数,call 的参数由自己指定。
this.onClick.bind(this) // bind: 创建一个新函数,call了指定的this
setTimeout(function(){
console.log(this)
}.bind({name: 'frank'}), 1000)
7.return
- 每个函数都有 return
- 如果你不写 return,就相当于写了 return undefined
8.柯里化/高阶函数
8.1 柯里化
- 柯里化:固定一个参数,将 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)
}
//早期模板引擎,给一个模板,一个数据,返回一个html
//柯里化之前
function Handlebar(template, data){
return template.replace('{{name}}', data.name)
}
//柯里化之后
function Handlebar2(template){
return function(data){
return template.replace('{{name}}', data.name)
}
}
// 柯里化之前 调用
// 改掉模板里的name 需要很多字符串 只想写一次
Handlebar('<h1>Hi,I am {{name}}</h1>', {name: 'emmm'})
Handlebar('<h1>Hi,I am {{name}}</h1>', {name: 'dododo'})
var template = '<h1>Hi,I am {{name}}</h1>';
Handlebar(template, {name: 'checkyo'});
// 柯里化之后 调用 先接收一个返回函数
// 可以用来做 惰性求值
// 调用第一个时候,什么都没做,使用时候生效,第二次调用时候
var t = Handlebar2('<h1>Hi,I am {{name}}</h1>');
t({name: 'freestyle'})
8.2 高阶函数
-
在数学和计算机科学中,高阶函数是至少满足下列一个条件的函数:
-
- 接受一个或多个函数作为输入:forEach sort map filter reduce
array.sort(function(a,b){a-b}) array.forEach(function(a){})-
- 输出一个函数:eg: lodash.curry
fn.bind.call(fn, {}, 1,2,3) //fn this 参数-
- 不过它也可以同时满足两个条件,接收函数以及返回函数:Function.prototype.bind
-
-
高阶函数,接收一个函数,返回新的函数,作用于函数的组合
9.回调
- 名词形式:被当做参数的函数就是回调
- 动词形式:调用这个回调
// 传过去,调用一下
// 同步回调
array.forEach(function(a){})
- 注意回调跟异步没有任何关系,同步回调和异步回调
// 异步回调
setTimeout(fn, 1000)
10.构造函数
- 返回对象的函数就是构造函数,一般首字母大写
- JS 的 new 到底是干什么的?
function Empty() {
{...}
}
var empty = new Empty // new 会自动执行return 语法糖