day0421
todolist实现
作用域与闭包
作用域
全局作用域只有一个,每个函数又都有函数作用域(环境)。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);
}
使用 let 关键字声明的全局作用域变量不属于 window 对象:
闭包
基本使用
function fn() {
let n = 1;
return function() {
console.log(++n);
};
}
let a = fn()
a()
a()
数组对象进行排序
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"))
对列表元素绑定事件
for (var i = 0; i < liList.length; i++) {
liList[i].onclick = (function (i) {
return function () {
console.log(`当前点击按钮的索引:${i}`)
}
})(i)
}
闭包实现模块化 保护
var myModule = (function () {
var name = '张三'
function getName() { return name }
return {
getName
}
})()
惰性调用
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')
函数柯里化
// 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
给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
注:IE6~8中基于attachEvent实现事件绑定,事件触发方法执行,方法中的this不在是元素本身,一般情况都是指向window
普通函数执行中的this
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 })
箭头函数执行中的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()
基于call/apply/bind强制改变中的this
function fn(x, y) {
// 通过call或apply的改变,this现在指向了obj对象
console.log('fn', this, x, y)
}
let obj = {
name: '张三'
}
fn.call(obj, 10, 20)
// ================== 注意点
function fn() {
console.log(this)
}
### 在非严格模式下,第1个参数不传入或传入null或undefined,this都改为window
### 严格模式下,不传是undefined,否则传递谁,this就改为谁
fn.call()
fn.call(null)
fn.call(undefined)
// 获取数组中的最大值
let arr = [3, 1, 6, 19, 5]
console.log(Math.max.apply(null, arr))
let obj = {
name: '张三'
}
function fn(ev) {
// this obj
console.log(this, ev)
}
document.body.onclick = fn.bind(obj)
防抖和节流
防抖
function debounce(fn, wait = 500, now = true) {
// 定时器返回值标识
let timer = null
return function (...params) {
// 保存this指向
let self = this
// 如果设置了开始执行且计时器还没有运行 为 true
let start = now && !timer
// 清除上次定时器
clearTimeout(timer)
timer = setTimeout(function () {
timer = null
// 最后执行,注意改变this指向
!now ? fn.call(self, ...params) : null
}, wait);
// 点击就立刻执行 取决于第3个参数
start ? fn.call(self, ...params) : null
}
}
const clickFn = debounce(function (evt) {
console.log('点击按钮了', evt, this)
}, 500, true);
document.getElementById('button').onclick = clickFn
节流
function throttle(fn, wait = 500) {
// 定时器返回值标识
let timer = null
// 上一次操作的时间
let prevTime = 0
return function (...params) {
let self = this
// 当前这次触发操作的时间
let nowTime = new Date().getTime()
// 剩余时间
let remaining = wait - (nowTime - prevTime)
if (remaining <= 0) {
// 达到设置的触发时间
// 清除定时器
clearTimeout(timer)
timer = null
// 上一次操作时间为当前触发时间
prevTime = nowTime
// 两次间隔时间超过wait了,直接执行即可
fn.call(self, ...params)
} else if (!timer) { // 此条件满足,上一次没有达到时长去触发,就已经不进行操作了
// 没有超过设置时长,则设置定时器,让其等待
timer = setTimeout(function () {
clearTimeout(timer)
timer = null
prevTime = new Date().getTime()
fn.call(self, ...params)
}, remaining);
}
}
}
const scrollFn = throttle(function (ev) {
console.log('ok', ev)
});
window.onscroll = scrollFn