《Javascript 高级程序设计(第三版)》笔记0xB 函数表达式

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/funkstill/article/details/84977448

目录

递归

闭包

    闭包与变量

    关于this对象

    内存泄漏

模仿块级作用域

私有变量

       特权方法(privileged method)

    静态私有变量

扫描二维码关注公众号,回复: 4490442 查看本文章

    模块模式

    增强的模块模式


        创建一个函数并将它赋值给变量 functionName。这种情况下创建的函数叫做匿名函数(anonymous function),因为 function 关键字后面没有标识符。匿名函数有时候也叫拉姆达函数,name 属性是空字符串。

//不要这样做!一般后面的会覆盖前面的
if(condition){
  function sayHi(){
    alert("Hi!");
  }
} else {
  function sayHi(){
    alert("Yo!");
  }
}
//可以这样做
var sayHi;
if(condition){
  sayHi = function(){
    alert("Hi!");
  };
} else {
  sayHi = function(){
    alert("Yo!");
  };
}

递归

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

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);
  }
});

闭包

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

//创建闭包的常见方式,就是在一个函数内部创建另一个函数
function createComparisonFunction(propertyName) {
	return function(object1, object2){
		var value1 = object1[propertyName];//访问了外部函数中的变量 propertyName
		var value2 = object2[propertyName];
		if (value1 < value2){
			return -1;
		} else if (value1 > value2){
			return 1;
		} else {
			return 0;
		}
	};
}
作用域链本质上是一个指向变量对象的指针列表,它只引用但不实际包含变量对象。
function compare(value1, value2){
  if (value1 < value2){
    return -1;
  } else if (value1 > value2){
    return 1;
  } else {
    return 0;
  }
}
var result = compare(5, 10);

        无论什么时候在函数中访问一个变量时,就会从作用域链中搜索具有相应名字的变量。一般来讲,当函数执行完毕后,局部活动对象就会被销毁,内存中仅保存全局作用域(全局执行环境的变量对象)。但是,闭包的情况又有所不同。

//创建函数
var compareNames = createComparisonFunction("name");
//调用函数
var result = compareNames({ name: "Nicholas" }, { name: "Greg" });
//解除对匿名函数的引用(以便释放内存)
compareNames = null;

    闭包与变量

        作用域链的这种配置机制引出了一个值得注意的副作用,即闭包只能取得包含函数中任何变量的最后一个值。

/*表面上看,似乎每个函数都应该返自己的索引值,即位置0的函数返回0,位置1的函数返回1,以此类推。但实际上,每个函数都返回 10。
因为每个函数的作用域链中都保存着createFunctions()函数的活动对象,所以它们引用的都是同一个变量i。 
当createFunctions()函数返回后,变量 i 的值是 10,此时每个函数都引用着保存变量 i 的同一个变量对象,
所以在每个函数内部 i 的值都是 10。*/
function createFunctions(){
  var result = new Array();
  for (var i=0; i < 10; i++){
    result[i] = function(){
      return i;
    };
  }
  return result;
}
//通过创建另一个匿名函数强制让闭包的行为符合预期
function createFunctions(){
  var result = new Array();
  for (var i=0; i < 10; i++){
    result[i] = function(num){
      return function(){
        return num;
      };
    }(i);
  }
  return result;
}

    关于this对象

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

        每个函数在被调用时都会自动取得两个特殊变量: this 和 arguments。内部函数在搜索这两个变量时,只会搜索到其活动对象为止,因此永远不可能直接访问外部函数中的这两个变量。

        不过,把外部作用域中的 this 对象保存在一个闭包能够访问到的变量里,就可以让闭包访问该对象了。

var name = "The Window";
var object = {
  name : "My Object",
  getNameFunc : function(){
    var that = this;
    return function(){
      return that.name;
    };
  }
};
alert(object.getNameFunc()()); //"My Object"

    内存泄漏

         由于 IE9 之前的版本对 JScript 对象和 COM 对象使用不同的垃圾收集例程因此闭包在 IE 的这些版本中会导致一些特殊的问题。具体来说,如果闭包的作用域链中保存着一个HTML 元素,那么就意味着该元素将无法被销毁。

