JS中一些重要部分

1:基本数据类型和引用数据类型

基本数据类型:按值访问,可以操作保存在变量中的值,undefined,null,string,Boolean,number
引用数据类型:一般访问的是保存在内存中的对象
区别:1:基本类型的值没有属性和方法,而引用类型的值有
2:在复制值的时候,当一个变量的值复制给另一个变量,然后改变其中一个变量,基本类型的值没有影响,而引用类型的值则会因为另一个的变化而改变,因为引用类型的值,当一个赋值给另一个的时候,他们的指针指向的是同一个对象,所以改变其中一个另一个也会改变

基本类型的值                       引用类型的值
var num1=5;                       var obj1=new object()
var num2=num1;                  var obj2=obj1;
num1=3;                               obj1.name="zhang";
num2//5                                obj2//zhang

3:检测,基本类型的值一般用typeof检测出来,而引用类型的值一般用instanceof判断
4:基本类型的赋值是简单赋值,复制的时候会在变量对象上面创建一个新的值,然后把该值复制到为新变量分配的位置上去,引用类型的赋值是对象引用

作用域和作用域链

作用域我们一般有三种类型:全局作用域,函数作用域,块级作用域
在全局作用域中声明的变量我们称作是全局变量

全局变量一般有这三种情况
1:window对象
2:在最外层函数中声明的变量或者是在最外层函数外部声明的变量称为是全局变量
3:没有定义直接被赋值的变量自动声明
局部变量:在局部代码块中声明,只能作用在局部代码块内部的变量。

函数作用域
在函数内部我们给未定义的变量赋值,这个变量会转化为一个全局变量
块作用域块级作用域限制在标识符{}中

作用域链:当我们查找一个变量的时候,此时在它所在的区域中没有找到这个变量(此时我们称它为自由变量),就会向他的父级区域去查找,知道找到全局作用域中都没有找到的话,就会宣布放弃,这样沿着父级向上一级一级查找的关系我们称作是作用域链

var,let,const

这三个都是用来定义变量的
区别
1:var定义的是全局变量,let只能在局部代码块中定义局部变量,而const定义的是一个只读变量
2:var 定义的变量未赋值可以提前声明,let定义的变量不能提前声明
3:在let定义的变量所在的区域中,不管这个变量在这个区域外有没有被定义过,都会以目前这个区域中定义的值为主,也就是说,此时会被称为是,暂时性死区
4:let声明过的变量在同一个区域中没有办法再被重新声明,否则就会报错
5:const声明的是一个只读变量,一旦声明,这个变量将不会被更改

垃圾回收机制

在JS中常用两种方法来回收垃圾:标记清除引用计数
标记清除:在运行的时候,我们给所有储存在内存中的变量都加上标记,然后去掉环境中的变量以及被环境中的变量引用的变量上面的标记,此时,还带有标记的变量就是即将要被删除的变量,因为此时已经无访问到这些变量了,最后,最后垃圾回收处理器就会将这些变量清除掉,销毁那些变量的值释放变量所占用的内存空间。

引用计数
(跟踪记录每个值被引用的次数)当生命一个变量并且用引用类型给这个变量赋值的时候,此时这个值的引用次数为1,当把这个值重新赋值给新的变量的时候,这个值的引用次数会加1,当这个值的引用次数变为0的时候,也就是说没有办法再访问这个值的时候,就会被垃圾处理器回收,,就会释放掉这个值所占用的内存。

原型与原型链

在说JS原型的之前我们先来说一下构造函数
构造函数:是专门用来生成对象的函数,提供生成对象的模板
构造函数必须有以下两个特点:
1:必须使用this关键字,指向实例对象
2:在生成对象的时候,必须使用new命令,如果没有使用new命令,那么构造函数就变成了普通函数,实例对象就变成了普通对象,可以在函数内部使用new.target方法判断函数是否使用new命令

new命令执行步骤
    {
    1:生成一个空对象,作为即将要返回的对象的实例
    2:将空对象的原型,指向构造函数的prototype属性
    3:将空对象赋值给函数内部的this关键字
    4:执行函数内部代码
    }

