js之作用域和闭包

1.两种创建函数方法的比较。

    fn ();
    function fn() {
      //声明,用这种写法时,fn()在它之前的话不会报错,执行到这里的时候,他会自动把函数声明这段代码提到前面去执行
      console.log(1);
    }

    fn1 ();
    var fn1 = function() {
      //表达式,用这种写法时,fn1()在它之前的话会报错,undefined.
      console.log(2)
    }
小知识:
		age = 10;
		console.log(age);
		var age;  //这种写法其实是一种占位,执行到age = 10的时候,他会把变量声明给提前,从而会打印出10.

2.作用域和闭包。

  • 执行上下文

先从下面代码中来理解下执行上下文:

	1.	console.log(a);  //a is not defined

	2.	console.log(a);  //undefined
		var a;

	3.	console.log(a);  //undefined
		var a = 100;

第1个console.log(a),结果为a is not defined;第2个和第3个结果都是undefined。可以发现浏览器在未执行console.log(a)之前就已经知道了a是undefined,但是不知道a是100。

在一段js代码拿过来真正一句一句运行之前,浏览器已经做了一些“准备工作”,其中就包括对变量的声明,而不是赋值。变量赋值是在赋值语句执行的时候进行的。可用下图模拟:

这是第一种情况。

 

下面还有。先来个简单的。

有js开发经验的朋友应该都知道,你无论在哪个位置获取this,都是有值的。至于this的取值情况,比较复杂,会专门拿出一篇文章来讲解。

与第一种情况不同的是:第一种情况只是对变量进行声明(并没有赋值),而此种情况直接给this赋值。这也是“准备工作”情况要做的事情之一。

 

下面还有。。。第三种情况。

在第三种情况中,需要注意代码注释中的两个名词——“函数表达式”和“函数声明”。虽然两者都很常用,但是这两者在“准备工作”时,却是两种待遇。

看以上代码。“函数声明”时我们看到了第二种情况的影子,而“函数表达式”时我们看到了第一种情况的影子。

没错。在“准备工作”中,对待函数表达式就像对待“ var a = 10 ”这样的变量一样,只是声明。而对待函数声明时,却把函数整个赋值了。

 

好了,“准备工作”介绍完毕。

我们总结一下,在“准备工作”中完成了哪些工作:

  • 变量、函数表达式——变量声明,默认赋值为undefined;
  • this——赋值;
  • 函数声明——赋值;

这三种数据的准备情况我们称之为“执行上下文”或者“执行上下文环境”。


  • this

this要在执行的时候才能确认值,定义的时候无法确认。(执行时是指函数后面加‘()‘,否则就是处于定义状态)

		var a = {
		  name: 'A',
		  fn: function () {
		    console.log(this.name);
		  }
		}

		a.fn();  //this === a

		a.fn.call({name: 'B'});  //this === {name: 'B'}

		var fn1 = a.fn;
		fn1();  //this === window

总结以下几种执行场景:

1.作为构造函数执行。

		function Foo(name) {
		  this.name = name;  //这里的this.name = name之前,省略了this.name = {},之后省略了return this.
		}

		var f = new Foo("zhangsan");

2.作为普通函数执行。(这时this是指window)

3.作为对象属性执行。(  如a.fn()  )

4.使用call  apply bind这些函数,

    function fn (name, age) {
      alert(name);
      console.log(this);  
      /*这里使用的是Call这个函数,所以这里this指的是{a:10}这个对象,然后后面那两个参数分别对应name和age*/
    }

    fn.call({a:10}, "zhangsan", 20); 

同理,apply也一样,只不过它后面的参数是通过数组来传递,实际工作中使用call这种写法比较多

    fn.apply({a:10}, ["zhangsan", 20]);  

bind:

		var fn = function (name, age) {
		  alert(name);
		  console.log(this);
		}.bind({x: 100});

	  fn("zhangsan", 25);  //正常我们如果不加bind({x: 100})的话,this指的是window,加了以后this就是指{x: 100}这个对象。

  • 作用域

