JS文章

本文围绕JavaScript展开,介绍了闭包、原型链、继承等要点。阐述闭包的概念、优缺点及应用场景;分析多种继承方式的特点;讲解原生事件绑定方法、ES6新特性、设计模式等。还涉及从输入URL到页面加载的过程,包括DNS解析、TCP连接等,以及HTTP状态码、三次握手和四次挥手等知识。

JavaScript部分要点

1、什么是闭包
就是函数嵌套函数,内部函数可以访问外部函数的变量
闭包就是能够读取其他函数内部变量的函数
(function() {
var a = 1;
function add() {
var b = 2
var sum = b + a
console.log(sum); // 3
}
add()
})()
闭包的作用
闭包最大的作用就是隐藏变量,内部函数总是可以访问其所在外部函数中表明的参数和变量,即使在其外部函数被返回了之后。
基于此特性,JavaScript可以实现私有变量、特权变量、储存变量等
我们就以私有变量举例,私有变量的实现方法很多,有靠约定的(变量名前加_),有靠Proxy代理的,也有靠Symbol这种新数据类型的。
闭包的优点
可以隔离作用域,不造成全局污染
变量长期驻扎在内存中
私有成员的存在
闭包的缺点
常驻内存增大内存的使用量,使用不当会造成内存泄漏
解决方法是,在退出函数之前,将不使用的局部变量全部删除,将暴露全外部的闭包变量置为null
闭包的应用场景
通过闭包制作计数器
封装私有属性方法
回调函数
函数防抖节流
function outer(){
var val = 0;
return function (){
val += 1;
document.write(val + “
”);
};
}
var outObj = outer();
outObj();//1,执行val += 1后,val还在
outObj();//2
outObj = null;//val 被回收
var outObj1 = outer();
outObj1();//1
outObj1();//2
这是一个返回闭包的函数

原型链

要想知道原型链必须要知道什么是原型?
1、每个函数对象下都有一个prototype属性。这个属性就是原型
2、在构造器里边有一个属性prototype,它是一个对象,叫做原型,是标准的属性,是程序员来使用的
3、在实例对象中有一个属性__proto__,它是一个对象,叫做原型,不是一个标准的属性,是浏览器来使用的
原型链
每个对象都有一个属性__proto__,当这个对象访问属性或者方法时候,如果这个对象内部不存在该属性或者方法,那么就会去他的__proto__指向的这个原型里边寻找,如果这个原型里边没有,去这个原型的__proto__所指向的这个原型里边寻找,最终会找到Object
.proto,如果这里边没有,返回的是一个null。
三者之间的关系:
实例对象的__proto__指向构造函数的prototype
构造函数prototype的__proto__指向Object.prototype
Object.prototype的__proto__指向null
原型的作用
实现数据共享、减少内存空间
可以实现继承

js继承(ES6)

1、原型链继承

利用原型让一个引用类型继承另一个引用类型的属性和方法
缺点 通过原型实现继承 原型会变成另一个类型的实例 原来的实例属性变成了现在的原型属性
该原型的引用类型属性会被所有实例共享
在创建子类型实例时没有办法在不影响所有对象实例的情况下给超类型构造函数传递参数
2、构造函数实现继承
在子类型的构造函数中调用超类型的构造函数
优点
可以向超类传递参数
解决了原型中包含引用类型值被所有实例共享的问题
缺点
方法都是在构造函数中定义的 所以函数复用就无从谈起了
超类型原型中的方法对于子类型而言都是不可见的
3、组合继承(原型链+构造函数)
使用原型链继承原型的属性和方法 构造函数实行实例属性的继承
优点
实现了函数复用 每个实例都有自己的属性 可以向超类传递参数
缺点
无论什么情况下 都会调用两次超类构造函数 一次在创建子类原型的时候 一次在子类构造函数内部
4、原型式继承(浅拷贝)
借助原型基于已有的对象创建新对象,同时还不必因此创建自定义类型
es5通过新增object.create方法规范了原型式继承,这个方法接收两个参数 一个用作新对象原型的对象
和一个为新对象定义额外属性的对象在传入一个参数的情况下 和object方法行为相同
缺点
和原型链实现继承一样 引用类型值的属性会被所有实例共享
5、寄生式继承
就是又创建了一个函数,然后在函数内部定义一个对象来实现原型式继承方式,然后再给这个对象添加方法或属性值,
最后再将这个对象返回,这就成为了寄生式继承方式
缺点
做不到函数复用
包含引用类型值的属性会被所有实例共享
6、寄生组合式继承
借用构造函数继承属性 通过原型链的混成形式继承方法
优点
只调用了一次超类型构造函数 效率更高 避免在 SuberType.prototype
上面创建了不必要 多余的属性
与此同时 原型链还能保持不变 因此寄生组合是继承是引用类型最理想的继承方式
es6主要利用class配合extends与super实现继承;
继承的作用
可以提高代码的复用率

