面试题 作用域和闭包

作用域 和 闭包

  • 题目
  • 知识点
  • 解答

题目

this 的不同应用场景,如何取值?

分类: 1:当作普通函数被调用时候,this指向的对象是window this 的指向一般理解为谁调用,this指向谁

比如:
这里的a()这里相当于window.a();


function a(){
    
    
	var user = "candy";
	console.log(this,user); // undefined
	console.log(this);      // window
};
a();

下一个例子:


var o = {
    
    
	user:"candy:,
	fn:function(){
    
    
		console.log(this.user); // candy
	},
};
0.fn();

这里面的fn 是由0对象调用的,所以此时this指向的是对象o
例子3:


var o = {
    
    
	a:1,
	b:{
    
    
		a:2,
		fn:function(){
    
    
			console.log(this.a); // 2
		}
	}
};
o.b.fn();

这个例子打印出来的却是2,也就说明了,
如果一个函数中有this,但是调用包含了多层,那么this指向的也只是它上一级的调用对象
尽管对象b中没有属性a,这个this指向的也是对象b,因为this只会指向它的上一级对象,不管这个对象中有没有this要的东西。
例4:比较特殊


var o = {
    
    
	a:1,
	b:{
    
    
		a:2,
		function(){
    
    
			console.log(this,a); // undefined
			console.log(this);   // window
		}
	}
};
var j = o.b.fn;
j();

这里this指向的是window,是不是有些蒙了?其实是因为你没有理解一句话,这句话同样至关重要。

this永远指向的是最后调用它的对象,也就是看它执行的时候是谁调用的,例子4中虽然函数fn是被对象b所引用,但是在将fn赋值给变量j的时候并没有执行所以最终指向的是window,这和例子3是不一样的,例子3是直接执行了fn。

2:使用call,apply, bind ,this指向的对象是传入的对象
bind()方法主要就是将函数绑定到某个对象,
bind()会创建一个函数,函数体内的this对象的值会被绑定到传入bind()第一个参数的值,
例如,fn.bind(obj),实际上可以理解为obj.fn(),这时,fn函数体内的this自然指向的是obj


function fn1(a,b,c){
    
    
	console.log('this',this);
	console.log(a,b,c);
	return 'this is fn1';
}
const fn2 = fn1.bind({
    
    x:100},10,20,30);
const res = fn2();
console.log(res);

打印出的结果为:
在这里插入图片描述
3:作为对象的方法被调用的时候,this返回的是调用的对象

4:在class 方法中

5:在箭头函数中被调用,指代的是箭头函数的上级作用域

6:构造函数中的this


function FU(){
    
    
	this.user = "candy";
};
var a = new Fu();
console.log(a.user);  // candy

此处是因为使用了new 关键字
new关键字就是创建一个对象实例,指向它构造函数的this

但是注意这里有一个☝️特例
就是当构造函数和 return 相遇的时候~
如果返回值是一个对象,那么this指向的就是那个返回的对象,如果返回值不是一个对象那么this还是指向函数的实例。

手写 bind 函数

MDN上的标准Polyfill


if (!Function.prototype.bind) {
    
    
  Function.prototype.bind = function(oThis) {
    
    
    if (typeof this !== 'function') {
    
    
      throw new TypeError('Function.prototype.bind - what is trying to be bound is not callable');
    }
    var aArgs   = Array.prototype.slice.call(arguments, 1),
        fToBind = this,
        fNOP    = function() {
    
    },
        fBound  = function() {
    
    
          // this instanceof fBound === true时,说明返回的fBound被当做new的构造函数调用
          return fToBind.apply(this instanceof fBound ? this : oThis,
                 // 获取调用时(fBound)的传参.bind 返回的函数入参往往是这么传递的
                 aArgs.concat(Array.prototype.slice.call(arguments)));
       };
    // 维护原型关系
    if (this.prototype) {
    
    
      // Function.prototype doesn't have a prototype property
      fNOP.prototype = this.prototype; 
    }
    // 下行的代码使fBound.prototype是fNOP的实例,因此
    // 返回的fBound若作为new的构造函数,new生成的新对象作为this传入fBound,新对象的__proto__就是fNOP的实例
    fBound.prototype = new fNOP();
    return fBound;
  };
}

实际开发中闭包的应用场景,举例说明

闭包的实际应用,主要是用来封装变量。即把变量隐藏起来,不让外面拿到和修改。


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) // true
firstLoad(10) // false
firstLoad(20) // true

