函数表达式和函数声明

变量/函数声明都会提前
console.log(a)
let a =1
那么打印出来的a为 undefined,因为会将a提到前面并赋予默认值undefined
函数声明:函数声明会将函数提到调用函数变量的前面
fn('里斯')//不会报错
function fn(name) {
console.log();
}
函数表达式:
fn1();//会保错,因为fn1为undefeated
const fn1=function fn(name) {
console.log();
}

执行上下文(变量提升)

  • 范围:一段<script> 或者一个函数
  • 全局:变量定义、函数声明 一段<script>
  • 函数:变量定义、函数声明、this、arguments

this

  • this需要在执行时才能确认值,定义时无法确认
  • this作为普通函数执行,指向的是window
  • this作为对象执行,指向的是调用者对象
  • this作为构造函数执行,指向的是构造函数对象
  • call apply bind

作用域

  • 无块级作用域
 
if(true) {
const name='zhangsan'
}
console.log(name);// 'zhangsan'
 
  • 全局作用域和函数作用域
 
const a=12;// 全局变量,全局都可以访问,该变量容易被污染
function() {
const b =32; // 函数作用域 函数内部可以访问
console.log(a); /// a变量在函数中没有定义,那么该变量称之为自由变量
console.log(b);
}
 
  • 指向函数对象与函数调用

var xiaoming = {
  name: '小明',
  birth: 1990,
  age: function () {
     var y = new Date().getFullYear();
     return y - this.birth;
   } };
xiaoming.age; // 只是一个Func对象
xiaoming.age(); // Func的执行
 
同时strict模式下(use strict)让函数的this指向undefined,事实是无论是strict模式,this指向undefined或window,可以通过apply和call来控制this的指向的,完成this的绑定。
 
  • apply
它接收两个参数,第一个参数就是需要绑定的this变量,第二个参数是Array,表示函数本身的参数。
用apply修复getAge()调用

function getAge() {
var y = new Date().getFullYear();
return y - this.birth;
}
var user= {
name: '小明',
birth: 1990,
age: getAge
};
user.age(); //
getAge.apply(user, []); // 25, this指向user, 参数为空
  • call
apply是通过将参数进行打包成数组Array的方式传入,call是通过将参数以顺序的方式传入Func

Math.max.apply(null, [3, 5, 4]); // 5
Math.max.call(null, 3, 5, 4); // 5

  

对于普通的函数this的指向一般设置为null
默认值
function test(x,y='world'){
console.log('');
}
  • bind
bind 通过函数变量的方式调用bind函数绑定this对象指向
const ageFunc=function getAge() { 
var y = new Date().getFullYear();
return y - this.birth;
}.bind({birth:20})
当我们调用时ageFunc()执行函数时,函数指向{birth:20}这个Object对象
 
  • rest参数
 
function foo(a, b, ...rest) { 
  console.log('a = ' + a);
  console.log('b = ' + b);
  console.log(rest);//3,4,5
}
foo(1, 2, 3, 4, 5);
 
  • This对象的绑定

function getAge() {
var y = new Date().getFullYear();
return y - this.birth;
}
var user= {
name: '小明',
birth: 1990,
age: getAge
}; xiaoming.age(); // 当通过user对象去调用指向age,那么getAge Func中的this对象指向的是调用者,即词法作用域
getAge(); // 如果直接getAge()执行,那么相当this属于window对象

  

单独调用函数,比如getAge(),此时,该函数的this指向全局对象,也就是window

闭包

定义: 内部函数可以引用外部函数的参数和局部变量,当内部函数返回函数时,相关参数和变量都保存在返回的函数中,这种称为“闭包(Closure)”
 
  1. 通过返回一个函数然后延迟执行
  2. 如果里函数引用了外函数的某个变量,那这个变量就能享受和全局变量一样的特权,不会被回收!因为该变量一直被Child函数一直访问着。同时享受全局变量不会被销毁的特权的闭包变量多到一定数量了,那内存就要撑爆了,一旦超过了计算机能接受的内存阀值,就会导致内存泄漏

函数作为返回值

函数作为参数传递

返回函数是根据作用域链一层一层往上找,找到即可,不是执行时生效,而是定义时生效

  • 函数作为返回值