Js原生事件如何绑定?

1、html事件处理程序
2、DOM0级事件处理程序
3、DOM2级事件处理程序

html事件现在早已不用了,就是在html各种标签上直接添加事件,类似于css的行内样式,缺点是不好维护,因为散落在标签中,也就是耦合度太高

例如: 点我< /button>

第二类是DOM0级事件,目前在PC端用的还是比较多的绑定事件方式,兼容性也好,主要是先获取dom元素,然后直接给dom元素添加事件

例如:var btn=document.getElementById(‘id元素’)
btn.οnclick=function() {
​ //要处理的事件逻辑
}
DOM0事件如何移除呢?很简单:btn.οnclick=null;置为空就行

优点:兼容性好

缺点:只支持冒泡,不支持捕获
DOM2级事件:
addEventlistener();
包含三个参数:第一个参数为不加on的事件类型,第二个参数为事件函数,第三个参数为布尔值。
removeEventListener(); 删除事件处理程序----不能删除匿名函数添加的事件

优点
(1)该方法同时支持事件处理的捕获和冒泡阶段。事件阶段取决于addEventListener最后的参数设置:false (冒泡) 或 true (捕获)。
(2)在事件处理函数内部,this关键字引用当前元素。
(3)事件对象总是可以通过处理函数的第一个参数(e)捕获。
(4)可以为同一个元素绑定你所希望的多个事件,同时并不会覆盖先前绑定的
事件

缺点
IE不支持,你必须使用IE的attachEvent()函数替代。 —删除事件用detachEvent();
//例子:
var btn=document.getElementById(‘id元素’)
​ //绑定事件
btn.addEventListener(‘click’,绑定的事件处理函数名,false)
//移除事件
btn.removeEventListener(‘click’,要移除的事件处理函数名,false)

js原生常用dom操作方法
查找:
getElementByid,
getElementsByTagName,
querySelector,
querySelectorAll

插入:
appendChild,insertBefore
删除:
removeChild
克隆:
cloneNode
设置和获取属性:
setAttribute(“属性名”,”值”)
getAttibute(“属性名”)
ES6新特性
1、新增了块级作用域(let,const)
2、提供了定义类的语法糖(class)
3.、新增了一种基本数据类型(Symbol)
4、新增了变量的解构赋值
5. 函数参数允许设置默认值,引入了rest参数,新增了箭头函数
6. 数组新增了一些API,如 isArray / from / of 方法;数组实例新增了 entries(),keys() 和 values() 等方法
7. 对象和数组新增了扩展运算符
8. ES6 新增了模块化(import/export)
9. ES6 新增了 Set 和 Map 数据结构
10. ES6 原生提供 Proxy 构造函数,用来生成 Proxy 实例
11、 ES6 新增了生成器(Generator)和遍历器(Iterator)
js设计模式有哪些
JS设计模式有很多,但我知道的有单例模式,观察者模式

- 单例模式:

就是保证一个类只有一个实例,实现的方法一般是先判断实例存在与否,如果存在直接返回,如果不存在就创建了再返回,这就确保了一个类只有一个实例对象。在JavaScript里,单例作为一个命名空间提供者,从全局命名空间里提供一个唯一的访问点来访问该对象。

- 观察者模式:

观察者的使用场合就是:当一个对象的改变需要同时改变其它对象,并且它不知道具体有多少对象需要改变的时候,就应该考虑使用观察者模式。

总的来说,观察者模式所做的工作就是在解耦,让耦合的双方都依赖于抽象,而不是依赖于具体。从而使得各自的变化都不会影响到另一边的变化
面向对象
面向对象和面向过程的思想有着本质上的区别,作为面向对象的思维来说,当你拿到一个问题时,你分析这个问题不再是第一步先做什么,第二步再做什么,这是面向过程的思维,你应该分析这个问题里面有哪些类和对象,这是第一点,然后再分析这些类和对象应该具有哪些属性和方法。这是第二点。最后分析类和类之间具体有什么关系,这是第三点。

  面向对象有一个非常重要的设计思维:合适的方法应该出现在合适的类里面。
  JS面向对象主要基于function来实现的,通过function来模拟类,通过prototype来实现类方法的共享,跟其他语言有着本质的不同,自从有了ES6后,把面向对象类的实现更像后端语言的实现了,通过class来定义类,通过extends来继承父类,其实ES6类的实现本质上是一个语法糖,不过对于开发简单了好多
  **js数组常用的方法**
  在开发中数组使用频率很频繁,js数组常用方法有:
  	push() 在数组的末尾添加一个或者多个元素,返回新数组的长度
