007——函数表达式

函数的定义方式有两种:一种是函数声明,另一种是函数表达式。


//函数声明:

function functionName(arg0, arg1, arg2){

// 函数体;

}

函数声明最重要的特征就是函数声明提升,意思就是执行代码之前会先读取函数声明。这就意味着可以把函数声明放在调用它的语句后面


sayHi();

function sayHi(){

alert("Hi");

}

以上例子不会报错。


// 函数表达式

var functionName = function(arg0, arg1, arg2) {

// 函数体

}

看起来像是常规的变量赋值语句,即创建一个函数并将它赋值给变量functionName。

以上创建函数的方式叫做匿名函数,也叫拉姆达函数,因为function后面没有标识符,也可以写成


var functionName = function aaa(arg0, arg1, arg2){

//函数体

}

韩式表达式与其他表达式一样,在使用前必须先赋值。


sayHi(); // 报错,函数名不存在

var sayHi(){

alert("Hi");

}

7.1 递归

递归函数是在一个函数通过名字调用自身的情况下构成的。


function factorial(num){

if(num <= 1){

return 1;

} else {

return num * factorial(num - 1);

}

}

一个问题: 先将factorial赋值给一个变量,再将factorial指向null


var anotherFactorial = factorial;

factorial = null;

alert(anotherFactorial(4)) // 报错!只有第一次anotherFactorial指向factorial,之后factorial指向null

arguments.calllee表示指向正在执行的函数的指针


function factorial(num){

if(num <= 1){

return 1;

} else {

return num * arguments.calllee(num - 1); // 替换factorial

}

}

严格模式不支持arguments.calllee,可使用命名函数方式,达到相同效果:


var factorial = (function f(num){

if(num <= 1){

return 1;

} else {

return num * f(num - 1); // f()函数表达式

}

});

7.2 闭包

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

创建闭包常见方式:在一个函数内部创建另一个函数


function createComparisonFunction(propertyName){

return function(object1, object2){

var val1 = object1[propertyName];

var val2 = object2[propertyName];

if(val1 < val2){

return 1;

} else {

return 0;

}

}

}

作用域链本质上是指向变量对象的指针列表,它只引用不实际包含变量对象。

一般情况下,函数执行完毕后,局部活动对象会被销毁,内存中仅保存全局作用域【全局执行环境的变量对象】。

但是闭包的情况不同。

