JavaScript高级程序设计读书感想——第七章

Yelena ·
更新时间:2024-09-21
· 501 次阅读

一.前言

这一章我们讨论函数表达式。主要涉及函数表达式的特征、闭包和私有变量等内容。

二.匿名函数

我们知道创建一个函数有两种方法:

//函数声明 function function1(a,b,c){ ...... } //函数表达式 var function1 = funcition(a,b,c){ ...... }

我们将第二种情况下创建的函数叫做匿名函数

三.闭包

什么是闭包:闭包是有权访问另一个函数作用域中的变量的函数
创建闭包的常见方式:在一个函数内部创建另一个函数

function createComparisonFunction(propertyName){ return function(object1,object2){ var value1 = object1[propertyName] var value2 = object2[propertyName] if(value1 value2){ return 1; } else { return 0; } } }

在这个例子中,即使这个内部函数在其他的地方被调用,仍然可以访问属性
propertyName,因为内部函数的作用域中包含createComparsionFunction这个函数的作用域

一般来说,当函数执行完毕后,函数内部的活动对象会被销毁,只留下全局作用域。但是当有闭包时,情况有所不同: 在外部函数执行结束后,他的作用域链会被销毁,但是他的活动对象仍然在内存中。只有匿名函数被销毁后,外部函数才会被销毁。在这里插入图片描述

闭包的副作用:闭包只能取得外部函数中任何变量的最后一个值。

function createFunction(){ var result = new Array() for(var i=0;i<10;i++){ result[i] = function(){ return i; } } return result; }

我们希望得到的是一个长度为10,数值分别为0-9的数组,但是实际上循环这么多个的内部函数,每个都是返回10。因为每个闭包函数作用域中都保存着外部函数的活动对象。他们引用的都是同一个i。外部函数返回时,i的值是10,所以每个内部函数的i的值也为10。

那么如何解决这个问题呢:

function createFunction(){ var result = new Array() for(var i=0;i<10;i++){ result[i] = function(num){ return function(){ return num; } }(i) } return result; }

我们又定义了一个匿名函数,并将立即执行该匿名函数的结果赋值给了数组。在这里为什么i是从0-9,因为我们是在最内部定义了一个闭包的匿名函数,用于访问num。而外面一层function(num)是按值传递的。会将i的当前值赋值给参数num。

闭包中this指向的问题:匿名函数的执行具有全局性,因此this对象通常指向window。但因为闭包的编写方式不同,可能会让人产生误解。

var name = 'hjh' var object = { name:'lym', getNameFunc:function(){ return function(){ return this.name } } } alert(object.getNameFunc()()) //'hjh'

按道理说,我们在object对象中定义了name属性,同样是在object对象中定义的函数,this应该指向object中的name才对,为什么会指向window中的name属性呢?我们之前说过,每个函数调用时,都会取得两个特殊变量:this和argument。内部函数在搜索这两个变量时,只会搜索到活动对象为止。因此不会访问到外部函数中的这两个变量,也无法取到外部函数中的定义的变量值。

那么我们想要取到外部函数中的变量和属性怎么办呢:

var name = 'hjh' var object = { name:'lym', getNameFunc:function(){ var that = this return function(){ return that.name } } } alert(object.getNameFunc()()) //'lym'

注意:我们将this赋值给that,此时的this是在object中定义的,指向的也是object。所以我们此时访问的都是外部函数中的变量和属性

四.模仿块级作用域

我们之前提到,JavaScript没有块级作用域的概念。比如在函数中定义一个循环,变量i在函数内部任何地方都可以获取。那么有没有办法实现私有作用域呢?

function outputNumber(count){ (function(){ for(var i=0;i<count;i++){ alert(i) } }) } alert(i) //报错

我们在for循环外插入了一个私有作用域。在匿名函数中定义的任何变量,都会在执行结束后被销毁。而在私有作用域中之所以能访问count,是因为这个匿名函数是一个闭包,他能访问外部函数作用域中的变量。

五.私有变量

任何在函数中定义的变量,都可以认为是私有变量,因为不能再函数的外部访问这些变量。私有变量包括:函数的参数、局部变量、在函数内部定义的其他函数。

那么怎么在函数外部访问这些变量呢:我们可以创建一个闭包,闭包通过自己的作用域链可以访问这些变量

我们把有权访问私有变量和私有函数的方法称为特权方法。有两种在对象上创建特权方法的方式:
1.构造函数中的特权方法

function Person(name){ this.getName = function(){ return name }; this.setName = function(){ name = value }; } var person = new Person('nico'); alert(person.getName()) //'nico' person.setName('lym') alert(person.getName()) //‘lym'

这其实就是我们在第五章讲的构造函数的方法,缺点很明显**:针对每个实例都会创建一组新方法**。

2.静态私有变量

(function()){ var name = '' Person = function(value){ name = value } Person.prototype.getName = function(){ return name } Person.prototype.setName = function(){ name = value } })(); var person1 = new Person('nico') alert(person1.getName()) //'nico' person1.setName('lym') alert(person1.getName()) //'lym' var person2 = new Person('hjh') alert(person1.getName()) //'hjh' alert(person2.getName()) //'hjh'

这个例子中,变量name变成一个静态的,由所有实例共享的属性在一个实例上调用原型上的方法,所有的实例对象都会一起改变,正是和我们之前提到的原型模式相同

六.模块模式

我们刚刚提到的两种方法,都是用于为自定义类型创建私有变量和特权方法的。我们现在要说的模块模式,是为单例创建私有变量和特权方法。所谓单例指的就是只有一个实例的对象。

var singletion = function(){ //私有变量和函数 var privateNum = 10 function privateFunction(){ return false } //特权方法 return{ publicMethod:function(){ privateNum ++; return privateFunction() } } }

这种模式适用于什么情况呢:如果必须创建一个对象并以某些数据对其进行初始化,同时还要公开一些能够访问这些私有数据的方法,就可以适用模块模式


作者:lovimogene



程序设计 程序 读书 JavaScript

需要 登录 后方可回复, 如果你还没有账号请 注册新账号
相关文章