作用域 和 闭包
- 题目
- 知识点
- 解答
题目
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