unshift() 在数组的开头添加一个或者多个元素,返回新数组的长度
pop() 删除数组的最后一个元素返回删除的元素
shift()  删除数组的第一个元素返回删除的元素
reverse 将数组中元素的顺序颠倒返回逆序的数组。
join()方法将数组中所有元素都转化为字符串并且连接在一起,返回最后生成的字符串。
sort()将数组中的元素进行排序,并且返回排序后的数组。
concat() 方法合并两个数组,并返回合并后的新数组。
slice()方法返回指定数组的一个片段或子数组,它的两个参数分别指定片段的开始和结束的位置(不包含该元素)。
splice() 方法用于插入、删除或替换数组的元素。并返回删除的部分数组。
- forEach
  • filter
  • map
  • some
  • every

js数组内置遍历方法
forEach

  • 这个方法是为了取代for循环遍历数组的,返回值为undefined例如:
    let arrInfo=[4,6,6,8,5,7,87]

    arrInfo.forEach((item,index,arr)=>{

    //遍历逻辑

    })

    其中:

    item代码遍历的每一项,

    index:代表遍历的每项的索引,

    arr代表数组本身

filter
是一个过滤遍历的方法,如果返回条件为true,则返回满足条件为true的新数组
let arrInfo=[4,16,6,8,45,7,87]

