秋招复习计划-JavaScript知识点3

  |  

前言

这部分复习的内容包括:深拷贝与浅拷贝的区别,防抖与节流,执行上下文,DOM常见的操作方法


25



深拷贝与浅拷贝的区别

深拷贝和浅拷贝的使用场景是在复杂对象中。

浅拷贝值复制一层对象,当对象的属性是引用类型时,实质上复制的是其引用。当引用指向的值改变时也会跟着变化。

深拷贝是指向复制对象的所有层级,而且深拷贝的副本不会影响到原先的值。JS中所有的原始数据类型默认执行深拷贝。

实现浅拷贝的方法

for…in只循环第一层

1
2
3
4
5
6
7
function simpleCopy(obj1) {
let obj2 = Array.isArray(obj1) ? [] : {};
for(let i in obj1) {
obj2[i] = obj1[i]
}
return obj2;
}

Object.assign()方法

1
let obj1 = Object.assgin({}, obj);

等号赋值

1
let obj1 = obj;

实现深拷贝的方法

采用递归去拷贝所有层级属性

当遍历完所有层级后,复制的就是原始值而不是引用值,所以就不会存在修改副本而影响到原对象

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
function deepClone(obj) {
let objClone = Array.isArray(obj)? [] : {};
if(obj && typeof obj==="object") {
for(key in obj) {
if(obj.hasOwnProperty(key)) {
if(obj[key] && typeof obj[key] === "object") {
objClone[key] = deepClone(obj[key]);
} else {
objClone[key] = obj[key]
}
}
}
}
return objClone
}

通过JSON对象来实现深拷贝

1
2
3
4
function deepClone(obj) {
let _obj = JSON.stringify(obj);
return objClone = JSON.parse(_obj);
}

这个方法的缺点就是无法深拷贝目标对象中的方法,会显示为undefined

通过jQuery的extend方法实现深拷贝

1
let newArray = $.extend(true, [], array); // true为深拷贝, false为浅拷贝

防抖和节流

节流(Throttle)

节流概念

当持续触发事件时,保证按照设定的时间固定调用一次事件处理函数函数,例如200ms执行一次。

throttle

应用场景有:scroll、touchmove

节流的实现方式
函数节流主要有两种实现方法:时间戳和定时器

两者的设计思路是不一样的。

时间戳,是第一次就执行的。

1
2
3
4
5
6
7
8
9
10
11
12
let throttle = function(func, delay) {
let prev = Date.now();
return function() {
let _this = this;
let args = arguments;
let now = Date.now();
if(now - prev >= delay) {
func.apply(_this, args);
prev = Date.now();
}
}
}

当事件首次触发与绑定之间的间隔时间大于delay时,第一次就会执行。而后无论事件如何频繁的被触发,只有与上一次执行间隔的时间大于delay的那次触发才会被执行,然后更新时间。

定时器,第一次不执行

1
2
3
4
5
6
7
8
9
10
11
12
13
let throttle = function(func, delay) {
let timer = null;
return function() {
let _this = this;
let args = arguments;
if(!timer) {
timer = setTimeout(function() {
func.apply(_this, args);
timer = null;
}, delay);
}
}
}

首次事件触发并不会执行,而是设定一个定时器,当在定时器的时间间隔内事件再次触发并不会执行,等到间隔时间到了后,执行操作然后将定时器设置为null。当下次事件触发后再次设定定时器。

时间戳和定时器虽然都能达到节流的效果,但是它们执行起来还是有区别的。时间戳被触发后会立即执行,然后在执行后的一定时间内无法再次执行。等这段时间过去后,事件触发还是立即执行。

而定时器则不同,它是事件触发后,间隔固定时间后执行。在触发和执行这段期间,再次触发没有任何结果。当间隔时间过去后,执行。然后等待下次触发,触发后还是等待固定时间后执行。

防抖(debounce)

防抖的概念

抖动(持续触发事件)停止后的时间超过设定的时间间隔时执行一次函数。需要注意的是,这里的抖动停止表示你停止了触发这个事件,从这个时间点开始计算。如果在设定的时间间隔内再次触发,则重新开始计时。

debounce

应用场景:input验证、搜索联想、resize

防抖的实现方式

防抖最普遍的还是定时器的方式,其实现原理是在首次运行时把定时器赋值给一个变量,第二次执行时,如果间隔没超过定时器设定的时间则会清除掉定时器,重新设置定时器,依次反复,当我们停止下来时,没有执行清除定时器,则超过一定时间后触发回调函数

1
2
3
4
5
6
7
8
9
10
function debounce(fn, delay=200) {
let timer = null;
return function() {
if(timer) clearTimeout(timer);
timer = setTimeout(() => {
fn.apply(this, arguments);
timer = null;
}, delay);
}
}

执行上下文

JS代码的执行环境

js的执行环境又称为执行上下文(Execution Context,之后简称为EC)

JS是单线程的,运行在全局EC,每进入一个function,就做一次入栈操作,向栈顶压入一个属于该function的新的EC。若function中又调用了另一个function,再执行一次入栈…依次执行完后在依次出栈,回到全局EC。全局EC一定是在栈底的,在浏览器关闭后出栈。

