xccjk / x-blog

学习笔记
18 stars 2 forks source link

JavaScript基础知识点 #74

Open xccjk opened 2 years ago

xccjk commented 2 years ago

JavaScript事件委托

什么是事件委托?

利用JavaScript事件冒泡动态为元素绑定事件的方法称为事件委托。

事件委托就是把原本需要绑定在子元素上的事件委托到它的父元素,让父元素监听子元素的冒泡事件,并在子元素发生事件冒泡时找到这个子元素。

为什么需要事件委托

页面的事件个数会直接影响页面的整体性能,因为每个事件处理程序都是对象,对象会占用内存,内存中的对象越多,页面的性能越差

事件会频繁的操作DOM元素,DOM元素的操作会引起浏览器的重绘与重排

事件委托优点:

  1. 减小内存消耗
  2. 动态绑定事件

事件委托原理

事件委托是利用事件冒泡来实现的,流程:

不采用事件委托绑定事件:

<!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>不采用事件委托绑定事件</title>
  </head>
  <body>
    <ul id="app">
      <li>1</li>
      <li>2</li>
      <li>3</li>
      <li>4</li>
    </ul>
  </body>
</html>
<script>
  window.onload = () => {
    const app = document.getElementById('app')
    const children = app.getElementsByTagName('li')
    for (let i = 0; i < children.length; i++) {
      children[i].onclick = () => {
        alert(children[i].innerHTML)
      }
    }
  }
</script>

采用事件委托绑定事件

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <meta http-equiv="X-UA-Compatible" content="ie=edge" />
    <title>采用事件委托绑定事件</title>
  </head>
  <style>
    #app{
      width: 300px;
      background: yellow;
    }
    li {
      width: 30px;
      height: 30px;
      background: #ccc;
      list-style-type: none;
    }
  </style>
  <body>
    <ul id="app">
      <li>1</li>
      <li>2</li>
      <li>3</li>
      <li>4</li>
    </ul>
  </body>
</html>
<script>
  window.onload = () => {
    const app = document.getElementById("app");
    app.onclick = (e) => {
      alert(e.target.innerHTML);
    };
  };
</script>

[在线demo](https://codesandbox.io/s/javascriptshi-jian-wei-tuo-demo-j8lohp?file=/index.html:365-385)

总结

xccjk commented 2 years ago

JavaScript作用域

函数作用域

函数中的作用域

JavaScript具有基于函数的作用域。函数内部标识符无论在函数何处,这个标识符所代表的变量或者函数都附属于当前函数所处的作用域

function foo(a) {
  var b = 1;
  var c = {};
  function bar() {
    // ...
  }
}
console.log(a,b,c)  // error

foo(...)的作用域中包含了标识符a、b、c、bar

由于标识符a,b,c,bar都属于作用域foo,因此无法从foo的外部对它进行访问。

定义:函数作用域是指属于这个函数的全部变量都可以在整个函数的范围内使用

什么是函数作用域

函数外部作用域无法访问包装函数内部的任何内容

var a = 2
function foo() {
  var a = 3
  console.log(a)  // 3
}
foo()
console.log(a) // 2

函数作用域的优点

避免所有变量、函数都可以被外部访问到
// bad
function foo(a) {
  b = a + bar(a * 2)
  console.log(b * 3)
}

function bar(a) {
  return a - 1
}

var b

foo(2)  // 5

该方法不仅可以访问到foo,还可以访问到变量b和函数bar

// good
function foo(a) {
  function bar(a) {
    return a - 1
  }
  var b
  b = a + bar(a * 2)
  console.log(b)
}
foo(2)  // 5

相比上面的方法,把变量b和函数bar变为了私有方法

规避同名标识符之间的冲突
function foo() {
  function bar(a) {
    i = 3
    console.log(a + i)
  }

  for(var i = 0; i < 10; i++) {
    bar(i * 2)
  }
}
foo()

函数bar内部变量i覆盖了for循环中的i,导致了无限循环

函数声明与函数表达式

区分函数声明和函数表达式,看function关键字出现在声明中的位置就可以判断。如果function是声明中的第一个词,就是一个函数声明,否则就是函数表达式

函数声明与函数表达式最重要的区别,就是它们的名称标识符会绑定在何处

函数声明

function foo() {
  console.log('我是一个函数声明')
}

函数表达式

(function foo() {
  console.log('我是一个函数表达式')
})()

匿名的函数表达式

(function () {
  console.log('我是一个匿名的函数表达式')
})()

匿名函数表达式优缺点

优点:

缺点:

立即执行函数表达式

(function foo() {

})()

或者

(function () {

})()

或者

(function foo() {

}())

块作用域

for(var i = 0; i < 5; i++) {

}
console.log(i)  // 5

for循环中,我们在头部定义了变量i,希望在for循环内部上下文中使用i实际情况下变量i还可以在外部作用域访问到

存在的问题:for循环内部的变量i可能会被外部变量i赋值给更改掉

块作用域是一个用来对之前的最小授权原则进行扩展的工具,将代码从在函数中隐藏信息 扩展为在块中隐藏信息

几种常见的块级作用域的定义

with

with(true ) {}

try/catch中的catch

try {

} catch(err) {
  console.log(err)
}
console.log(err) // error

let

let 关键字可以将变量绑定到所在的任意作用域中

if (true) {
  var a = 2
}
console.log(a)  // 2
if (true) {
  let b = 2
}
console.log(b) // b is not defined

使用 let 进行的声明不会在块作用域中进行提升

console.log(a) // undefined
var a = 1
console.log(a)  // SyntaxError: Identifier 'a' has already been declared
let a = 1
var a = 1
a = 2
console.log(a)  // 2
let a = 1
a = 2  // SyntaxError: Identifier 'a' has already been declared
for(var i = 0; i < 5; i++) {}
console.log(i)  // 5

// 等价于
var i
for(i = 0; i < 5; i++) {}
console.log(i)  // 5
for(let i = 0; i < 5; i++) {}
console.log(i)  // ReferenceError: i is not defined