一、作用域与闭包
1.1、作用域
全局作用域只有一个,每个函数又都有作用域(环境)。
es6+中多了一个块级作用域
作用域链只向上查找才,找到全局window为止
函数被执行后其环境变量将从内存中删除,函数每次被调用都会创建一个新作用域
function count() {
let total = 0;
console.log(total)
return total
}
count();
如果子函数被使用时,其父级环境将会被保留 即闭包
function fn() {
let n = 1;
return function() {
console.log(n++);
};
}
let a = fn()
a();
块级作用域:使用 let/const 可以将变量声明在块作用域中(放在新的环境中,而不是全局中)
{
let a = 1;
}
// 调用报错,a变量找不到
console.log(a);
for (let i = 0; i < 10; i++) {
setTimeout(() => {
console.log(i);
}, 500);
}
1.2、闭包
闭包是函数运行的一种机制,函数执行会形成一个私有的作用域(上下文),
如果私有作用域的某些内容被私有作用域以外的一些事物(如:变量/事件绑定等)所占用,
则当前私有作用域不能被出栈释放。
创建闭包的最常见的方式就是在一个函数内创建另一个函数,通过另一个函数访问这个函数的局部变量。
优点:
保护私有上下文中的“私有变量”和外界互不想通
私有上下文中的"私有变量“和”值“都会被保存起来,可以供其下级上下文中使用
缺点:
会导致内存太大,致使页面渲染变慢,性能受影响。所以在项目中要“合理应用闭包
1.2.1、基本使用:
function fn() {
let n = 1;
return function() {
console.log(++n);
};
}
let a = fn()
a();
1.2.2、数组对象进行排序:
let lessons = [
{
title: "Nodejs快速入门",
click: 100,
price: 3200
},
{
title: "html+css",
click: 100,
price: 1200
},
{
title: "js入门到精通",
click: 2100,
price: 2000
}
];
function myOrder(field) {
return (a, b) => a[field] - b[field];
}
lessons.sort(myOrder("price"));
1.2.3、对列表元素绑定事件:
for (var i = 0; i < liList.length; i++) {
liList[i].onclick = (function (i) {
return function () {
console.log(`当前点击按钮的索引:${i}`)
}
})(i)
}
1.2.4、闭包实现模块化 保护:
var myModule = (function () {
var name = '张三'
function getName() { return name }
return {
getName
}
})()
1.2.5、惰性调用
简单来说就是再处理多次调用的函数时进行闭包操作,使其运用方便
例如:
兼容性的判断函数,在相同页面中,每一次执行函数,进来后
都要重复的兼容判断(但是理论上第一次执行,我们就知道兼容性了,后期再执行,
没必要每一次都判断兼容,也就是把兼容处理只处理一次 =>“懒”)
function getStyle(el, attr) {
// 判断当前属性是不是属于此对象
if ('getComputedStyle' in window) {
getStyle = function (el, attr) {
return window.getComputedStyle(el)[attr]
}
} else {
getStyle = function (el, attr) {
return el.currentStyle[attr]
}
}
return getStyle(el, attr)
}
getStyle (document.getElementById('box'), 'color');
1.2.6、函数柯里化
柯里化是一个预处理思想,使用闭包形成一个不被释放的上下文,
把一些信息存储起来,以后基于作用域链,访问到事先存储的信息,
然后进行相关的处理,我们把这种模式称为柯里化函数。
// x为预先存储的值
function curring(x) {
return function (...args) {
args.unshift(x)
return args.reduce((prev, curr) => prev + curr, 0)
}
}
var sum = curring(10) // 10
console.log(sum(20)) // 10+20
console.log(sum(20, 30)) // 10+20+30
二、this指向
this函数的执行主体(不等价于执行上下文),取决于谁把这个函数执行的,共分为以下几类:
2.1、事件绑定中的this
给dom元素的某个事件行为绑定方法,当事件触发方法执行,方法中的this是当前dom元素本身。
document.body.onclick = function () {
console.log(this)
}
document.body.addEventListener('click', function () {
console.log(this)
})
/* document.body.attachEvent('onclick', function () {
console.log(window)
console.log(this)
}) */
注:IE6~8中基于attachEvent实现事件绑定,事件触发方法执行,方法中的this不在是元素本身,一般情况都是指向window
2.2、普通函数执行中的this
函数执行,看函数前面是否有“点”,有“点”,“点”前面是谁this就是谁,没有点“点”this是window,(js严格模式下是undefined)
自执行函数执行,其内的this一般都是window,严格模式下为undefined
回调函数中的this为window或undefined,除非做过特殊处理(如:数组中的forEach)
function fn() {
console.log(this)
}
var obj = {
name: 'aaaa',
fn
}
// window,如果严格模式为undefined
// fn()
// obj对象
// obj.fn()
// 自执行函数
(function () {
// window
console.log(this)
})()
var obj = {
numb: (function () {
// window
console.log(this)
return 10
})()
}
setTimeout(function () {
// window
console.log(this)
}, 1000);
[1].forEach(function () {
// window
console.log(this)
});
// forEach对this内部进行了处理
[1].forEach(function () {
// { id:1 }
console.log(this)
}, { id: 1 })
2.3、箭头函数执行中的this
箭头函数中没有自己的this,所用到的this都是所处上下文中的this
let obj = {
n: 1000,
fn() {
setTimeout(function () {
// this ==> window
console.log(this)
}, 500)
setTimeout(() => {
// this 所处上下文中的this => obj
console.log(this)
}, 1000);
}
}
obj.fn();