函数的高级特性

函数的高级特性(高阶函数)

作用域

背景:在开发中定义变量,你可以在函数内部定义变量,函数内部定义变量只能函数内部使用,函数内部也可以使用外部定义的变量。这种特性是因为作用域造成。

定义:定义了一个变量的可见性和声明周期。

分类:根据变量作用的范围,我们一般将作用域分为两种,全局作用域(代码写在js文件最外层),函数作用域(函数内部 局部)

为什么要有作用域:

  1. 避免了代码混乱
  2. 有时候所有对象函数都要用到内容,申明到全局,但是有时候仅仅函数内部使用。用完了就需要销毁。需要全局和局部一起作用。
  3. 全局变量不容易被回收。

全局变量和局部变量

局部变量就是函数中定义只能函数自己访问。

function add(){
	var i = 1;
}
console.info(i); 

变量的提升

JavaScript对于变量的定义有一个细节,变量会默认提升。一般在使用变量之前都会先定义这个变量,如果没有定义正常流程就是报错(not defined)

但是在js中如果你先输出变量,在定义变量。默认输出的结果undefined。原因就js中使用var关键字来声明变量,默认会有变量提升。

console.inf9(number)var number = 10

当我们浏览器或者node来执行代码的时候,第一步将目前定义好的全局变量先在内存里面定义出来。默认分配的空间为undefined。我们可以先输出在定义。当你执行到 变量定义的代码,这个时候动态将值覆盖内存默认undefined。内存空间会根据你的结果来进行扩容。

变量的提升:var关键字来定义变量。

定义变量的时候,不要将var省略掉。

在js中定义变量的时候没有加上var关键字,默认这个变量全局变量。在不同的运行环境中。这个变量表现不一致。

  1. 在浏览器运行代码的时候,没有var声明的变量 默认将这个变量绑定window对象。在浏览运行代码的时候,输出this----window窗口对象。
  2. 在node中运行代码的时候,没有var声明变量,默认将这个变量绑定了全局(global)对象,在node中运行代码的时候。输出this—

变量在函数内部定义,如果要变量提升,提升到函数的顶部。

如果变了是全局作用域,变量提升就是提升到全局作用域顶部。

函数作用域链

背景:定义全局变量的时候,我们函数内部是可以跑到外面来获取这个变量。函数内部还有函数。那他们的变量寻址就遵循作用域链。一层层的外上找。找到就返回。找不到报错。

var number = 10function show(){
	console.info(number)
}

/**
 * 函数内部嵌套函数
 */
var a = 1;
function add(){
    var a = 2;
    console.info(a);
    //内部定义的函数
    function cheng(){
        console.info(a);
    }
    cheng();
}

add();
console.info(a);

当我们需要某个变量的时候,js首先会在自己的局部空间里面去寻找变量,找到了就直接使用,如果没有找到变量,则到作用域莲中的第二个作用域去找,一次类推。直到找到这个变量。我们将这种结构称为作用域链。

IIFE

IIFE: Immediately Invoked Function Expression 含义表示立即执行函数。也就是说,在定义函数的时候立即调用。

基础语法

function(){
    
})();

(function(){
    
}())

//IIFE和箭头函数
(()=>{
    console.info("小王")
})();

一旦定义IIFE函数,运行代码就立即执行。

IIFE函数名和参数

在IIFE中尽量使用匿名函数,不管传统函数箭头函数。但是你在写代码过程中非的给IIFE里面的执行函数加上函数名字也可以支持。

var a = 2;
(function show(){
	console.info(a);
})()

为什么有IIFE

  1. 早期的js会出现一些问题,js只有函数作用域和全局作用域,并不存在块作用。从ES6开始才有块作用域的概念。ES5中我们可以使用IIFE来模拟出块作用域。减少函数的定义和调用。维护代码的结构
  2. 有些函数设计出来可能只会执行一次,这种函数你还要单独定义,还要单独调用。直接使用IIFE来进行优化。代码结构友好。