知识点

  • 作用域和自由变量
  • 闭包
  • this
作用域
  • 全局作用域
  • 函数作用域
  • 块级作用域(ES6新增)

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

JavaScript 作用域

作用域是可访问变量的集合。

JavaScript 作用域

在 JavaScript 中, 对象和函数同样也是变量。
在 JavaScript 中, 作用域为可访问变量,对象,函数的集合。
JavaScript 函数作用域: 作用域在函数内修改。

JavaScript 局部作用域

变量在函数内声明,变量为局部作用域。
局部变量:只能在函数内部访问。
实例:


// 此处不能调用 carName 变量
function myFunction() {
    
    
    var carName = "Volvo";
    // 函数内可调用 carName 变量
}

因为局部变量只作用于函数内,所以不同的函数可以使用相同名称的变量。
局部变量在函数开始执行时创建,函数执行完后局部变量会自动销毁。

JavaScript 全局变量

变量在函数外定义,即为全局变量。
全局变量有 全局作用域: 网页中所有脚本和函数均可使用。
实例:


var carName = " Volvo";
 
// 此处可调用 carName 变量
function myFunction() {
    
    
    // 函数内可调用 carName 变量
}

如果变量在函数内没有声明(没有使用 var 关键字),该变量为全局变量。
以下实例中 carName 在函数内,但是为全局变量。
实例:


// 此处可调用 carName 变量
 
function myFunction() {
    
    
    carName = "Volvo";
    // 此处可调用 carName 变量
}

JavaScript 变量生命周期

JavaScript 变量生命周期在它声明时初始化。
局部变量在函数执行完毕后销毁。
全局变量在页面关闭后销毁。

函数参数

函数参数只在函数内起作用,是局部变量。

HTML 中的全局变量

在 HTML 中, 全局变量是 window 对象: 所有数据变量都属于 window 对象。
实例:


//此处可使用 window.carName
 
function myFunction() {
    
    
    carName = "Volvo";
}

自由变量
  • 一个变量在当前作用域没有定义,但被使用了
  • 向上级作用域,一层一层一次寻找,直到找到为止
  • 如果到全局作用域都没有找到,则报错 xx is not defined
    在这里插入图片描述
闭包
  • 作用于应用的特殊情况,有两种表现
  • 函数作为参数被传递
  • 函数作为返回值被返回
    在这里插入图片描述
    函数作为返回值

function create(){
    
    
	const a = 100
	return function (){
    
    
		console.log(a)
	}
}
const fn = create()
const a = 200
fn() // 100

函数作为参数被传递

function print (fn){
    
    
	const a = 200
	fn()
}
const a = 100
function fn(){
    
    
	console.olg(a)
}
print(fn) // 100

所以的自由变量的查找,实在函数定义的地方,向上级作用域查找,不是在执行的地方!!!!

在这里插入图片描述

this

  • 作为普通函数
  • 使用 call apply bind
  • 作为对象方法被调用
  • 在 class 方法中调用
  • 箭头函数中调用
    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述

实际开发中闭包的应用

  • 隐藏数据
  • 如做一个简单的 cache 工具
    闭包隐藏数据, 只提供 API
 
 function craeteCache(){
    
    
 	const data = {
    
    } // 闭包中的数据,被隐藏,不被外界访问
 	return {
    
    
 	 	set:function (key,val){
    
    
 	 		data[key] = val
 	 	},
 	 	get:function (key){
    
    
 	 		return data[key]
 		}
 	}
 }
 const c = craeteCache()
 c.set('a',100)
 console.log(c.get('a'))
 	 

在这里插入图片描述

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>js 基础知识 演示</title>
</head>
<body>
    <p>一段文字 1</p>
    <p>一段文字 2</p>
    <p>一段文字 3</p>

</body>
<script>
    let a
    // for(i=0; i<10;i++){
    
    
    for(let i=0; i<10;i++){
    
    
        a = document.createElement('a')
        a.innerHTML = i + '<br>'
        a.addEventListener('click',function(e){
    
    
            e.preventDefault()
            alert(i)
        })
        document.body.appendChild(a)
    }
</script>
</html >

小结

  • 作用于和自由变量
  • 闭包:两种常见方式 & 自由变量查找规则
  • this

猜你喜欢

转载自blog.csdn.net/WLIULIANBO/article/details/114945839