Function
- 声明式
1 | function foo() { |
- 表达式
1 | fnName(); |
构造函数方式
1
var fn = new Function("arg1", "arg2", "arg3", ..."argN", "body");
- “arg1”, “arg2”, “arg3”, …”argN” 为生成函数的形参列表,且为可选的参数列表
- “body” 为生成函数的函数体部分
- 如果不给构造函数传参数的话,就会创建一个没有形参列表也没有函数体实现的函数。
- 如果只传入一个参数,那么这个参数值会交给 “body” 形参。
几个概念
- 函数对象:在绘制函数的原型链的时候,将函数称为函数对象
- 除了函数,都称为 普通对象。
- 非严格模式下,声明的全局变量都是 window 对象的属性
- 函数的名字存储的是函数对象的地址
函数相关属性
- caller: 返回调用当前函数的函数
- 所谓函数调用函数 就是指在函数体内部调用其他函数
- length: 记录的是形参的个数
- name: 记录函数的名字
arguments 伪数组对象,用于存储实参
arguments.callee: 返回正在执行的函数。- length: 存储实参的个数
- 当一个匿名函数作为某个对象的属性值时,在其内部只有使用
arguments.callee来获取函数 - 用途:可以用在不定项形参个数的函数实现
- 函数没有重载,可以用arguments模拟函数重载
函数四种调用模式
确定调用模式的作用:确定this执向的
普通函数执行模式:声明一个函数后,直接通过函数的名字调用。
1
2
3
4function foo() {
console.log(this); // this -> window
}
foo(); // 普通函数执行模式构造函数模式: 通过 new 操作符创建一个对象,此时该函数的执行模式为 构造函数模式
1
2
3
4
5function fn() {
console.log(this); // this -> 构造函数创建出来的对象
}
var f = new fn; // 构造函数执行模式
var ff = new fn;方法调用模式:通过对象来调用
1
2
3
4var o = {say: function() {
console.log(this); // this -> 方法的调用者, 即对象o。
}};
o.say(); // 方法调用模式上下文模式 ( call/apply 模式):
- 作用:动态改变 this 的指向
- this -> call|apply方法的第一个参数
1
2
3
4
5function print() {
console.log(this.name);
}
var obj = {name: "tom"};
print.call(obj); // tom
call 和 apply 的区别
fn.call(thisObj, arg1, arg2, arg3, …argN);
- thisObj –> 在fn函数内部的this指向(必需)
- arg1, arg2, arg3, …argN –> 函数fn在执行时,传入的实参。(可选)
fn.apply(thisObj, [array]);
- thisObj –> 在fn函数内部的this指向(必需)
- 数组对象 –> 将数组的所有元素值当做fn执行时的实参。(可选)
上下文模式应用
借用方法
- 数组合并
- 借用Math对象方法
- 获取对象类型
- 实现伪数组对象转换为真数组
实现借用构造函数继承
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15function Person(name, age, gender, high) {
this.name = name;
this.age = age;
this.gender = gender;
this.high = high;
}
function Teacher(name, age, gender, high, codeNo, cellphoneNo) {
Person.apply(this, Array.prototype.slice.call(arguments, 0, 4));
this.codeNo = codeNo;
this.cellphoneNo = cellphoneNo;
}
var t = new Teacher('guojing', 18, 'boy', '226', '91248', '13813813811');
console.log(t);改变this指向
构造函数的执行过程
- 先创建一个空对象
- 将构造函数的作用域 交给 上述对象 || this -> 空对象
- 开始执行构造函数内部代码
return this
注意: 在构造函数中,如果 显式 返回一个基本数据类型数据 会被忽略掉。当然返回 对象 就不会
实例成员与静态成员
- 实例成员:通过构造函数创建出来的对象上的成员,称为实例成员
- 静态成员:函数对象上的成员,被称为静态成员
区别:实例成员必须先创建实例,再访问;而静态成员直接通过函数的名字即可访问。
- 在实际开发时,如果遇到 工具类方法,此时可以考虑写成静态成员。
- 静态成员相对于实例成员好处为:不需要创建对象,直接通过函数名字来访问成员。
递归:函数调用自己
- 场景:在遍历 树形结构 时,优先考虑使用递归方式。
- 摸索规律,找到一个简单的方式来解决复杂问题 。
词法作用域
- 全局作用域
- 局部作用域
- 全局变量
- 局部变量
- 定义:变量的作用域是由 代码书写的位置 决定,而不是 变量被使用的位置 决定;只有函数可以限定作用域。
js中预解析
- 词法分析: 在此阶段,如果代码有语法错误会直接抛出异常。
- 变量名提升 和 函数名提升
函数声明最好不要放在if语句块内部。
- 在现代浏览器不会将函数声明提升
- 在早期浏览器会得到提升。
作用域种类
- 弱类型语言:大部分为静态作用域
- 强类型语言:动态作用域
in
- 语法: in 左边的操作数必须为 字符串类型,表示属性的名字,如果不是,会尝试将其转换成字符串,如果失败了,就报错。右边的操作数为对象。不为对象,报错。
- 含义:判断对象是否能访问到该属性,如果能,就返回 true,否则返回 false。
delete
- 可以删除对象的指定属性
- 注意: 在操作DOM对象时,只能删除通过”.”或”[]”,方式添加的自定义属性。
eval 与 Function
- 共同点
- 都可以将一个字符串当做一段js代码来执行。
- eval 具有作用域安全问题,有可能污染变量,而Function并不会。
- 可以肆意在全局上执行一段字符串代码,会造成网站不安全。
闭包
就是可以访问其他函数内部数据的函数。
- 在js中,函数内部的数据在外部无法访问。
- 为了访问到函数内部的数据,提出闭包技术。
闭包的作用
- 计数器
- 沙箱模式 – 匿名自调用函数。很多的框架都使用了沙箱模式
- 与外界隔离,即可以分割作用域
- 内部代码自执行。
- 在实际开发中,可以考虑将全局对象当做实参传入沙箱内部的变量,以提高 js 性能。
- 用例:如果在开发中,遇到只需要执行一次的代码块。可以将其放到沙箱内部。
- 缓存 – 优化fib递归调用性能
- 实现面向对象的封装
- 在Java 中,对象的属性可以添加访问修饰符,private, public,而在 js 并没这些关键字
Object.defineProperty
- 科里化:将一个 拥有多个参数 的函数转化成一个 单一参数 函数的形式。
闭包的优缺点
- 缺点: 常驻内存,增大内存的开销,使用不当就会造成内存泄漏。
- 优点:
- 缓存
- 实现封装性
- 避免污染全局对象
- 处理逻辑的连续性。
- 应用
- 参数复用
- 延迟计算/执行
- 提前返回
1 | // 闭包,返回sayAlert函数的执行需要依赖say667()函数的num变量,所以num变量在say667()函数执行后不会回收 |
变量的搜索原则
当访问一个变量时,
- 首先在当前作用域上查找,如果找到就直接使用,并停止查找;
- 如果没有找到,就会向上一级作用域上查找,如果找到就直接使用,并停止查找;
- 如果还没有找到就继续向上一级作用域查找,直到全局作用域,如果找到就直接使用,并停止查找;
- 否则报错(xxx is not defined.)
在变量搜索的时候,如果访问的是全局变量,那么会搜索整个作用域链。性能会降低。在实际开发时,可以将常用的全局对象传入局部变量内。在同级链上的变量,互相不能访问。