![8bda8c70b21e837e60a75049b9dadf3a.png](https://img-blog.csdnimg.cn/img_convert/8bda8c70b21e837e60a75049b9dadf3a.png)
本文 目录:
- 函数的 5 种声明
- return
- 词法作用域
- 执行上下文
- js的变量提升
- this arguments
- 不同场景的this指向
- call apply bind
- call stack
- 闭包
- 柯里化/高阶函数
- 回调
- 构造函数
函数的 5 种声明
1.具名函数
function
2.匿名函数
var
3.具名函数赋值
var
4.window.Function 函数对象
var
5.箭头函数
var
return
每个函数都有 return
如果你不写 return,就相当于写了 return undefined
词法作用域
如题:
![3bd75893706efb58c432bf8d1f6fdb37.png](https://img-blog.csdnimg.cn/img_convert/3bd75893706efb58c432bf8d1f6fdb37.png)
作用域其实就是一个树:
![05819dc604676f01a4f0d93a5fbc6d7d.png](https://img-blog.csdnimg.cn/img_convert/05819dc604676f01a4f0d93a5fbc6d7d.png)
编译型语言,编译步骤分为:词法分析、语法分析、语义检查、代码优化和字节生成。
而解释型语言,通过词法分析和语法分析得到语法分析树后,就可以开始解释执行了。
js执行过程:
- 读入第一个代码段(js执行引擎并非一行一行地执行程序,而是一段一段地分析执行的)
- 做词法分析和语法分析,有错则报语法错误(比如括号不匹配等),报错后不再向后执行
- 对【var】变量和【function】定义做“预解析“(永远不会报错的,因为只解析正确的声明)
- 执行代码段,有错则报错(比如变量未定义)
- 如果还有下一个代码段,则读入下一个代码段,重复步骤2
- 结束
上面的过程,我们主要是分成两个阶段
javascript解析:就是通过语法分析和预解析构造合法的语法分析树。javascript执行:执行具体的某个function,JS引擎在执行每个函数实例时,都会创建一个执行环境(ExecutionContext)和活动对象(activeObject)(它们属于宿主对象,与函数实例的生命周期保持一致)
测试:
var
查看:词法作用域
执行上下文
每调用一个函数,就会创建一个新的执行上下文。
上下文建立过程:
- 声明变量,函数,确定arguments对象,参数
- 做词法分析形成语法分析树,建立作用域链
- 确定this的值
- 变量赋值,函数引用,执行其它代码
var
第一步:
var a,b,foo;
第二步:
![48ad2a3e842cc4cb4ddc8058e47327ba.png](https://img-blog.csdnimg.cn/img_convert/48ad2a3e842cc4cb4ddc8058e47327ba.png)
第三步:
全局环境内this为window
b.fn 和 b.fn1内this为b
foo内this为window
第四步:
a
题外:如果 let a = 10, foo > this.a 返回undefined
因为let存在块级作用域,所以let a=10 仅在全局window下有意义
![402dba63accb576efc79ee9895562614.png](https://img-blog.csdnimg.cn/img_convert/402dba63accb576efc79ee9895562614.png)
如下的道理:
{
js的变量提升
执行代码前,先变量提升!!
例1:
![7bbafa20c6cf9319f94da7a9af30370c.png](https://img-blog.csdnimg.cn/img_convert/7bbafa20c6cf9319f94da7a9af30370c.png)
结果:3 undefined 1
例2:
var
例3: ul 下有5个li,点击打印的都是5,因变量i提升到全局
![d3feb9ac1ea7d54dc1e6cdd451294679.png](https://img-blog.csdnimg.cn/img_convert/d3feb9ac1ea7d54dc1e6cdd451294679.png)
this 和 arguments
设计this的初衷是js必须长得像java。this 就是 call 的第一个参数!call 的其他参数统称为 arguments
当传参为undefined时,this为window(除严格模式下是undefined):
![2da61b2c67f85963a082f969941f4f66.png](https://img-blog.csdnimg.cn/img_convert/2da61b2c67f85963a082f969941f4f66.png)
this 是call的第一个参数,且一般是对象或undefined,this让函数有一个可依托的对象,每个函数都有两个对象,如果不传this就是window,如果传的不是对象,就会帮你转换为对象(除严格模式),不传argument就是空数组,arguments是一个伪数组
function
转为对象:
![ef6dc83c22e139305056d53e4851a90d.png](https://img-blog.csdnimg.cn/img_convert/ef6dc83c22e139305056d53e4851a90d.png)
- 希望里面的函数和外面的函数 this是一样的,需要使用箭头函数
- function本身一定有this,每次进入函数,一定把他弄到callStack里面,确定一个this,没传也会给你一个this,就是window,如果不想指定this,就使用箭头函数,因为没有this,且永远无法指定,this是每次进入一个函数都会被传一个值的变量,不是普通变量
- 编译器打印的this是global, global对象是单体内置对象,即不依赖宿主环境的对象,而window对象依赖浏览器。
- 箭头函数不可以做构造函数,没有this,arguments
- 箭头函数也不能指定this
![58fd299fa3b19118712bf856a5fd9b31.png](https://img-blog.csdnimg.cn/img_convert/58fd299fa3b19118712bf856a5fd9b31.png)
关于arguments的length:
![7d4ed5b75c30f1d49f286123ad336321.png](https://img-blog.csdnimg.cn/img_convert/7d4ed5b75c30f1d49f286123ad336321.png)
此外:局部变量i和形参i指向同一个存储地址
function
不同场景的this指向
- fn() 里面的 this 就是 window
- fn() 是 strict mode,this 就是 undefined
- a.b.c.fn() 里面的 this 就是 a.b.c
- new Fn() 里面的 this 就是新生成的实例
- () => console.log(this) 里面 this 跟外面的 this 的值一模一样
- $("#btn").on("click",function(){ 事件监听里面的this是监听的那个DOM对象,就是#btn})
- 使用事件委托的事件监听里面的this仍然是开头绑定的DOM对象
- setInterval,settimeout 里面的this是window
改变this指向通过 apply,call ,bind
call apply bind
函数调用的本质 call,call原意为调用
语法:f.call(asThis, input1,input2)
其中 asThis 会被当做 this,[input1,input2] 会被当做 arguments
fn.call(asThis, p1,p2) 是函数的正常调用方式,而fn(p1,p2)是语法糖。
当你不确定参数的个数时,就使用 apply 。
没有call的世界:
var
如果用call:
var
call 和 apply 是直接调用函数,而 bind 则是返回一个新函数(并没有调用原来的函数),这个新函数会 call 原来的函数,call 的参数由你指定。
举例:错误示范:
var
这里this.element.onclick 里面的this是element,如果需要调用click方法,
第一种:
var
第二种使用bind:
var
具体查看call apply bind 的整理
call stack (栈,先进后出)
js是单线程语言,执行函数1进入新环境时会做一个记号,return后从这里退出,如果函数1里面还有函数2,再做一个记号,这些记号就保存在栈里面,这个栈就叫做调用栈,出来的时候先退出函数2,再退出函数1,这就是调用堆栈。
1.普通调用 1+1+1
function
2.嵌套调用 1>2>3
![7d169b4b45ae7c6d663d6c1b741e7deb.png](https://img-blog.csdnimg.cn/img_convert/7d169b4b45ae7c6d663d6c1b741e7deb.png)
a.call() >>进入a(栈1),
b.call() >>进入b(栈3),
c.call() >>进入c(栈5),
其中a先进去最后出来,出来次序为栈5>栈3>栈1
3.递归
function
![ddf0d25135df50ae7bceb3a30a6c632a.png](https://img-blog.csdnimg.cn/img_convert/ddf0d25135df50ae7bceb3a30a6c632a.png)
栈溢出:超出最大栈数则溢出报错
测试: 我的电脑最多 9665个栈
![a05a0f7234ab5b27ed6bc709108ddd05.png](https://img-blog.csdnimg.cn/img_convert/a05a0f7234ab5b27ed6bc709108ddd05.png)
闭包Closure
如果一个函数,使用了它范围外的变量,那么(这个函数+这个变量)就叫做闭包。
查看:关于闭包的理解
柯里化/高阶函数
柯里化
简单说:返回函数的函数,参数比原函数少一个参数
应用:柯里化可以将真实计算拖延到最后再做, 可以做惰性求值
>>柯里化:将 f(x,y) 变成 f(x=1)(y) 或 f(y=1)x
//柯里化之前
关于柯里化的高级文章:
- http://www.yinwang.org/blog-cn/2013/04/02/currying
- https://zhuanlan.zhihu.com/p/31271179
高阶函数
wiki:在数学和计算机科学中,高阶函数是至少满足下列一个条件的函数:
1.接受一个或多个函数作为输入:forEach sort map filter reduce
2.输出一个函数:bind, lodash.curry
fn.bind.call(fn,1,2,3)
3.不过它也可以同时满足两个条件:Function.prototype.bind
应用:将函数任意的组合
![7b44c4f744416882d1009cc2f9364482.png](https://img-blog.csdnimg.cn/img_convert/7b44c4f744416882d1009cc2f9364482.png)
回调callback
名词形式:被当做参数的函数就是回调
动词形式:调用这个回调
注意回调跟异步没有任何关系
-
- 同步回调 eg: arr.forEach(function(){})
- 异步回调 eg: setTimeout(f, 1000) >> setTimeout("console.log(1)",1000)>>字符串其实就是一个函数
构造函数
返回对象的函数就是构造函数
一般首字母大写,使用:
function
关于new:
new是语法糖,只是少几行代码。。
柯里化测试题:
请写出一个柯里化其他函数的函数 curry,这个函数能够将接受多个参数的函数,变成多个接受一个参数的函数,具体见示例(这是 lodash.curry 的文档示例):
function
答案:
function
end~