资讯专栏INFORMATION COLUMN

《JavaScript高级程序设计》笔记:函数表达式(七)

awesome23 / 1027人阅读

摘要:闭包闭包是指有权访问另一个函数作用域中的变量的函数。我们可以通过创建另一个匿名函数强制让闭包的行为符合预期。不过,匿名函数的执行环境具有全局性,因此其对象通常指向。

递归

</>复制代码

  1. function factorial(num){
  2. if(num<=1){
  3. return 1;
  4. }else {
  5. return num * arguments.callee(num-1);
  6. }
  7. }
  8. console.log(factorial(4));

但是如果代码是在严格模式下开发:

</>复制代码

  1. "use strict";
  2. function factorial(num){
  3. if(num<=1){
  4. return 1;
  5. }else {
  6. return num * arguments.callee(num-1);
  7. }
  8. }
  9. console.log(factorial(4));

结果:Uncaught TypeError: "caller", "callee", and "arguments" properties may not be accessed on strict mode functions or the arguments objects for calls to them

在严格模式下不能通过脚本访问arguments.callee,访问这个属性会报错,那么可以使用命名函数表达式来达到相同的结果:

</>复制代码

  1. "use strict";
  2. var factorial = (function f(num){
  3. if(num<=1){
  4. return 1;
  5. }else {
  6. return num * f(num-1);
  7. }
  8. })
  9. console.log(factorial(4)); //24

以上代码创建了一个名为f()的命名函数表达式,然后将它赋值给变量factorial,即是把函数赋值给另外一个变量,函数的名字仍然有效。

闭包

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

闭包与变量

作用域链的这种配置机制引出了一个值得注意的副作用,即闭包只能取得包含函数中任何变量的最后一个值。别忘了闭包所保存的是整个变量对象,而不是某个特殊的变量。

</>复制代码

  1. function createFunctions(){
  2. var result = new Array();
  3. for (var i=0; i<10; i++){
  4. result[i] = function(){
  5. return i;
  6. }
  7. }
  8. return result;
  9. }

我们可以通过创建另一个匿名函数强制让闭包的行为符合预期。

</>复制代码

  1. function createFunctions(){
  2. var result = new Array();
  3. for (var i=0; i<10; i++){
  4. result[i] = function(num){
  5. return function(){
  6. return num;
  7. };
  8. }(i);
  9. }
  10. return result;
  11. }
关于this对象

在全局函数中,this等于window,而当函数被作为某个对象的方法调用时,this等于那个对象。不过,匿名函数的执行环境具有全局性,因此其this对象通常指向window。

</>复制代码

  1. var name = "The window";
  2. var object = {
  3. name: "My Object",
  4. getNameFunc: function(){
  5. return function(){
  6. return this.name;
  7. };
  8. }
  9. };
  10. console.log(object.getNameFunc()()); // The window

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

</>复制代码

  1. var name = "The window";
  2. var object = {
  3. name: "My Object",
  4. getNameFunc: function(){
  5. var that = this;
  6. return function(){
  7. return that.name;
  8. };
  9. }
  10. };
  11. console.log(object.getNameFunc()()); // My Object

看下面代码:

</>复制代码

  1. var name = "The window";
  2. var object = {
  3. name: "My Object",
  4. getName: function(){
  5. console.log(this.name);
  6. }
  7. }
  8. object.getName(); // My Object
  9. (object.getName)(); // My Object
  10. (object.getName = object.getName)(); // The window

来分析下调用的结果:

第一行代码跟平常一样调用了object.getName()返回了My Object ,因为this.name就是object.name。

第二行代码在调用这个方法之前给它加了一个括号。虽然加了一个括号后,就好像只是在引用一个函数,但是this的值得到了维持,因为object.getName(object.getName)的定义是相同的。

第三行代码先执行了一条赋值语句,然后再调用赋值后的结果。因为这个赋值表达式的值是函数本身,所以this的值不能得到维持,结果就返回了The window

当然你不大可能像第二行和第三行代码一样调用这个方法。这个例子只是说明了一个细微的语法变化,都有可能意外的改变this的值。

内存泄露

</>复制代码

  1. function assignHandler(){
  2. var element=document.getElementById("someElement");
  3. element.onclick=function(){
  4. alert(element.id);
  5. }
  6. }

上述代码它所占用的内存不会永远消失。修改一下代码如下解决:

</>复制代码

  1. function assignHandler(){
  2. var element = document.getElementById("someElement");
  3. var id = element.id;
  4. element.onclick = function(){
  5. alert(id);
  6. }
  7. element = null;
  8. }
模仿块级作用域

用块级作用域(通常称为私用作用域)的匿名函数的语法如下所示:

</>复制代码

  1. (function(){
  2. })();
私有变量

</>复制代码

  1. function add(num1,num2){
  2. var sum=num1+num2;
  3. return sum;
  4. }

在这个函数内部,有三个私有变量:sum,num1,num2。在函数内部可以访问这几个变量。但是在函数外部则不能访问它们。如果在这个函数内部创建一个闭包,那么闭包可以通过自己的作用域链也可以访问这些变量。而利用这一点,就可以创建用于访问私有变量的公有方法。

我们把有权访问私有变量和私有函数的公有方法称为特权方法。有两种在对象上创建特权方法的方式。第一种是在构造函数中定义特权方法。基本模式如下:

</>复制代码

  1. function myObejct(){
  2. //私有变量和私有函数
  3. var privateVariable=10;
  4. function privateFunction(){
  5. return false;
  6. }
  7. //特权方法
  8. this.publicMethod=function(){
  9. privateVariable++;
  10. return privateFunction();
  11. }
  12. }

