JavaScript基础(二)
一、事件冒泡与捕获
1、什么是事件冒泡、事件捕获
事件冒泡:最具体的元素接收,然后逐级向上传播到较为不具体的节点(IE提出)
事件捕获:不太具体的DOM节点应该更早接收到事件,而最具体的节点应该最后接收到事件(网景)
DOM事件流:事件流分为三个阶段:事件捕获阶段、目标阶段、事件冒泡阶段。目标阶段前不会触发事件。目标阶段以及事件冒泡阶段会触发事件。
我们现在浏览器默认的事件触发是遵循DOM事件流规则。
<div class="red">
<div class="green">
<div class="blue">
</div>
</div>
</div>
<script>
const capture = false // 是否在捕获阶段就触发事件
// 事件绑定
document.querySelector('.red').addEventListener('click', (e) => {
console.log('red')
}, capture)
document.querySelector('.green').addEventListener('click', (e) => {
console.log('green')
// 阻止事件继续传递(默认的情况下是阻止事件冒泡, 但是如果是捕获阶段可以阻止事件捕获)
e.stopPropagation()
}, capture)
document.querySelector('.blue').addEventListener('click', (e) => {
console.log('blue')
}, capture)
</script>
运用场景:
由于事件冒泡机制, 有些场景中我们需要阻止事件的继续传递。例如我们常见的购物车页面,数量增减的点击事件需要阻止事件的继续传递。
二、原型与原型链
prototype
1、在JavaScript中,每个函数都有一个prototype属性,这个属性指向函数的原型对象。
例如: 有小黑和小白两条狗,他们都属于狗,所以他们都会有狗的特性,如:汪汪汪。
function Dog(name) {
this.name = name
}
Dog.prototype.wang = function () {
console.log('汪汪汪')
}
var blackDog = new Dog('小黑')
var whiteDog = new Dog('小白')
console.log(blackDog.wang()) // 汪汪汪
console.log(whiteDog.wang()) // 汪汪汪
函数的prototype指向了一个对象,而这个对象正是调用构造函数时创建的实例的原型,也就是blackDog和whiteDog的原型。
原型的概念:每一个javascript对象(除null外)创建的时候,就会与之关联另一个对象,这个对象就是我们所说的原型,每一个对象都会从原型中“继承”属性。例如:我们定义的每一个数组,都有数组的方法 push、shift等。
构造函数和实例原型之间的关系:
__proto__
2、这是每个对象(除null外)都会有的属性,叫做__proto__,这个属性会指向该对象的原型。
function Dog(name) {
this.name = name
}
var dog = new Dog();
console.log(dog.__proto__ === Dog.prototype); // true
关系:
3、constructor
每个原型都有一个constructor属性,指向该关联的构造函数(构造器)。实例也有constructor属性,也指向该实例的构造函数。
function Dog(name) {
this.name = name
}
var dog = new Dog();
console.log(Dog===Dog.prototype.constructor) //true
console.log(Dog===dog.constructor) //true
4、实例与原型
当读取实例的属性时,如果找不到,就会查找与对象关联的原型中的属性,如果还查不到,就去找原型的原型,一直找到最顶层为止。
如:
function Dog(name) {
this.name = name
}
Dog.prototype.wang = function () {
console.log('汪汪汪')
}
var blackDog = new Dog('小黑')
var whiteDog = new Dog('小白')
// 教会小黑规律的叫
blackDog.wang = () => {
console.log('汪, 汪汪, 汪汪汪汪汪汪')
}
console.log(blackDog.wang()) // 汪, 汪汪, 汪汪汪汪汪汪
console.log(whiteDog.wang()) // 汪汪汪
在例子中,blackDog 定义了 wang 的方法,所以读取时会优先读取blackDog 已经定义了的方法,若查不到再找原型的方法(whiteDog 找到了它原型有 wang方法)。若再找不到该方法,则会去找原型的原型的方法。
这里我们知道 blackDog 和 whiteDog 的原型指向 原型对象 Dog.prototype。那么 Dog.prototype 的原型又是什么呢?
5、原型的原型
原型对象(Dog.prototype)也是一个对象,既然是对象,我们就可以用最原始的方式创建它。或者我们可以直接 使用 Dog.prototype.__proto__
来看看它的原型是什么
function Dog(name) {
this.name = name
}
var blackDog = new Dog('小黑')
console.log(blackDog.__proto__.__proto__)
console.log(blackDog.__proto__.__proto__ === Object.prototype) // true
关系图:
6、原型链
简单的回顾一下构造函数、原型和实例的关系:
每个构造函数都有一个原型对象,原型对象都包含一个指向构造函数的指针,而实例都包含一个指向原型对象的内部指针。
那么假如我们让原型对象等于另一个类型的实例,结果会怎样?显然,此时的原型对象将包含一个指向另一个原型的指针,相应地,另一个原型中也包含着一个指向另一个构造函数的指针。假如另一个原型又是另一个类型的实例,那么上述关系依然成立。如此层层递进,就构成了实例与原型的链条。这就是所谓的原型链的基本概念。
Object.prototype.proto 的值为 null , 或者说 Object.prototype 没有原型。
原型链: 相互关联的原型组成的链状结构就是原型链(橙色)
即: 实例 => 实例的原型 => 实例的原型的原型 => 实例的原型的原型的原型
参考文献:
会议视频
主题 :赵志星的快速会议
日期 :2021-12-08 19:01:52