6.函数


函数的天生属性length

函数的length等于行参的个数

function fn(a,b,c,d,e){}
console.log(fn.length)	//5

函数执行过程

  1. 为函数创建一个执行环境

  2. 复制函数的 [[scopes]] 属性中的对象构建起执行环境的作用链域

  3. 创建函数活跃对象并推入执行环境作用链域的前端

  4. 执行代码

  5. 销毁执行环境和活跃对象(闭包情况下活跃对象仍被引用没被销毁)

函数的声明方式

表达式声明

表达式声明时,其实分为两步,先声明变量,再把函数赋值给这个变量,后面的函数作为值并不会发生声明提升

var fn=function (){

}
var fn =function fn(){

}

字面量声明

function fn(){

}

函数的参数

注意:

形参可以理解为在函数体内部声明了一个新的变量

形参与实参的关系可以理解为形参=实参(注意:实参是引用数据类型时,实际传的是引用数据类型的地址)

var a=1;
function fn(a){	//形参可以理解为var a=1;
	a=2;		//形参a重新赋值为2
}
fn(a)
var a=[1];		//假设[1]的地址为0X100,0x100指向[1];
function fn(a){	//形参可以理解为var a=0X100;
	a=2;		//形参a重新赋值为2
	//假设[2]的地址为0x200
	a=[2];		//形参a重新赋值为0x200
}
fn(a)

参数分类

函数的参数分为形参和实参:

形参可以理解为在函数体内部声明了一个新的变量,形参的值可以理解为定义了一个新变量接收了实参的值(或地址)

实参是调用作用域内对应的同名数据(可以是任何类型),也可以是手动输入的数据,实参传递给函数时,函数体内部有个arguments属性会将实参收集起来组成一个类数组

形参和实参可以个数不相等,当形参的个数大于实参时,多余的形参为undefined,当实参的个数大于形参时,除了arguments有变化外没有任何影响

var a=1,b=1;
function fn(a,b){
	
}
fn(a,b)		//把变量a和b的值当作实参传给形参
//类似这样
function fn(){
	var a=a,b=b;		//把变量a,b的值赋值给形参	
}

函数的参数表达式

函数的形参如果有实参传值则为实参的值,如果没有则为表达式的值

var a=1,b=2;
function fn(c=a?b:a,d=b){
console.log(c,d);
}
fn(a)

函数的返回值

函数的return可以理解为函数调用表达式==return;

闭包函数

闭包是什么?

深层理解:

闭包是拥有外层函数对象所对应的活动对象引用的函数对象

一般理解:

闭包是嵌套函数的内部函数,包含外部函数的变量(对象)的函数对象

怎么判断闭包?

内部函数包含外部函数的变量,这个内部函数被返回并调用(通常会被赋值给别人);

闭包的形成

https://zhuanlan.zhihu.com/p/66701792

函数调用的时候会在栈空间开辟一块空间,这个空间叫函数执行环境,一般函数调用完毕出栈后,其内的活跃对象会被垃圾回收机制回收,但是当这个活跃对象被其他函数的scope引用时,活跃对象将无法释放,这样就形成了闭包(此时这个活跃对象只能被闭包函数找到)

闭包函数的执行过程

当闭包函数调用进栈时,产生一个新的活跃对象,这个活跃对象scope属性的__parent__指向外部函数产生的活跃对象,当我们要修改某个变量(或引用类型)的时候,在当前作用域找不到,就往上一级找,一直到找到后修改这个变量(或引用类型)

当闭包调用结束出栈,闭包自身的活跃对象会被销毁,但是外部函数的活跃对象仍然被闭包函数自身的scope引用,依旧无法释放.

闭包代码加图分析

function addAge(){
    var age = 21;
    return function(){
        age++;
         console.log(age);
     }
}
var clourse = addAge();
clourse();

第一阶段

第二阶段

第三阶段

第四阶段

第五阶段

递归函数

定义