function assignHandler(){
    var element = document.getElementById("someElement");
        element.onclick = function(){
        alert(element.id);
    };
}

模仿块级作用域

        JavaScript 没有块级作用域的概念。这意味着在块语句中定义的变量,实际上是在包含函数中而非语句中创建的。

function outputNumbers(count){
  for (var i=0; i < count; i++){
    alert(i);
  }
  alert(i); //计数
}

        在 Java、 C++等语言中,变量 i只会在 for 循环的语句块中有定义,循环一旦结束,变量 i 就会被销毁。可是在 JavaScrip 中,变量 i是定义在 ouputNumbers()的活动对象中的,因此从它有定义开始,就可以在函数内部随处访问它。

        JavaScript 从来不会告诉你是否多次声明了同一个变量;遇到这种情况,它只会对后续的声明视而不见(不过,它会执行后续声明中的变量初始化)。匿名函数可以用来模仿块级作用域并避免这个问题。

/*
*  (function(){
*    //这里是块级作用域
*  })();
*以上代码定义并立即调用了一个匿名函数。将函数声明包含在一对圆括号中,表示它实际上是一个函数表达式。
*而紧随其后的另一对圆括号会立即调用这个函数。
*/
function outputNumbers(count){
  (function () {
    for (var i=0; i < count; i++){
      alert(i);
    }
  })();
  alert(i); //导致一个错误!
}

(function(){
  var now = new Date();
  if (now.getMonth() == 0 && now.getDate() == 1){
    alert("Happy new year!");
  }
})();

私有变量

        严格来讲,JavaScript 中没有私有成员的概念;所有对象属性都是公有的。不过,倒是有一个私有变量的概念。任何在函数中定义的变量,都可以认为是私有变量,因为不能在函数的外部访问这些变量。私有变量包括函数的参数、局部变量和在函数内部定义的其他函数。

function add(num1, num2){
  var sum = num1 + num2;
  return sum;
}
/*
*    在这个函数内部,有 3 个私有变量: num1、 num2 和 sum。在函数内部可以访问这几个变量,但在
*函数外部则不能访问它们。如果在这个函数内部创建一个闭包,那么闭包通过自己的作用域链也可以访问这
*些变量。而利用这一点,就可以创建用于访问私有变量的公有方法。
*/

       特权方法(privileged method)

//在构造函数中定义特权方法
function MyObject(){
  //私有变量和私有函数
  var privateVariable = 10;
  function privateFunction(){
    return false;
  }
  //特权方法
  this.publicMethod = function (){
    privateVariable++;
    return privateFunction();
  };
}

    静态私有变量

//在私有作用域中定义私有变量或函数,同样也可以创建特权方法
(function(){
	//私有变量和私有函数
	var privateVariable = 10;
	function privateFunction(){
		return false;
	}
	//构造函数
	MyObject = function(){
	};
	//公有/特权方法
	MyObject.prototype.publicMethod = function(){
		privateVariable++;
		return privateFunction();
	};
})();

    模块模式

//通过为单例添加私有变量和特权方法能够使其得到增强
var singleton = function(){
	//私有变量和私有函数
	var privateVariable = 10;
	function privateFunction(){
		return false;
	}
	//特权/公有方法和属性
	return {
		publicProperty: true,
		publicMethod : function(){
			privateVariable++;
			return privateFunction();
		}
	};
}();

    增强的模块模式

var singleton = function(){
	//私有变量和私有函数
	var privateVariable = 10;
	function privateFunction(){
		return false;
	}
	//创建对象
	var object = new CustomType();
	//添加特权/公有属性和方法
	object.publicProperty = true;
	object.publicMethod = function(){
		privateVariable++;
		return privateFunction();
	};
	//返回这个对象
	return object;
}();

猜你喜欢

转载自blog.csdn.net/funkstill/article/details/84977448