在另一个函数内部定义的函数将包含函数(即外部函数)的活动对象添加到它的作用域链中。在createComparisonFunction()函数内部定义的匿名函数的作用域链中,实际上将会包含外部函数createComparisonFunction()的活动对象,如下图:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-h96IKCPH-1615212121716)(evernotecid://41E469EA-0EFB-49ED-9229-E2CD30653BA7/appyinxiangcom/7805470/ENResource/p112)]

匿名函数从createComparisonFunction()中被返回后,作用域链被初始化为包含createComparisonFunction()函数的活动对象和全局变量对象。这样,匿名函数就可以访问在createComparisonFunction()中定义的所有变量。

更为重要的,createComparisonFunction()函数执行完毕之后,其活动对象也不会被销毁,因为匿名函数的作用域链仍然在引用这个活动对象。

换句话说,当createComparisonFunction()函数返回后,其执行环境的作用域链会被销毁,但它的活动对象仍会留在内存中;直到匿名函数被销毁后,createComparisonFunction()的活动对象才会被销毁。


// 创建函数

var compareNames = createComparisonFunction("name");

// 调用函数

var result = compareNames({name:"Nicoles"},{name:"Greg"});

//解除对匿名函数的引用(以便释放内存)

compareNames = null;

7.2.1 闭包与变量

闭包能取得包含函数中任何变量的最后一个值。


function createFunctions(){

var result = new Array();

for(var i=0; i<10; i++){

result[i] = function(){

return i;

}

}

return result;

}

乍看似乎每个函数应该返回自己的索引值。但实际结果都返回10。

因为每个函数的作用域链中都保存着createFunctions()函数活动对象。,所以他们都是引用的同一个变量i。

通过新增立即执行匿名函数可以解决上述问题:


function createFunctions(){

var result = new Array();

for(var i=0; i<10; i++){

result[i] = function(num){

return function(){

return num;

}

}(i);

}

return result;

}

7.2.2 关于this对象

全局函数中this等于window,而当函数被作为某个对象方法调用时,this等于那个对象。

匿名函数的执行环境具有全局性,因此其this指向window。但有时,编写闭包方式不同,这一点不那么明显。


var name = "The Window";

var object = {

name: "My Object",

getNameFunc: function(){

return function(){

return this.name;

}

}

};

alert(object.getNameFunc()()); // "The Window"(非严格模式下)

函数在被调用时,都会自动取得两个特殊变量this,arguments。

内部函数在搜索这两个变量时,只会搜索到其活动的对象为止

如果把外部作用域的this临时赋给that等变量,使得闭包能够访问得到


var name = "The Window";

var object = {

name: "My Object",

getNameFunc: function(){

var that = this;

return function(){

return that.name;

}

}

};

alert(object.getNameFunc()()); // "My Object"(非严格模式下)

几个特殊情况:


var name = "The Window";

var object = {

name: "My Object",

getName: function(){

return this.name;

}

}

alert(object.getName()); // "My Object"

alert((object.getName)()); // "My Object"

alert((object.getName = object.getName)()); // "The Window"

// 第三行赋值语句,this的值被改变,不再指向object

7.2.3 内存泄漏

闭包在IE9之前版本中,如果闭包的作用域链中保存着一个HTML元素,那么久意味着该元素无法被销毁。


function assignHandlee(){

var element = document.getElementById('someElement');

element.onclick = function(){

alert(element.id);

}

}

通过将element.id指向一个变量id,再闭包中引用这个变量id以消除循环引用。最后将element指向null,就能消除对DOM对象的引用,确保垃圾回收正常进行。

7.3 模仿块级作用域

匿名函数可以用来模仿块级作用域【通常称为私有作用域】并避免这个问题。


(function(){

// 这里是块级作用域

}()) // 立即执行匿名函数

7.4 私有变量

js中没有私有成员的概念,所有对象属性都是公有的。

不过,在函数中定义的变量,都可以认为是私有变量,因为不能在函数外部访问这些变量。

私有变量包括:

  1. 函数的参数

  2. 局部变量

  3. 函数内部定义的其他函数


function(num1, num2){

var sum = num1 + num2;

return sum;

}

其中,num1、num2、sum都是私有变量。

能够访问私有变量和私有属性的公有方法叫做特权方法


function MyObj(){

// 私有变量和私有函数

var privateVal = 10;

function privateFunc(){

return false;

}

// 特权方法

this.publicMethod = function(){

privateVal++;

return privateFunc();

}

}

7.4.1 静态私有变量

通过静态私有变量定义私有变量或函数,创建特权方法,不会每次创建同样的新方法。


// ... 私有变量、私有函数声明

MyObj.prototype.publicFunc = function(){

privateVal++;

return privateFunc();

}

7.4.2 模块模式

静态私有变量导致视力共享所有属性,导致实例没有自己的私有变量。

对象字面量方式创建单例对象:


var singleton = {

name: value,

method: function(){

// 这里是代码方法

}

}

模块模式通过为单例添加私有变量和特权方法使其得到增强


var singleton = function(){

// 私有变量和私有函数

var privateVal = 10;

function privateFunc(){

return false;

}

// 特权/公有方法和属性

return {

publicProperty: value,

publicMethod: function(){

privateVal++;

return privateFunc();

}

};

}();

如果必须创建一个对象,并以某些数据对其进行初始化

同时,还要公开一些能够访问这些私有数据的方法就可以使用模块模式。

7.4.3 增强的模块模式

单例必须是某种指定类型的实例,同时还必须添加某些属性和(或)方法对其加以增强。


var singleton = function(){

// 私有变量和私有函数

var privateVal = 10;

function privateFunc(){

return false;

}

// 创建对象

var obj = new CustomType();

// 特权/公有方法和属性

obj.publicProperty = true;

publicMethod: function(){

privateVal++;

return privateFunc();

};

// 返回对象

return obj;

}();

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值