this与target和事件委托的相爱相杀
如何让我们写最少的代码,干最多的活呢?有的时候this&target用好了可以让我们如鱼得水。在前端初学者中,肯定有部分人对this的指向是模糊的。
首先在使用this之前我们应该搞清楚this指向的问题
1.函数中的this:指向Window。
我们肯定听过一局话this指向调用者,为什么下文的的函数是指向Window呢?
function that() {
console.log(this) //输出Window
}
//这里明明只写了that() 为什么会指向Window呢?
that()
其实我们完整的写法是Window.that(),作为顶级对象的Window,你只要全局声明函数&var 声明变量都会挂载到Window身上(这有点违反常理)我声明就想我自己玩跟你Window有啥关系,es6新语法let、const声明的变量&函数就不会挂载到Window身上,来我们看以下代码。
function that() {
console.log(this)
}
var a = 0
let b = 1
const c = 1
console.log(window.that)
console.log(window.a)
console.log(window.b)
console.log(window.c)
输出结果:
但是现在又有新问题了,为什么同样是全局声明凭啥你们两个搞特殊呢?
在ES5中,顶层对象的属性和全局变量是等价的,var 命令和 function 命令声明的全局变量,自然也是顶层对象。
但ES6规定,var 命令和 function 命令声明的全局变量,依旧是顶层对象的属性,但 let命令、const命令、class命令声明的全局变量,不属于顶层对象的属性。
在事件当中this 指向的是注册事件的对象
<div>我是div</div>
document.querySelector('div').addEventListener('click', function () {
console.log(this)
})
//输出内容
在箭头函数中this 指向最近作用域中对象的this,例如:
<div>我是div</div>
document.querySelector('div').addEventListener('click', () => {
console.log(this)
})
//输出内容
箭头函数在事件当中要慎用,当然他也有秒用比如我们现在要做一个功能,点击按钮时按钮禁用,2秒后解除禁用
<button>发送短信</button>
document.querySelector('button').addEventListener('click', function () {
this.disabled = true
//第一种
// let that = this
// setTimeout(function () {
// that.disabled = false
// }, 2000)
//第一种
//第二种
// setTimeout(
// function () {
// this.disabled = false
// }.bind(this),
// 2000
// )
//第二种
// 第三种
setTimeout(() => {
this.disabled = false
}, 2000)
})
实现这个功能我们可以声明变量保存this,也可以使用bind方法改变this的调用者,但无疑使用箭头函数是最方便的。
事件委托
事件委托在jquery中叫事件代理
如果我们不用事件委托给ul中的里添加事件
<!-- 这里就不写样式了 -->
<ul></ul>
<script>
let html = ``
//循环
for (let i = 0; i < 10; i++) {
// let li = document.createElement('li')
// li.innerHTML = `我是第${i}个li`
// document.querySelector('ul').appendChild(li)
//字符串的拼接的效率高一些
html += `<li>我是第${i}个li</li>`
}
document.querySelector('ul').innerHTML = html
//获取所有的li标签
let lis = document.querySelectorAll('li')
//循环lis 给所有的li添加事件 这里使用ES6新特性for of
for (let item of lis) {
item.addEventListener('click', function () {
//点击li 之后首先要清空所有的li 里面的颜色
for (let items of lis) {
items.style.backgroundColor = ''
}
//这里的this 指向我们点击时的对象(li)
this.style.backgroundColor = 'red'
})
}
想想如果我们有更多的li 标签 点击一次就要循环所有的li 太影响性能了。
事件委托给li 添加事件
let html = ``
for (let i = 0; i < 10; i++) {
html += `<li>我是第${i}个li</li>`
}
document.querySelector('ul').innerHTML = html
let lis = document.querySelectorAll('li')
document.querySelector('ul').addEventListener('click', (e) => {
//e是事件对象 这里为了让代码看起来简洁一点使用了 es6 的箭头函数
for (let item of lis) {
item.style.backgroundColor = ''
}
e.target.style.backgroundColor = 'red'
})
事件委托的优点:
1.可以大量节省内存占用,减少事件注册。
2.可以实现当新增子对象时,无需再对其进行事件绑定,对于动态内容部分尤为适合。
事件委托的缺点:
1.事件委托的实现依靠的冒泡,因此不支持事件冒泡的事件就不适合使用事件委托。
2.不恰当使用反而可能导致不需要绑定事件的元素也被绑定上了事件。
我们可以解决让不想绑定的元素不触发,只需要一个判断就行了
let html = ``
for (let i = 0; i < 10; i++) {
html += `<li>我是第${i}个li</li>`
}
document.querySelector('ul').innerHTML = html
let lis = document.querySelectorAll('li')
document.querySelector('ul').addEventListener('click', (e) => {
//e是事件对象 这里为了让代码看起来简洁一点使用了 es6 的箭头函数
// console.log(e) 我们打印 事件对象 找到target对象中的nodeName 或者 localName 进行判断
//注意:不加判断点到ul身上就会出bug
if (e.target.nodeName == 'LI') {
for (let item of lis) {
item.style.backgroundColor = ''
}
e.target.style.backgroundColor = 'red'
}
console.log(`你没点击li`)
})
使用事件委托:首先我们要清楚事件委托是利用事件冒泡的机制,在我们对元素产生行为时事件冒泡就触发了(当前元素>父元素>body>html)结束,除了事件冒泡还有事件捕获,事件捕获的机制是跟事件冒泡相反的(html>body>父元素>当前元素)但我们在实际开发中大部分的情况下都是使用事件冒泡的机制,对于事件捕获我们有一个概念就行。
相比js来说jquery的事件代理就简单的多,在jQuery中我们事件排他是不用循环的,它的底层隐式迭代帮我们处理了,但jQuery还是逃不出前端技术的迭代
let html = ``
for (let i = 0; i < 10; i++) {
html += `<li>我是第${i}个li</li>`
}
$('ul').html(html)
$('ul').on('click', 'li', function () {
$(this).addClass('red').siblings('.red').removeClass('red')
})