在这里插入图片描述
说完了构造函数,接下来我们来说原型与原型链
话不多说,先上一张经典到不能再经典的图
在这里插入图片描述
每个生成的构造函数都有一个prototype属性,指向这个原型对象,原型对象同时又有一个constructor属性指向构造函数;同时,构造函数生成的实例对象又有一个_proto_属性指向原型对象。
原型对象的作用就是定义所有的实例对象可以共享的属性和方法,而实例对象可以看做是原型对象衍生出来的子对象
而何为原型链呢,就是在查找某个属性的时候,JS会先从这个对象自身的属性中查找,如果没有的,就会沿着原型链一级一级向上查找,直到查找到Object.prototype都没有的话,就会返回undefined
方法:instanceof返回一个布尔值,用于检测某个对象是不是指定对象的实例
Object.getPrototypeOf()返回一个对象的原型,这是获取对象原型的标准方法
在这里插入图片描述
Object.setPrototypeOf()为现有对象设置原型
Object.create()接受一个对象为参数,以它为原型,返回一个实例对象,该对象继承了原型对象所有的属性和方法
在这里插入图片描述
Object.prototype.isPrototypeOf()判断一个对象是不是另一个对象的原型

this关键字以及 call,bind,apply

this关键字的应用场所:
1:在全局中使用this,它就相当于是window顶层对象
2:在构造函数中使用this,指向生成的实例对象
在JS中创建了三个方法 call.bind,apply 来改变/固定this指向
call用来指定函数内部this指向。call方法的参数,应该是一个对象。如果参数为空、null和undefined,则默认传入全局对象。
在这里插入图片描述
apply和bind的方法很相似,都是用来改变this指向,但是不同的是,apply接收一个数组作为参数
func.apply(thisValue, [arg1, arg2, …])
而bind将bind绑定到某个对象上面,然后返回一个新函数,与call()和apply不同的是,bind除了调用this关键字之外,还可以调用函数自身的参数
在这里插入图片描述

扫描二维码关注公众号,回复: 9294072 查看本文章

继承

原型链继承,构造函数继承,实例继承,拷贝继承,组合继承,寄生继承,类的静态继承
原型链继承:将父类的实例作为子类的原型,特点是实例既是子类的实例也是父类的实例,父类新增的属性和方法,子类都可以访问,但是缺点是无法实现多继承
构造函数继承:使用父类的构造函数来增强子类的实例。也就是复制子类的实例属性给父类;构造函数可以向父类传递参数,实现多继承,但是这样的继承只能继承父类的实例属性和方法,不能继承圆形的属性以及方法
实例继承:为父类实例添加新属性,作为子类的实例返回
拷贝继承:支持多继承,但是效率低,内存占用高
组合继承:通过调用父类构造,继承父类属性并保留传参的优点,然后将父类实例作为子类的原型,实现函数的复用
寄生组合继承:通过寄生方式,砍掉父类的实例继承
类的继承
说类的继承之前,我们先来看看JS类的声明:
在这里插入图片描述
extends类的继承,我们可以用extends扩展一个类并继承它的行为,在构造函数中,也可以通过super关键字引用父类的构造函数
在这里插入图片描述
在这里插入图片描述

String,Array,Math常用方法

String
indexof:返回字符第一次出现的索引
lastindex:返回字符最后一次出现的索引
提取字符:1:slice(start,end):取小不取大,可以接受负的索引
2: substring(start,end) 不能接受负的索引 取大不取小
3:substr(start,length);start是起始位置,length是长度
search:搜索特定的字符串,返回特定的位置
replace:用另一个值替换在字符串中指定的值
concat:链接两个字符串
CharAt:返回字符串中指定位置的字符串
split():将字符串转化为数组

Array
遍历数组方法:instanceof,foreach
push:向数组添加新的元素
toSting:将数组转化为字符串
join:将所有的数组元素组合成为一个字符串
pop():从数组中删除最后一个元素,并且返回这个元素
shift()删除数组中的第一个元素
unshift()向数组的第一个位置添加元素
splice:可以用来拼接数组或者是删除数组参数一添加新元素的位置,参数二是删除元素数
concat()拼接
slice()将数组中某些片段切出新的数组

Math
Math.round()返回四舍五入的值
Math.ceil()返回向上取整的数
Math.floor()返回向下取整的数
Math.random()返回0到1之间的随机数

事件流

事件流是用来描述从页面中接受事件的顺序

document.querySelector()如果没有找到匹配元素,则返回 null,如果找到多个匹配元素,则返回第一个匹配到的元素。

DOM事件一般包括以下三个阶段:
1:事件捕获阶段
2:处于目标阶段
3:事件冒泡阶段

target.addEventListener(type, listener[, useCapture]) addeventListener:是DOM事件新增的指定事件处理程序操作,这个方法接受三个参数,参数一是要处理的事件类型,参数二是事件触发之后调用的函数,参数三是一个布尔值,默认值为false,也就是冒泡传递,当布尔值为true的时候,事件处于捕获阶段。IE之支持事件冒泡