ES6新特

ES6新特性分成两部分:基础部分

ES6进阶部分:

let

在ES6新增了一个关键字let关键字,这个关键字是用来定义变量的。在es5中变量的定义都用var来定义。

作用:新增了一个作用域,弥补了var关键字的不足(历史原因)

使用:直接使用let可以定义变量,如果是在一个块中{},使用let表示这个块的变量。局部变量。

let特点:

  1. 块级作用域
  2. 不存在变量提升
  3. 暂时性死区
  4. 同一个作用域不能使用let重复定义变量。
块作用域
for(let i=0;i<10;i++){
	console.info(i);
}
console.info(i);
for(let i=0;i<10;i++){
    // 只控制将函数放在数组中
    a[i] = function(){
        console.info(i);
    }
}
a[0]()
a[1]()
a[2]()

在js循环中,用let关键,变量就变成块作用域,在块中我们每次循环都会将这个变量保存起来。当你要用的时候在找到对应i这样就可以输出i的值。

为什么需要块作用域?

  1. 解决var定义变量默认提升,在代码中有可能会污染当前这个变量。变量泄露(用完本来就消失,var不会消失)问题。
  2. 使语法更加规范,设计更加合理。
变量提升
let num = 10;
console.info(num);

使用let的时候不会出现变量提升的情况。在函数里面也不会出现变量提升。

暂时性死区

只要在块级作用域中有let,它所声明的变量就会绑定到这个块区域。不在受外部变量的影响、

什么是暂时性死区(temporal dead zone):

在一个块区间,你先使用变量,在来使用let变量变量。let前面的代码就是一个死区。必须先定义变量在使用。这样就不会产生死区。死区只是用于描述我们编程这种异常的概念。

typeof x
let x;
重复定义
if(true){
    let a = 2;
    let a = 3;
    console.info(a);
}

const

ES6提出 一个关键字,let一样都是用来定义当前程序中需要使用内容。在程序要使用圆周率PI,PI这种值一般是固定不变的。定义一个值,但是这个值在程序一般不会改动。我们将这种在程序中运行过程一直保持边的量称为常量。

常量和变量的区别,变量的值随时改变。let可以用定义变量

const关键字用来定义常量。

const PI = 3.1415926;
let number = 23.5;
number = 35.6; //执行成功。
PI = 4.56789 //报错
在整个程序运行期间,PI值都不能被改变。

作用:定义一个只读的量。这个一般和生活实际相关

特点:一旦const定义过后的值,就不能在更改。具有let大部分的特点

- 变量不提升
- 不允许重复定义
- 块作用域
const a =10;
function show(){
	console.info(a);
    const a = 20;
}
show();

上面也会产生一个TDZ(暂时性死区)。

规范:

在开发中,一般使用const定义都是常量。为了在开发的时候区分常量和变量,我们会将const定义的量大写。

在代码块中单独定义常量的时候,最好是大写,在循环中没有大写。

const PI = 3.14;
const WEEK = 7;
const a = [1,2,3]
console.info(a.length);

const arr = [1,2,3]
console.info(arr.length);
// arr = null;
console.info(arr);

const arr2 = [1,2,3]
arr2[0] = 45;
console.info(arr2);

const引用一个对象,当前这个常量值不能发生变化,但是引用的对象内部如果发生变化,不影响这个常量。

定义了常量,以后开发要用到这个常量就直接使用,如果初始化改了这个值,一处改处处改。

闭包

闭包在以前的面试中是一种非常重要的点,以前开发很多问题需要使用闭包来解决问题,但是ES6里面有很多方案可以替换闭包。现在闭包也有使用场景,相对来说我们有更好的那就会放弃闭包。

设计一个计数器:

let count = 0;
function add(){
	count++;
}
add();
console.inf(count);
function getCount(){
	let count = 0;
	function add(){
		return count++;
	}
	return add;
}

