一、为什么说函数是特殊的“对象”呢?
1. 因为函数可以通过Function()构造函数来定义:var f = new Function("x","y","return x*y;");
2. 函数属性:
1)length(形参个数)
2)prototype:每个函数都有这个属性!(引用)指向一个(原型)对象。每个函数都会有,构造函数也有。所以用构造函数去创建对象的时候,这个对象的原型是 构造函数.prototype这个属性的值。(这里边构造函数是对这个属性做个赋值了的)
3. 函数方法:函数对象的两个方法call()和apply();
二、函数的定义
1. 函数声明语句
function f(x){}这个是函数声明语句,它实际上是声明了一个变量f,并把一个函数对象:{} 赋值给它!
1) 这句话定义了一个变量名为f。并给它赋值(即指向函数体{}):这个定义创建了一个函数对象,并将其赋值给变量f。函数的名字实际上是可不见的,f仅仅是一个变量名而已,这个变量名指向(引用)函数对象。
2)不管这个函数声明放在哪里,它在整个脚本全局域里都可见。因为这里有提升的机制在里面:函数名和函数体(赋值)均被提前到(顶部)。
这也是为什么可以调用自己的原因了。
3) 不同于变量提升,变量提升只是把声明提升,但是赋值定义却没有提升。
4.)函数声明语句非真语句。不能插在任何语句之中。而函数定义表达式可以出现在任何地方,因为他们自带;分号自己本身就是语句了。
2. 函数表达式(匿名函数方式)
var f = function(x){};这是函数表达式(原始)
1)如果在这个表达式之前调用了f(x);会报错。因为只提升了变量f的声明,变量的赋值初始化也就是绑定函数对象没有被提升,所以f还是个undefined,还不具备函数性质。
2)当函数表达式加上了名称 var a = function f(x){};实际上函数的名称f将成为函数内部的一个局部变量。
3)function(x){}就是一个匿名函数那它怎么调用呢?
A. 如果这里,把他赋值给 f 。这样f就能去引用它。所以我们可以直接用f(x)进行调用。
B. 自调用。把匿名函数整改放在这样的表达式括号里();并在这个表达式括号中匿名函数的尾部加上()就是自调用。(function(x){..}(3)); 或者(function(x){..})(3);
C. 匿名函数最大的用途是创建闭包(这是JavaScript语言的特性之一),并且还可以构建命名空间,以减少全局变量的使用。
3. 构造函数的方式
var f = new Function("x","y","return x*y;");
4. 当一个函数定义之后JavaScript会自动给这个函数的原型添加一个constructor属性并并赋值这个函数的类。
三、函数调用
默认是undefined,如果有return something 就返回具体值,没有return 也是undefined。
1. 自调用 (function(){}());——》b=function(){} ——》(b());——》b();
2.
四、特殊函数
1. 构造函数(类)
首先第一个概念:类 就是构造函数,构造函数就是类 //不是java里面的类里面有构造函数。
var F = function(){};
var obj = New Object();
F.prototype = obj; //继承其他类
F.prototype.construtor = F;
//现在添加自己的
F.prototype.x = function(){...};
F.prototype.y = function(){...};
var f = new F();
f.construtor ——》F.prototype.construtor = F ——》f.construtor = F;
f.prototype = Object() 另外f还有自有属性 x 和 y。
当不继承任何人时
可以是
var F = function(){....};
var f = New F();
F.prototype = {....} = f;
F.prototype.construtor = F;
F.prototype = f
f.construtor= F
2. 匿名函数与自调用
匿名匿名就是没有名字的函数定义:function(){};
自调用:(function(){}());或者(function(){})();
2. 工厂函数
五、闭包和作用域链概念
1. 作用域链
1)首先我们从全局出发<script>中,其实就是一个全局函数变量(如果C的main()函数一样),我们在其中定义的所有Function定义的函数(它里面也有可能嵌套)也都是嵌套在这个全局函数而已,所以整个js代码我们可以从某种程度上看成一系列的函数嵌套过程。既然有嵌套,随之就是有嵌套深度,那,怎么表现这个深度呢,我们这里就可以用(函数对象)作用域链来表示。记住js和其他语言不同,不是块作用域,而是函数作用域。这条作用域上保存这个这些嵌套的函数对象,我们可以定义全局函数对象在作用域链中是第1层,随着嵌套依次叠加。
2)函数调用时作用域链起到的作用:
作用域链在函数定义的时候决定的(也就是在定义这个函数的时候它的作用域随之确定,函数被绑定在上面)。调用时依赖这个作用域,所以何时何地调用,它只会在这个作用域里去找它需要的变量。如果函数定义是变量就在这个作用域上,它就跑不掉了(闭包)。就好像,一个家谱(作用域链)一样,(函数)你生下来,就被写进了一个家谱,不管你身在何方,中国还是美国,你何时何地都可以(打电话)问家里(问爸妈问爷爷奶奶)要钱(变量)。但是不能跟同级的姐妹或者自己的子女要。
当函数调用需要找变量时,从自身出发(作用域链最高层)开始找,如果自己有了,就拿,没有就会沿着作用域链一层层往下找,直到第0层(全局函数变量)为止。
3)变量提升定义是在函数体内的声明的所有变量在函数体内任何地方都是可见的。按照正常的习惯我们现在函数顶部先var声明好变量并定义好,但是有些“变量”比较调皮,没有var声明变量之前,它先在前面直接使用,但是后面它又想起来了,在函数后面补上var 并赋值。这是可以的,因为js的规则是在所用函数执行之前,把所有变量的声明都统一提升到函数顶部。(虽然js帮忙提升,也就只是声明提升,我们知道只声明不赋值的变量是undefined,所以前面引用的那个会是undefined但如果没有var声明但赋值了,后面又追加声明,它可是拿到赋值的值。只有在真正他想起来赋值的地方,这个变量才算真正有意义)。
2. 闭包
1)(从全局开始一直嵌套)的所有函数对象可以通过作用域链相互关联起来,函数体内部的变量都可以保存在作用域内叫闭包。如果函数定义是变量就在某个作用域上,它就跑不掉了。何时何地调用函数,它都能在自己相应的作用域链上找到自己所需要的变量。这个就是闭包在js中起到的作用。
1. 在函数中定义里一个局部变量a和一个嵌套函数f()绑定在一起,不管在何时何地只要能调用到f(),就一定用到这个变量a。这样的a变量我们可以叫私有变量。
2. 如何把两个闭包放在一个作用域中,然后能共享同样的私有变量?
七、call()和apply()和bind()(点击可查看)