作用域、作用域链精解、立即执行函数、闭包、闭包精细版

作用域、作用域链精解、立即执行函数、闭包、闭包精细版

作用域、作用域链精解基本概念

  1. 运行期上下文
    当函数执行时,会创建一个称为执行期上下文的内部对象。一个执行期上下文定义了一个函数执行时的环境,函数每次执行时对应的执行上下文都是独一无二的,所以多次调用一个函数会导致创建多个执行上下文,当函数执行完毕,它所产生的执行上下文被销毁。
  2. 查找变量
    从作用域链的顶端依次向下查找
  3. [[scope]]
    每个JavaScript函数都是一个对象,对象中有些属性我们可以访问,但有些不可以,这些属性仅供JavaScript引擎存取,[[scope]]就是其中一个。
    [[scope]]指的就是我们所说的作用域,其中存储了运行期上下文的集合。
  4. 作用域链
    [[scope]]中所存储的执行期上下文对象的集合,这个集合呈链式链接,我们把这种链式链接叫作用域链。
示例
function a() {
	function b() {
		var b = 234;
	}
	var a = 123;
	b();
}
var glob = 100;
a();
函数作用域链详解:

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

在这里插入图片描述

立即执行函数

功能:

此类函数没有声明,在一次执行过后即释放,适合做初始化工作。
实例如下

(function () {
	var a = 1;
	var b = 2;
	console.log(a + b);
}())

在这里插入图片描述

(function (a , b , c) {
	console.log(a + b + c * 2);
}(1 , 2 , 3))

在这里插入图片描述

var result = (function (a , b , c) {
	var d = a + b * 2 + c * a;
	return d
}(1 , 2 , 3))
console.log(result);

在这里插入图片描述
两种常用写法
(function() {} () ); W3C建议使用第一种
(function() {} ) ();

阿里巴巴曾经的一个考试题

function test(a , b , c , d) {
	console.log( a + b + c + d);
}(1 , 2 , 3 , 4);

上面的代码就相当于下面的代码,讲函数test()的声明与(1 , 2 , 3 , 4);分开执行,但不报错也不输出任何的东西

function test(a , b , c , d) {
	console.log( a + b + c + d);
}





(1 , 2 , 3 , 4);

在这里插入图片描述

闭包

基本概念

闭包:
当函数内部被保存到外部时,将会生成闭包。闭包会导致原有作用域链不释放,造成内存泄漏。

function a() {
	function b() {
		var bb = 234;
		aa++;
		console.log(aa);
	}
	var aa = 123;
	return b;
}
var glob = 100;
var demo = a();
demo();
demo();

代码执行期上下文过程
在这里插入图片描述
函数a返回的是b的引用
在这里插入图片描述

作用
  1. 实现共有变量(函数累加器)

      function add() {
      	var count = 123;
     	function demo() {
     		count++;
     		console.log(count);
     	}
     	return demo;
      }
      var counter = add();
      counter();
      counter();
      counter();
      counter();
      counter();
      counter();
    

在这里插入图片描述

  1. 可以做缓存(存储结构)

      function eater() {
      	var food = "";
      	var obj = {
      		eat : function () {
     		console.log( "I am eating " + food);
     		food = "";
     		},
     		push: function (myFood) {
     		food = "myFood";
     		}
     	}
     	return obj;
      }
      var eater1 = eater();
      eater1.push("banana");
      eater1.eat();
    

在这里插入图片描述

  1. 可以实现封装,属性私有化

  2. 模块化开发,防止污染全局变量

详解

经典代码

function test() {
	var arr = [];
	for(var i = 0; i < 10; i++) {
		arr[i] = function () {
		document.write(i + " ");
		}
	}
	return arr;
}
var myArr = test();
for(var j = 0; j < 10; j++) {
	myArr[j]();
}

代码执行结果
在这里插入图片描述

代码执行过程,test()函数中的arr[i] = function() {}执行时并不执行function中的代码,只是传了一个引用值给arr[i],等到真正执行的时候才会回来看function中的代码并执行
要解决上述矛盾,必须使用立即执行函数,代码如下

function test() {
	var arr = [];
	for(var i = 0; i < 10; i++) {
		(function (j) {
			arr[j] = function () {
				document.write(j + " ");
			}
		}(i));
	}
	return arr;
}
var myArr = test();
for(var j = 0; j < 10; j++) {
	myArr[j]();
}
基本规律

只有表达式才能被执行

function() {}

上面的代码称为函数声明,不是表达式,后面加()后会爆出低级语法错误
function() {} ();

在这里插入图片描述
而下面的代码会正常执行,相当于var demo = function () {console.log(“hello”);}是一个表达式,运行完一次之后就不能再运行了,相当于立即执行函数

var demo = function () {
	console.log("hello");
}();

在这里插入图片描述

再函数声明前面加上 + - ! 后面加()都能将其变成函数表达式,也类似于立即执行函数,代码如下

+ function test() {
	console.log("hello");
}();

- function test() {
	console.log("hello");
}();

! function test() {
	console.log("hello");
}();

在这里插入图片描述

阿里曾经一道笔试题
使用原生js,addEventListener,给每个li元素绑定一个click事件,输出它们的顺序

<!doctype html>
<html>
<head>
<meta charset="utf-8">
<title>demo</title>
	<style>
		*{
			margin : 0;
			padding : 0;
		}
		ul {
			list-style: none;
		}
		li:nth-of-type(2n) {
			background-color : red; 
		}
		li:nth-of-type(2n + 1) {
			background-color : green; 
		}
	</style>
</head>

<body>
	<ul>
		<li>first</li>
		<li>second</li>
		<li>third</li>
		<li>forth</li>
	</ul>
	<script>
	        function test() {
				var liCollection = document.getElementsByTagName('li');
				for(var i = 0; i < liCollection.length; i++) {
					(function (j) {
						liCollection[j].onclick = function() {
							console.log(j);
						}
						
					}(i))
				}
			}
		test();
	</script>
</body>
</html>
扩展

写一个方法,求一个字符串的字节长度。(提示:字符串有一个方法charCodeAt() ,一个中文占两个字节,一个英文占一个字节)
定义和方法
charCodeAt()方法可返回指定位置字符的Unicode编码,这个返回值是0~65535之间的整数。(当返回值 <= 255时,为英文,当返回值 > 255时为中文)

语法

stringObject.charCodeAt(index)

eg:
<script>
	var str = "Hello, World!";
	document.write(str.charCodeAt(1));	//输出101
</script>

代码如下所示:

function getBytesLen(str) {
	var count = 0;
	for(var i = 0; i < str.length; i ++) {
		if(str.charCodeAt(i) <= 255) {
			count ++;
		} else if(str.charCodeAt(i) > 255) {
			count += 2;
		}
		}
		console.log(count);
}
getBytesLen("Hello, World!我爱矿大");

简化代码

function getBytesLen(str) {
	var len = str.length;
	var count = len;
	for(var i = 0; i < len; i ++) {
		if(str.charCodeAt(i) > 255) {
			count ++;
		}
		}
		console.log(count);
}
getBytesLen("Hello, World!我爱矿大");

结果如下图
在这里插入图片描述

微店前端面试题

写出下面程序的执行结果(此题考查逗号运算符的结果,结果返回最后一个逗号后面的表达式的值)

var f = (
	function f() {
		return "1";
	},
	function g() {
		return 2;
	}
	());
console.log(typeof f);

在这里插入图片描述

var x = 1;
	if(function f() {}) {
		x += typeof f;
	}
console.log(x);

在这里插入图片描述

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值