专栏首页达达前端一篇文章带你了解JavaScript中的函数表达式,递归,闭包,变量,this对象,模块作用域

一篇文章带你了解JavaScript中的函数表达式,递归,闭包,变量,this对象,模块作用域

file

作者 | Jeskson

来源 | 达达前端小酒馆

定义函数的方式:

第一种为 函数声明; 第二种为 函数表达式。

语法:

function functionName(arg0, arg1, arg2) {
 // 函数体
}

在Firefox,Safari,Chrome和Opera有效:

就是通过这个属性可以访问到这个函数指定的名字。

console.log(functionName.name); // 'functionName'

函数声明:

它的一个重要特点就是:函数声明提升,就是在执行代码前先读取函数声明,可以把函数声明放在调用它的语句后。

// 调用函数
dada();
// 函数声明
function dada() {
 console.log('dada');
}

file

使用函数表达式,函数表达式有多种不同的形式

var functionName = function(arg0, arg1, arg2){
 // 函数体
}

上面语句,用话语表示,创建一个函数,把它赋值给一个变量,这个函数,我们叫做匿名函数,因为没有函数名称,在关键字function后面是没有标识符的,匿名函数的name值,获取的结果为空字符串。

注意,函数表达式和其他表达式是一样的,需要在使用前必须赋值,否则:

// 调用
da();
var da = function() {
 console.log('dada');
}

file

函数声明和函数表达式区别,关键就是在函数提升

// 函数表达式
var dada;
if(name) {
 dada = function() {
  console.log('da1');
 };
}else{
 dada = function() {
  console.log('da2');
 };
}

file

file

递归函数

什么是递归函数,就是一个函数通过名字调用自身。

function da(num) {
 if(num < 5) {
  return 'da';
 }else {
  return num * da(num-2);
 }
}

闭包

闭包就是可以访问 另一个函数中的变量的 函数,创建闭包即是在一个函数内创建另一个函数。

JavaScript 闭包

JavaScript 变量可以是局部变量或全局变量。私有变量可以用到闭包。

闭包就是能够读取其他函数内部变量的函数。

例如在javascript中,只有函数内部的子函数才能读取局部变量,所以闭包可以理解成“定义在一个函数内部的函数“。

在本质上,闭包是将函数内部和函数外部连接起来的桥梁。

闭包 (closure)是个精确但又很难解释的电脑名词。

在 Perl 里面,闭包是以 匿名函数的形式来实现,具有持续参照位于该函数范围之外的文字式变数值的能力。这些外部的文字变数会神奇地保留它们在闭包函数最初定义时的值 (深连结)。

在Javascript中闭包的创建过程

function a(){
 var i=0;
  function b(){
  alert(++i);
  }
 return b;
}
var c=a();
c();

1、函数b嵌套在函数a内部; 2、函数a返回函数b。

面试官问我:什么是闭包,我该如何回答?

简单讲,就是指有权访问另一个函数作用域中的变量的函数。

它由两部分构成:函数,以及创建该函数的环境。环境由闭包创建时在作用域中的任何局部变量组成。

内存泄漏

闭包会引用包含函数的整个变量对象,如果闭包的作用域链中保存着一个HTML元素,那么就意味着该元素无法被销毁。我们有必要在对这个元素操作完之后主动销毁。

function da() {
 var element = document.getElementById('nameDa');
 var id = element.id;
 element.onclick = function() {
  console.log(id);
 };
 element = null;
}

函数内部的定时器

当函数内部的定时器引用了外部函数的变量对象时,该变量对象不会被销毁。

(function() { 
 var da = 0;
 setInterval(function() {
  console.log(da++);
 },1000);
})();

运用闭包的过程

闭包引用外部函数变量对象中的值,在外部函数的外部调用闭包。

求和的函数是这样定义的:

function sum(arr) {
    return arr.reduce(function (x, y) {
        return x + y;
    });
}

sum([1, 2, 3, 4, 5]); // 15

函数表示:

function lazy_sum(arr) {
    var sum = function () {
        return arr.reduce(function (x, y) {
            return x + y;
        });
    }
    return sum;
}

var f = lazy_sum([1, 2, 3, 4, 5]); // function sum()

f(); // 15

闭包特点:

让外部访问函数内部变量成为可能; 局部变量会常驻在内存中; 可以避免使用全局变量, 防止全局变量污染; 会造成内存泄漏 (有一块内存空间被长期占用,而不被释放)

每个执行环境都有一个表示变量的对象,变量对象,一般作用域链中包含两个变量对象,本地活动对象和全局变量对象,作用域链的本质就是一个指向变量对象的指针列表,它只引用但不实际包含变量对象。

在函数中访问一个变量时,会从作用域链搜索具有相同的名字的变量,一般地,当函数执行完成后,局部活动对象就会被销毁,内存中保存全局作用域。

一个内部函数会将它的外部函数的活动对象添加到它的作用域链中。闭包会带着它的函数的作用域,会占用更多的内存,多度使用闭包会导致内存占用过多。

函数表达式可以不用命名,就可以实现动态编程,函数表达式不需要名称,函数声明要求要有名字,没有名字的函数表达式叫做匿名函数,递归函数使用arguments.callee来递归地调用自身。

闭包的作用域链包含着自己的作用域,包含函数的作用域和全局作用域,一般,函数的执行后会被销毁,但是,函数返回一个闭包,这个函数的作用域将会一直在内存中保存到闭包不存在为止。

模块模式是为单例创建私有变量和特权方法。单例就是只有一个实例的对象,是以对象字面量的方法创建单例对象。

var da = {
 name: 'dada',
 eat: function() {
  // 代码
 }
};

私有建立和私有函数

var singleton = function(){ 
 //私有变量和私有函数
 var privateVariable = 10; 
 function privateFunction(){ 
 return false; 
 }
 //特权/公有方法和属性
 return { 
 publicProperty: true, 
 publicMethod : function(){ 
 privateVariable++; 
 return privateFunction(); 
 } 
 }; 
}();
var application = function(){ 
 //私有变量和函数
 var components = new Array(); 
 //初始化
 components.push(new BaseComponent()); 
 //公共
 return { 
 getComponentCount : function(){ 
 return components.length; 
 }, 
 registerComponent : function(component){ 
 if (typeof component == "object"){ 
 components.push(component); 
 } 
 } 
 }; 
}();
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; 
}();
var application = function(){ 
 //私有变量和函数
 var components = new Array(); 
 //初始化
 components.push(new BaseComponent()); 
 //创建 application 的一个局部副本
 var app = new BaseComponent(); 
 //公共接口
 app.getComponentCount = function(){ 
 return components.length; 
 }; 
 app.registerComponent = function(component){ 
 if (typeof component == "object"){ 
 components.push(component); 
 } 
 }; 
 return app; 
}();

模仿块级作用域

function outputNumbers(count){ 
 for (var i=0; i < count; i++){ 
 alert(i); 
 } 
 alert(i); //计数
}
function outputNumbers(count){ 
 for (var i=0; i < count; i++){ 
 alert(i); 
 } 
 var i; //重新声明变量
 alert(i); //计数
}

闭包包含的是整个变量对象

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

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

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

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

私有变量包含函数的参数,局部变量和函数内部定义的其他函数。

function add(num1, num2){ 
 var sum = num1 + num2; 
 return sum; 
}
function MyObject(){ 
 //私有变量和私有函数
 var privateVariable = 10; 
 function privateFunction(){ 
 return false; 
 } 
 //特权方法
 this.publicMethod = function (){ 
 privateVariable++; 
 return privateFunction(); 
 }; 
}
function Person(name){ 
 this.getName = function(){ 
 return name; 
 }; 
 this.setName = function (value) { 
 name = value; 
 }; 
} 
var person = new Person("dada"); 
alert(person.getName()); //"dada" 
person.setName("da"); 
alert(person.getName()); //"da"

静态私有变量

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

JavaScript this 关键字

面向对象语言中 this 表示当前对象的一个引用。

但在 JavaScript 中 this 不是固定不变的,它会随着执行环境的改变而改变。

在方法中,this 表示该方法所属的对象。 如果单独使用,this 表示全局对象。 在函数中,this 表示全局对象。 在函数中,在严格模式下,this 是未定义的(undefined)。 在事件中,this 表示接收事件的元素。 类似 call() 和 apply() 方法可以将 this 引用到任何对象。

this 总是返回一个对象,简单说,就是返回属性或方法“当前”所在的对象。

