JS笔记进阶补充

1.作用域及闭包

变量作用域

  • 全局变量:(var) 声明在函数外部的变量(所有没有var直接赋值的变量都属于全局变量)
  • 局部变量:(var) 声明在函数内部的变量(所有没有var直接赋值的变量都属于全局变量)

执行环境(execution context)

  • 执行环境定义了变量或者函数有权访问的其他数据,每个执行环境都有一个与之关联的变量对象(variable object),执行环境中定义的变量和函数就保存在这个变量对象中;

  • 全局执行环境是最外围的一个执行环境,通常被认为是window对象;

  • 执行环境和变量对象在运行函数时生成;

  • 执行环境中的所有代码执行完以后,执行环境被销毁,保存在其中的变量和函数也随之销毁;(全局执行环境到应用退出时销毁);

作用域链

  • 当代码在一个执行环境中执行时,会创建变量对象的一个作用域链(scope chain),作用域链用来指定执行环境有权访问的所有变量和函数的访问顺序;
  • 作用域链的最前端,始终是当前代码执行环境的变量对象,如果这个环境是函数,则其活动对象就是变量对象;
  • 作用域链的下一个变量对象,来自外部包含环境,再下一个变量对象,来自下一个外部包含环境,以此类推直到全局执行环境;
  • 在函数执行过程,根据当前执行环境的作用域链来逐层向外查找变量,并且进行标识符解析;

闭包

  • 闭包,指的是词法表示包括不被计算的变量的函数,也就是说,函数可以使用函数之外定义的变量。
	// 使用全局变量是一个简单的闭包实例
	var sMessage = "hello world";
	
	function sayHelloWorld() {
	  alert(sMessage);
	}
	
	sayHelloWorld();

	//在一个函数中定义另一个会使闭包变得更加复杂
	var iBaseNum = 10;

	function addNum(iNum1, iNum2) {
	  function doAdd() {
	    return iNum1 + iNum2 + iBaseNum;
	  }
	  return doAdd();
	}	//这里,函数 addNum() 包括函数 doAdd() (闭包)。内部函数是一个闭包,因为它将获取外部函数的参数 iNum1 和 iNum2 以及全局变量 iBaseNum 的值。 
		 //addNum() 的最后一步调用了 doAdd(),把两个参数和全局变量相加,并返回它们的和。
		 //这里的doAdd() 函数根本不接受参数,它使用的值是从执行环境中获取的

举例

	<script>
	    function A(){
	        var x = 1;
	        return function(){
	            x++;
	            console.log(x);
	        }
	    }
	    var m1 = A();//第一次执行A函数
	    m1();//2
	    m1();//3
	    var m2 = A();//第二次执行A函数
	    m2();//2
	    m1();//4
	</script>
	// 每次执行A函数时,都会生成一个A的活动变量和执行环境,执行完毕以后,A的执行环境销毁。
	// 但是活动对象由于被闭包函数引用,所以仍然保留,所以,最终剩下两个A的变量对象,因此m1和m2在操作x时,指向的是不同的数据。

1.为什么连续执行m1的时候,x的值在递增?
answer:因为m1在引用的活动对象A一直没有释放(想释放的话可以让m1=null),所以x的值一直递增。
2.定义函数m2的时候,为什么x的值重新从1开始了?
answer:因为又一次运行了A函数,生成一个新的A的活动对象,所以m2的作用域链引用的是一个新的x值。
3.m1和m2里面的x为什么是相互独立,各自维持的?
answer:因为在定义m1和m2的时候,分别运行了A函数,生成了两个活动对象,所以,m1和m2的作用域链是指向不同的A的活动对象的。
	 <script>
	    function A(){
	        var funs=[];
	        for(var i=0;i<10;i++){
	           funs[i]=function(){
	               return i;
	           }
	        }
	        return funs; 
	    }
	    var funs = A();//定义funs[0]-funs[9],10个函数
	    console.log(funs[0]());//10
	    console.log(funs[1]());//10
	    console.log(funs[6]());//10
	</script>
	//执行环境和变量对象在运行函数时生成
	//当执行var funs = A()时,只是定义函数,而没有执行,真正产生环境变量的时间是在console.log(funs[0]());
	//当执行 console.log()时,此时A的变量对象中i值是什么呢?很简单,看它return的时候,i的值(10)
	//所以,最后三句输出的都是10
2.this
  1. 作为对象方法调用,this 指代上级对象
	var person = {
			fname:'world',
			lname:'hello',
			fullname:function(){
				console.log(this);			// {fname: "world", lname: "hello", fullname: ƒ}this指向上级对象
				return this.lname + ' ' +this.fname;
			}
		}
		
		console.log(person.fullname());	//hello world
	>------------------------------------------------------------------------------------------------------
	
		var person = {
			fname:'world',
			lname:'hello',
			a:{
				fullname:function(){
					console.log(this);			// {fullname: ƒ}this指向上级对象
					return this.lname + ' ' +this.fname;
				}
			}
		}
		
		console.log(person.fullname());	
  1. 一般函数(全局函数,匿名函数),this 指代全局对象
	var person = {
			fname:'world',
			lname:'hello',
			fullname:function(){
				console.log(this);			// Window{}
				return this.lname + ' ' +this.fname;
			}
		}
		
		var get_full_name = person.fullname; 
		console.log(get_full_name());	// undefined
		console.log(person.fullname()); // {fname: "world", lname: "hello", fullname: ƒ}
										// hello world
  1. 作为构造函数调用,this 指代new 出的对象
	function User(){
		console.log(this);		//Window{}
	}
	User();

	>----------------------------------------
	function User(){
		console.log(this);		//User {}
		this.name = 'world';
		this.age = 12;
	}
	var person = new User();
	console.log(person);	//User {name: "world", age: 12}

好用!!!动态!!!

	function yo(){
		console.log('Yo, I am ' + this.name );
	}
	
	var first = {
		name:'hello',
	}
	var last = {
		name:'world',
	}
	first.yo = yo;
	last.yo = yo;
	
	first.yo();	// Yo, I am hello
	last.yo();	// Yo, I am world
	//在不同环境下,给不同父级赋能

call, apply, bind()

	function yo(name,a,b,c){
		console.log('Yo, '+name+ '. I am ' + this.name+'! '+ a + b + c );
	}
	
	var first = {
		name:'hello',
	}
	var last = {
		name:'world',
	}
	yo.call(first,'BIBI',1,2,3);	//(object,parm...)
	yo.call(last,'CICI',3,2,1);
	
	yo.apply(last,['CICI',3,2,1]);	//(object,array[])
	
	var last2 = yo.bind(last);	// bind()返回一个新方法
	last2('CICI',3,2,1);

猜你喜欢

转载自blog.csdn.net/weixin_43000780/article/details/89484587