- 函数是对象
- 函数的要素
- call指定this
- 箭头函数
- 立即执行函数
-曾老湿, 江湖人称曾老大。
-多年互联网运维工作经验,曾负责过大规模集群架构自动化运维管理工作。 -擅长Web集群架构与自动化运维,曾负责国内某大型金融公司运维工作。 -devops项目经理兼DBA。 -开发过一套自动化运维平台(功能如下): 1)整合了各个公有云API,自主创建云主机。 2)ELK自动化收集日志功能。 3)Saltstack自动化运维统一配置管理工具。 4)Git、Jenkins自动化代码上线及自动化测试平台。 5)堡垒机,连接Linux、Windows平台及日志审计。 6)SQL执行及审批流程。 7)慢查询日志分析web界面。
函数是对象
定义函数1 |
---|
//具名函数
function 函数名(形式参数1,形式参数2){
语句
return 返回值
}
//匿名函数,也叫做函数表达式
let f = function(x,y){return x y}
代码语言:javascript复制function fn(x,y){
return x y
}
let a = function(x,y){
return x y
}
//合并
let f = function fn(x,y){
return x y
}
//如果此时调用fn(1,2),是否能成功?

很明显,没有成功,为什么呢?如果使用上面的方式定义函数,fn只能作用在等于号的右边,出了作用域就失败。
代码语言:javascript复制f(1,2)
3

