函数定义
匿名函数
函数声明的时候 不要给名字 把它付给一个变量 作为函数的引用
变量里面存的是 函数的地址
函数是个对象 对象是存在堆内存里面 一般存它的地址
它是匿名函数 但是它有name具名函数
声明在顶级空间的话 作用域整个文件
具名函数传给一个变量 作用域就是 具名函数部分
超过这个部分 就访问不到它了
函数后面加() 就代表 取它的返回值
function foo(){
console.log(1)
}
var x = foo
var y = foo()
- 箭头函数
var fn6 = i => i+1
var fn7 = (i,j) => i+j
var fn8 = (i,j) => {console.log(i);return i+j}
//只有一句话 就不需要return了
//有两句话 就把return 现示 的 说出来
词法作用域
不会立即执行
抽象词法树 先看你语法对不对 再从头开始执行
- 声明相关
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)
}
}
- 示意图
window
global1 fn1
param1(形参也算) local1 local2 fn2 fn3
-------------------------------- local2 local2
思路
首先 当前作用域 在fn2 里面找 local1 找不到
那么 找当前作用域上一层作用域 在fn2 同级里面找 local 找到了
fn3 里面的 local2 不会影响fn2 里面的 local2s
一个函数里面 能访问那些变量 在做词法分析的时候 就确定了
跟你调用不调用 没关系
注意的是 词法分析 只是 判断 这个变量 是不是 这个变量
不能判断它的值 是不是 这个变量的值 (因为 这个值可能会变)
分析语义 不分析值
call stack 栈
- 先进后出
a 在执行之前先把它记下来
执行代码时 也把它的当前记下来
碰到函数 在执行之前 先把它的当前记下来
运行完了 就回到 刚才记下的地方
return 就离开函数 回到函数执行前 记下的位置
call stack 里 记录的是你是从哪儿离开的
-
递归
斐波拉契数列
1 1 2 3 5 8 13...
js是单线程的 所以它在执行一长串代码的时候
他就会把当前的环境都记住
我能访问那些变量 (this,arguments) 函数的地址
突然它看到一个函数 这个时候 它就要切换环境 因为它要进入这个函数
这个函数的 代码并不在这里 存在另一块内存
那它进入这个函数之前 它有可能会忘掉怎么回来
这个时候 它就在这里做个记号
但是 它要做很多记号 也有可能 这个函数里面还有函数
于是 它把每一层函数放到一个栈里面 这个栈就叫调用栈
只要它进入一层调用栈 栈里面 就有多一个 关于它进入的时候的一个 记录
于是 它就进入一个新的函数 开始执行了
新的函数 如果还有函数呢 它就把第二个函数 又放到栈里面
等它回来的时候 每次它就先回 最后进入的地方
function fab(n) {
if (n >= 3) {
return fab(n - 1) + fab(n - 2);
} else {
return 1;
}
}
function foo(n) {
function _foo(n, cur, total) {
if (n <= 2) return total;
return foo(n - 1, total, cur + total);
}
return _foo(n, 1, 1);
}//尾递归
call & apply & this & arguments
在你进入一个函数的时候 除了记录你进去的地址
还有你传给函数 的参数有哪些
-
this & arguments
函数调用
在函数调用时,要准备好 this
如果没有this 就是 undefined 浏览器就转换成了window
第二个 准备arguments 伪数组
如果没有 arguments 就是空数组[]
function f(){
console.log(this)
console.log(arguments)
}
-
f.call()
Window 就是 window
f() 是阉割版的 f.call()
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')
person.sayHi()的时候
用this就能访问到 调用时候的person
可以这么写
既然你没有传person
那 我得找个 关键字来 代替 函数里面的person --> 用this关键字
person.sayHi() //以 person 为this 调用sayHi
person.sayHi.call(person) //以 person 为this 调用sayHi
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)
}
}
js之父 为了 满足 person.sayHi() 这样的代码能够运行
不用传person 我也能知道 当前对象是什么
于是 它创建了 this
这样的关键字
this是什么? 就是 函数之前的 .前面的东西
如果没.呢? this就是 window
var fn = person.sayHi
person.sayHi() //Hi ,I amfrank
fn() //Hi ,I am
window.name = 'xxx' //Hi ,I amxxx
如果 我不想传 person 就给你传个对象 {name:‘xxx’}
person.sayHi.call({name:'xxx'}) //Hi ,I amxxx
function(){
console.log('Hi, I am' + this.name)
}
这个函数 是独立存在的
它和sayHi 、person 没有任何关系
它就是和 call有关系
call 传什么this 过来
函数里面的 this 就是什么
结论:
- person.sayHi 等价于 person.sayHi.call(person)
- fn() 等价于 fn.call()
- call 的第一个参数 this
- 如果你不想传 就传个 undefined 或者 null
- 当你 没办法 把这个函数写完的时候
你就直接把 参数的数组 传进去
fn.apply(null,[1,2,3,4,5,6])
当你不知道数组长度的时候 长度不固定 长度非常长的时候
fn.bind(this)
返回一个函数 这个函数 会把 bind 前面的函数 包起来
//函数 还没有执行
function(){
fn.call()
}
call里面是什么?
bind里面是什么 它就是什么
bind的作用 就是在 调用fn时 在新fn的后面 加call
fn.bind = function(x,y,z){
var oldFn = this //外面的fn bind .前面的东西
return function(){
oldFn.call(x,y,z)
}
}
旧函数调用bind 的时候会返回一个新函数
新的函数被调用的时候 会去调用旧函数
怎么调呢 在旧函数后面加个 call
call的第一个参数 就是bind 的第一个参数
如果你希望 一个函数里面和外面的 this一样的话 用箭头函数
因为箭头函数 本身是没有this的
箭头函数 对它来说 this永远没办法指定
它的this永远是它外面的this
function 本身是一定有this的
当进入一个function的时候
把它压到 call stack 里面
确定一个this 一定会确定this 没传也确定this
箭头函数呢
压到 call stack
不绑定this 绑定 arguments