一.四种方式定义函数
1.如何定义一个函数
- 具名函数
function 函数名(形参1,形参2){
语句
return 返回值
}
- 匿名函数
上面的具名函数,去掉函数名就是匿名函数let a = function(x,y){return x+y}
也叫函数表达式
//a只是放了匿名函数的地址,这个函数没有名字let f4=x=>({name:x }) //如果是对象,那么要加圆括号
- 箭头函数
let f1 = x => x*x
let f2 = (x,y) => x+y //圆括号不可省略
let f3 = (x,y) =>{return x+y} //花括号不可省略
let f4 = (x,y)=>({name:x,age:y})//直接返回对象会出错,要加圆括号
- 用构造函数
let f=new Function('x','y','return x+y')
- 基本没人用,但是能让你知道函数是谁构造的
- 所有的函数都是Function构造的
- 包括Object,Array,Function
- 函数自身VS函数调用,fn VS fn()
fn 是指函数本身,fn()是指调用函数
- 函数自身
- 代码
- let fn = () => console.log(‘hi’)
- fn不执行,而fn()才会执行
- 结果
- 不会有任何影响
- 因为fn没有执行
- 代码
例6是每次都会把i复制一份,留在setTimeout里面,所以打印0,1,2,3,4,5,很智障!!!
三.作用域:就近原则&闭包
作用域规则:
- 如果有多个作用域有同名变量a
- 那么查找a的声明时,就向上取最近的作用域
- 简称就近原则
- 查找a的过程与函数执行无关
- 但a的值与函数执行有关
闭包:(看上面的图)
如果一个函数用到了外部的变量,那么这个函数加上这个变量就叫做闭包
左边的a和f3就组成了闭包
闭包的用途以后再说**
四.参数和返回值
1.形式参数
- 形式参数的意思就是非实际参数
function add(x,y){
return x+y
}
//其中x,y就是形参,因为并不是实际的参数
add(1,2)
//调用add时,1和2是实际参数,会被赋值给x,y
- 形参可认为是变量声明
//上面代码近似等价于下面代码function add(){
var x = arguments[0]
var y = arguments[1]
return x+y
}
2.返回值
- 每个函数都有返回值
function hi(){
console.log('hi')
}
hi()
没写return ,所以 返回值是undefined
function hi(){return console.log('hi')}
hi()
返回值为console.log('hi')的值,即undefined
- 函数执行完了才会返回
只有函数有返回值
什么是调用栈
- JS 引擎在调用一个函数前
- 需要把函数所在的环境push到一个数组里
- 这个数组叫做调用栈
- 等函数执行完了,就会把环境pop出来
- 然后return到之前的环境,继续执行后续代码
- 举例
- console.log(1)
- console.log(‘1+2的结果为:’+add(1,2))
- console.log(2)
2.爆栈
如果调用栈中压入的栈过多,程序就会崩溃function computerMaxCallStackSize() {
try{
return 1 + computerMaxCallStackSize();
}catch(e){
//报错了说明stack overflow了
return 1
}
}
3.函数提升
函数永远会跑到最前面
- 什么是函数提升
function fn() {}
不管你把具名函数声明在哪,它都会跑到第一行
- 什么不是函数提升
let fn = function(){}
这是赋值,右边的匿名函数声明不会提升
六.person.sayHi()的this
1.如果不给任何条件,this默认指向window(小写的),例如:
function fn(){
console.log(this)
}
2.如果传入的this不是对象,js会帮你自动封装成对象
3.arguments 和 this
- 代码
function fn(){
console.log(arguments)
console.log(this)
}
- 如何传arguments
- 调用fn即可传arguments
- fn(1,2,3)那么arguments就是[1,2,3]伪数组
- 如何传入this
- 目前可以用fn.call(xxx,1,2,3)传this和arguments
- 而且xxx会被自动转化成对象(JS的糟粕)
上面图片里,person.sayHi()相当于
person.sayHi(person)
然后person被传给this了(person是个地址)
这样,每个函数都能用this获取一个未知对象的引用了
4.总结
- 我们想让函数获取对象的引用
- 但是并不想通过变量名做到
- Python通过额外的self参数做到
- JS通过额外的this做到:
- person.sayHi()会把person自动传给sayHi,sayHi可以通过this引用person
- 其他:
- 注意peson.sayHi和person.sayHi()的区别
- 注意person.sayHi()的断句(person.sayHi)()
另一种理解的思路:
this就相当于person.sayHi()中,点前面的person
上面的图片:
用person.sayHi()是对的
而person.sayHi(person)反而不对了
七.call指定this
forEach中用的this,例子如下所示:
array.forEach2.call(array,(item)=>console.log(item))//大师写法
等价于:
array.forEach(item=>console.log(item))//小白写法
this的两种使用方法:
隐式传递:
fn( 1 , 2 ) //等价于 fn.call(undefined,1,2)
obj.child.fn(1) //等价于 obj.child.fn.call(obj.child,1)
显示传递: 用call
fn.call(undefined,1,2)
fn.apply(undefined,[1,2])
绑定this
- 使用.bind可以让this不被改变
function f1(p1,p2){
console.log(this,p1,p2)
}
let f2=f1.bind({name:’liulei’})
//那么f2就是f1绑定了this之后的新函数
f2() //等价于f1.call({name:’frank’})
- .bind还可以绑定其他参数
let f3 = f1.bind({name:’liulei’},’hi’)
f3() //等价于f1.call({name:’liulei’},hi)
八.箭头函数
没有arguments和this
箭头函数
- 里面的this就是外面的this
- console.log(this) //window
- let fn =() =>console.log(this)
- 执行fn() //window
- 就算你加call也么有用
- fn.call({name:’liulei’}) //window
九.立即执行函数
只有JS有的变态玩意,现在用的少了
1.老版的js造一个局部变量的方法
js里面得到一个局部变量的方法,!加上括号! function () {
var a = 2
console.log(a)
} ( )
2.新版的js造一个局部变量的方法
3.立即执行函数(最好使用感叹号)
原理:
推荐用感叹号 ! 来代替其他的符号!!!