js基础
一.垃圾回收机制
-
全局变量一般不会被回收,局部变量执行完毕就回收
-
内存生命周期:内存分配------内存使用------内存回收
-
引用 计数法:跟踪引用的次数(ie 浏览器),嵌套引用(相互引用)失效
-
标记清除法:主流浏览器使用,从根部(全局变量)定期扫描,找不到就清除
二.闭包
-
内层函数引用外层函数的变量就会形成闭包
-
变量私有化
-
闭包会导致内存泄漏
三.原型和原型链
- 每个函数都有一个prototype(显示原型)属性,prototype属性指向的对象就是构造函数的原型对象,prototype中的有个constructor属性对象构造函数
- 通过new一个构造函数得到实例对象,实例对象的__proto__指向了原型对象,实例对象的__proto__===构造函数的prototype。proto(也叫隐式原型)不是语言本身的特性,是浏览器厂商添加的私有属性,所有获取实例对象的原型对象,可使用Object.getPrototypeOf()方法
- 实例对象.hasOwnProperty("name") 判断是否为实例本身的属性
- 实例对象的__proto__===构造函数的原型对象(prototype).proto ===Object的原型对象.proto===null
四.js六种继承方式
1. 原型链继承:将子类构造函数的原型对象(prototype)指向另一个构造函数的实例
JS
function Person() {
this.head = 1 this.hand = 2
}
function YellowRace() { }
YellowRace.prototype = new Person()
const hjy = new YellowRace()
console.log(hjy.head) // 1 console.log(hjy.hand) // 2
这种继承方式的弊端:
-
创建hjy实例时不能传参,也就是YellowRace构造函数本身不接受参数。
-
当原型上的属性是引用数据类型时,所有实例都会共享这个属性,即某个实例对这个属性重写会影响其他实例。
2.盗用构造函数(也叫对象伪装或者经典继承):通过子类调用父类的构造函数实现上下文的绑定
JS
function Person(eyes) { this.eyes = eyes this.colors = ['white', 'yellow', 'black'] }
function YellowRace() { Person.call(this, 'black') // 调用构造函数并传参 }
const hjy = new YellowRace() hjy.colors.push('green')
console.log(hjy.colors) // ['white', 'yellow', 'black', 'green'] console.log(hjy.eyes) // black
const laowang = new YellowRace()
console.log(laowang.colors) // ['white', 'yellow', 'black'] console.log(laowang.eyes) // black
- 必须在构造函数中定义方法,通过盗用构造函数继承的方法本质上都变成了实例自己的方法,不是公共的方法,因此失去了复用性。
- 子类不能访问父类原型上定义的方法,因此所有类型只能使用构造函数模式,原因如上图所示,YellowRace构造函数、hjy和laowang实例都没有和Person的原型对象产生联系
3.组合继承:先通过盗用构造函数实现上下文绑定和传参,然后再使用原型链继承的手段将子构造函数的prototype指向父构造函数的实例
JS
function Person(eyes) {
this.eyes = eyes
this.colors = ['white', 'yellow', 'black']
}
Person.prototype.getEyes = function () {
return this.eyes
}
function YellowRace() {
Person.call(this, 'black') // 调用构造函数并传参
}
YellowRace.prototype = new Person() // 再次调用构造函数
const hjy = new YellowRace()
hjy.colors.push('green')
const laowang = new YellowRace()
console.log(hjy.colors) // ['white', 'yellow', 'black', 'green']
console.log(laowang.colors) // ['white', 'yellow', 'black']
console.log(hjy.getEyes()) // black
组合继承还是有一个小小的缺点,那就是在实现的过程中调用了两次Person构造函数,有一定程度上的性能浪费。这个缺点在最后的寄生式组合继承可以改善。
4.原型式继承:Object.create()
JS
const object = function (o) {
function F() { }
F.prototype = o
return new F()
}
缺点:
-
不能传参,使用手写的object()不能传,但使用Object.create()是可以传参的。
-
原对象中的引用类型的属性会被新对象共享。
5.寄生式继承
JS
function inherit(o) {
let clone = Object.create(o)
clone.sayHi = function () { // 增强对象
console.log('Hi')
}
return clone
}
const hjy = {
eyes: 'black',
colors: ['white', 'yellow', 'black']
}
const laowang = inherit(hjy)
console.log(laowang.eyes) // black
console.log(laowang.colors) // ['white', 'yellow', 'black']
laowang.sayHi() //
6.寄生式组合继承:通过盗用构造函数继承属性,但使用混合式原型链继承方法。基本思路就是使用寄生式继承来继承父类的原型对象,然后将返回的新对象赋值给子类的原型对象。
JS
function inherit(Son, Father) {
const prototype = Object.create(Father.prototype) // 获取父类原型对象副本
prototype.constructor = Son // 将获取的副本的constructor指向子类,以此增强副本原型对象
Son.prototype = prototype // 将子类的原型对象指向副本原型对象
}
function Person(eyes) {
this.eyes = eyes
this.colors = ['white', 'yellow', 'black']
}
Person.prototype.getEyes = function () {
return this.eyes
}
function YellowRace() {
Person.call(this, 'black') // 调用构造函数并传参
}
inherit(YellowRace, Person) // 寄生式继承,不用第二次调用构造函数
const hjy = new YellowRace()
hjy.colors.push('green')
const laowang = new YellowRace()
console.log(hjy.colors) // ['white', 'yellow', 'black', 'green']
console.log(laowang.colors) // ['white', 'yellow', 'black']
console.log(hjy.getEyes()) // black
原型与实例的关系可以用两种方式来确定:instanceof操作符和isPrototypeOf()方法。
JS
function Perosn(name) {
this.name = name
}
const hjy = new Perosn('滑稽鸭')
const laowang = {
name: '老王'
}
console.log(hjy instanceof Perosn) // true
console.log(laowang instanceof Perosn) // false
五.事件模型
1.事件流都会经历三个阶段
- 事件捕获阶段(capture phase)
- 处于目标阶段(target phase)
- 事件冒泡阶段(bubbling phase)
2.事件模型
原始事件模型(DOM0):
- html直接绑定的事件
<div onclick="fun1()">
- js事件绑定
document.getElementByid("app").onCLick=function(){]
特性:
- 只支持冒泡,不支持捕获
- 同一个类型的事件只能绑定一次
- 速度快
标准事件模型
- 添加事件
document.getElementById('app').addEventListener('click',function(e){},false)
addEventListener第一个参数事件名称不带on
第二个参数处理函数
第三个参数可省略默认为false(冒泡传递),true为捕获传递 - 删除事件 removeEventListener(eventType, handler, useCapture)
- e.stopPropagation :阻止事件冒泡,不阻止默认事件。当前元素绑定多个事件都可以执行
- e.stopImmediatePropagation():阻止事件冒泡,不阻止默认事件。后面绑定的事件不可执行。
- e. preventDefault():不阻止魔炮,阻止默认事件(连接跳转、表单等)
- html直接绑定事件reture false 阻止冒泡,阻止默认事件
html
<a href="http://www.baidu.com">
<div id="click" style="width:100px;height:100px;background-color: rgb(18, 56, 246);" onclick="return false">
clickme</div>
</a>
IE事件模型(基本不用)
- 绑定事件:attachEvent(eventType, handler)
- 删除事件:detachEvent(eventType, handler)
六.js阻塞html解析,异步加载defer和async区别
html 解析器运行于主线程中,如果遇到<script>
标签后会阻塞,直到脚本从网络中下载并被执行,也就是说<script>
标签的脚本会阻塞浏览器的渲染
页面生命周期时间
- DOMContentLoaded:页面已经完全加载了 html 并且构建了 dom 树,但样式和 img 这样的资源还没有加载完
- load:浏览器不仅加载完成了 HTML,还加载完成了所有外部资源:图片,样式等。
- beforeunload/unload:当用户正在离开页面时。
异步加载
- async 执行时机:下载完后,立即执行
- defer 下载完后,在 dom 解析完之后、触发 DOMContentLoaded 方法之前执行