JS基础知识(三)作用域和闭包

作用域和闭包

问题:

  1. 说一下对变量提升的理解

  2. 说明this几种不同的使用场景

  3. 创建10个<a>,点击的时候弹出来对应的序号

  4. 如何理解作用域

  5. 实际开发中闭包的应用

 

知识点:

   1.执行上下文(execution context)简称EC
      概念:执行上下文,简单的理解就是会提前做的一些“准备工作”。存在变量提升的现象。

     

变量:来看三个小例子,当你直接去打印一个不存在的变量,他自然会报错,但当你在打印一个变量时,没有提前声明,在你需要打印的这句话下面再声明,此时,就会显示出准备工作的作用。会将他提前声明,默认值为undefined

 

 this:无论在哪里去读取this,都会有值,但是在不同的地方不同。这个比较复杂。

 函数声明:

此处有分别为两点,一个是函数,另外一个是函数表达式。

函数声明是会被提前的,但是函数表达式不会。

每一个script里面的变量,函数都会被提前,同时还会一开始就确定thisarguments的值。

 

  1. 范围:一段<script>或者一个函数
  2. 全局:变量定义,函数声明 一段<script>
  3. 函数:变量定义,函数声明,this,arguments 函数执行之前生成

我们在日常的开发中,最好做到:先定义后执行

函数覆盖:

  函数声明和变量声明都会被提升,但是函数声明会覆盖变量声明。

 但是当变量已经被赋值的时候,最终的值就会为变量的值。

  变量的重复声明是无用的,但是函数的重复声明会覆盖前面的声明

 

我们在日常开发中,应该避免在同一作用域内重复声明

   2.this

    this是一个有趣的点,this要在执行时才能确认值,定义时无法确认

   (1)作为构造函数执行

       在构造函数中,会有一个this为空一开始,最后会返回那个被赋好值的this.把this看作一个变量,属性可以扩展。

(2)作为对象属性执行

       当作为一个对象有某一个属性时,this就指向那个对象。

(3)作为普通函数执行

     作为普通函数,this就指向window

(4)Call apply bind

         call、apply、bind的作用是改变函数运行时this的指向

         call第一个参数是this指向,后面为参数,

        apply第一个参数是this指向,后面为参数数组,

        bind有点像call,同样第一个参数是this指向,从第二个参数开始是接收的参数列表。

但他是方法.bind(对象)。这样子的。

需要注意的是:.bind必须是一个函数表达式,不能是一个函数声明

//构造函数
function Foo(name) {
	this = {}
	this.name = name
	return this
}

var f = new Foo('zhangsan')

//对象属性

var obj = {
	name: 'A',
	printName: function () {
		console.log(this.name)
	}
}
obj.printName()
//此时函数作为一个对象的属性,this就是指向该对象

//普通函数
function fn() {
	console.log(this) //this === window
	//当作为普通函数执行,this就是指向window
}
fn()

//call apply bind

function fn1 (name,age) {
	alert(name)
	console.log(this)
}
fn1.call({x:100}, 'zhangsan', 20)

//call 第一个参数说明this指向谁,后面的参数为函数需要的参数

//apply几乎一样,只是把后面的参数当作数组来传递
fn1.apply({x:100}, ['zhangsan', 20])

var fn2 = function (name,age) {
	alert(name)
	console.log(this)
}.bind({y:200})

//定义函数的时候,直接就在后面调用一个bind绑定一个this的对象,然后在后面就直接是执行
fn2('zhangsan',20)

//.bind必须是一个函数表达式,不能是一个函数声明

   3.作用域

(1)没有块级作用域

在ES6就有了,let,{}会形成块级作用域。此处我们都讲的是var。

(2)只有函数和全局作用域

这里即使是在函数内部声明,也同样会在外面读到。

if (true) {
	var name = 'zhangsan'
}
console.log('name')
//name

var name;
if (true) {
	name = 'zhangsan'
}
console.log('name')
//name



 4.作用域链

当前作用域没有定义的变量,为自由变量
当前找不到就到函数定义父级作用域里面找到变量

//作用域链
var a = 100
function fn() {
	var b = 200
	console.log(a) //自由变量 到父级作用域找,函数定义时的
	console.log(b)
}
fn()
//100
//200

var a = 100
function F1() {
	b = 200
	function F2() {
		var c = 300
		console.log(a) //自由变量
		console.log(b)  //自由变量
		console.log(c)
	}
	F2()
}
F1()

5.闭包

 函数内部还有函数。

  闭包使用的场景
(1)函数作为返回值。

里面的变量若为自由变量,就到父级作用域中去找,函数定义时的父级作用域。

bar函数作为返回值,赋值给f1变量。
(2)函数作为参数传递

作为参数传递时候,

function F1() {
	var a = 100
	return function bar() {
		console.log(a) //自由变量,父级作用域寻找,函数定义时的父级作用域
	}
}

var f1 = F1()
var a = 200
f1()
//100

function F1() {
	var a = 100
	return function () {
		console.log(a) //自由变量,父级作用域寻找,函数定义时的父级作用域
	}
}

var f1 = F1()

function F2(fn) {
	var a = 200
	fn()
}
F2(f1)
//100

解答:

  1. 说一下对变量提升的理解
    1.变量定义时,会自动提升到全局变量
    2.函数声明(注意和函数表达式的区别),函数声明会提前,函数表达式不会提前
    3.<scipt></scirpt>和函数中,要用到的变量都会被提前
  2. 说明this几种不同的使用场景
    1.作为构造函数执行 构造函数里面的this一开始为空,后来赋值后返回
    2.作为对象属性执行 哪个对象使用就指向谁
    3.作为普通函数执行 指向window
    4.call,apply,bind 三个分别可以改变this的指向作用域。
  3. 创建10个<a>,点击的时候弹出来对应的序号
    var i, a
    for(i = 0; i < 10 ; i++){
    	a = document.createElement('a')
    	a.innerHTHML = i + '</br>'
    	a.addEventListener('click', function (e) {
    		e.preventDefault()
    		alert(i)
    	})
    	document.body.appendChild(a)
    }
    
    //自执行函数,就是不用调用,只要定义完成,就立即执行
    
    var i
    for(i = 0; i < 10 ; i++){
    	(function (i) {
    	var a = document.createElement('a')
    	a.innerHTHML = i + '</br>'
    	a.addEventListener('click', function (e) {
    		e.preventDefault()
    		alert(i)
    	})
    	document.body.appendChild(a)	
    	})(i)
    }
  4. 如何理解作用域
    1.自由变量,在这个作用域里找不到的变量
    2.作用域链条,即自由变量的查找
    3.闭包的两个场景(函数作为返回值,函数作为参数)
  5. 实际开发中闭包的应用
    封装变量,收敛权限
    function isFirstLoad() {
    	var _list = []
    
    	return function (id) {
    		if(_list.indexOf(id) >= 0) {
    			return false
    		} else {
    			_list.push(id)
    			return true
    		}
    	}
    }
    
    var firstLoad = isFirstLoad()
    firstLoad(10)
    firstLoad(10)
    firstLoad(20)
    firstLoad(20)
    //你在函数外面,根本不可能修改_list的值

猜你喜欢

转载自blog.csdn.net/qq_37021554/article/details/84638172