JS
- 1.JS 的数据类型有哪些
- 2.JS 原型和原型链
- 3.谈谈你对闭包的理解吧(可以从函数的作用域,还有内存泄漏拓展到垃圾回收,应用的话可以拓展到防抖和节流)
- 4.了解什么是event loop吗(可以拓展到同步任务,异步任务,以及宏任务和微任务)
- 5.new 操作符具体做了什么
- 6. call,apply和bind的区别(可以先讲讲call和apply的区别,再讲讲和它们和bind的区别)
- 7. 你了解柯里化吗?
- 8. **知道什么是深拷贝吗**
- 9. **知道模块化的概念吗**
- 10. **了解事件委托(代理)吗(就是介绍冒泡和捕获的概念)**
- 11.Ajax 是什么? 如何创建一个Ajax?
- TCP的三次握手和四次挥手
1.JS 的数据类型有哪些
(1)值类型(基本类型)
- 字符串(String)
- 数字(Number)
- 布尔(Boolean)
- 对空(Null)
- 未定义(Undefined)
- Symbol(实例是唯一且不可改变的;一种唯一标识符,可用作对象的唯一属性名,这样其他人就不会改写或覆盖你设置的属性值。)
(2)引用类型
- 对象(Object)
- 数组(Array)
- 函数(Function)
2.JS 原型和原型链
-
对应名称
-
prototype : 原型
-
proto : 原型链
-
从属关系
-
prototype -> 函数的属性:对象{}(内置对象)
-
proto -> 对象Object的一个属性: 对象{}
-
对象的_proto_储存着保存着该对象的构造函数的prototype
/** -
对象 test {
-
a: 1,
-
__proto__: Test.prototype{
-
b: 2,
-
__proto__: Object.prototype{对象的构造函数Object
-
c: 3
-
没有__proto__
-
}
-
}
-
}
-
test沿着__proto__查找找到就停止
*/
// 对象test有无该属性(内置方法 hasOwnProperty() )
console.log(test.hasOwnProperty('a'));
console.log(test.hasOwnProperty('b'));
console.log(test.hasOwnProperty('c'));
// 查找__proto__上有无该属性('a' in test)
console.log("-----------------");
console.log('a' in test);
console.log('b' in test);
console.log('c' in test);
// test.constructor -> 实例化test对象的构造函数 可以更改但是yun
function Test1(){
this.a=333
}
test.constructor = Test1;
console.log(test);
3.谈谈你对闭包的理解吧(可以从函数的作用域,还有内存泄漏拓展到垃圾回收,应用的话可以拓展到防抖和节流)
闭包:内部的函数保存到了外部并且在外部执行可以访问到内部变量
以计数器为例:
原理:外部函数执行时和内部函数定义时使用同一条作用域链,当外部函数执行结束作用域链销毁,但是由于内部函数此时还未执行还保存着该条作用域链,并且被保存到了外部,因此可以访问到外部函数内定义的变量,正因为外部函数定义的变量任然被内部函数引用,所以该变量没有有被垃圾回收机制回收。
写法一:
function func(){
var i = 0;
function func1(){
i++;
return i;
}
return func1;
}
let func2 = func(); func2保存着func1函数,
原本func函数执行完,销毁作用域链了就访问不到变量i,
但是func1函数还没执行没被销毁,所以func1函数内的i任然可以访问
console.log(func2()); i = 1 在外部执行func2函数可以访问func的变量
console.log(func2()); i = 2
console.log(func2()); i = 3
console.log(func2()); i = 4
console.log(func2()); i = 5
写法二(简便写法):
与写法一效果相同
var add = (function() {
var count = 0;
return function () {return count++}
})();
console.log(add());
console.log(add());
console.log(add());
写法三(不推荐):
const plus = function () {
var count = 10;
function less () {
count--
return count;
}
return less;
}
!注意在使用该方法是后面加两个括号,只有一个括号返回的是less()函数,
console.log(plus()());
函数作用域:就是js的执行上下文(context)分为全局执行上下文和局部执行上下文,一
函数作用域链:函数作用域连接起来的链,一个函数定义时有一个作用域链,执行时也有一个作用域链,函数在执行完成后要进行销毁,函数的销毁就是作用域链的断裂
函数func1的定义和函数func的执行是共用一个作用域链,只有函数func执行了函数func1才被定义
垃圾回收机制:JS的垃圾回收机制是为了以防内存泄漏,内存泄漏的含义就是当已经不需要某块内存时这块内存还存在着,垃圾回收机制就是间歇的不定期的寻找到不再使用的变量,并释放掉它们所指向的内存。
当一个变量的生命周期结束之后它所指向的内存就应该被释放。JS有两种变量,全局变量和在函数中产生的局部变量。局部变量的生命周期在函数执行过后就结束了,此时便可将它引用的内存释放(即垃圾回收),但全局变量生命周期会持续到浏览器关闭页面
由于函数func1任然引用着变量 i 所以垃圾回收机制没有将变量 i 回收
内存泄漏:函数内部变量可以在外部被访问,变量一直保存在内存中
经典应用
防抖函数:当持续触发事件时,一段事件内没有再触发该事件,事件处理函数才会执行一次,如果设定时间到来之前又一次触发了事件,就重新开始延时。
理解:一个能量条持续充能,只有充能完成才能点击按钮触发世纪,如果在充能过程中点击按钮触发事件,能量条就重新充能
代码实现
<input type="text" id="input">
js闭包经典应用,当你一直输入时不会触发inputFunc函数
只有当你停止输入后1000毫秒才会触发inputFunc函数
function debounce (fun, delay) {
let timer;
return function (arg) {
clearInterval(timer); 清除上一次的计时器,
timer = setTimeout(function () { 设置本次计时器
fun(arg)
},delay)
};
};
function inputFunc (value) {
console.log(`你输出的结果是${value}`); 发起搜索请求
};
const input = document.getElementById('input');
const debounceInput = debounce(inputFunc,1000)
input.addEventListener('keyup',function(e){
debounceInput(e.target.value)
});
典型案例就是输入搜索:输入结束后n秒才进行搜索请求,n秒内又输入内容,就重新计时
节流:当持续触发事件时,保证一段时间内,值调用一次事件处理函数
<button id="btn">点击</button>
<script>
function throttle (func, wait) {
let timeOut;
return function () {
第二次触发时 timeOut就不熟undefined了就进不到if里面
if (!timeOut) {
timeOut = setTimeout(function () {
timeOut = null;
func();
},wait)
}
}
}
function handle () {
console.log(Math.random());
}
document.getElementById('btn').onclick = throttle (handle, 2000);
实际应用表单提交规定在n秒内的提交只有一次生效
</script>
防抖节流结合实现图片懒加载
4.了解什么是event loop吗(可以拓展到同步任务,异步任务,以及宏任务和微任务)
事件循环机制 ,由三部分构成,调用栈,微任务,消息队列(宏任务)
函数在执行时会先压入调用栈,执行完弹出调用栈,当js有异步操作
(setTimeout,setInterval,,ajax,DOM)压入到调用栈中的时候里面的消息会进入消息队列中去,消息队列中,会等到调用栈清空了之后再执行
promise, async, await的异步操作会加入到微任务中去,会在调用栈清空时立即执行(比消息队列先执行)
调用栈中加入的微任务会立马执行
function func1 () {
console.log(1);
}
function func2 () {
console.log(2);
func1();
console.log(3);
}
// 结果2,1,3
function func1 () {
console.log(1);
}
function func2 () {
setTimeout(()=>{
console.log(2);
},0);
func1();
console.log(3);
}
// 结果1,3,2
var p = new Promise(resolve => {
console.log(4);
resolve(5)
});
function func1 () {
console.log(1);
}
function func2 () {
setTimeout(()=>{
console.log(2);
},0);
func1();
console.log(3);
p.then(resolve => {
console.log(resolve);
})
}
func2()
// 结果4,1,3,5,2
5.new 操作符具体做了什么
- 创建了一个新对象)
var obj = new Object() - 设置原型链(__ propo__)使其指向他的构造函数的prototype
- 将构造函数的作用域赋予新对象(就是使this指向该对象)
- 执行构造函数内的代码
- 返回新对象。()
6. call,apply和bind的区别(可以先讲讲call和apply的区别,再讲讲和它们和bind的区别)
call,apply和bind都是函数Function的prototype中的方法
call: 一个函数test如果test.call(a,b)则执行该函数,函数的this指向call的第一个参数a,
var person = {
fullName: function() {
return this.firstName + " " + this.lastName;
}
}
var person1 = {
firstName:"Bill",
lastName: "Gates",
}
var person2 = {
firstName:"Steve",
lastName: "Jobs",
}
var person1FullName = person.fullName.call(person1); // 将返回 "Bill Gates"
console.log(person1FullName);
call()方法接受参数,但this 指向对象还是一个传入的对象,不在该对象中的属性,不能用this
var person = {
fullName: function(city, country) {
return this.firstName + " " + this.lastName + "," + city + "," + country;
}
}
var person1 = {
firstName:"Bill",
lastName: "Gates"
}
person.fullName.call(person1, "Seattle", "USA");
call(),改变this指向实例
// 此时this指向windows
function foo () {
console.log(this);
}
// 直接执行函数
foo()
结果:
function foo () {
console.log(this);
}
foo.call(foo)
// 使用call()执行函数此时this就是执行call()传入的第一个参数
// 例如此时就是this就是指向foo这个函数本身
const obj= {
a: 1,
b: 2
}
foo.call(obj);
// 此时this就指向obj这个对象
ps:如果第一个参数传入的是基本类型会转换为对象例如 let p = 2 此时this指向Number {2}
但是数组不会,就是指向数组,因为数组本身就是一个对象
apply()
call()和apply()的区别
call() 方法分别接受参数。【call(传入的对象,参数1,参数2,参数3)】
apply() 方法接受数组形式的参数。【apply(传入的对象,[参数1,参数2,参数3])】
var person = {
fullName: function(city, country) {
return this.firstName + " " + this.lastName + "," + city + "," + country;
}
}
var person1 = {
firstName:"John",
lastName: "Doe"
}
console.log(person.fullName.apply(person1, ["Oslo", "Norway"]));
bind() bind()是Jquery的方法bind() 方法为被选元素添加一个或多个事件处理程序,并规定事件发生时运行的函数。bing(当该事件发生是,执行此事件)
当点击鼠标时,隐藏或显示 p 元素:
$("button").bind("click",function(){
$("p").slideToggle();
});
7. 你了解柯里化吗?
是把接受多个参数的函数变换成接受一个单一参数(最初函数的第一个参数)的函数,并且返回接受余下的参数而且返回结果的新函数的技术。
8. 知道什么是深拷贝吗
浅拷贝::创建一个新对象,这个有着原始对象属性值的一份精确拷贝,如果属性是基本类型,拷贝的就是基本类型的值,如果属性是引用类型(对象,数组,函数),拷贝的就是内存地址(共用一块内存),所以如果其中一个对象改变了这个地址,另一个的也会改变
深拷贝:深拷贝是将一个对象从内存中完整的拷贝一份出来,从堆内存中开辟一个新的区域存放行对象且修改新对象不会影响原对象
浅拷贝实现
function shallowCopy (obj) {
var target = {};
for (const i in obj) {
if (obj.hasOwnProperty(i))
{
target[i] = obj[i]
}
}
return target;
}
var person = {
id: 1,
name: '张三'
}
var person1 = shallowCopy(person);
深拷贝
function deepClone (obj) {
var cloneObj = new obj.constructor();
if(obj == null) return obj;
// 利用null与undefine在使用==时相等省一次判断
if(obj instanceof Date) return new Date(obj);
// A instanceof B B的ptopotypeof是否在A的原型链(__propo__)上
if(obj instanceof RegExp) return new RegExp(obj);// 正则
if (typeof obj !== 'object') return obj;
for (let i in obj) {
if(obj.hasOwnProperty(i))
{
cloneObj[i] = deepClone(obj[i])
}
}
return cloneObj;
}
var person = {
id: 1,
name: '张三',
hobu: ['study', 'play']
}
var person1 = deepClone(person)
// 有坑无法判断Date和正则
var person1 = JSON.parse(JSON.stringify(person))
9. 知道模块化的概念吗
由于JS代码愈来愈庞大,为了防止出错,将JS代码分为了许多块例如
CommonJs中实现模块引用的require模块,实现模块定义的exports模块,实现模块标识的module模块,
AMD的异步加载定义的define模块。
10. 了解事件委托(代理)吗(就是介绍冒泡和捕获的概念)
事件委托:由于多个子元素需要绑定事件,可以将这些事件绑定到父元素,使用父元素触发不同子元素的事件。将子元素的事件委托给父元素就是事件委托,利用了冒泡
冒泡流:即事件开始由最具体的元素(文档中嵌套层次最深的那个节点)接收,然后逐层向上传播到较为不具体的节点,比如你点击页面中的一个div节点,事件触发的顺序为1.div 2.body 3.html 4.document
事件捕获相反,接收事件的顺序为4.div 3.body 2.html 1.document
11.Ajax 是什么? 如何创建一个Ajax?
ajax的全称:Asynchronous Javascript And XML。
异步传输+js+xml。
所谓异步,在这里简单地解释就是:向服务器发送请求的时候,我们不必等待结果,而是可以同时做其他的事情,等到有了结果它自己会根据设定进行后续操作,与此同时,页面是不会发生整页刷新的,提高了用户体验。
(1)创建XMLHttpRequest对象,也就是创建一个异步调用对象
(2)创建一个新的HTTP请求,并指定该HTTP请求的方法、URL及验证信息
(3)设置响应HTTP请求状态变化的函数
(4)发送HTTP请求
(5)获取异步调用返回的数据
(6)使用JavaScript和DOM实现局部刷新
AJAX四个步骤
- 创建ajax实例
- 执行open确定要访问的连接以及同步异步
- 监听请求状态
- 发送请求
AJAX状态码
2开头
200 : 代表请求成功;
3开头
301 : 永久重定向;
302: 临时转移
304 : 读取缓存 [表示浏览器端有缓存,并且服务端未更新,不再向服务端请求资源]
307:临时重定向
以4开头的都是客户端的问题;
400 :数据/格式错误
401: 权限不够;(身份不合格,访问网站的时候,登录和不登录是不一样的)
404 : 路径错误,找不到文件
以5开头都是服务端的问题
500 : 服务器的问题
503服务器超负荷
TCP的三次握手和四次挥手
建立连接
三次握手
第一次握手:客户端的发送一个SYN码给服务器,要求建立数据连接
第二次握手:服务器SYN和自己处理一个SYN(标志);SYN+ACK(确认包);发送给客户端表示可以建立连接
第三次握手: 客户端再次发送ACK向服务器,服务器验证ACK没有问题,建立连接
断开连接
四次挥手
第一次挥手:客户端发送FIN(结束)报文,用盒子服务器数据已经传输完毕
第二次挥手: 服务器接收到智慧,通知客户端我收到了SYN,服务器发送ACK(确认)给客户端,数据传输还未完成
第三次挥手:服务器已经传输完毕,服务器再次发送FIN通知客户端,数据传输完毕
第四次挥手: 客户端再次发送ACK,进入TIME_WAIT状态,服务器和客户端关闭连接