定义函数2 |
---|
//箭头函数
let f1 = x => x * x
f1(2)
4
let f2 = (x,y) => x * y
f2(2,3)
6
let f3 = (x,y) => {
console.log('hi')
return x * y //如果使用了大括号,那么return就不能省略
}
f3(6,8)
hi
48
//直接返回一个对象的时候,必须用括号,把对象括起来
let f4 = x => ({name:x})
f4('zls')
{name: "zls"}
//构造函数(没人用)
let fn1 = new Function('x','y','console.log('hi');return x y')
fn1(2,3)
hi
5
函数的要素
每个函数都有以下几点要素: 1.调用时机 2.作用域 3.闭包 4.形参 5.返回值 6.调用栈 7.函数提升 8.arguments(除了箭头函数) 9.this(除了箭头函数)
调用时机 |
---|
如果下面的几段代码,都能完美的答出正确答案,那么你对调用时机,已经掌握的很好了。
代码语言:javascript复制//请问下面的函数执行结果是什么?
let a = 1
function fn(){
console.log(a)
}
//请问下面的函数执行结果是什么?
let a = 1
function fn(){
console.log(a)
}
fn()
//请问下面的函数执行结果是什么?
let a = 1
function fn(){
console.log(a)
}
a = 2
fn()
//请问下面的函数执行结果是什么?
let a = 1
function fn(){
console.log(a)
}
fn()
a = 2
//请问下面的函数执行结果是什么?
let a = 1
function fn(){
setTimeout(()=>{
console.log(a)
},0)
}
fn()
a = 2
//请问下面的函数执行结果是什么?
let i = 0
for(i=0;i<6;i ){
setTimeout(()=>{
console.log(i)
},0)
}
//请问下面的函数执行结果是什么?
for(let i=0;i<6;i ){
setTimeout(()=>{
console.log(i)
},0)
}
作用域 |
---|
每个函数都会创建一个作用域
代码语言:javascript复制// 请问打印出的结果是多少?
function fn(){
let a = 1
}
console.log(a)
// 请问打印出的结果是多少?
function fn(){
let a = 1
}
fn()
console.log(a)
所以a就是一个局部变量
在顶级作用域中声明的变量,是全局作用域 挂在window上的属性,都是全局作用域
代码语言:javascript复制// 请问打印出的结果是多少?
function f1(){
let a = 1
function f2(){
let a = 2
console.log(a)
}
console.log(a)
a = 3
f2()
}
f1()
如果多个作用域,有同名的变量a
1.向上取最近的作用域
2.作用域和函数的执行无关(静态作用域 也叫:词法作用域 )
// 请问打印出的结果是多少?
function f1(){
let a = 1
function f2(){
let a = 2
function f3(){
console.log(a)
}
a = 22
f3()
}
console.log(a)
a = 100
f2()
}
f1()
闭包 |
---|
讲完了...
代码语言:javascript复制// 请问打印出的结果是多少?
function f1(){
let a = 1
function f2(){
let a = 2
function f3(){
console.log(a)
}
a = 22
f3()
}
console.log(a)
a = 100
f2()
}
f1()
如果一个函数用到了外部的变量,那么这个函数加这个变量就叫做闭包。
上面代码中 a 和 f3 组成了闭包
参数 |
---|
形参的意思:形式参数,非实际参数
代码语言:javascript复制function add(x,y){
return x y
}
其中x和y就是形参,因为并不是实际的参数
代码语言:javascript复制add(1,2)
调用add时,1和2是实际参数,会被赋值给x和y
形参可认为是变量的声明,上面的代码等价于下面的代码
代码语言:javascript复制function add(){
var x = arguments[0]
var y = arguments[1]
return x y
}
function add(x){
return x arguments[1]
}
形参可多可少
返回值 |
---|
每个函数都有返回值
代码语言:javascript复制//那么请问,下面这个函数,返回值是什么?
function hi(){
console.log('hi')
}
hi()
//那么请问,下面这个函数,返回值是什么?
function hi(){
return console.log('hi')
}
hi()
注意: 1.函数执行完了后,才会返回 2.只有函数有返回值
代码语言:javascript复制1 2返回值是3 ??? 这个 说法是错误的
1 2的值是3
就好比,linux命令:ifconfig 它的返回值是什么?是0
但是他的结果,是好几个IP地址...
递归,调用栈,爆栈 |
---|
什么是调用栈?
JS引擎在调用一个函数之前,需要把函数所在的环境push到一个数组里,这个数组叫做调用栈 等函数执行完了,就会把这个环境pop出来 然后return到之前的环境,继续执行后续代码
代码语言:javascript复制console.log(1)
console.log('1 2的结果为' add(1,2))
console.log(2)
递归函数
代码语言:javascript复制//阶乘
function f(n){
return n !== 1 ? n* f(n-1) : 1
}
f(4)
= 4 * f(3)
= 4 * (3 * f(2))
= 4 * (3 * (2 * f(1)))
= 4 * (3 * (2 * (1)))
= 4 * (3 * (2))
= 4 * (6)
24
//压栈次数 11434,使用递归函数测调用栈
function computeMaxCallStackSize(){
try{
return 1 computeMaxCallStackSize();
}catch (e){
//报错就说明 stack overflow了
return 1;
}
}
函数提升 |
---|
什么是函数提升?
代码语言:javascript复制function fn(){}
不管把这个函数声明在哪里,它都会跑到第一行
代码语言:javascript复制add(1,2)
add(1,2)
function add(x,y){
return x y
}
3

这段代码,可以看出,就算是先调用函数,但是如果使用function add(){}
的方式,就可以调用到这个函数,因为这样函数会自动跑到第一行。
如果同时有一个变量和一个函数怎么办?
代码语言:javascript复制let add = 1
function add(){}
//会发现上面这段代码,报错,这就是因为我们喜欢使用let而不是使用var
//但是如果用var会出现什么问题?
var add = 1
function add(){}
add
1
//function是会提升函数,但是let赋值,永远不会提升
add(1,2)
let add = function(x,y){ return x y}

arguments和this |
---|
JS 三座大山的第二座:this
代码语言:javascript复制function fn(){
console.log(arguments)
}

从上面我们看出,arguments
是一个数组...
No ! ! ! ! !
代码语言:javascript复制fn(1,'b')

不包含数组的原型链的数组都是伪数组,这个可以看出,他没有push pop等数组的方法...
this
JS的千古奇案,如果不给任何条件,this默认指向window
代码语言:javascript复制function fn(){
console.log(this)
}

