1. 概念
- 定义
- 函数也是对象,函数名也是指向对象的指针,所以与引用类型变量用法相同
- 使用不带圆括号的函数名时访问函数的指针
- 签名(即函数不同的参数):无签名、无重载
- 定义方式:函数声明、函数表达式、Function构造函数
function f(){}
let f = function(){}
let f = (function fn(){})
//Function构造函数中,最后一项参数被看成函数体
let f = new Function(n1,n2,'return n1+n2')
- 调用
- 函数内部的属性:arguments、this
- 参数:arguments
- 值传递,不是引用传递
- 调用时没有传入值的命名参数(函数定义时有名字的参数),值为undefined
- arguments(类数组):函数内部用来表示传入参数的,所以函数不会介意传入参数的个数和类型
- arguments[i]:可以和命名参数一起使用,且与命名参数的值保持一致
- arguments.length:传入参数的个数,可以根据length的不同,实现不同的操作,类似其他语言的函数签名(函数参数的个数或类型不用)不同实现的重载
- arguments.callee:指向拥有这个arguments对象的函数
- arguments.caller:非严格模式下为undefined
- 函数的caller:指向调用当前函数的外层函数,当全局调用当前函数时为null
- this
- 普通调用:指向调用该函数的对象(点之前的对象)
- 构造函数调用:指向生成的实例
- call、apply、bind:指向新绑定的this
- 函数的属性和方法
- prototype:函数的原型对象,保存着给实例用的方法和属性
- call(obj,arg1,arg2,…):在特定的作用域中(this指向obj)执行该函数,函数原本需要的参数要分别传入
- apply(obj,agrs数组):在特定的作用域中(this指向obj)执行该函数,函数原本需要的参数要以数组形式传入
- bind(obj):创建一个函数的实例,但该函数并没有执行,其this指向obj
- 返回值:
执行完return语句,立即停止并退出,默认返回undefined
- 重载:
- 定义两个同名函数,函数签名(接收的参数的类型或数量)不同,就为函数重载,js中不允许,后面定义的函数会覆盖前面的,仅可以用arguments.length来模仿
- 变量提升优先于函数提升
- 代码执行前,解析器会向执行环境中加载数据,此时会先读取变量和函数声明,使其在执行任何代码前可用
- 函数表达式的变量名会先提升函数表达式是执行到它所在的代码行时,才会被解析执行
2. 函数的用途
- 函数可作为参数或返回值
- 递归:
函数通过名字调用自身
- 闭包:
- 有权访问另一个函数作用域中的变量的函数
- 在函数中return另一个函数
- 内部函数的作用域链中包含外部函数的作用域
- 闭包只能取得外层函数中变量的最后一个值,可以使用匿名函数自调用包裹return的函数,产生块级作用域解决
- 单例:只有一个实例的对象,可用匿名函数返回对象实现
- 私有变量:函数的参数、局部变量、函数内部定义的函数
- 特权方法:有权访问私有变量和私有函数的方法
- 函数表达式(不写var定义全局变量)+构造函数
- 函数表达式+原型
3. 执行环境、作用域链、作用域、变量对象、执行上下文对象
- 环境栈:
当执行流进入一个函数时,函数的执行环境就会被推入一个环境栈中,而在函数执行后,栈将其执行环境弹出,把控制权返回给之前的执行环境,环境栈最下面为全局执行环境
- 作用域(变量对象、执行上下文对象、this):
函数创建时产生
- 执行环境:
- 全局执行环境:始终存在,关闭浏览器或网页时销毁
- 函数执行环境:函数执行过程中存在
- 变量对象:
每个执行环境中都有一个表示变量的对象,保存着函数中所有可以访问到的变量
- 作用域链:
变量对象的作用域链,作用域链本质上是一个执行变量对象的指针列表
- 产生过程:
- 创建函数时会创建一个预先包含全局变量对象的作用域链,保存在其[[Scope]]属性中,当调用函数时,会创建一个函数的执行环境,然后复制该[[Scope]]属性中的对象构建当前执行环境的作用域链
- 代码执行前:变量、函数提升,初始化全局执行环境极其变量对象
- 创建函数时:创建预先包含全局变量对象的作用域链,并保存到函数内部的[[Scope]]属性中
- 调用函数时:创建函数的执行环境,使用arguments和其他命名参数的值来初始化函数执行环境中的变量对象,并把该对象推入到作用域链的最前端,函数的外部函数的变量对象在第二位,外部的外部在第三位,作用域链的终点为全局执行环境的变量对象
4. 基本类型和引用类型
- 基本类型:
- 按值访问(访问变量的实际值)
- 按值复制,为两个变量,完全独立
- 引用类型:
- 按引用访问
- 按值(指针)复制,两个指针相等,都指向同一个对象
- 可动态添加属性和方法(分别通过两个相等的指针给同一个对象添加)
- 参数传递:
- 按值传递:基本类型传实际值,引用类型传递指针
5. ES6新增
- 参数的默认值:
- 参数与解构赋值:
- length:没有默认值、rest参数的参数的个数
- name:返回该函数的函数名
- rest参数(…/扩展运算符):
- …变量名(数组),获取函数多余参数,调用时可传多个参数
- function test(arr,…arr1){} // test([a,2],3,4,5)
- 箭头函数
- ()=>{}
- 单行无{}默认有return
- 箭头函数没有自己的this,可以把箭头函数看成一行普通的代码,不生成作用域,一般用于回调函数
- this指向外层作用域的this
- arguments指向外层作用域的arguments,可以用rest参数代替
- 不能当成构造函数,即不能用new
- 不能使用bind、call、apply
- 不能使用yield,即不能用作generator函数
- 只有函数和全局作用域,函数中才会有this,全局的this时window
- this(上下文环境)指向xx对象,绑定xx作用域
- 绑定this::(对象和函数绑定)
- 对象::函数
- ::对象.函数
- ::运算符返回原对象,所以可以用链式写法
- ::运算符会自动将左边的对象作为上下文环境(即this对象)绑定到右边的函数上
//变量a的变化
let a = {n:1}; //{n:1}
a.x = a; //{n:1,x:{n:1}}
a.x = {n:2} //{n:1,x:{n:2}}
a = {n:2} //{n:2}
a.x = a = {n:2} //同上面的两步操作a最终为{n:2}
console.log(a.x) //undefined