function F() {
let a = 10
return function() {
console.log(a) // 自由变量 父级作用域中寻找
}
}
let a =23 let f = new F() f() // 10
  • 函数作为参数传递
function F() {
let a = 10
return function() {
console.log(a) // 自由变量 父级作用域中寻找
}
}
function F1(fn) {
let a = 120
fn()
}
let f = new F()
let f1 = new F1(f)
f1() // 10
 
自由变量是由定义时的作用域来决定的,不是由执行时的作用域来决定的.

特性

根据闭包相关特性,总结其特点如下:
懒执行
function lazy_sum(arr) {
var sum = function () {
return arr.reduce(function (x, y) {
return x + y;
});
}
return sum;
}
var results=lazy_sum([1,2,3,4,5]);
当去调用results()时才会去真正计算求和
私有变量
在面向对象语言中,一般在函数内部通过private定义私有变量,而在闭包中则通过内部函数携带状态返回。
function rememberCount(initial ) {
var count = initial || 0;
return {
inc: function() {
count = count + 1;
return count;
}
}
}
var rc=rememberCount();
rc.inc(); //1
rc.inc(); //2
rc.inc(); //3
在返回的对象中,实现了一个闭包,该闭包携带了局部变量count,并且,从外部代码根本无法访问到变量count。换句话说,闭包就是携带状态的函数,并且它的状态可以完全对外隐藏起来

注意

在闭包中,函数内部定义了数组的变量,当函数返回函数,内部的变量还被新的函数所引用,而是直到调用了f()才执行
function count() {
var arr = [];
for (var i=1; i<=3; i++) {
arr.push(function () {
return i * i;
});
}
return arr;
} var results = count();
var f1 = results[0];
var f2 = results[1];
var f3 = results[2];
返回结果并非是我们想象的1,4,9
f1(); // 16 f2(); // 16 f3(); // 16
全部都是16!原因就在于返回的函数引用了变量i,但它并非立刻执行。等到3个函数都返回时,它们所引用的变量i已经变成了4,因此最终结果为16。
返回闭包时牢记的一点就是:返回函数不要引用任何循环变量,或者后续会发生变化的变量。
 
通过创建并立即执行,创建一个匿名函数并立刻执行:
(function (x) {
return x * x;
})(3); // 9 function (x) { return x * x } (3);// 语法会解析错误
(function (x) { return x * x }) (3); function count() {
var arr = [];
for (var i=1; i<=3; i++) { //创建了3个function对象,保护了i变量的污染
arr.push((function (n) {
return function () {
return n * n;
}
})(i));
}
return arr;
} var results = count();
var f1 = results[0];
var f2 = results[1];
var f3 = results[2]; f1(); // 1
f2(); // 4
f3(); // 9

实际开发中的闭包应用

  • 用于封装变量,收敛权限
function isHasWatchList(){
const _list =[];
return function (id) {
if(_list.indexOf(id) >= 0){
return false;
} else {
_list.push(id);
return true;
}
}
} // 使用
const hasWatchList = new isHasWatchList();
hasWatchList('10') // true
hasWatchList('10') // false
hasWatchList('10') // false

 

  