执行上下文,出入栈的过程

ECStack

EC的构成

EC的构成

JavaScript对于作用域(Scope)和上下文(Context)的实现是这门语言的一个非常独到的地方,部分归功于器独特的灵活性。函数可以接收不同的上下文和作用域。

作用域和上下文

作用域和上下文是两个完全不同的概念。作用域是基于函数的,而上下文是基于对象的。作用域涉及到所被调用函数中的变量访问,而上下文是函数被调用的场景,上下文始终是this关键字的值,它是拥有当前所执行代码的对象的引用

变量作用域

一个变量可以被定义在局部或全局作用域中,这建立在运行时期间变量的访问性的不同作用于范围。全局变量在函数体的外部被声明,并且存活于整个运行时,并且在任何作用域中都可以访问到。在ES6之前,局部变量只能存在于函数体中,并且函数的每次调用它们都拥有不同的作用域范围。局部变量只能在其被调用期的作用域范围内被赋值检索和操作。

this上下文

当一个函数被当作对象的方法调用时,this被设置为调用该方法的对象上。

当使用new操作符调用函数时,this被绑定到新创建的对象上。

当调用一个未绑定函数时,在严格模式下thi为undefined,非严格模式下指向window对象。

执行环境

JavaScript是一个单线程语言,意味着同一时间只能执行一个任务。当JavaScript解释器初始化执行代码是,它首先默认进入全局执行环境,从此刻开始,函数的每次调用都会创建一个新的执行环境。

执行环境,它定义了变量或函数有权访问的其它数据,决定了它们各自的行为。单从这方面来看,执行环境的概念更贴近于作用域的概念。

每个函数都有自己的执行环境。当执行流进入要一个函数时,函数的环境就会被推入一个环境栈。在函数执行完后,栈将其环境弹出,把控制权返回给之前的执行环境。

执行环境可以分为创建和执行两个阶段。在创建阶段,解析器首先会创建一个变量对象(也称为活动对象),它由定义在执行环境汇中的变量、函数声明和参数组成。在这个阶段,作用域会被初始化,this的值也会被绑定确认。在执行阶段,代码被解释执行。这个变量对象无法手动访问,只有解析器才能访问。

作用域链(The Scope Chain)

当程序在一个环境中执行时,或创建一个由变量对象组成的作用域链。作用域链的用途是保证对执行环境有权访问的所有变量和函数的有序访问。作用域链包含在环境栈中的每个执行环境对应的变量对象。注意,全局执行环境的变量对象始终都是作用域链的最后一个对象。

1
2
3
4
5
6
7
8
9
10
11
let color = 'blue';
function changeColor() {
let anotherColor = 'red';
function swapColors() {
let tempColor = anotherColor;
anotherColor = color;
color = tempColor;
}
swapColors();
}
changeColor()

这个示例代码一共包括三个执行环境:全局环境、changeColor()的局部环境、swapColors()的局部环境,那么由这三个环境构成的作用域链如下图所示

作用域链

内部环境可以通过作用域链访问所有的外部环境,但是外部环境无法访问内部环境中的任何变量和函数。

对于标识符的解析是沿着作用域链一级一级的搜索标识符的过程。

常见的DOM操作方法

查找节点

document.querySelector(selectors):接受一个CSS选择器作为参数,返回第一个匹配该选择器的元素节点。
document.querySelectorAll(selectors) :接受一个CSS选择器作为参数,返回所有匹配该选择器的元素节点。
document.getElementsByTagName(tagName): 返回所有指定HTML标签的元素
document.getElementsByClassName(className):返回包括了所有class名字符合指定条件的元素
document.getElementById(id):返回匹配指定id属性的元素节点。
document.getElementsByName(name) :用于选择拥有name属性的HTML元素(比如\<form>、\<radio>、\<img>、\<frame>、\<embed>和 <object>等)

生成节点

document.createElement(tagName) : 用来生成HTML元素节点。
document.createTextNode(text) :用来生成文本节点
document.createAttribute(name) :生成一个新的属性对象节点,并返回它。
document.createDocumentFragment() : 生成一个DocumentFragment对象

Element节点的属性

