函数的作用及用法
什么是函数?
- 具有特定功能的 n 条语句的封装体
- 只有函数是可执行的,其它类型的数据是不可执行的
- 函数也是对象,
instanceof Object===true
为什么要用函数?
- 提高代码复用
- 便于阅读和交流
如何定义函数?
- 函数声明
- 表达式
调用(执行)函数
- 直接调用:
test()
- 构造函数:
new test()
- 对象方法:
obj.test()
- 间接调用其他对象方法:
test.call/apply(obj)
回调函数
函数定义了但没有直接调用,但最终在特定条件下执行了
常见的回调函数:
- DOM 事件函数
- 定时器函数
- ajax 回调函数
- 生命周期回调函数
匿名函数
专业术语为: IIFE (Immediately Invoked Function Expression) 立即调用函数表达式
1 | ;(function (w, obj) { |
作用:
- 隐藏内部实现
- 不污染外部命名空间
函数中的 this
- 显式指定谁:
obj.xxx()
- 通过 call/apply 指定谁调用:
xxx.call(obj)
- 不指定谁调用:
xxx() : window
- 回调函数:看背后是通过谁来调用的,window/其它
原型与原型链
原型 prototype
每个函数都有一个 prototype 属性, 它默认指向一个 Object 空对象(即称为: 原型对象)。原型对象中有一个属性 constructor, 它指向函数对象。
给原型对象添加属性(一般都是方法),可以使函数的所有实例对象自动拥有原型中的属性(方法)。因此原型一般只在创建实例对象时起作用。
显式原型和隐式原型
每个函数 function 都有一个prototype
属性,即显式原型;每个实例对象都有一个__proto__
属性,可称为隐式原型。对象的隐式原型的值为其对应构造函数的显式原型的值。

显式原型和隐式原型
函数的 prototype 属性在定义函数时自动添加,默认值是一个空 Object 对象;对象的 proto 属性在创建对象时自动添加,默认值为构造函数的 prototype 属性值。
原型对象即为当前实例对象的父对象。
原型链
所有的实例对象都有__proto__
属性,指向原型对象。这样通过__proto__
属性就形成了一个链的结构——原型链。
当查找对象内部的属性/方法时,js 引擎自动沿着这个原型链查找:先在自身属性中查找,找到返回;如果没有,再沿原型链向上查找,找到返回;如果最终没找到,返回 undefined。
当给对象属性赋值时不会使用原型链,而只是在当前对象中进行操作。
执行上下文
变量提升与函数提升
变量声明提升:通过 var 定义(声明)的变量,在定义语句之前就可以访问到。值为 undefined。
函数声明提升:通过 function 声明的函数,在之前就可以直接调用值为函数定义(对象)。
先有变量提升, 再有函数提升。
执行上下文
执行上下文是由 js 引擎自动创建的对象,包含对应作用域中的所有变量属性。
将代码分为全局代码和函数代码两种。
全局执行上下文:在执行全局代码前将 window 确定为全局执行上下文,对全局数据进行预处理。var 定义的全局变量==>undefined,添加为 window 的属性;function 声明的全局函数==>赋值(fun),添加为 window 的方法;this==>赋值(window)。然后开始执行全局代码。 当页面刷新/关闭时死亡。
函数执行上下文:在调用函数,准备执行函数体之前,创建对应的函数执行上下文对象。对局部数据进行预处理。形参变量==>赋值(实参)==>添加为执行上下文的属性;arguments==>赋值(实参列表的伪数组),添加为执行上下文的属性;var 定义的局部变量==>undefined,添加为执行上下文的属性;function 声明的函数 ==>赋值(fun),添加为执行上下文的方法;this==>赋值(调用函数的对象,如果没有指定就是 window)。然后开始执行函数体代码。函数执行完时死亡。
执行上下文栈
- 在全局代码执行前,JS 引擎就会创建一个栈来存储管理所有的执行上下文对象;
- 在全局执行上下文(window)确定后,将其添加到栈中(压栈);
- 在函数执行上下文创建后,将其添加到栈中(压栈);
- 在当前函数执行完后,将栈顶的对象移除(出栈);
- 当所有的代码执行完后,栈中只剩下 window。
作用域与作用域链
作用域
作用域就是一块代码区域。它是静态的(相对于上下文对象),在编写代码时就确定了。作用是隔离变量,不同作用域下同名变量不会有冲突。
分类:
- 全局作用域
- 函数作用域
- 块作用域
作用域与执行上下文
区别 1
- 全局作用域之外,每个函数都会创建自己的作用域,作用域在函数定义时就已经确定了。而不是在函数调用时;
- 全局执行上下文环境是在全局作用域确定之后, js 代码马上执行之前创建;
- 函数执行上下文环境是在调用函数时, 函数体代码执行之前创建。
区别 2
- 作用域是静态的, 只要函数定义好了就一直存在, 且不会再变化;
- 上下文环境是动态的, 调用函数时创建, 函数调用结束时上下文环境就会被释放。
联系:上下文环境(对象)是从属于所在的作用域
- 全局上下文环境==>全局作用域;
- 函数上下文环境==>对应的函数使用域。
作用域链
多个上下级关系的作用域形成的链,它的方向是从内到外,查找变量时就是沿着作用域链来查找,直到全局作用域, 如果还找不到就抛出找不到的异常。
闭包
闭包的概念
当一个嵌套的内部(子)函数引用了嵌套的外部(父)函数的变量(函数)时, 就产生了闭包。闭包存在于嵌套的内部函数中。
产生闭包的条件是函数嵌套以及内部函数引用了外部函数的数据(变量/函数)。
闭包可以理解为嵌套的内部函数,也可以理解为包含被引用变量(函数)。
闭包程序示例:
1 | function fn1() { |
闭包的意义
作用:
- 使函数内部的变量在函数执行完后,仍然存活在内存中(延长了局部变量的生命周期);
- 让函数外部可以操作到函数内部的数据(变量/函数)。
缺点:
- 变量占用内存的时间可能会过长;
- 可能导致内存泄露。
- 解决:及时释放,让内部函数对象成为垃圾对象。
f = null
应用:
闭包应用:
- 模块化: 封装一些数据以及操作数据的函数, 向外暴露一些行为;
- 循环遍历加监听。