浅谈JS作用域和闭包的更多相关文章

  1. 浅谈JS中的闭包

    浅谈JS中的闭包 在介绍闭包之前,我先介绍点JS的基础知识,下面的基础知识会充分的帮助你理解闭包.那么接下来先看下变量的作用域. 变量的作用域 变量共有两种,一种为全局变量,一种为局部变量.那么全局变 ...

  2. 浅谈 js eval作用域

    原文:浅谈 js eval作用域 就简单聊下如何全局 eval 一个代码. var x = 1; (function () { eval('var x = 123;'); })(); console. ...

  3. 浅谈JS中 var let const 变量声明

    浅谈JS中 var let const 变量声明 用var来声明变量会出现的问题: 1. 允许重复的变量声明:导致数据被覆盖 2. 变量提升:怪异的数据访问.闭包问题 3. 全局变量挂载到全局对象:全 ...

  4. 浅谈JavaScript中的闭包

    浅谈JavaScript中的闭包 在JavaScript中,闭包是指这样一个函数:它有权访问另一个函数作用域中的变量. 创建一个闭包的常用的方式:在一个函数内部创建另一个函数. 比如: functio ...

  5. 浅谈 js 语句块与标签

    原文:浅谈 js 语句块与标签 语句块是什么?其实就是用 {} 包裹的一些js代码而已,当然语句块不能独立作用域.可以详细参见这里<MDN block> 也许很多人第一印象 {} 不是对象 ...

  6. 浅谈JS严格模式

    浅谈JS严格模式 简介 何为严格模式?严格模式(strict mode)即在严格的条件下运行,在严格模式下,很多正常情况下不会报错的问题语句,将会报错并阻止运行. 但是,严格模式可以显著提高代码的健壮 ...

  7. 浅谈JS之AJAX

    0x00:什么是Ajax? Ajax是Asynchronous Javascript And Xml 的缩写(异步javascript及xml),Ajax是使用javascript在浏览器后台操作HT ...

  8. 浅谈 js 正则字面量 与 new RegExp 执行效率

    原文:浅谈 js 正则字面量 与 new RegExp 执行效率 前几天谈了正则匹配 js 字符串的问题:<js 正则学习小记之匹配字符串> 和 <js 正则学习小记之匹配字符串优化 ...

  9. 浅谈 js 字符串之神奇的转义

    原文:浅谈 js 字符串之神奇的转义 字符串在js里是非常常用的,但是你真的了解它么?翻阅<MDN String>就可以了解它的常见用法了,开门见山的就让你了解了字符串是怎么回事. 'st ...

随机推荐

  1. CentOS6.5(4)----宿主机无法访问虚拟机中的web服务解决方案

    宿主机无法访问虚拟机中的web服务 在Windows7宿主机中的VMware虚拟机中安装了CentOS6.5操作系统,并且基于Nginx搭建了Web服务器,网页刚刚搭建好的时候,通过宿主机的浏览器可以 ...

  2. Mac安装Dart的SDK

    最近了解到谷歌推迟Flutter兼容开发iOS.Android移动应用的框架,该框架使用的语音是Dart.作为一个iOS开发者来说,不感兴趣就不正常了,于是开始从学习Dart开始,所有的开发语音其实都 ...

  3. 一步一步pwn路由器之路由器环境修复&&rop技术分析

    前言 本文由 本人 首发于 先知安全技术社区: https://xianzhi.aliyun.com/forum/user/5274 拿到路由器的固件后,第一时间肯定是去运行目标程序,一般是web服务 ...

  4. apache 配置PHP的支持重写伪静态

    1.开启rwrite模块 LoadModule rewrite_module modules/mod_rewrite. 允许任何目录使用.htaccess AllowOverride None 改成 ...

  5. Docker-容器数据卷

    docker容器数据卷相当于外置的移动硬盘 docker容器数据卷主要功能是:容器的持久化.容器间继承+共享数据 特点: (1)数据卷可以容器之间共享或重用数据 (2)卷中更改可以直接生效 (3)数据 ...

  6. 基于双下划线的跨表查询 (join查询)

    因为你的数据库中的查询就是重点  那么你的django提供的orm也是查询语句最重点 ,也提供的查询方法比较的多,下面我们学习下类似于MYSQL的连表(join)查询 Django 还提供了一种直观而 ...

  7. Exchange Server 2007 多名称证书配置

    Exchange Server 2007上配置多名称证书,有两种方式,一种是通过Exchange Management Shell利用命令行工具进行创建:另一种是通过证书管理器控制台进行创建.本文将介 ...

  8. Win10离线安装.NET Framework 3.5的方法技巧(附离线安装包下载)

    原文链接: https://www.windows10.pro/win10-net-framework-3-5/ 在Windows10中,当我们安装某些软件的时候会提示“你的电脑上的应用需要使用以下W ...

  9. 深入浅出SharePoint2010——请假系统无代码篇之数据框架设计

    文档库SOP:上传用户操作手册等系统相关文档. 员工信息列表EmployeeInfo:用来存储员工基本信息.直属领导和假卡信息. 请假申请列表LeaveRequest:申请人Requester填写请假 ...

  10. Chapter 1 Secondary Sorting:Introduction

    开始学习<数据算法:Hadoop/Spark大数据处理技巧>第1-5章,假期有空就摘抄下来,毕竟不是纸质的可以写写画画,感觉这样效果好点,当然复杂的东西仍然跳过.写博客越发成了做笔记的感觉 ...