如何让事件先冒泡后捕获
在DOM标准事件模型中,是先捕获再冒泡的。如果要实现先冒泡在捕获的话,对于同一个事件,监听捕获和冒泡,分别对应相应的处理函数,监听到捕获事件,先暂缓执行,知道冒泡事件被执行了之后再执行捕获

事件模型常用方法:
event.stopPropagation:阻止捕获和冒泡阶段中,当前事件的进一步传播
evevt.stopImmediatePropagation阻止调用相同事件的其他监听器
event.preventDefault:取消该事件,而不停止事件的进一步传播
event.target:指向触发事件的元素,在事件冒泡过程中这个值不变

事件委托/代理

事件代理就是在祖先级DOM元素绑定一个事件,当触发子孙级DOM元素的事件时,利用事件流的原理来触发绑定在祖先级DOM的事件。

深拷贝浅拷贝

在引用类型中,一般当由一个对象生成另一个对象的时候,改变其中一个对象,另一个对象也会改变,但是要改变这个问题,实现当一个值改变的时候,另一个值不变,就要使用深浅拷贝
浅拷贝只能实现一层这样的问题,一般有两种方法实现浅拷贝:
1:使用Object.assign()

let a={age:1};
let b=object.assign({},a);
a.age=2;
console.log(b.age)//1

2:使用位运算符

let a={age:1};
let b={...a};
a.age=2;
console.log(b.age)//1

由于浅拷贝只能解决一层的问题,如果多层的话,要通过深拷贝来实现,一般深拷贝JSON.parse(JSON.Stringify(Object));

let a = {
    age: 1,
    jobs: {
        first: 'FE'
    }
}
let b = JSON.parse(JSON.stringify(a))
a.jobs.first = 'native'
console.log(b.jobs.first) // FE

但是使用这样的方法会忽略undefined,symbol,不能序列化函数,不能解决循环引用对象
堆内存和栈内存:
栈内存:当一个方法执行时,每个方法都会建立自己的内存栈,在这个方法内定义的变量将会逐个放入这块栈内存里,随着方法的执行结束,这个方法的内存栈也将自然销毁了。因此,所有在方法中定义的变量都是放在栈内存中的
堆内存:当我们在程序中创建一个对象时,这个对象将被保存到运行时数据区中,以便反复利用(因为对象的创建成本通常较大),这个运行时数据区就是堆内存。堆内存中的对象不会随方法的结束而销毁,即使方法结束后,这个对象还可能被另一个引用变量所引用(方法的参数传递时很常见),则这个对象依然不会被销毁,只有当一个对象没有任何引用变量引用它时,系统的垃圾回收机制才会在核实的时候回收它。
栈中存储的是基础变量以及一些对象的引用变量,基础变量的值是存储在栈中,而引用变量存储在栈中的是指向堆中的数组或者对象的地址,这就是为何修改引用类型总会影响到其他指向这个地址的引用变量

闭包

通常情况下,在函数中定义的局部变量只能在函数中起作用,通过闭包我们可以访问其他函数中的局部变量,当在一个函数内部定义另外一个函数就会产生闭包,但是这样也有很大的缺点,闭包会占用大量内存,导致页面内存泄露,所以一般避免使用闭包
实例:

  function f1(){
  var a=10;
  return a;
}
function f2(){
console.log(f1());
}
f2();//10

实例二

1:var name="glo";
function foo1(){
console.log(name);
}
function foo2(){
var name="loc";
foo1();
}
foo2()//glo
2:var name="glo";
   function foo2(){
     console.log(name);//undefined
     var name="loc";
     function foo(){
    console.log(name);//local
    }
    foo();
}
foo2();

为什么要使用:
匿名自执行函数:当声明一个变量的时候,不过不添加var关键字,就会使这个变量添加到全局对象的属性上去,这样的变量添加到全局上有很多坏处,比如别的函数可能会误用这些变量,造成全局变量过于庞大,影响访问速度等,当有的函数只需要执行一次,其内部变量不需要维护的时候,就可以使用闭包

AJAX

手写原生AJAX:
在这里, 我们创建了一个能向服务器发出 HTTP 请求的类的实例。然后调用其 open 方法,其中第一个参数是 HTTP 请求方法,第二个参数是请求页面的 URL。第三个参数是true(异步)或 false(同步);最后,我们调用参数为 null 的 send 方法。假如使用 POST 请求方法(这里我们使用了 GET),那么 send 方法 的参数应该包含任何你想发送的数据。
在这里插入图片描述

JS中获取元素的位置以及尺寸

获取元素的尺寸

