1. 函数的 5 种声明
1.1 声明方式1:具名函数
语法:
function xx(input1,input2){
input1 + input2
return undefined
}
如果不写 return 会自动加上 return undefined;
function----关键字,类似于 var;
xx----函数名;
console.log(xx)
ƒ xx(input1,input2){
input1 + input2
return undefined
}
undefined
xx.toString()
"function xx(input1,input2){
input1 + input2
return undefined
}"
console.log('1')
1
关于console.log():console.log(x)只接受 字符串 ,如果 x 不是字符串,会先执行 x.toSstring() 后再 console.log();console.log() 只返回 undefined,打印的值与返回值无关;
1.2 声明方式2:匿名函数
语法:
var x
x = function (input1,input2){
input1 + input2
return undefined
}
1.3 声明方式3:将一个具名函数赋值给一个变量
语法:
var x
x = function y(input1,input2){
input1 + input2
return undefined
}
思考: 方式2 与 方式3 的区别???
var x
x = function y(input1,input2){
input1 + input2
return undefined
}
console.log(y)
//打印结果是:
// A 函数
// B undefined
Uncaught ReferenceError: y is not defined
at <anonymous>:6:13
// 结果直接报错????
上面代码中,结果直接报错????
1.4 声明方式4:window.Functiong 函数对象
语法:
var x
x = window.Function('x','y','return x + y')
或者 x = new Function('x','y','return x + y')
问题:
var x
var n = 1
x = window.Function('x','y','return x +' + n + '+ y')
x(1,2)
// 结果是:
// A 4
// B '1n2'
// C '112'
4
搞清 n 和 'n' 的区别;
x = window.Function('x','y','return x +' + n + '+ y')
<=> x = window.Function('x','y','return x +' + 1 + '+ y')
<=> x = window.Function('x','y','return x + 1+ y')
实际打印结果为: 4;
1.5 声明方式5:箭头函数
语法:
var sum = (x,y) => {return x +y}
<=> var sum = (x,y) => x +y // 函数体只有一句时,{} 和 return 可以同时省略,且必须同时,且不返回 对象;
sum(1,2)
3
------------------------
var sum = x => x*x // 只有一个参数时;
箭头函数 如:(x,y) => {return x +y} 只能是匿名的;声明后赋值给一个变量;
1.6 小结:函数的 name 属性
例:
function f(){}
undefined
f.name
"f"
var f2 = function(){}
undefined
f2.name
"f2"
var f3 = function f4(){}
undefined
f3.name
"f4"
var f5 = new Function('x','y','return x + y')
undefined
f5.name
"anonymous"
2. 如何调用函数 f.call
Function 这种特殊的对象的用法与其他 数据类型 不一样,其他数据类型可以直接用,而Function需要 调用/call;
函数到底是什么???
例:
function 求三角形面积(width,height){
var n = width * height
var m = n/2
return m
}
undefined
求三角形面积
ƒ 求三角形面积(width,height){
var n = width * height
var m = n/2
return m
}
求三角形面积(4,3) //6
求三角形面积(6,3) //9
如上,调用函数时,需要加 '()';
那么,内存里时怎么存的????
求三角形面积.toString()
"function 求三角形面积(width,height){
var n = width * height
var m = n/2
return m
}"
内存里存的就是一堆 字符串;
Function.prototype 里有一个 call(),调用函数时就是在执行 call() 方法;
用一般对象的方式写一个函数:
var f = {
'name': 'f',
'params': ['x','y'],
'functionBody': 'console.log("fff")',
'call': function(){
return window.eval(f.functionBody)
},
}
undefined
f.call()
fff
undefined // 返回值
------------------------
console.log(f)
{name: "f", params: Array(2), functionBody: "console.log("fff")", call: ƒ}
undefined
根据上面代码:理解 f 与 f.call() 的区别;
那么,函数------可以执行代码的对象;
图形解说:
函数调用的2种方式:
3. this 和 arguments
3.1 什么是 this
var f = function(){
console.log(arguments)
}
f.call(undefined,1,2,3)
// Arguments(3) [1, 2, 3, callee: ƒ, Symbol(Symbol.iterator): ƒ]
// undefined
var f = function(){
console.log(this)
console.log(this === window)
}
f.call(undefined,1,2,3)
// window {postMessage: ƒ, blur: ƒ, focus: ƒ, close: ƒ, frames: Window, …}
// true
// undefined
var f = function(){
'use strict'
console.log(this)
console.log(this === window)
}
f.call(undefined,1,2,3)
// undefined
// false
// undefined
f.call(4,1,2,3)
// 4
// false
// undefined
f.call('ss',1,2,3)
// 'ss'
// false
// undefined
可知:this 就是 call() 的第一个参数;在非严格模式下,call() 的第一个参数为 undefined 时,this 会被指向 window;
3.2 什么是 arguments
var f = function(){
console.log(arguments)
}
f.call(undefined,1,2,3)
// Arguments(3) [1, 2, 3, callee: ƒ, Symbol(Symbol.iterator): ƒ]
// 0: 1
// 1: 2
// 2: 3
// callee: ƒ ()
// length: 3
// Symbol(Symbol.iterator): ƒ values()
// __proto__: Object
// undefined
arguments 就是 call() 的第一个参数后面的所有参数;
arguments 是一个伪数组;就是长得像数组,但是整个原型链中没有 Array.prototype;
4. 什么是 call stack
call stack----和内存中的 stack 无关,只是一种数据结构;stack: 先入后出;
5. 作用域
作用域------是 树,或者说 与树紧密相关
问题:对于
a = xxx
,在省略 var、let、...等的情况下,a 将会是一个全局变量????图形解说:
根据上图:在当前及所有外层作用域都没有找到 声明 的情况下,才会作全局的声明。
只有有函数,就有一个作用域
测试题 1:(1)处的 a 会打印出什么???
var a = 1
function f1(){
f2.call()
console.log(a) // (1):a 会打印出什么???
var a = 2
function f2(){
var a = 3
console.log(a)
}
}
f1.call()
console.log(a)
所以上面代码,声明提升后:
var a
function f1(){
var a
function f2(){
var a
a = 3
console.log(a)
}
f2.call()
console.log(a) // (1):a 会打印出什么???
a = 2
}
a = 1
f1.call()
console.log(a)
代码省,声明提升后,答案显而易见;
测试题 2:(2):a 会打印出什么???
var a = 1
function f1(){
console.log(a) // (1)
var a = 2
f4.call()
}
function f4(){
console.log(a) // (2):a 会打印出什么???
}
f1.call()
console.log(a)
----------------------------------------
声明提升后如下:
var a
function f1(){
var a
console.log(a) // (1)
a = 2
f4.call()
}
function f4(){
console.log(a) // (2):a 会打印出什么???
}
a = 1
f1.call()
console.log(a)
答案是:1;????
原因:(2)处的 a 到底是哪个 a ??;首先是 f4 声明时的作用域中的 a,如果找不到,其次是 f4 声明时 外层作用域的 a;也就是说,a 是在声明时就确定了是哪个 a ,而不是运行时才确定的那个外层作用域 f1;因此 a 到底是哪个容器是在声明时确定的,当然,如果在 f4 执行前,这个容器里的值产生了变化, f4 里的 a 的打印值会随之变化;也就是说,声明的那一刻确定是哪个 a 容器,运行那一刻才确定值是多少;
测试题 3:i 会打印出什么???
<ul>
<li>选项1</li>
<li>选项2</li>
<li>选项3</li>
<li>选项4</li>
<li>选项5</li>
</ul>
<script>
var liTags = document.querySelectorAll('li')
for(var i = 0; i < liTags.length; i++){
liTags[i].onclick = function(){
console.log(i) //1小时后
}
}
//
console.log(i)
声明提升后:
var liTags
var i
liTags = document.querySelectorAll('li')
for(i = 0; i < liTags.length; i++){
liTags[i].onclick = function(){
console.log(i) //1小时后
}
}
//for 循环结束后 i 就是 6
console.log(i)
很明显,所有作用域只声明了一个 i ,因此 此题中,所有 i 都是相同的容器;1小时后,值也一样。
6. 闭包
var a = 1
function f4(){
console.log(a)
}
// 如果一个函数,使用了它范围外的变量,那么 (这个函数 + 这个变量) 就叫做闭包