第三周
(第八章)
finish
1:第八章(函数)
函数是一段:只定义一次,但是可以被执行或调用任意次的javascript代码。每次调用函数时会产生一个值—调用上下文(this指向)。若函数挂载在一个对象上,作为对象的一个属性,则称为对象方法。调用函数时this指向该对象。
1、函数定义
定义&&声明:1
2
3
4
5
6
7
8
9// 定义表达式
var fun = function() {}
// 生命表达式
function fun() {}
// 自我调用:(此时一定要有一个终止条件,防止无限递归,造成内存泄漏)
function self() {
if(xxx) return;
slef();
}
2、函数调用:
1、调用方式:
1、作为函数 -》function a() {}
2、作为方法 -》obj.a = function(){}
3、作为构造函数 -》new A(){}
4、通过call/apply间接调用。 a.call(context, arguments);
2、构造函数调用:
构造函数调用创建一个新的空对象(或者是包含构造函数中this指向的属性对象);
这个对象继承自构造函数的prototype(obj.__proto__ === Objcet.prototype)。
构造函数试图初始化这个新创建的对象,并将这个对象用作其调用上下文。
即构造函数中的this指向这个新对象。如果构造函数使用return返回一个值。则新实例则等于这个返回值。
3、间接调用:
通过apply/call间接的调用方法并指定具体的调用上下文(this)
3、函数的参数:
1、arguments是指向实参对象的引用。且它是一个类数组对象。
2、在严格模式下的arguments:
举例验证严格与非严格模式下arguments的区别:
1 | function a(x) { |
3、callee和caller属性:
是argument的自带属性。callee指代当前正在执行的函数。caller则指代调用当前在执行函数的函数。
在严格模式中不可用。caller是非标准,但是浏览器厂商大多实现了。
4、作为值得函数:
函数不仅是一种语法也是值。也就是说函数可以赋值给变量。
即function x(fun) {fun();} => 传入fun是一个方法。
提前剧透:一般函数式编程会把函数当作一个值来传入一个函数,并且像操控对象一样操控它。
5、闭包:
JavaScript采用的是词法作用域。也就是说,函数的执行依赖于变量作用域。这个作用域是在函数定义的时候就决定的。而不是在调用的时候确定的。
为了实现这种逻辑:函数对象的内部不仅包含代码逻辑,还必须引入当前的作用域链。
函数对象可以通过作用域链相互关联起来,函数体内部的变量都可以保存在函数作用域内。(闭包)
复习作用域链:
假设将局部变量看做成一个自定义对象的属性(obj.x)。这个自定义对象可看作当前作用域链的指向(或者可以理解为挂载)对象
每一段js代码都有一个关联作用域链。当使用局部变量时,可以理解为在当前作用域对象上找该属性,如果没找到,顺着作用域链继续向上查找。直到找到顶部,若没有则报错。
所有作用域链的顶部都死global全局对象
一个正常函数作用域链包含两个对象。一个是当前函数作用域对象,另一个是全局对象。
当是嵌套函数时,至少包含3个作用域对象。
当定义一个函数时,其实是保存了一个作用域链,但此时当前函数的作用域对象没有挂在该作用域链上。
当执行一个函数时,函数会创建一个新对象,将所有变量属性挂在它身上,并且该对象被添加在之前保存的作用域链上。
在内嵌函数中,每次调用外部函数,内部函数的作用域链都会新生成(都是不同的)。因为每次调用外部作用域链也是不同的。
闭包:调用函数时所指向的作用域链和定义时的作用域链不是同一个。(即外部函数将嵌套函数作为返回值返回时)。
我的理解是在定义时生成了一个作用域链-》function p() {return function c(){}};
而使用时,首先 var c = p(); 此时生成了一个关于c对象的新作用域链。所以作用域链有所不同。
1 | // 举个例子: |
6、函数的属性、方法、构造函数
1、函数的length:
代表函数期望的实参个数。而arguments.length则代表函数实际传入形参的个数。
2、prototype:
每个函数都有该属性,该属性指向一个对象,该对象即为原型对象。当函数用作构造函数时,新创建的对象就会将该原型对象挂在自己的原型链上。
3、call/apply:
第一个参数为执行函数上下文的指定(this指向)。在严格模式中,this严格按照传入的值指定。在非严格模式中,若传入的是null或undefined则this指向的是全局对象。
4、bind:
将函数绑定在某个对象上(第一个参数传入的值机位该对象)
若将bind用作构造函数,则将忽略绑定的对象,而是以构造函数的方式进行调用。
bind生成的函数无prototype属性,所以新实例将继承原始构造函数的prototype。
bind参数为持续绑定的(实参在调用时被绑定)。举例:
1 | // 非严格模式下(严格模式null会报错) |
5、toString:
一般返回函数的完整源码字符串形式。内置函数返回的则是一个[native code];
6、Function()构造函数:
可以在运行时动态创建函数并编译。
它创建的函数的this永远指向顶层。
eg: var f = new Function('x', 'y', "return x * y;") =>
var f = function(x, y) {return x * y};
7、可调用对象:
可以调用执行但不是真正的函数对象(概念类似类数组)
举例document.getElementById.
Regexp();
7、函数式编程:
在Javascript中,可以像操控对象一样操控函数,不关心函数发生的状态,而只关心得到的结果。即为函数式编程。
类似Array.map(function(el) {});传入了函数,但是开发者不知道map对函数做了怎样的处理。只知道返回的el参数为数组的每个元素。
1、高阶函数:
接受一个或多个函数,并返回一个新函数以供调用。eg:
1 | function a(f, g) { |
2、不完全函数:
直接上例子:
fun(1, 2, 3, 4) => fun(1, 2)(3, 4);
3、记忆:
利用闭包保存局部变量的技术,将已经处理过的值保存在局部变量中,并且在下一次使用函数时,优先查询变量中是否含有该值。
1 | function m(f) { |
总结:
1、this指向调用上下文
2、
1)定义表达式:var a = function(){}; 声明表达式:function a() {};
2)函数名称自我调用
3)定义表达式出现任意位置,但是声明表达式不能出现在循环中。
3、
1)函数的四种调用方式。
2)嵌套函数的this不指向调用他的函数。若是方法调用则this指向调用他的对象,若是函数调用this指向全局或者undefined
3)构造函数的实例上下文为自己。vm.__proto__ == Function.prototype;
4、
1)arguments指向实参对对象引用。
2)严格模式中arguments无法修改形参的值,非严格模式中可以。
5、
1)函数作用域是在定义时确定的。
2)函数对象通过作用域链连接,并可以将变量保存在内部。这类使用方法我们称为闭包
3)定义和调用函数不是相同作用域链
4)每次定义表达式后,都会形成新的作用域链。
6、
1)call/apply的第一个参数是调用上下文(this指向)
2)bind不立即执行,持续绑定传入的形参。(其实可以理解为定义时,作用域对象就把传入的形参绑定在自身属性了)
3)bind生成的函数的length是函数参数个数(绑定过的个数)。
4)函数的length是定义时需要的形参个数。
5)bind方法生成的函数无prototype,所以生成实例原型链挂载的是指向原函数的原型对象。
6)Function构造函数生成的作用域对象为Global。
7、
1)像操控对象一样操控函数,不关心传入函数的内容和状态称为函数式编程模式。
2)高阶函数,接受一个或多个函数并返回函数。
3)不完全函数;
4)记忆:函数中利用闭包来保留一个对象用于存储历史数据。