1:【elem.style.width(可写)】CSS写入的宽高
jQuery-【$().width()】
2:【clientHeight】内宽高—— 内边距 + 内容框(如果有滚动条,还需要减去滚动条的宽度)jQuery-【$().innerwidth()】
3.【offsetHeight】 外宽高—— 边框 + 内边距 + 内容框 
jQuery-【$().outerwidth()】
4.【scrollHeight】 用来计算可滚动容器的大小,包括不可见的部分,当然,scrollHeight 的值需要加上 padding 的值。
5.【getComputedStyle】获取元素内容的尺寸(最终计算后的结果)
6.【elem.getBoundingClientRect()】获取元素宽高 等同于 offsetHeight——边框 + 内边距 + 内容框

获取元素的位置

1.【elem.getBoundingClientRect()】只读、相对于视口(浏览器窗口)
2.【elem.offsetTop(可写)】子元素距离父元素的距离,即子元素的边框到父元素的边框的距离(子元素的margin + 父元素的padding)

获取客户端以及屏幕高度

(1)screen.width:显示器的水平方向的像素时,不随着我们浏览器窗口的变化而变化,是用设备像素衡量的
(2)window.innerWidth,window.innerWidth指的是浏览器窗口的宽度,是可以变化的,所以使用的是CSS像素
(3)document.documentElement.clientWidth,指的是网页可见区域高的宽度,与window.innerWidth的区别就只差了一个滚动条(不包含滚动条)
(4)document.documentElement.offsetWidth,取得html标签的宽度
(5)document.documentElement.scrollHeight 网页正文全文高
(6)document.documentElement.scrollTop,网页被卷去的高

防抖和节流

防抖:
在事件被触发n秒后再执行回调,如果在这n秒内又被触发,则重新计时,简单的说,函数去抖就是对于一定时间段的连续的函数调用,只让其执行一次。
应用(适合多次事件一次响应的情况):
(1)文本输入的验证(连续输入文字后发送 AJAX 请求进行验证,验证一次就好)
(2)判断scroll是否滑到底部,滚动事件+函数防抖
(3)调节浏览器size,监听resize()函数
在这里插入图片描述
节流:规定一个单位时间,在这个单位时间内,只能有一次触发事件的回调函数执行,如果在同一个单位时间内某事件被触发多次,只有一次能生效,函数节流的核心是,让一个函数不要执行得太频繁,减少一些过快的调用来节流。
应用(适合大量事件按时间做平均分配触发):
(1)DOM 元素的拖拽功能实现(mousemove)
(2)射击游戏的 mousedown/keydown 事件(单位时间只能发射一颗子弹)
(3)计算鼠标移动的距离(mousemove)
(4)Canvas 模拟画板功能(mousemove)
(5)搜索联想(keyup)
(6)监听滚动事件判断是否到页面底部自动加载更多:给 scroll 加了 debounce 后,只有用户停止滚动后,才会判断是否到了页面底部;如果是 throttle 的话,只要页面滚动就会间隔一段时间判断一次

在这里插入图片描述

异步

promise

Promise函数体的内部包裹着一个异步的请求或者操作或者函数;然后我们可以在这个异步的操作完成的时候使用resolve函数将我们获得的结果传递出去,或者使用reject函数将错误的消息传递出去。
创建promise
(1)因为Promise是一个构造函数,所以我们使用了new操作符来创建promise,(2)构造函数Promise的参数是一个函数(暂时叫它func),这个函数(func)有两个参数resolve和reject,它们分别是两个函数,这两个函数的作用就是将promise的状态从pending(等待)转换为resolved(已解决)或者从pending(等待)转换为rejected(已失败),(3)创建后的promise有一些方法,then和catch。当然我们也可以人为的在Promise函数上添加一些满足我们自己需求的方法,方便每一个promise对象使用。
在这里插入图片描述

浏览器渲染过程和Event Loop

关于多线程:JS多线程是由单线程模拟出来的
同步任务:当我们打开网站时,网页的渲染过程就是一大堆同步任务,比如页面骨架和页面元素的渲染。
异步任务:而像加载图片音乐之类占用资源大耗时久的任务,就是异步任务。
javascript事件循环(Event Loop)
在这里插入图片描述
(1)同步和异步任务分别进入不同的执行"场所",同步的进入主线程,异步的进入Event Table并注册函数。
(2)当指定的事情完成时,Event Table会将这个函数移入Event Queue(事件队列)。
(3)主线程内的任务执行完毕为空,会去Event Queue(事件队列)读取对应的回调函数,进入主线程执行。
(4)上述过程会不断重复,也就是常说的Event Loop(事件循环)。

