JavaScript-函数

88 阅读6分钟
function a (){
    
}

函数

函数实际上是对象

参数

  1. ECMAScript中,参数在内部是使用一个数组来表示,所以,参数可以传多个也没有问题,什么类型都可以

  2. 可以通过arguments对象,来访问这个参数数组

  3. arguments对象,不是Array, 但可以有length, 使用argument[index]来访问

  4. 它的值与对应命名的参数的值保持同步

    function a(num1, num2) {
        arguments[1] = 10;
        console.log(num2);//10
    }
    
    a(1, 2)
    

没有重载

  1. ECMAScript 定义了两个名字相同的函数, 该名字属于后定义的函数, 后一个函数,会覆盖前面同名的

    function a() {
        return arguments[0] + 100
    }
    
    function a() {
        return arguments[0] + 200
    }
    
    console.log(a(1))//201
    

函数名实际也是一个指向函数对象的指针

函数声明与函数表达式

//函数声明
function sum(){
    
}

//函数表达式
var sum = function(){
    
}
  • 解析器会率先读取函数声明, 并使其在执行任何代码之前可用

  • 函数表达式,必须等到解析器执行它所在的代码行,才会真正被解释执行

  • 函数声明提升

    //  sum...  函数声明提升到顶部
    console.log(sum(10,10))//20,  函数
    function sum(num1,num2){
        return num1+num2
    }
    
    //如果是表达式,会报错
    console.log(sum(10,10));
    var sum = function(){
        
    }
    

函数本身就是变量

  • 函数可以作为值来使用
  • 函数可以用于传递参数,返回结果

函数内部属性

  • arguments

    • callee 指针,指向拥有arguments对象的函数, 作用,无论函数名 如何修改,都可以正常使用

      function a(num){
          return num * arguments.calle(num-1) 
      }
      //等于
      function a(num){
          return num * a(num-1)
      }
      
  • this

    • this引用的就是函数执行的环境对象

    • 全局作用域中,this就是window

      window.color='red'
      var o = {color:'blue'}
      function sayColor(){
          alert(this.color)
      }
      sayColor();//red
      o.sayColor = sayColor;
      o.sayColor(); //'blue'
      
  • caller

    • 指向调用当前函数的函数的引用, 全局就是null
  • length 参数的个数

  • prototype 原型链

    • 保存所有的实例方法
    • 不可被枚举, for-in不会发现prototype属性

函数的非继承而来的方法

用于设置函数体内this对象的值,则设置函数的执行环境

  • apply

    • 两个参数,函数作用域,参数数组(可以是Array实例,也可以是arguments)

      function sum(num1,num2){
          return num1+num2
      }
      function callSum1(num1,num2){
          return sum.apply(this,arguments)
      }
      
  • call

    • 作用和apply一样,但是call方法,第一个参数是this值,其它参数依次传入

      function callSum(num1,num2){
          return sum.call(this,num1,num2)
      }
      
  • bind() ecmascript5

    • 绑定this到某个值

      window.color = 'red';
      var o = {color:'blue'};
      function sayColor(){
          console.log(this.color)
      }
      var a = sayColor.bind(o);
      a()//blue
      

用武之地:扩充函数运行的作用域, 解除耦合

window.color = 'red';
var o = {color:'blue'};
function sayColor(){
    console.log(this.color)
}

//扩充
sayColor.call(this); // red
sayColor.call(window) // red
sayColor.call(o) //blue
var a = sayColor.bind(o);
a()//blue

闭包

闭包是指有权访问另一个函数作用域中的变量的函数

创建方式

function a() {
    const a = 'ddd';
    return function () {
        console.log(this.a);//作用域链包含了外部作用域
    }
}
var aF= a();
aF();//ddd

理解

  1. 当某个函数被调用时,会创建一个执行环境,及相应的作用域链
  2. 使用arguments和其它命名参数的值来初始化函数的活动对象
  3. 作用域链中,外部函数的活动对象处于第二位,外部函数的外部函数的活动对象处于第三位,直至作为作用域终点的全局执行环境
  4. 作用域链本质上是一个指向变量对象的指针列表
  5. 一般来讲,当函数执行玩后,局部活动对象会被销毁,内存中仅保存全局作用域(全局执行环境的变量对象)