1.js没有块级作用域。

		//js无块级作用域
		if (true) {
			var one = 'zhangsan';
		} 
		console.log(one);

		//上面相当于下面这个,实际过程中为了可读性,推荐使用下面这种写法。
		var two;
		if (true) {
			two = 'lisi';
		}
		console.log(two);

2.js有函数作用域和全局作用域。

		var a = 100;  //这就是全局作用域,在任何地方都能够使用a。
		function fn() {
		  var b = 20;
		  console.log(b);  //20
		}
		fn();
		console.log(b);  //报错,b is not defined,这是由于函数作用域,b是在函数内部定义的,函数以外我们使用不了它。

  • 作用域链

一个自由变量一层一层的往它的父级作用域去找它,所形成的链式结构就称为作用域链。

函数在哪里定义,他的父级作用域就在哪。

		var a = 100;
		function fn () {
		  var b = 200;

		  //当前作用域没有定义的变量,即‘自由变量’。如下面的a,在fn函数作用域内没有定义这个变量,所以a是自由变量。
		  console.log(a);
		  console.log(b);
		}
		fn();
		var a = 100;
		function fn () {
		  var b = 200;
		  function fn1 () {
		    var c = 300;
		    console.log(a);  //a是自由变量
		    console.log(b);  //b是自由变量
		    console.log(c);
		  }
		  fn1();
		}
		fn();

  • 闭包

闭包的使用场景:

1.函数作为返回值。

		function F1() {
		  var a = 100;
		  //返回一个函数(函数作为返回值)
		  return function () {
		    console.log(a);  //100
		  }
		}

		//f1得到一个函数
		var f1 = F1();
		var a = 200;
		f1();

2.函数作为参数传递

		function F1() {
		  var a = 100;
		  return function () {
		    console.log(a);
		  }
		}

		var f1 = F1();
		function F2(fn) {
		  var a = 200;
		  fn();    //打印出100,因为执行fn()的时候,即执行F1()函数,a是一个自由变量,应该去他的父级作用域去查找,即定义a的作用域查找,所有打印出100. 
		} 
		F2(f1);

问题1:说一下对变量提升的理解。

  • 变量定义
  • 函数声明(注意和函数表达式的区别)

问题2:说明this的几种不同的使用场景。

  • 作为构造函数执行
  • 作为对象属性执行
  • 作为普通函数执行
  • call apply bind

问题3:创建10个a标签,点击的时候弹出对应的序号

		//错误的写法
		var i, a;
		for(i=0; i<10; i++) {
			a=document.createElement('a');
			a.innerHTML = i + '<br/>';
			a.addEventListener('click', function(e) {
			  e.preventDefault();
			  alert(i);  //i是自由变量,去定义他的位置找,定义他的位置在全局变量里,此时for循环已经结束,所以i的值是10
			})
			document.body.appendChild(a);
		}
		//正确的写法
		var i;
		for(i=0; i<10; i++) {
			(function (i) {
			var a=document.createElement('a');
			a.innerHTML = i + '<br/>';
			a.addEventListener('click', function(e) {
			  e.preventDefault();
			  alert(i);  //i是自由变量,去定义他的位置找,即父作用域获取值,定义他的位置在全局变量里,此时for循环已经结束,所以i的值是10
			})
			document.body.appendChild(a);
		  })(i);
		}  //上面那种括号里面的叫自执行函数,就是不用调用,只要定义完成,立即执行的函数
问题4:如何理解作用域。
  • 自由变量
  • 作用域链,即自由变量的查找
  • 闭包的两个场景

问题5:实际开发过程中闭包的应用。

    //闭包实际应用中主要用于封装变量,收敛权限
    function isFirstLoad() {
      var list = [];
      return function (id) {
        if (list.indexOf(id) >= 0) {
        	console.log(false);
        } else {
          list.push(id);
          console.log(true);
        }
      }
    }

    var firstLoad = isFirstLoad();
    firstLoad(10);  //true
    firstLoad(10);  //false
    firstLoad(20);  //true





猜你喜欢

转载自blog.csdn.net/sinat_40697723/article/details/80877339