闭包来实现计数器

/**
 * 计数器
 */
function getCount(){
	let count = 0;
	function add(){
		return ++count;
	}
	return add;
}

//获取到计数器
var count1 = getCount();
console.info(count1())
console.info(count1())
console.info(count1())

var count2 = getCount();
console.info(count2())
console.info(count2())
console.info(count2())

var count3 = getCount();
console.info(count3())

代码剖析:getCount就代表计数器对象,每次调用都相当于将计数器值归零。

调用返回值的时候,其实就在调用函数内部的函数。函数内部的函数就是来执行累加的过程。

主要调用getCount就返回一个新的计数器,原来的计数器按照内容执行自己值。

概念:闭包就是可以访问其他函数中变量的函数。当前函数可以使用其他函数的变量,基于函数的嵌套。内部函数总是可以访问外部函数的参数和变量。即使你外部函数已经执行结束了。

简单来说:函数A里面定义了一个函数B,B函数可以使用A函数的变量的参数,那我们就将这种模式成为闭包。

闭包学习的内容:

  1. 闭包的模式
  2. 闭包的作用
  3. 闭包的优点和缺点
  4. 闭包的应用

闭包的模式

闭包一共有两种模式:

第一种:函数闭包

第二种:对象闭包

函数闭包:

funtion f1(){
	var number = 10;
	function f2(){
		console.info(number);
	}
	f2();
}
f1()

对象闭包:

function fun(){
	var number = 20;
	var obj = {money:number};
	console.info(obj.money);
}
fun();

执行fun函数,输出一个20.

函数里面的对象引用了函数里面局部变量,重要对象不销毁,number一直无法销毁。函数空间也无法弹出去。

闭包的内存结构

var username = "xiaowang" //基本类型
var arr = [1,2,3]  //引用类型

在内存中,基本数据类型存放在栈空间里面。对象是存放堆空间里面。

栈里面存放了堆里面对象引用(地址)。

var value = 10;
let address = "武侯区"
function f1(){
	var number = 10;
    let arr = [];
	function f2(){
		console.info(number);
        xxxx();
	}
	f2();
}
f1()
login();
login(){
   if(){
      menu();
      }
}

栈空间的特点:

  1. 栈是一种数据结构,内存里面栈采用这种数据结构来保存数据
  2. 栈满足先入后出,入栈、出栈。栈顶、栈低。

代码执行流程:

  • 代码在运行环境中加载的时候,首先会读取代码中的全局变量,var先提升,遇到let按照顺序来定义。在栈里面要创建一个全局的执行环境(在浏览器运行window)。此刻全局作用域里面存放 value、address这些变量。此刻全局作用域在栈底。
  • 代码执行f1()的时候,此刻我们栈内存里面就push一个f1的上下文执行空间。在这个空间里面存放一个number变量和一个f2函数的引用。
  • 进入f1过后,接下来要执行f2(),此刻在栈内存里面push一个f2的上下文执行环境,f2这个环境可以访问到f1和全局环境里面的变量。获取变量的时候,自顶向下去获取变量。最后到栈底找不到,排除一个异常。
  • 随着f2()执行完毕,f2的上下文环境应该被销毁,接着f1的环境也会被销毁。现在就剩下全局环境。最后来销毁。

在开发中,尽量避免全局变量随意定义。

闭包的优点和缺点

优点:闭包可以缓存数据,延长作用链的时间。

缺点:一旦用了闭包,局部变量不能及时的被销毁,会造成一致占用空间的特点。

《JavaScript高级编程》:闭包会携带包含他的作用域,因此会占用比其他程序更多的空间。过度使用闭包内存占用多大。

慎用。

闭包的坑

function outer(){
	var result = [];
    for(var i=0;i<10;i++){
        result[i] = function(){
            console.info(i);
        }
    }
    return result;
}
var arr = outer();

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值