怎么知道主线程执行栈为空啊?
js引擎存在monitoring process进程,会持续不断的检查主线程执行栈是否为空,一旦为空,就会去Event Queue那里检查是否有等待被调用的函数。

Renderer渲染进程
对于普通的前端操作来说,最终要的是渲染进程,页面的渲染,JS的执行,事件的循环,都在这个进程内进行
在这里插入图片描述
GUI渲染线程:负责渲染浏览器界面,解析HTML,CSS,构建DOM树和RenderObject树,布局和绘制等。当界面需要重绘(Repaint)或由于某种操作引发回流(reflow)时,该线程就会执行
JS引擎线程:JS引擎线程负责解析Javascript脚本,运行代码。JS引擎一直等待着任务队列中任务的到来,然后加以处理,一个Tab页(renderer进程)中无论什么时候都只有一个JS线程在运行JS程序
事件触发线程当JS引擎执行代码块如setTimeOut时(也可来自浏览器内核的其他线程,如鼠标点击、AJAX异步请求等),会将对应任务添加到事件线程中
定时触发器线程
setInterval与setTimeout所在线程,因此通过单独线程来计时并触发定时(计时完毕后,添加到事件队列中,等待JS引擎空闲后执行)
异步http请求线程
在XMLHttpRequest在连接后是通过浏览器新开一个线程请求,将检测到状态变更时,如果设置有回调函数,异步线程就产生状态变更事件,将这个回调再放入事件队列中。再由JavaScript引擎执行。

浏览器渲染流程:

1:浏览器输入url,浏览器主进程接管,开一个下载线程,然后进行 http请求(略去DNS查询,IP寻址等等操作),然后等待响应,获取内容,随后将内容通过RendererHost接口转交给Renderer进程
浏览器渲染流程开始
2:在这里插入图片描述
解析html建立dom树,解析css构建render树(将CSS代码解析成树形的数据结构,然后结合DOM合并成render树),布局render树(Layout/reflow),负责各元素尺寸、位置的计算,绘制render树(paint),绘制页面像素信息,浏览器会将各层的信息发送给GPU,GPU会将各层合成(composite),显示在屏幕上。渲染完毕后就是load事件了,之后就是自己的JS逻辑处理了
3:当 DOMContentLoaded 事件触发时,仅当DOM加载完成,不包括样式表,图片。 (譬如如果有async加载的脚本就不一定完成)
当 onload 事件触发时,页面上所有的DOM,样式表,脚本,图片都已经加载完成了。 (渲染完毕了)

Commonjs、AMD、CMD

CommonJS:它有四个重要的环境变量为模块化的实现提供支持:module、exports、require、global,实际使用时,用module.exports定义当前模块对外输出的接口(不推荐直接用exports),用require加载模块。
特点:commonJS用同步的方式加载模块。在服务端,模块文件都存在本地磁盘,读取非常快,所以这样做不会有问题。但是在浏览器端,限于网络原因,更合理的方案是使用异步加载。
在这里插入图片描述
AMD:AMD规范采用异步方式加载模块,模块的加载不影响它后面语句的运行。所有依赖这个模块的语句,都定义在一个回调函数中,等到加载完成之后,这个回调函数才会运行。使用require.js实现AMD规范的模块化

setTimeout、setInterval、requestAnimationFrame区别

setInterval() :按照指定的周期(以毫秒计)来调用函数或计算表达式。方法会不停地调用函数,直到 clearInterval() 被调用或窗口被关闭。可以调用多次
setTimeout() :在指定的毫秒数后调用函数或计算表达式。只调用一次
与setTimeout和setInterval不同,requestAnimationFrame不需要设置时间间隔
requestAnimationFrame使用一个回调函数作为参数,这个回调函数会在浏览器重绘之前调用。它返回一个整数,表示定时器的编号,这个值可以传递给cancelAnimationFrame用于取消这个函数的执行

特点:1:requestAnimationFrame会把每一帧中的所有DOM操作
集中起来,在一次重绘或回流中就完成,并且重绘或回流的时间间隔
紧跟随浏览器的刷新频率
2:在隐藏或不可见的元素中,requestAnimationFrame将不会进行
重绘或回流,这当然就意味着更少的CPU、GPU和内存使用量
3:requestAnimationFrame是由浏览器专门为动画提供的API,在运
行时浏览器会自动优化方法的调用,并且如果页面不是激活状态下话,
动画会自动暂停,有效节省了CPU开销
发布了35 篇原创文章 · 获赞 5 · 访问量 814

猜你喜欢

转载自blog.csdn.net/weixin_43332220/article/details/103068162