Element.attributes : 返回当前元素节点的所有属性节点
Element.id : 返回指定元素的id属性,可读写
Element.tagName : 返回指定元素的大写标签名
Element.innerHTML : 返回该元素包含的HTML代码,可读写
Element.outerHTML : 返回指定元素节点的所有HTML代码,包括它自身和包含的的所有子元素,可读写
Element.className : 返回当前元素的class属性,可读写
Element.classList : 返回当前元素节点的所有class集合
Element.dataset : 返回元素节点中所有的data-*属性。
Element.clientHeight : 返回元素节点可见部分的高度
Element.clientWidth : 返回元素节点可见部分的宽度
Element.clientLeft : 返回元素节点左边框的宽度
Element.clientTop : 返回元素节点顶部边框的宽度
Element.scrollHeight : 返回元素节点的总高度
Element.scrollWidth : 返回元素节点的总宽度
Element.scrollLeft : 返回元素节点的水平滚动条向右滚动的像素数值,通过设置这个属性可以改变元素的滚动位置
Element.scrollTop : 返回元素节点的垂直滚动向下滚动的像素数值
Element.offsetHeight : 返回元素的垂直高度(包含border,padding)
Element.offsetWidth : 返回元素的水平宽度(包含border,padding)
Element.offsetLeft : 返回当前元素左上角相对于Element.offsetParent节点的垂直偏移
Element.offsetTop : 返回水平位移
Element.style : 返回元素节点的行内样式
Element.children : 包括当前元素节点的所有子元素
Element.childElementCount : 返回当前元素节点包含的子HTML元素节点的个数
Element.firstElementChild : 返回当前节点的第一个Element子节点
Element.lastElementChild : 返回当前节点的最后一个Element子节点
Element.nextElementSibling : 返回当前元素节点的下一个兄弟HTML元素节点
Element.previousElementSibling : 返回当前元素节点的前一个兄弟HTML节点
Element.offsetParent : 返回当前元素节点的最靠近的、并且CSS的position属性不等于static的父元素。

节点属性

Node.nodeName : 返回节点名称,只读
Node.nodeType : 返回节点类型的常数值,只读
Node.nodeValue : 返回Text或Comment节点的文本值,只读
Node.textContent : 返回当前节点和它的所有后代节点的文本内容,可读写
Node.baseURI : 返回当前网页的绝对路径
Node.ownerDocument : 返回当前节点所在的顶层文档对象,即document
Node.nextSibling : 返回紧跟在当前节点后面的第一个兄弟节点
Node.previousSibling : 返回当前节点前面的、距离最近的一个兄弟节点
Node.parentNode : 返回当前节点的父节点
Node.parentElement : 返回当前节点的父Element节点
Node.childNodes : 返回当前节点的所有子节点
Node.firstChild : 返回当前节点的第一个子节点
Node.lastChild : 返回当前节点的最后一个子节点
Node.children : 返回指定节点的所有Element子节点
Node.firstElementChild : 返回当前节点的第一个Element子节点
Node.lastElementChild : 返回当前节点的最后一个Element子节点
Node.childElementCount : 返回当前节点所有Element子节点的数目。

生成节点

document.createEvent(type) : 生成一个事件对象,该对象能被element.dispatchEvent()方法使用
document.addEventListener(type,listener,capture) : 注册事件
document.removeEventListener(type,listener,capture) : 注销事件
document.dispatchEvent(event) : 触发事件

属性

Element.getAttribute():读取指定属性
Element.setAttribute():设置指定属性
Element.hasAttribute():返回一个布尔值,表示当前元素节点是否有指定的属性
Element.removeAttribute():移除指定属性

操作

Node.appendChild(node) : 向节点添加最后一个子节点
Node.hasChildNodes() : 返回布尔值,表示当前节点是否有子节点
Node.cloneNode(true); : 默认为false(克隆节点), true(克隆节点及其属性,以及后代)
Node.insertBefore(newNode,oldNode) : 在指定子节点之前插入新的子节点
Node.removeChild(node) : 删除节点,在要删除节点的父节点上操作
Node.replaceChild(newChild,oldChild) : 替换节点
Node.contains(node) : 返回一个布尔值,表示参数节点是否为当前节点的后代节点。
Node.compareDocumentPosition(node) : 返回一个7个比特位的二进制值,表示参数节点和当前节点的关系
Node.isEqualNode(noe) : 返回布尔值,用于检查两个节点是否相等。所谓相等的节点,指的是两个节点的类型相同、属性相同、子节点相同。
Node.normalize() : 用于清理当前节点内部的所有Text节点。它会去除空的文本节点,并且将毗邻的文本节点合并成一个。
Node.remove() : 用于删除当前节点
Node.before() :
Node.after()
Node.replaceWith()

事件方法

document.createEvent(type) : 生成一个事件对象,该对象能被element.dispatchEvent()方法使用
document.addEventListener(type,listener,capture) : 注册事件
document.removeEventListener(type,listener,capture) : 注销事件
document.dispatchEvent(event) : 触发事件

文章目录
  1. 1. 深拷贝与浅拷贝的区别
    1. 1.1. 实现浅拷贝的方法
    2. 1.2. 实现深拷贝的方法
  2. 2. 防抖和节流
    1. 2.1. 节流(Throttle)
    2. 2.2. 防抖(debounce)
  3. 3. 执行上下文
    1. 3.1. JS代码的执行环境
    2. 3.2. 作用域和上下文
    3. 3.3. 执行环境
    4. 3.4. 作用域链(The Scope Chain)
  4. 4. 常见的DOM操作方法
    1. 4.1. 查找节点
    2. 4.2. 生成节点
    3. 4.3. Element节点的属性
    4. 4.4. 节点属性
    5. 4.5. 生成节点
    6. 4.6. 属性
    7. 4.7. 操作
    8. 4.8. 事件方法
|