- 函数的 5 种声明
- 如何调用函数 f.call
- this 和 arguments
- 什么是 call stack
- 作用域
- 闭包
如果你意犹未尽,可以查看我的《JS深入浅出》里讲的《函数》
函数的 5 种声明
-
具名函数
function x(input1,input2){ return input1+input2 } x.name // 'x' console.log(x) //如果x是字符串,直接打印x //如果x不是字符串,调用x.toString()
输入:x,y
返回:写在return后面,如果不写,自动return undefined
-
匿名函数
var f f = function(x,y){ return x+y } f.name // 'f'
如果使用匿名函数,必须要把这个匿名函数赋值给一个对象
-
具名函数赋值
var f f = function f2(x,y){ return x+y } f.name // 'f2' console.log(f2) // undefined
function y(){}
console.log(y) //√
//刷新下页面
var x = function y(){}
console.log(y) //× 报错
- 面试中经常问第3种
-
window.Function
var f = new Function('x','y','return x+y') f.name // "anonymous"
var n=1
var f = new Function('x','y','return x+'+n+'+y')
f(1,2) //返回结果是多少?4还是'1n2'?
-
箭头函数
var x = (x,y) => { return x+y } var sum = (x,y) => x+y //如果{}里面只有一句话,且这一句话是return, //那么可以把{}和return一起去掉。必须一起去掉 //等价于var sum = (x,y) => {return x+y} var n2 = n => n*n //如果参数只有一个,()可以省略掉
函数的name属性
如何调用函数
f.call(asThis, input1,input2)
其中 asThis 会被当做 this,[input1,input2] 会被当做 arguments
禁止使用 f(input1, input2),因为学会 .call 才能理解 this
- 先了解下什么是函数?
见阮一峰javascript函数这一章的概述
函数是一段可以反复调用的代码块。函数还能接受输入的参数,不同的参数会返回不同的值。
eval()传的参数是一个字符串,eval将这段字符串当做代码执行
函数是一个对象,函数执行就是eval函数体的过程
f是这个对象,f.call()是执行这个函数的函数体
函数是一个对象,这个对象可以执行一段我们写的代码
- js有7种数据类型
- 6种简单数据类型:number,string,boolean,undefined,null,symbol
- 1种复杂数据类型Object,原型链中有Object.prototype的都是对象
- Array,Function,Date,RegExp,Error等都属于Object类型,因为他们的原型链中有Object.prototype。
- Array有自己独有的原型Array.prototype
- Function有自己独有的原型Function.prototype
-
Function.prototype有三个很重要的方法:call() apply() bind()
以后不准用f(1,2)这样调用函数,要用f.call(undefined,1,2)!!!!
this 和 arguments
function f(){
'use strict'
console.log(this)
console.log(arguments)
return undefined
}
f.call(1,2,3) // this 为 1,arguments 为 [2,3]
- call的第一个参数可以用this得到
- call的第一个参数后面的参数可以用arguments得到(arguments是一个伪数组)
- 在普通模式下,如果this为undefined,那个浏览器会把this转换为window
-
在严格模式下('use strict'),传的是什么,this就是什么。this是call的第一个参数
js中的new和this看起来没什么必要用,是有历史原因的,但是设计js的时候公司的要求是长得像java
伪数组:长得像数组,但是原型链中没有Array.prototype。不可以使用push()、pop()这些数组的方法
什么是 call stack
stack:栈(先进后出)
调用栈:每进入一个函数,会把这个函数的位置记录到栈里面。等回来的时候直接就回到栈最上面的那个位置
function sum(n){
console.log(n)
if(n==1){
console.log('return 1')
return 1
}else{
console.log(`return ${n} + sum.call(undefined,${n-1})`)
return n + sum.call(undefined, n-1)
}
}
sum.call(undefined,5)
//sum(5) //5+sum(4)
//sum(4) //4+sum(3)
//sum(3) //3+sum(2)
//sum(2) //2+sum(1)
//sum(1) //1
可以打断点,在控制台看call stack↓
但是这个栈是有上限的,太多的栈会爆掉的 stack overflow(栈溢出)
stack overflow
作用域
- 按照语法树,就近原则
-
我们只能确定变量是哪个变量,但是不能确定变量的值
找当前作用域有没有这个变量的值,没有就向上一层找有没有,采取就近原则
//请分别说出每个位置的a是什么值?
var a = 1
function f1(){
var a = 2
f2.call()
console.log(a)
function f2(){
var a = 3
console.log(a)
}
}
f1.call()
console.log(a)
题1
var a = 1
function f1(){
alert(a) // 是多少
var a = 2
}
f1.call()
拿到代码直接做——必然会错。请先提升声明!!!
变量提升!!!
等价于↓
var a = 1
function f1(){
var a
alert(a) // 是多少
a = 2
}
f1.call()
答案是:undefined
题2
var a = 1
function f1(){
var a = 2
f2.call()
}
function f2(){
console.log(a) // 是多少
}
f1.call()
拿到代码直接做——必然会错。请先提升声明
答案是:1
题3
var liTags = document.querySelectorAll('li')
for(var i = 0; i<liTags.length; i++){
liTags[i].onclick = function(){
console.log(i) // 点击第3个 li 时,打印 2 还是打印 6?
}
}
拿到代码直接做——必然会错。请先提升声明
闭包
- 搜索:方应杭 闭包
- 看文章
//这个就是闭包
var a = 1
function f4(){
console.log(a)
}
//如果一个函数,使用了它范围外的变量,那么(这个函数+这个变量)就叫做闭包