概述
函数是一个可以执行代码的对象。每个函数都是 Function 类型的实例,并且都与其他引用类型一样具有属性和方法。

每个 Function 的实例的 __proto__ 都指向了 Function.prototype(原型),函数原型(Function.prototype)的 __proto__ 指向了 Object.prototype(对象的原型)
定义函数
-
函数声明
1
2
3function name( param1[ , param2[, ...] ] ){
return
}如果
return省略,那么浏览器将自动添加return undefined
function是关键字,它只能用来声明函数,而var则可以用来声明7 种数据类型(number、string、boolean、null、undefined、symbol、 object)中的任意一种。 -
函数表达式
1
2
3var x = function( param1[ , param2[, ...] ] ){
return
}匿名函数需要赋给变量,之后才能调用。这个匿名函数称为函数表达式
- 混合式
1
2
3var fn = function name( param1[ , param2[, ...] ] ){
return
}
混合式中,函数名 name 只在函数体内部有效,指代函数表达式本身,在函数体外部无效。
-
Function 构造函数
最后一个参数被始终看成函数体,而前面的参数则枚举了新函数的参数。1
2
3var fn = new Function( 'param1'[ , 'param2'[,' ...'] ] , functionBoby ){
return
}例子:
1
2
3let n = 1;
let fn = new Function( 'x' , 'y' , 'return x + ' + n + ' + y' );
fn( 1,2 ) //4,此时的 n 是1 -
箭头函数(ES6)
1
(param1[ , param2[, ...] ])=> { return }
说明
- 函数会在执行完 return 语句之后停止并立即退出,因此,位于 return 语句之后的任何代码都永远不会执行。
- 没有传递值的命名参数将自动被赋值 undefined
- 所有参数传递的都是值,不可能通过引用传递参数。
函数声明和函数表达式
- 函数声明
浏览器解析器会率先读取函数声明(声明前置),并使其在执行任何代码之前可用 - 函数表达式
必须要等到浏览器解析器执行到它所在的代码行,才会被解析执行
声明前置
先声明变量,后声明 function,如果 function 名字和 var 声明名字一样
function 优先。如果赋值就按照赋值的类型。

API
自身 API

name==> 返回函数实例的名称

length==> 指明函数的形参个数
Function.prototype API

-
call()==> 调用一个函数, 其具有一个指定的this值和分别地提供的参数(参数的列表)1
fun.call(this, arg1, arg2, ...)
参数:
-
this :函数运行时的指定的 this 值。
-
非严格模式下,若 this 为 null 或者 undefined ,则 this 自动指向全局对象(global[ window ]),同时值为原始值(数字,字符串,布尔值)的 this 会指向该原始值的自动包装对象。

-
严格模式下,若 this 为 undefined ,则 this === undefined

-
-
arguments :伪数组,元素为函数的参数
- 函数中的参数在内部是用一个伪数组(
arguments)来表示,在函数内部通过arguments对象来访问参数从而获取传递给函数的每一个参数。 arguments可以被用作被调用对象的所有未指定的参数。可以使用arguments来把所有的参数传递给被调用对象。arguments对象的长度是由传入的参数个数决定的,不是由定义函数时的命名参数的个数决定的。
- 函数中的参数在内部是用一个伪数组(
-
-
apply()==> 调用一个函数, 其具有一个指定的this值,以及作为一个数组(或类似数组的对象)提供的参数。1
fun.apply(this, arguments)
-
bind()==> 创建一个新的函数, 当被调用时,将其this关键字设置为提供的值,在调用新函数时,在任何提供之前提供一个给定的参数序列。1
fun.bind( this [ , arg1 [ , arg2 [ , ... ] ] ] )
说明:call() 和 apply() 的区别
apply 与 call() 作用相同,不同之处在于提供参数的方式。
apply 使用参数数组
call() 使用一组参数列表
apply可以使用数组字面量,也可以使用 arguments对象, arguments 是一个函数的局部变量。
Call Stack(调用栈)
栈是一种数据结构,特点是先进后出,js 代码执行时都遵循 Call Stack 的规则。如果压栈太多会导致错误(Stack Overflow[ 栈溢出 ])
作用域 和 作用域链
scope(范围、视野、眼界)
如果在函数作用域中没有写 var 直接写 a = 3
1. 优先赋值
2. 沿着作用域链寻找 a 的声明
3. 若作用域链中没有 a 的声明,则声明全局变量并赋值
变量提升
将作用域链中首先要做变量提升
例子1:
var a = 1;
function fn(){
console.log( a );
var a = 2;
}
fn.call(); // a === undefined
例子2:
var a = 1;
function fn1(){
console.log( a ); // a === undefined
var a = 2;
fn2.call();
}
function fn2(){
console.log( a ); // a === 1
}
fn1.call();
例子3:
var obj = { name: ' obj ' }
function fn1(){
function fn2(){
console.log( this ); // this === window
}
fn2.call();
}
fn1.call( obj );
例子5:
var liTags = document.querySelectAll( ' li ' );
for( let i = 0, len = liTags.length; i < len; i++ ){
liTags[ i ].onclick = function(){
console.log( i ) // 打印出的 i 都是最后的数字
}
}
闭包
如果一个函数使用了它范围外的值,那么这个函数 + 这个变量就叫做闭包。

立即调用函数
window下有很多全局属性

当定义变量的时候,有可能不小心覆盖了 window 的全局属性,为了避免覆盖 window 上的属性,可以:
- 不用全局属性
- 用局部变量
-
使用立即调用函数,从而使用局部变量
1
2() => { }.call()
() => { }() -
但是如果像上述写法,浏览器会报错,所以要这么写:
1
2
3
4
5
6
7(() => { }.call() )
(() => { } ).call()
-() => { }.call()
+() => { }.call()
!() => { }.call()
~() => { }.call()
{ let } // let 作用域在块级作用域中
套路
-
如果同一个函数被多次声明,后面的声明就会覆盖前面的声明
-
递归:函数调用自身
-
JavaScript将函数看做一种值,函数只是一个可以执行的值
-
当函数表达式和函数声明同时有的时候

-
不能在条件语句中声明函数(变量提升,是的 if 无效)
-
函数执行时所在的作用域,是定义时的作用域,而不是调用时所在的作用域。
-
参数传值传递,内存中体现原始值的拷贝。
参数传址传递(复杂类型的值),内存中体现原始值的地址,唯一一种不改变参数的是替换掉整个参数,这时不会影响到原始值(添加属性或方法会影响到原始值) -
如果有同名的参数,则取最后出现的那个值
-
eval命令的作用是,将字符串当作语句执行。eval没有自己的作用域,都在当前作用域内执行,因此可能会修改当前作用域的变量的值,造成安全问题。为了防止这种风险,JavaScript 规定,如果使用严格模式,eval内部声明的变量,不会影响到外部作用域
本文深入探讨JavaScript函数的基础概念、定义方式及使用技巧,包括函数声明、表达式、构造函数及ES6箭头函数等。详细解释了函数的作用域、闭包、参数传递特性,并介绍了重要的函数方法如call(), apply() 和 bind()。
6049

被折叠的 条评论
为什么被折叠?