1.创建compare()函数时,会创建一个预先包含全局变量对象的作用域链,这个作用域链保存在内部的[[scope]]属性中
2.调用compare()函数时,会为函数创建一个执行环境,通过复制函数的[[scope]]属性中的对象构建执行环境的作用域链。
3.然后,有一个活动对象被创建推入执行环境作用域链的前端。
function compare(value1,value2){
    if(value1<value2){
        return -1;
    }
}
var result = compare(5,10);

闭包有所不同

function a() {
    const a = 'ddd';
    return function () {
        console.log(this.a);//作用域链包含了外部作用域
    }
}
var aF= a();
aF();//ddd

  • 当闭包的匿名函数的作用域链在外部函数a执行完后,a的活动对象没有被销毁,因为匿名函数还引用着这个活动对象
  • a函数返回后,其执行环境的作用域链会被销毁,但它的活动对象仍然留在内存中,被内部匿名函数引用着。

闭包与变量

  • 闭包只能包含函数中任何变量的最后一个值(现在执行,不存在这个问题,有待考究)
function createFunctions() {
    var result = new Array();
    for (let i = 0; i < 10; i++) {
        result[i] = function () {
              return i;
        };
    }
    return result;
}

const result = createFunctions();
console.log(result[7]());//7

function createFunctions1() {
    var result = new Array();
    for (let i = 0; i < 10; i++) {
        result[i] = function (num) {
          return function () {
              return num;
          }
        }(i);
    }
    return result;
}
const result = createFunctions1();
console.log(result[7]());//7

闭包的作用域链中保存着一个HTML元素,那么该元素就无法被销毁

function assignHandler(){
    var element = document.getElementById('someElement');
    element.onclick = function(){
        console.log(element.id)
    }
}

模仿块级作用域

利用匿名函数

经常用于全局作用域中被用在函数外部,限制向全局作用域中添加过多的变量和函数

(function(){
    //这里是块级作用域
})();

私有变量

任何函数中定义的变量,都可以认为是私有变量,包括函数的参数,局部变量,和在函数内部定义的其他函数

  • 特权方法,有权访问私有变量和私有函数的公有方法,可以隐藏那些不应该被直接修改的数据
function add(num1,num2){
    var num=1;
    function privateF(){
        return false;
    }
    //特权方法
    this.publicMethod= function(){
        num++;
        return privateF();
    }
}

静态私有变量

(function () {
    var name = ""
    //声明没有使用var, 创建一个全局变量,能够在私有作用域外被使用
    Person = function (value) {
        name=value;
    }
    Person.prototype.getName=function () {
        return name;
    }
    Person.prototype.setName=function (value) {
        name = value;
    }
})();

var person1 = new Person('Nicholas');
console.log(person1.getName());//'Nicholas'
person1.setName('Greg')
console.log(person1.getName());//'Greg'

var person2 = new Person('Michael');
console.log(person1.getName());//'Michael'
console.log(person2.getName());//'Michael'
//name变成了一个静态的,由所有实例共享的属性

模块模式与单例

  • javascript以对象字面量的方式创建单例对象

模块模式通过单例模式添加私有变量和特权方法

var singleton = function(){
    //私有变量和私有函数
    var privateVariable =10;
    function privateF(){
        return false;
    }
    //特权/公有方法和属性
    return {
        publicP:true,
        publicM:function(){
            privateVariable++;
            return privateF();
        }
    }
}();

增强的模块模式

适合单例必须是某种类型的实例

var application = function(){
    //私有变量和函数
    var components = new Array();
    //初始化
    components.push(new BaseComponent());
    //创建application 的一个局部副本
    var app = new BaseComponent();
    //公共接口
    app.getComponentCount = function(){
        return components.length;
    }
    return app;
}