如果 要指定this只能通过call
来指定
fn.call(1)
//如果 传的this不是对象,JS会自动帮你封装成对象

但是我们又不想让他给我瞎封装,我们不想要这个功能怎么办?
那么就只能,在生命函数的时候,加上'use strict'
function fn(){
'use strict'
console.log(this)
}
fn.call(1)

但是,没有人写代码,都会写'use strict'
call传参会被分成两段,第一段是this
剩下的是arguments
function fn(){
console.log(arguments)
console.log(this)
}
fn.call(1,2,3,4)

假设没有this
代码语言:javascript复制let person = {
name: 'zls',
sayHi(){
console.log(`你好,我叫:` person.name)
}
}
如果用class,这样我们就没有办法调用person
我们需要得到一个对象,如何在没有对象名字 的时候,拿到那个对象呢?
土办法,用参数
代码语言:javascript复制//对象中
let person = {
name: 'zls',
sayHi(p){
console.log(`你好,我叫:` p.name)
}
}
person.sayHi(person)

代码语言:javascript复制//类中
class Person{
constructor(name){ this.name = name }
sayHi(p){console.log(`你好,我叫:` p.name)}
}
谁会用这种方法,,,,python就用了...
代码语言:javascript复制class Person:
def __init__(self,name):
self.name = name
def sayHi(self):
print('你好,我叫:' self.name)
person = Person('zls')
person.sayHi()
特点: 每个函数 都接收一个额外的 self 这个 self 就是传进来的对象 只不过 Python 会偷偷帮你传对象 person.sayHi() 等价于 person.sayHi(person) person 就被传给 self 了
所以...JS没有走Python的路,他选择了另一种,更难的路...这就是第二座大山this
使用this获取那个未来的对象,JS在每个函数中都加了this
代码语言:javascript复制let person = {
name: 'zls',
sayHi(-this-){
console.log(`你好,我叫:` this.name)
}
}
person.sayHi()
你好,我叫:zls

代码语言:javascript复制//小白调用方法,会自动把person传到函数里,作为this
person.sayHi()
//大师调用方法,需要自己手动把person传到函数里,作为this
person.sayHi.call(person)
let person = {
name: 'zls',
sayHi(){console.log(this.name)}
}
person.sayHi.call({name:1})
1
call指定this
回顾之前的例子 |
---|
// 没有指定this
function add(x,y){
return x y
}
add.call(undefined,1,2)
add.call('zls',1,2)
add.call('fuck',1,2)
两种传递方式 |
---|
1.隐式传递
代码语言:javascript复制fn(1,2) //等价于 fn(undefined,1,2)
obj.child.fn(1) //等价于 fn(obj.child,1)
2.显示传递
代码语言:javascript复制fn.call(undefined,1,2)
fn.apply(undefined,[1,2])
绑定this |
---|
使用bind可以让this不被改变
代码语言:javascript复制function f1(p1,p2){
console.log(this,p1,p2)
}
let f2 = f1.bind({name: 'zls'})
//那么 f2 就是 f1 绑定了 this 之后的新函数
f2() //等价于 f1.call({name: 'zls'})
.bind
还可以绑定其他参数
let f3 = f1.bind({name: 'zls'},'hi')
f3() //等价于 f1.call({name: 'zls'},hi)
箭头函数
箭头函数中,没有 arguments
和 this
,JS最新版,把这俩东西干掉了... 父爱
立即执行函数
我们以前想要声明一个局部变量
代码语言:javascript复制var a = 1
function fn(){
var a = 2
}
console.log(a)

代码语言:javascript复制//声明 一个匿名函数,直接调用
function(){
var a = 2
console.log(a)
}()
function(){
var a = 2
console.log(a)
}()
2
- function(){
var a = 2
console.log(a)
}()
2
//最终,JS想要生成一个局部变量,使用立即执行函数
! function(){
var a = 2
console.log(a)
}()
2
true
//但是在新版函数中,我们只需要一个代码块
{
let a = 10
console.log(a)
}
console.log(a)
10