​       let resultArr=arrInfo.filter((item,index,arr)=>{

  //例如返回数组每项值大于9的数组

  return item>9

})
```

map

这个map方法主要对数组的复杂逻辑处理时用的多,特别是react中遍历数据,也经常用到,写法和forEach类似

some

这个some方法用于只要数组中至少存在一个满足条件的结果,返回值就为true,否则返回fasel, 写法和forEach类似

every

这个every方法用于数组中每一项都得满足条件时,才返回true,否则返回false, 写法和forEach类似
js作用域
js作用域也就是js识别变量的范围,作用域链也就是js查找变量的顺序
作用域包括:
全局作用域:也就是定义在window下的变量范围,在任何地方都可以访问,
局部作用域:是只在函数内部定义的变量范围,
ES6块级作用域:用let和const在任意的代码块中定义的变量都认为是块级作用域中的变量,
尽量不要使用全局变量,因为容易导致全局的污染,命名冲突,对bug查找不利。
而所谓的作用域链就是由最内部的作用域往最外部,查找变量的过程.形成的链条就是作用域链
从输入URL到页面加载完中间发生了什么
1、浏览器的地址栏输入URL并按下回车。
  2、浏览器查找当前URL是否存在缓存,并比较缓存是否过期。
  3、DNS解析URL对应的IP。
  4、根据IP建立TCP连接(三次握手)。
  5、HTTP发起请求。
  6、服务器处理请求,浏览器接收HTTP响应。
  7、渲染页面,构建DOM树。
  8、关闭TCP连接(四次挥手)。
  这里会延伸出问 http 状态码,和三次握手和四次挥手相关问题:
http状态码
1xx(临时响应)
表示临时响应并需要请求者继续执行操作的状态代码
2xx(成功)
表示成功处理了请求的状态码。
常见的 2 开头的状态码有:200 – 服务器成功返回网页
3xx(重定向)
表示要完成请求,需要进一步操作。 通常,这些状态代码用来重定

常见的 3 字开头的状态码有:

301 (永久移动) 请求的网页已永久移动到新位置。 服务器返回此响应时,会自动将请求者转到新位置。
302 (临时移动) 服务器目前从不同位置的网页响应请求,但请求者应继续使用原有位置来进行以后的请求。
304 (未修改) 自从上次请求后,请求的网页未修改过。服务器返回此响应时,不会返回网页内容。
4xx(请求错误)
这些状态代码表示请求可能出错,妨碍了服务器的处理
常见的 4 字开头的状态有:404 – 请求的网页不存在
5xx(服务器错误)
这些状态代码表示服务器在尝试处理请求时发生内部错误。 这些错误可能是服务器本身的错误,而不是请求出错。
常见的以 5 开头的状态码有:
500 (服务器内部错误) 服务器遇到错误,无法完成请求。
503 (服务不可用) 服务器目前无法使用(由于超载或停机维护)。 通常,这只是暂时状态

三次握手和四次挥手
三次握手
三次握手是一个建立连接的过程
TCP 协议中,主动发起请求的一端称为『客户端』,被动连接的一端称为『服务端』。不管是客户端还是服务端,TCP 连接建立完后都能发送和接收数据。
第一次握手:
客户端向服务端发送连接请求报文段
发送后,客户端便进入 SYN-SENT 状态
第二次握手:
服务端收到连接请求报文段后,如果同意连接,则会发送一个应答
发送完成后便进入 SYN-RCVD 状态
第三次握手:

当客户端收到连接同意的应答后,还要向服务端发送一个确认报文段,表示: 服务端发来的连接同意应答已经成功收到
客户端发完这个报文段后便进入 ESTABLISHED 状态,服务端收到这个应答后也进入 ESTABLISHED 状态,此时连接的建立完成!

为什么连接建立需要三次握手,而不是两次握手

防止失效的连接请求报文段被服务端接收,从而产生错误
四次挥手:
TCP 连接是双向的,因此在四次挥手中,前两次挥手用于断开一个方向的连接,后两次挥手用于断开另一方向的连接。

第一次挥手:
若 A 认为数据发送完成,则它需要向 B 发送连接释放请求
第二次挥手:
B 收到连接释放请求后,会通知相应的应用程序,告诉它 A 向 B 这个方向的连接已经释放。此时 B 进入 CLOSE-WAIT 状态,并向 A 发送连接释放的应答
A 收到该应答,进入 FIN-WAIT-2 状态,等待 B 发送连接释放请求。
第二次挥手完成后,A 到 B 方向的连接已经释放,B 不会再接收数据,A 也 不会再发送数据。但 B 到 A 方向的连接仍然存在,B 可以继续向 A 发送数据。

第三次挥手:
当 B 向 A 发完所有数据后,向 A 发送连接释放请求
B 便进入 LAST-ACK 状态。

第四次挥手:
A 收到释放请求后,向 B 发送确认应答,此时 A 进入 TIME-WAIT 状态。该状态会持续 2MSL 时间,若该时间段内没有 B 的重发请求的话,就进入 CLOSED 状态, 撤销 TCB。当 B 收到确认应答后,也便进入 CLOSED 状态,撤销 TCB。

为什么A 要先进入TIME-WAIT 状态,等待 2MSL 时间后才进入
CLOSED 状态?
为了保证 B 能收到 A 的确认应答。
若 A 发完确认应答后直接进入 CLOSED 状态,那么如果该应答丢失,B 等待超时后就会重新发送连接释放请求,但此时 A 已经关闭了,不会作出任何响应,因此 B 永远无法正常关闭。
js事件代理也称事件委托是什么,及实现原理
JS事件代理就是通过给父级元素(例如:ul)绑定事件,不给子级元素(例如:li)绑定事件,然后当点击子级元素时,通过事件冒泡机制在其绑定的父元素上触发事件处理函数,主要目的是为了提升性能,因为我不用给每个子级元素绑定事件,只给父级元素绑定一次就好了,在原生js里面是通过event对象的targe属性实现

var ul = document.querySelector("ul");

 ul.onclick = function(e){//e指event,事件对象

 var target = e.target || e.srcElement; //target获取触发事件的目标(li)

   if(target.nodeName.toLowerCase() == 'li'){//目标(li)节点名转小写字母,不转的话是大写字母

​     alert(target.innerHTML)

   }

 }

jq方式实现相对而言简单 $(“ul”).on(“click”,“li”,function(){//事件逻辑}) 其中第二个参数指的是触发事件的具体目标,**特别是给动态添加的元素绑定事件,这个特别起作用**
**js的数据类型**
基本数据类型:
number、string、boolean、null、undefined、symbol
复合类型:object、function
**call、apply、bind的区别**
call,apply,bind主要作用都是改变this指向的,但使用上略有区别,说一下区别
call和apply的主要区别是在传递参数上不同,**call后面传递的参数是以逗号的形式分开的,apply传递的参数是数组形式  [Apply是以A开头的,所以应该是跟Array(数组)形式的参数]**
 bind返回的是一个函数形式,如果要执行,则后面要再加一个小括号 例如:bind(obj,参数1,参数2,)(),bind只能以逗号分隔形式,不能是数组形式
 ** JavaScript的作用域链理解吗**

JavaScript属于静态作用域,即声明的作用域是根据程序正文在编译时就确定的,有时也称为词法作用域。

其本质是JavaScript在执行过程中会创造可执行上下文,可执行上下文中的词法环境中含有外部词法环境的引用,我们可以通过这个引用获取外部词法环境的变量、声明等,这些引用串联起来一直指向全局的词法环境,因此形成了作用域链。
**ES6模块与CommonJS模块有什么区别?**

ES6 Module和CommonJS模块的区别:

 CommonJS是对模块的浅拷贝,ES6 Module是对模块的引用,即ES6 Module只存只读,不能改变其值,具体点就是指针指向不能变,类似const
 import的接口是read-only(只读状态),不能修改其变量值。 即不能修改其变量的指针指向,但可以改变变量内部指针指向,可以对commonJS对重新赋值(改变指针指向),但是对ES6 Module赋值会编译报错。

ES6 Module和CommonJS模块的共同点:

 CommonJS和ES6 Module都可以对引入的对象进行赋值,即对对象内部属性的值进行改变。
 **null与undefined的区别是什么?**

null表示为空,代表此处不应该有值的存在,一个对象可以是null,代表是个空对象,而null本身也是对象。

undefined表示『不存在』,JavaScript是一门动态类型语言,成员除了表示存在的空值外,还有可能根本就不存在(因为存不存在只在运行期才知道),这就是undefined的意义所在

**那么箭头函数的this指向哪里?**

箭头函数不同于传统JavaScript中的函数,箭头函数并没有属于自己的this,它的所谓的this是捕获其所在上下文的 this 值,作为自己的 this 值,并且由于没有属于自己的this,而箭头函数是不会被new调用的,这个所谓的this也不会被改变.
 **async/await**
 async 函数,就是 Generator 函数的语法糖,它建立在Promises上,并且与所有现有的基于Promise的API兼容。
解决了promise参数传递和.then链式回调过于繁琐的问题 语义更清晰。
Async 和 await 是一种同步的写法,但还是异步的操作,两个内容还是必须同时去写才会生效不然的话
也是不会好使,而且 await 的话有一个不错的作用就是可以等到你的数据加载过来以后才会去运行下边
的 js 内容,大多数情况下await 接收的对象都是一个 promise 对象,如果是接收数据的时候就可以直接
用一句话来接收数据值,所以这个 async await 我的主要应用是在数据的接收,和异步问题的处理,主要
是还是解决不同执行时机下的异步问题!
可以用来改善promise的,.then链
总结
1,async 函数返回一个promise对象,当函数执行的时候,一旦遇到await就会先返回,等到触发的异步操作完成,在接着
执行函数体后面的语句
2,await关键字必须位于async函数内部
3,await关键字后面需要一个promise对象(不是的话就调用resolve转换它)
4,await关键字的返回结果就是其后买你promise执行的结果,可能是resolved或者rejecter的值
**async/await相比于Promise的优势?**

代码读起来更加同步,Promise虽然摆脱了回调地狱,但是then的链式调用也会带来额外的阅读负担
 Promise传递中间值非常麻烦,而async/await几乎是同步的写法,非常优雅
 错误处理友好,async/await可以用成熟的try/catch,Promise的错误捕获非常冗余
 调试友好,Promise的调试很差,由于没有代码块,你不能在一个返回表达式的箭头函数中设置断点,如果你在一个.then代码块中使用调试器的步进(step-over)功能,调试器并不会进入后续的.then代码块,因为调试器只能跟踪同步代码的『每一步』。
 **promise是什么**
 是es6新增的异步编程解决方案
主要用于异步计算
将异步操作队列化,按照期望的顺序执行,返回预期的结果
可以在对象之间传递和操作promise,帮助我们处理队列
promise有三个状态
pendding 初始状态
reslove操作成功
reject 操作失败
当promise状态发生改变,就会触发then()里的响应函数处理后续步骤
promise的状态是不可逆的,一经改变,不会再变
promise有两个参数一个是resolve 一个是reject
resolve将promise对象从未成功变为成功,成功调用,将异步操作的结果作为参数返回
reject将promise对象从未完成变为失 失败调用,将异步操作的结果作为参数返回
使用场景
使用promise封装api接口
使用promise进行异步操作
 
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值