利用私有和特权成员,可以隐藏那些不应该被直接修改的数据,例如:

</>复制代码

  1. function Person(name){
  2. this.getName=function(){
  3. return name;
  4. }
  5. this.setName=function(value){
  6. name=value;
  7. }
  8. }
  9. var person=new Person("Nicholas");
  10. alert(person.getName());//Nicholas
  11. person.setName("Greg");
  12. alert(person.getName());//Greg
静态私有变量

通过在私有作用域中定义私有变量或函数,同样也可以创建特权方法。其基本模式如下:

</>复制代码

  1. (function(){
  2. //私有变量和私有函数
  3. var privateVariable=10;
  4. function privateFunction(){
  5. return false;
  6. }
  7. //构造函数
  8. MyObject=function(){
  9. };
  10. //公有/特权方法
  11. MyObject.prototype.publicMethod=function(){
  12. privateVariable++;
  13. return privateFunction();
  14. }
  15. })();

再看一个例子:

</>复制代码

  1. (function(){
  2. var name = "";
  3. Person = function(value){
  4. name = value;
  5. };
  6. Person.prototype.getName = function(){
  7. return name;
  8. };
  9. Person.prototype.setName = function(value){
  10. name = value;
  11. };
  12. })();
  13. var person1 = new Person("Nicholas");
  14. console.log(person1.getName()); //Nicholas
  15. person1.setName("Grey");
  16. console.log(person1.getName()); //Grey
  17. var person2 = new Person("Michael");
  18. console.log(person1.getName()); //Michael
  19. console.log(person2.getName()); //Michael

在一个实例上调用setName()会影响所有的实例。

模块模式

模块模式是为单例创建私有变量和特权方法。所谓单例,指的就是只有一个实例的对象。按照惯例,js是以对象字面量的方式来创建单例对象的。

</>复制代码

  1. var singleton={
  2. name:value,
  3. method:function(){
  4. //这里是方法的代码
  5. }
  6. };

模块模式通过为单例添加私有变量和特权方法能够使其得到增强。其语法形式如下:

</>复制代码

  1. var singleton=function(){
  2. //私有变量和私有函数
  3. var privateVariable=10;
  4. function privateFunction(){
  5. return false;
  6. }
  7. //特权/公有属性和方法
  8. return {
  9. publicProperty:true,
  10. publicMethod:function(){
  11. privateVariable++;
  12. return privateFunction();
  13. }
  14. }
  15. }();
增强的模块模式

</>复制代码

  1. var singleton=function(){
  2. //私有变量和私有函数
  3. var privateVariable=10;
  4. function privateFunction(){
  5. return false;
  6. }
  7. //创建对象
  8. var object=new CustomType();
  9. //添加特权/公有属性和方法
  10. object.publicProperty=true;
  11. object.publicMethod=function(){
  12. privateVariable++;
  13. return privateFunction();
  14. }
  15. //返回这个对象
  16. return object;
  17. }();

文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。

转载请注明本文地址:https://www.ucloud.cn/yun/87646.html

相关文章

  • 26天学通前端开发(配资料)

    摘要:网上有很多前端的学习路径文章,大多是知识点罗列为主或是资料的汇总,数据量让新人望而却步。天了解一个前端框架。也可以关注微信公众号晓舟报告,发送获取资料,就能收到下载密码,网盘地址在最下方,获取教程和案例的资料。 前言 好的学习方法可以事半功倍,好的学习路径可以指明前进方向。这篇文章不仅要写学习路径,还要写学习方法,还要发资料,干货满满,准备接招。 网上有很多前端的学习路径文章,大多是知...

    blair 评论0 收藏0
  • JavaScript红宝书笔记)---Function类型

    摘要:函数实际上是对象。所以需要消除这种紧耦合。函数内部属性引用的是函数据以执行的环境对象或者也可以说是值函数的名字仅仅是一个包含指针的变量而已。因此,即使是在不同的环境中执行,全局的函数与函数指向的仍然是同一个函数。 1.函数实际上是对象。每个函数都是 Function 类型的实例,而且都与其他引用类型一样具有属性和方法 2.由于函数名仅仅是指向函数的指针,因此函数名与包含对象指针的其他变...

    cyrils 评论0 收藏0
  • SegmentFault 技术周刊 Vol.30 - 学习 Python 来做一些神奇好玩的事情吧

    摘要:学习笔记七数学形态学关注的是图像中的形状,它提供了一些方法用于检测形状和改变形状。学习笔记十一尺度不变特征变换,简称是图像局部特征提取的现代方法基于区域图像块的分析。本文的目的是简明扼要地说明的编码机制,并给出一些建议。 showImg(https://segmentfault.com/img/bVRJbz?w=900&h=385); 前言 开始之前,我们先来看这样一个提问: pyth...

    lifesimple 评论0 收藏0
  • javascript高级程序设计》第六章 读书笔记javascript对象的几种创建方式

    摘要:三种使用构造函数创建对象的方法和的作用都是在某个特殊对象的作用域中调用函数。这种方式还支持向构造函数传递参数。叫法上把函数叫做构造函数,其他无区别适用情境可以在特殊的情况下用来为对象创建构造函数。 一、工厂模式 工厂模式:使用字面量和object构造函数会有很多重复代码,在此基础上改进showImg(https://segmentfault.com/img/bVbmKxb?w=456&...

    xiaotianyi 评论0 收藏0

发表评论

0条评论

awesome23

|高级讲师

TA的文章

阅读更多
最新活动
阅读需要支付1元查看
<