this.property上面代码中, this 就代表 property 属性当前所在的对象。

var obj = {
  foo: function () {}
};

var foo = obj.foo;

// 写法一
obj.foo()

// 写法二
foo()

虽然obj.foo和foo指向同一个函数,但是执行结果可能不一样

var obj = {
  foo: function () { 
  console.log(this.bar) 
  },
  bar: 1
};

var foo = obj.foo;
var bar = 2;

obj.foo() // 1
foo() // 2

file

var obj = { foo:  5 };

file

变量obj是一个地址(reference)。后面如果要读取obj.foo,引擎先从obj拿到内存地址,然后再从该地址读出原始的对象,返回它的foo属性。

file

file

file

http://www.ruanyifeng.com/blog/2018/06/javascript-this.html

在博客平台里,未来的路还很长,也希望自己以后的文章大家能多多支持,多多批评指正,我们一起进步,一起走花路。

非常感谢读者能看到这里,如果这个文章写得还不错,觉得「达达」我有点东西的话,觉得我能够坚持的学习,觉得此人可以交朋友的话, 求点赞? 求关注❤️ 求分享? 对暖男我来说真的

非常有用!!!

❤️ 不要忘记留下你学习的脚印 [点赞 + 收藏 + 评论]

作者Info:

【作者】:Jeskson 【原创公众号】:达达前端小酒馆。 【福利】:公众号回复 “资料” 送自学资料大礼包(进群分享,想要啥就说哈,看我有没有)! 【转载说明】:转载请说明出处,谢谢合作!~

大前端开发,定位前端开发技术栈博客,PHP后台知识点,web全栈技术领域,数据结构与算法、网络原理等通俗易懂的呈现给小伙伴。谢谢支持,承蒙厚爱!!!


若本号内容有做得不到位的地方(比如:涉及版权或其他问题),请及时联系我们进行整改即可,会在第一时间进行处理。


请点赞!因为你们的赞同/鼓励是我写作的最大动力!

欢迎关注达达的CSDN!

这是一个有质量,有态度的博客

前端技术栈

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

我来说两句

0 条评论
登录 后参与评论

相关文章

  • java中substring与substr的用法(转)

    1.substring 方法 定义和用法 substring 方法用于提取字符串中介于两个指定下标之间的字符。 语法 stringObject.substrin...

    似水的流年
  • Object类解析

    Object类是所有类的父类,任何类都默认继承Object,以下是Object的方法导图。

    用户6055494
  • 高可用架构 - 系统性能评估

    top 命令重要指标:load average,表示任务队列的平均长度(1分钟、5分钟、15分钟前到现在平均值)。

    dys
  • axios 拦截器显示和关闭Loading

    使用Loading分为2种情况,第一种是使用一些组件库自带的loading,另一种是使用我们自己写的loading,现分开介绍使用方法

    tianyawhl
  • 【领会要领】web前端-轻量级框架应用(jQuery基础)

    jquery的安装和语法,jquery的多种选择器,dom操作和jquery事件。

    达达前端
  • 初学者接触web前端需要注意什么?避免走上弯路

    初学Web前端要注意什么?如何学好JS模块化编程?JavaScript是前端三要素之一,也是很多初学Web前端的人遭遇的第一条拦路虎。很多同学表示JavaScr...

    用户5827212
  • vue.js-详解三大流行框架VUE_快速进阶前端大咖-Vue基础

    MVX模式简介: MVX框架模式:MVC+MVP+MVVM MVC: Model模型+View视图+Controller控制器

    达达前端
  • 再谈|Rowkey设计_HBase表设计

    HBase的rowkey设计可以说是使用HBase最为重要的事情,直接影响到HBase的性能,常见的RowKey的设计问题及对应访问为:

    Spark学习技巧
  • 为什么要学Python编程?(附Python学习路线)

    在此,估计不少开发者都会予以反驳,自己明明就没有选择 Python,不能一概而论。下面,我们就用数据一窥如今最流行的编程语言。

    AI科技大本营
  • Jmeter之json提取器实战(二)

    之前写过一篇文章【Jmeter篇】后置处理器之正则提取器、Json提取器 不是很完善,今天我们再来写一篇json提取器进行补充说明。

    橙子探索测试

扫码关注云+社区

领取腾讯云代金券