欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

函数表达式

程序员文章站 2022-03-18 20:09:51
...

函数表达式

函数提升

先了解一下提升的概念:提升-> 是JS 将所有声明提升到当前作用域顶部的默认行为(提升到当前脚本或当前函数的顶部)

  • 关于函数声明:一个重要特征就是函数声明提升(在代码执行之前先读取函数声明
sayHi();
function sayHi(){
alert("Hi!");
}

复习:定义函数的两种方式:函数声明、函数表达式

var functionName = function(arg0, arg1, arg2){
//函数体
};
  • 这个函数叫做匿名函数(function关键字后面没有标识符)=>匿名函数的name属性时空字符串
sayHi(); //错误:函数还不存在
var sayHi = function(){
alert("Hi!");
};

在把函数当成值来使用时,都可以使用匿名函数,(比如将函数作为其他函数的值返回)

递归(与其他语言一样就是调用自身的过程)

  • 以一个阶乘函数的递归为例:
function factorial(num){
if (num <= 1){
return 1;
} else {
return num * arguments.callee(num-1);
}
}

arguments.callee 是一个指向正在执行的函数的指针,因此可以用它来实现对函数的递归调用

  • 也可以用命名函数表达式:
var factorial = (function f(num){
if (num <= 1){
return 1;
} else {
return num * f(num-1);
}
});

闭包

创建闭包的常见方式,就是在一个函数内部创建另一个函数(创建闭包必须维护额外的作用域,所以过度使用它们可能会占用大量内存)

  • 全局变量能够通过闭包实现局部
    不通过关键词 var 创建的变量总是全局的,即使它们在函数中创建。

  • 变量的生命周期:

    • 全局变量和应用程序(窗口、网页)一样的生命周期
    • 局部变量在函数调用时被创建函数完成后删除
  • 以一个简单的计数器为例来理解闭包:

// 初始化计数器
var counter = 0;

// 递增计数器的函数
function add() {
  var counter = 0; 
  counter += 1;
}

// 调用三次 add()
add();
add();
add();

//此时计数器应该是 3。但它是 0。
// 递增计数器的函数
function add() {
  var counter = 0; 
  counter += 1;
  return counter;
}

// 调用三次 add()
add();
add();
add();

//此时计数器应该是 3。但它是 1。
var add = (function () {
    var counter = 0;
    return function () {return counter += 1;}
})();

add();
add();
add();

// 计数器目前是 3

理解:(既要做到不能让其他函数更改cnt的值即必须处于一个函数内不能处于全局不然就可能被修改,又要做到能增加计数,所谓“函数的私有变量”的实现)=>闭包也可以理解为指的是有权访问父作用域的函数,即使在父函数关闭之后

  • 而闭包有权访问函数内部的所有变量的原理如下:
    • 闭包的作用域链包含着它自己的作用域、包含函数的作用域全局作用域
    • 通常,函数的作用域及其所有变量都会在函数执行结束后被销毁
    • 当函数返回了一个闭包时,这个函数的作用域将会一直在内存中保存到闭包不存在为止

this

  • 在方法中,this 指的是所有者对象。
  • 单独的情况下,this 指的是全局对象。
  • 在函数中,this 指的是全局对象。
  • 在函数中,严格模式下,this 是 undefined。
  • 在事件中,this 指的是接收事件的元素

匿名函数的执行环境具有全局性,因此this对象通常指向window

  • 一个例子的理解:
    函数表达式

这个返回结果是the window,匿名函数的全局性并且,每个函数在被调用时自动取得:this和arguments,内部函数在搜索这两个变量时,只会搜索到其活动对象为止。

函数表达式

将外部作用域的this对象保存在闭包,就可以让闭包访问,因此这个例子的结果是the object,当然如果想要arguments也是如此

var name = "The Window";
var object = {
name : "My Object",
getName: function(){
return this.name;
}
};
object.getName(); //"My Object"
(object.getName)(); //"My Object"
(object.getName = object.getName)(); //"The Window",在非严格模式下

理解第三句话:赋值语句有返回值,这个语句返回的是object.getName(后面没有括号,即函数不执行只是传递了引用),所以返回指向的函数体本身,原式其实相当于(function(){return this.name})();而该段代码的调用者是window

模仿块级作用域

  • 模仿的语法格式:
(function(){
//这里是块级作用域
//作用:限制向全局作用域中添加过多的变量和函数,即避免内部临时变量影响全局空间
})();

理解:将函数声明包含在()中,表示它实际是一个函数表达式(注:函数表达式后面可以跟(),要将函数声明转换成函数表达式,只要用()将函数声明包括起来即可),而紧随其后的另一对()会立即调用这个函数。

  • 所以(function(){})();可以理解成:
var someFunction=function(){};
someFunction();

此时要注意模拟创建块级作用域后在匿名函数中定义的任何变量都会在执行结束时被销毁,例如:

function outputNumbers(count){
(function () {
for (var i=0; i < count; i++){
alert(i);
}
})();
alert(i); //导致一个错误!
}

在私有作用域中能够访问变量 count ,是因为这个匿名函数是一个闭包,它能够访问包含作用域中的所有变量

私有变量

即使 JavaScript 中没有正式的私有对象属性的概念,但可以使用闭包来实现公有方法,而通过公有方法可以访问在包含作用域中定义的变量——《JavaScript高级程序设计》

  • 任何在函数中定义的变量,都可以认为是私有变量,因为不能在函数的外部访问这些变量。(但是没有私有成员的概念)私有变量包括
    • 函数的参数
    • 局部变量
    • 在函数内部定义的其他函数
  • 特权方法(闭包)
    • 概念:有权访问私有变量和私有函数的公有方法
    • 两种方式:
      • 使用构造函数模式、原型模式来实现自定义类型的特权方法
      • 使用模块模式、增强
        的模块模式来实现单例的特权方法
  • 以构造函数方式为例:(基本模式如下)
function MyObject(){
//私有变量和私有函数
var privateVariable = 10;
function privateFunction(){
return false;
}
//特权方法
this.publicMethod = function (){
privateVariable++;
return privateFunction();
};
}

1、能够在构造函数中定义特权方法,是因为特权方法作为闭包有权访问在构造函数中定义的所有变量和函数
2、这个例子中,变量 privateVariable 和函数 privateFunction() 只能通过特
权方法 publicMethod() 来访问

相关标签: javascript