直接或间接的调用自身的函数称为递归函数

分析

首先,函数的return可以理解为函数调用表达式==return;

分析如下代码:

function fn(n){
    if(n==1){
        return n;
    }
    return n+fn(--n);
}
console.log(fn(5));

函数执行过程为:

fn(5)=5+fn(4);

fn(4)=4+fn(3);

fn(3)=3+fn(2);

fn(2)=2+fn(1);

fn(1)=1;

//综上所述
fn(5)=5+4+3+2+1;

回调函数

什么是回调函数?

一个函数(假设为a)被作为参数传递给另一个函数(假设为b),a函数在b函数中被调用。这个a函数就是回调函数

回调函数特点

(1)自己定义的函数

(2)你没有调用

(3)最终它执行了

  function fn(){
  }
  function fn1(func){
      func();
  }
  fn1(fn);  //这个fn就是回调函数

特殊的回调函数

dom0的事件回调函数是以以下形式写的

on事件=事件回调

构造函数

构造函数通过new关键字调用

new关键字的执行顺序

new关键字一直到遇到的第一个()为一个表达式,如果没有碰到括号,则默认调用表达式最后的方法

new a.b.c;
//等同于
//new a.b.c();
new new a.b().c();
//等同于
new ( new a.b() ).c();
new Person()

我认为的new Person()执行过程

1.new person()开辟一块执行环境

2.以person的prototype为原型创建一个对象

3.改变this指向,指向第二步创建的对象,调用person方法同时传参

4.person调用结束拿到person返回值同时销毁person的函数执行环境,判断person的返回值是否是引用数据类型,不是就返回第二步创建的对象(实例),执行完毕销毁执行环境.

构造函数当成普通函数图解

new做的事情图解

? 1、开辟内存空间(堆)

? 2、this指向该内存(让函数内部的this)

? 3、执行函数代码

? 4、生成对象实例返回

new关键字做了什么:

1.以构造函数的prototype属性为原型,创建新对象;

2.使用指定的参数调用构造函数,并将 this 绑定到新创建的对象

3.如果构造函数没有手动返回对象(引用类型),则返回第一步创建的对象(实例),否则返回手动设置的返回值

实现一个简单的new方法

// 构造器函数
let Parent = function (name, age) {
    this.name = name;
    this.age = age;
};
Parent.prototype.sayName = function () {
    console.log(this.name);
};
//自己定义的new方法
let newMethod = function (Parent, ...rest) {
    // 1.以构造函数的prototype属性为原型,创建新对象;
    let child = Object.create(Parent.prototype);
    // 2.使用指定的参数调用构造函数,并将 `this` 绑定到新创建的对象
    let result = Parent.apply(child, rest);
    // 3.如果构造函数没有手动返回对象(引用类型),则返回第一步创建的对象(实例),否则返回手动设置的返回值
    return typeof result  === 'object' ? result : child;
};
//创建实例,将构造函数Parent与形参作为参数传入
const child = newMethod(Parent, 'echo', 26);
child.sayName() //'echo';

//最后检验,与使用new的效果相同
child instanceof Parent//true
child.hasOwnProperty('name')//true
child.hasOwnProperty('age')//true
child.hasOwnProperty('sayName')//false
child.__proto__===Parent.prototype//true
let a=1,b=2,c=3;
    function fn(a,b,c){
    }
    function new1(...str){
    	//1.创建一个空的简单JavaScript对象(即`{}`)
        let obj={};
        //2.为步骤1新创建的对象添加属性`__proto__`,将该属性链接至构造函数的原型对象 
        obj.__proto__=fn.prototype;
        //3.将步骤1新创建的对象作为`this`的上下文
        let result=fn.call(obj,...str);
        //4.如果该函数没有返回对象,则返回`this`
        return result instanceof Object==='object'?fn():obj;
    }
    let fn1=new1(a,b,c);
    console.log(Object.getPrototypeOf(fn1)===fn.prototype);

相关