JS基础

js基础

一.垃圾回收机制

  1. 全局变量一般不会被回收,局部变量执行完毕就回收

  2. 内存生命周期:内存分配------内存使用------内存回收

  3. 引用 计数法:跟踪引用的次数(ie 浏览器),嵌套引用(相互引用)失效

  4. 标记清除法:主流浏览器使用,从根部(全局变量)定期扫描,找不到就清除

二.闭包

  1. 内层函数引用外层函数的变量就会形成闭包

  2. 变量私有化

  3. 闭包会导致内存泄漏

三.原型和原型链

  1. 每个函数都有一个prototype(显示原型)属性,prototype属性指向的对象就是构造函数的原型对象,prototype中的有个constructor属性对象构造函数
  2. 通过new一个构造函数得到实例对象,实例对象的__proto__指向了原型对象,实例对象的__proto__===构造函数的prototype。proto(也叫隐式原型)不是语言本身的特性,是浏览器厂商添加的私有属性,所有获取实例对象的原型对象,可使用Object.getPrototypeOf()方法
  3. 实例对象.hasOwnProperty("name") 判断是否为实例本身的属性
  4. 实例对象的__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()
}

缺点:

  1. 不能传参,使用手写的object()不能传,但使用Object.create()是可以传参的。

  2. 原对象中的引用类型的属性会被新对象共享。

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 方法之前执行
相关推荐
江号软件分享5 分钟前
轻松解决Office版本冲突问题:卸载是关键
前端
致博软件F2BPM12 分钟前
Element Plus和Ant Design Vue深度对比分析与选型指南
前端·javascript·vue.js
慧一居士1 小时前
flex 布局完整功能介绍和示例演示
前端
DoraBigHead1 小时前
小哆啦解题记——两数失踪事件
前端·算法·面试
一斤代码7 小时前
vue3 下载图片(标签内容可转图)
前端·javascript·vue
中微子7 小时前
React Router 源码深度剖析解决面试中的深层次问题
前端·react.js
光影少年7 小时前
从前端转go开发的学习路线
前端·学习·golang
中微子7 小时前
React Router 面试指南:从基础到实战
前端·react.js·前端框架
3Katrina7 小时前
深入理解 useLayoutEffect:解决 UI "闪烁"问题的利器
前端·javascript·面试
前端_学习之路8 小时前
React--Fiber 架构
前端·react.js·架构