Vue是以数据为驱动的,Vue自身将DOM和数据进行绑定,一旦创建绑定,DOM和数据将保持同步,每当数据发生变化,DOM会跟着变化。
ViewModel是Vue的核心,它是Vue的一个实例。Vue实例时作用域的这个HTML元素可以是body,也可以是某个id所指代的元素。
DOMListeners和DataBindings是实现双向绑定的关键。
DOMListeners监听页面所有View层DOM元素的变化,当发生变化,Model层的数据随之变化;
DataBindings监听Model层的数据,当数据发生变化,View层的DOM元素随之变化。
MVVM框架提倡把渲染和逻辑分离。简单来说就是不要再让 JS
直接操控 DOM
,JS
只需要管理状态即可,然后再通过一种模板语法来描述状态和界面结构的关系。
24.v-show
和v-if
指令的共同点和不同点?
v-show
指令是通过修改元素的display
CSS属性让其显示或者隐藏
v-if
指令是直接销毁和重建DOM达到让元素显示和隐藏的效果
25.keep-alive
keep-alive是Vue提供的一个抽象组件,用来对组件进行缓存,从而节省性能,由于是一个抽象组件,所以在vue页面渲染完毕后不会被渲染成一个DOM元素
https://www.jianshu.com/p/4b55d312d297
26.Vue生命周期
1)beforeCreate:
完成实例初始化,初始化非响应式变量;
this指向创建的实例;
可以加载loading事件;
data computed watch methods 上的方法和数据均不能访问;
2)created:
实例创建完成;
完成数据(data props computed)的初始化,导入依赖项;
可访问data computed watch methods上的数据和方法;
未挂载DOM,不能访问$el,$ref为空;
可在这结束loading,还能做一些初始化,实现函数自执行;
可以对data数据进行操作,可进行一些请求,请求不易过多,避免白屏时间太长;
若在此阶段进行的 DOM 操作一定要放在 Vue.nextTick() 的回调函数中
3)beforeMount:
有了el,编译了template/outerHTML;
能找到对应的template,并编译成render函数;
4)mounted:
完成创建vm.$el,和双向绑定;
完成挂载DOM 和渲染;可在mounted钩子对挂载的dom进行操作;
即有了DOM 且完成了双向绑定,可访问DOM节点,$ref;
可在这发起后端请求,拿回数据,配合路由钩子做一些事情;
可对DOM 进行操作;
5)beforeUpdate:
数据更新之前;
可在更新前访问现有的DOM,如手动移除添加的事件监听器;
6)updated:
完成虚拟DOM的重新渲染和打补丁;
组件DOM 已完成更新;
可执行依赖的dom 操作;
注意:不要在此函数中操作数据,会陷入死循环的。
7)activated:
在使用vue-router时有时需要使用<keep-alive></keep-alive>来缓存组件状态,这个时候created钩子就不会被重复调用了
如果我们的子组件需要在每次加载的时候进行某些操作,可以使用activated钩子触发
8)deactivated:
keep-alive 组件被移除时使用
9)beforeDestory:
在执行app.$destroy()之前
可做一些删除提示,如:你确认删除XX吗?
可用于销毁定时器,解绑全局时间 销毁插件对象
10)destroyed:
当前组件已被删除,销毁监听事件 组件 事件 子实例也被销毁
这时组件已经没有了,你无法操作里面的任何东西了
https://segmentfault.com/a/1190000014640577
27.父子组件的生命周期?
仅当子组件完成挂载后,父组件才会挂载
当子组件完成挂载后,父组件会主动执行一次beforeUpdate/updated钩子函数(仅首次)
父子组件在data变化中是分别监控的,但是在更新props中的数据是关联的(可实践)
销毁父组件时,先将子组件销毁后才会销毁父组件
兄弟组件的初始化(mounted之前)分开进行,挂载是从上到下依次进行
当没有数据关联时,兄弟组件之间的更新和销毁是互不关联的
28.什么是Vue的生命周期?
Vue实例从创建到销毁的过程,就是生命周期。
29.Vue生命周期的作用是什么?
Vue生命周期有很多事件钩子,让我们在控制整个Vue实例的过程更容易形成好的逻辑。
30.Vue生命周期总共有几个阶段?
答:它可以总共分为8个阶段:创建前/后, 载入前/后,更新前/后,销毁前/销毁后。
31.Vue双向绑定的原理是什么?
Vue采用的是数据劫持结合发布和-订阅者模式的方式,通过Object.defineProperty()来劫持各个属性的setter,getter,在数据变动时发布消息给订阅者,触发相应的监听回调
https://blog.csdn.net/onlyhqm/article/details/84333640
32.Vue组件之间的参数传递?
1.父组件与子组件传值
父组件传给子组件:子组件通过props方法接受数据;
子组件传给父组件:$emit方法传递参数
2.非父子组件间的数据传递,兄弟组件传值
eventBus,就是创建一个事件中心,相当于中转站,可以用它来传递事件和接收事件。项目比较小时,用这个比较合适。
(虽然也有不少人推荐直接用VUEX,具体来说看需求咯。技术只是手段,目的达到才是王道。)
33.Vuex中有5中默认的基础对象
- state:存储状态(变量)
- getters:对数据获取之前的再次编译,可以理解为state的计算属性。我们在组件中使用 $sotre.getters.fun()
- mutations:修改状态,并且是同步的。在组件中使用$store.commit(‘‘,params)。这个和我们组件中的自定义事件类似。
- actions:异步操作。在组件中使用是$store.dispath(‘‘)
- modules:store的子模块,为了开发大型项目,方便状态管理而使用的。这里我们就不解释了,用起来和上面的一样。
34.Vuex是什么?
答:vuex 是一个专门为vue.js应用程序开发的状态管理模式。
这个状态我们可以理解为在data中的属性,需要共享给其他组件使用的部分。
也就是说,是我们需要共享的data,使用vuex进行统一集中式的管理。
35.Vuex使用
https://www.cnblogs.com/chinabin1993/p/9848720.html
36.Vuex中mutation和action的区别
答:mutation 有必须同步执行这个限制,Action 就不受约束!我们可以在 action 内部执行异步操作
https://www.cnblogs.com/shirliey/p/10457667.html
37.原型、原型链、constructor
每个函数都有一个prototype属性,prototype是函数的原型对象。原型对象是用来给实例共享属性和方法的
Javascript中所有的对象都是Object的实例,并继承Object.prototype的属性和方法,也就是说,Object.prototype是所有对象的爸爸
每个对象都有__proto__属性,这个__proto__指向的是创建这个对象的构造函数的prototype
Javascript语言的继承机制一直很难被人理解
它没有"子类"和"父类"的概念,也没有"类"(class)和"实例"(instance)的区分,全靠一种很奇特的"原型链"(prototype chain)模式,来实现继承
每个对象都有一个constructor属性,它引用了初始化该对象的构造函数
https://www.cnblogs.com/loveyoume/p/6112044.html
http://www.ruanyifeng.com/blog/2011/06/designing_ideas_of_inheritance_mechanism_in_javascript.html
38.JS的基本数据类型和引用数据类型以及深拷贝和浅拷贝
基本数据类型:
他们的值在内存中占据着固定大小的空间,并被保存在栈内存中;
当一个变量向另一个变量复制基本类型的值,会创建这个值的副本,并且我们不能给基本数据类型的值添加属性;
Undefined / Null / Boolean / Number / String,它们是直接按值存放的,可以直接访问。
引用数据类型:(对象、函数、数组)
复杂的数据类型即是引用类型,它的值是对象,保存在堆内存中,包含引用类型值的变量实际上包含的不是对象本身,而是一个指向该对象的指针;
从一个变量向另一个变量复制引用类型的值,复制的其实是指针地址而已,因此两个变量最终都指向同一个对象。
访问引用数据类型的值时,首先从栈中获得该对象的地址指针,然后再从堆内存中取得所需的数据。
浅拷贝:是拷贝一层,深层次的对象级别的就拷贝引用;
深拷贝:是拷贝多层,每一级别的数据都会拷贝出来;
浅拷贝的时候如果数据是基本数据类型,那么就如同直接赋值那种,会拷贝其本身,如果除了基本数据类型之外还有一层对象,那么对于浅拷贝而言就只能拷贝其引用,对象的改变会反应到拷贝对象上;
但是深拷贝就会拷贝多层,即使是嵌套了对象,也会都拷贝出来。
当我们使用对象拷贝时,如果属性是对象或数组时,这时我们传递的只是一个地址。因此子对象在访问该属性时,会根据地址回溯到父对象指向的堆内存中,即父子对象发生了关联,两者的属性值会指向同一内存空间。
看一个比较经典的面试题:
var a = function() console.log(11);
var b = function() console.log(11);
console.log( a==b ); //false
console.log( == ); //false
console.log( []==[] ); //false
为什么结果都是false?
变量 a 实际保存的是指向堆内存中对象的一个指针,而 b 保存的是指向堆内存中另一个对象的一个指针;
虽然这两个对象的值是一样的,但它们是独立的2个对象,占了2份内存空间;所以 a == b 为 false
var a = ;
var b = a;
console.log( a == b ); // true
这时变量 b 复制了变量 a 保存的指针,它们都指向堆内存中同一个对象;所以 a == b 为 true
传值与传址:
基本类型与引用类型最大的区别实际就是 传值 与 传址 的区别
var a = [1,2,3,4,5];
var b = a;
var c = a[0];
console.log(b); // [1,2,3,4,5]
console.log(c); // 1
b[4] = 6;
c = 7;
console.log(a[4]); //6
console.log(a[0]); //1
从上面代码可以得知,当改变b中的数据时,a也发生了变化;但是当我们改变c的数值时,a却没有发生改变
这就是传值与传址的区别。因为a是数组,属于引用类型,所以a给b传的是栈中的地址,而不是堆内存中的对象。
而c仅仅是从a堆内存中获取的一个数值,并保存在栈中。所以b修改的时候,会根据地址回到a堆内存中修改;c则直接在栈中修改,并且不能指向a堆内存中。
https://blog.csdn.net/Bruce__taotao/article/details/82690506
https://www.cnblogs.com/c2016c/articles/9328725.html
https://blog.csdn.net/weixin_37719279/article/details/81240658
39.什么是递归?
https://pushy.site/2018/02/20/recursion/
40.什么是闭包?闭包有什么作用?使用闭包的好处是什么?
41.什么是变量作用域?
变量的作用域就两种:全局变量和局部变量
全局作用域:
最外层函数定义的变量拥有全局作用域,即对任何内部函数来说,都是可以访问的
局部作用域:
和全局作用域相反,局部作用域一般只在固定的代码片段内可访问到,而对于函数外部是无法访问的,最常见的例如函数内部
<script>
function fn()
var innerVar = "inner";
fn();
console.log(innerVar);// ReferenceError: innerVar is not defined
</script>
需要注意的是,函数内部声明变量的时候,一定要使用var命令。如果不用的话,你实际上声明了一个全局变量!
<script>
function fn()
innerVar = "inner";
fn();
console.log(innerVar);// result:inner
</script>
再来看一个代码:
<script>
var scope = "global";
function fn()
console.log(scope);//result:undefined
var scope = "local";
console.log(scope);//result:local;
fn();
</script>
很有趣吧,第一个输出居然是undefined,原本以为它会访问外部的全局变量(scope=”global”),但是并没有。
这可以算是javascript的一个特点,只要函数内定义了一个局部变量,函数在解析的时候都会将这个变量“提前声明”:
<script>
var scope = "global";
function fn()
var scope;//提前声明了局部变量
console.log(scope);//result:undefined
scope = "local";
console.log(scope);//result:local;
fn();
</script>
然而,也不能因此草率地将局部作用域定义为:用var声明的变量作用范围起止于花括号之间。
javascript并没有块级作用域
那什么是块级作用域?
像在C/C++中,花括号内中的每一段代码都具有各自的作用域,而且变量在声明它们的代码段之外是不可见的,比如下面的c语言代码:
for(int i = 0; i < 10; i++)
//i的作用范围只在这个for循环
printf("%d",&i);//error
但是javascript不同,并没有所谓的块级作用域,javascript的作用域是相对函数而言的,可以称为函数作用域:
<script>
for(var i = 1; i < 10; i++)
//coding
console.log(i); //10
</script>
42.什么是作用域链?什么是执行环境?
我的理解就是,根据在内部函数可以访问外部函数变量的这种机制,用链式查找决定哪些数据能被内部函数访问。
想要知道js怎么链式查找,就得先了解js的执行环境
每个函数运行时都会产生一个执行环境,而这个执行环境怎么表示呢?js为每一个执行环境关联了一个变量对象。环境中定义的所有变量和函数都保存在这个对象中。
全局执行环境是最外围的执行环境,全局执行环境被认为是window对象,因此所有的全局变量和函数都作为window对象的属性和方法创建的。
js的执行顺序是根据函数的调用来决定的,当一个函数被调用时,该函数环境的变量对象就被压入一个环境栈中。而在函数执行之后,栈将该函数的变量对象弹出,把控制权交给之前的执行环境变量对象。
举个例子:
<script>
var scope = "global";
function fn1()
return scope;
function fn2()
return scope;
fn1();
fn2();
</script>
上面代码执行情况演示:
https://blog.csdn.net/whd526/article/details/70990994
43.Vue组件
https://www.jianshu.com/p/7e825cd5f086
44.优化图片加载的方法有哪些?
a. 图片懒加载,滚动到相应位置才加载图片。
b. 图片预加载,如果为幻灯片、相册等,将当前展示图片的前一张和后一张优先下载。
c. 使用CSSsprite,SVGsprite,Iconfont、Base64等技术,如果图片为css图片的话。
d. 如果图片过大,可以使用特殊编码的图片,加载时会先加载一张压缩的特别厉害的缩略图,以提高用户体验。
45.什么是token验证?
Token是在客户端频繁向服务端请求数据,服务端频繁的去数据库查询用户名和密码并进行对比,判断用户名和密码正确与否,并作出相应提示,在这样的背景下,Token便应运而生。
Token是服务端生成的一串字符串,以作客户端进行请求的一个令牌,当第一次登录后,服务器生成一个Token便将此Token返回给客户端,以后客户端只需带上这个Token前来请求数据即
可,无需再次带上用户名和密码。
46.使用基于 Token 的身份验证方法,在服务端不需要存储用户的登录记录。大概的流程是这样的:
1.客户端使用用户名跟密码请求登录
2.服务端收到请求,去验证用户名与密码
3.验证成功后,服务端会签发一个 Token,再把这个 Token 发送给客户端
4.客户端收到 Token 以后可以把它存储起来,比如放在 Cookie 里或者 Local Storage 里
5.客户端每次向服务端请求资源的时候需要带着服务端签发的 Token
6.服务端收到请求,然后去验证客户端请求里面带着的 Token,如果验证成功,就向客户端返回请求的数据
47.堆、栈
栈(stack):先进后出;自动分配内存空间,由系统自动释放;使用的是一级缓存,他们通常都是被调用时处于存储空间中,调用完立即释放。
堆(heap):队列优先,先进先出;动态分配内存,大小不定也不会自动释放;存放在二级缓存中,生命周期由虚拟机的垃圾回收算法来决定;一般由程序员分配释放,若程序员不释放,程序结束时可能由OS回收。
48.js中 == 和 === 有什么区别?
== 用于一般比较,=== 用于严格比较,== 在比较的时候可以转换数据类型,=== 严格比较,只要类型不匹配就返回 flase
49.JS语言特点
JS作为脚本语言,它的主要用途是与用户互动,以及操作DOM。这决定了它只能是单线程,否则会带来很复杂的同步问题,也就是同一时刻只能做一件事情。所有任务只能在一个线程上完成,一次只能做一件事。前面的任务没做完,后面的任务只能等着。
但是随着业务的不断增加,只是单纯的单线程模式已经可能无法满足我们的需求了。于是在html5中新增了后台任务worker API。
浏览器的线程,浏览器中主要的线程包括,UI渲染线程,JS主线程,GUI事件触发线程,http请求线程
看一个经典的面试题:
function a()
setTimeout(function ()
alert(1)
, 0);
alert(2);
a();
代码中的 setTimeout
设为 0,也就是延迟 0ms,看上去是不做任何延迟立刻执行,即依次弹出 “1”、“2”。但实际的执行结果确是 “2”、“1”。其中的原因得从 setTimeout
的原理说起:
JavaScript 是单线程执行的,也就是无法同时执行多段代码,当某一段代码正在执行的时候,所有后续的任务都必须等待,形成一个队列,一旦当前任务执行完毕,再从队列中取出下一个任务。这也常被称为 “阻塞式执行”。
所以一次鼠标点击,或是计时器到达时间点,或是 Ajax 请求完成触发了回调函数,这些事件处理程序或回调函数都不会立即运行,而是立即排队,一旦线程有空闲就执行。
假如当前 JavaScript 进程正在执行一段很耗时的代码,此时发生了一次鼠标点击,那么事件处理程序就被阻塞,用户也无法立即看到反馈,事件处理程序会被放入任务队列,直到前面的代码结束以后才会开始执行。
setTimeout(fn, 0)的含义是,指定某个任务在主线程最早可得的空闲时间执行,也就是说,当前代码执行完(执行栈清空)以后,尽可能的早执行。它在“任务队列”的尾部添加一个事件,因此要等到同步任务和“任务队列”现有的事件都处理完,才会得到执行。
HTML5标准规定了setTimeout()的第二个参数的最小值不得小于4毫秒,如果低于这个值,则默认是4毫秒。在此之前。老版本的浏览器都将最短时间设为10毫秒。另外,对于那些DOM的变动(尤其是涉及页面重新渲染的部分),通常是间隔16毫秒执行。这时使用requestAnimationFrame()的效果要好于setTimeout();
注意:setTimeout()只是将事件插入了“任务队列”,必须等当前代码(执行栈)执行完,主线程才会去执行它指定的回调函数。要是当前代码消耗时间很长,也有可能要等很久,所以并没办法保证回调函数一定会在setTimeout()指定的时间执行。所以,setTimeout()的第二个参数表示的是最少时间,并非是确切时间。
settimeout(0)就起到了一个将事件加入到队列中,待执行的一个功能效果!
定时器占用cpu较多,建议酌情使用。
50.如何实现js的多线程?
JS为我们提供了一个Worker类,它的作用就是为了解决这种阻塞的现象。当我们使用这个类的时候,它就会向浏览器申请一个新的线程。这个线程就用来单独执行一个js文件。
Web Worker 是运行在后台的 JavaScript,独立于其他脚本,不会影响页面的性能。您可以继续做任何愿意做的事情:点击、选取内容等等,而此时 Web Worker 在后台运行。
Worker就是为了JavaScript 创造多线程环境,允许主线程创建 Worker 线程,将一些任务分配给后者运行。
开启后台线程,在不影响前台线程的前提下做一些耗时或者异步的操作。
因为是不同的线程,所以主线程与worker线程互不干扰。也不会相互打断。所以在一些场景可以提高页面的流程性。Worker 线程一旦新建成功,就会始终运行,不会被主线程上的活动(比如用户点击按钮、提交表单)打断。这样有利于随时响应主线程的通信。
但是,这也造成了 Worker 比较耗费资源,不应该过度使用,而且一旦使用完毕,就应该关闭。
var worker = new Worker(js文件路径); // 这个语句就会申请一个线程用来执行这个js文件
当然,在主线程中有一些方法来实现对新线程的控制和数据的接收。在这里,我们只说比较常用的几个方法。
//postMessage(msg);
//postMessage方法把在新线程执行的结果发送到浏览器的js引擎线程里
worker.onmessage = function()
//获取在新线程中执行的js文件发送的数据 用event.data接收数据
console.log( event.data )
;
setTimeout( function()
worker.terminate();
//terminate方法用于关闭worker线程
,2000)
setTimeout( function()
worker = new Worker("js/test22.js");
//再次开启worker线程
,3000)
使用规则:
- 必须同源:也就是说js文件的路径必须和主线程的脚本同源。防止了外部引用。
- dom限制:在worker线程中不能操作dom(document,window,parent)。注意可以使用浏览器的navigator和location对象。
- 通讯限制:worker线程和主线程不在一个上下文中所以不能直接通讯。也就是说主线程定义的变量在worker中也是不能使用的。所有只能通过消息完成。
- 提示禁止:worker线程不能alert和confirm,这个不知到具体原因?
- 传值dom:进行消息通讯也不能传值dom只能是变量。
- ie限制:ie9不能使用!ie9不能使用!ie9不能使用!
https://www.cnblogs.com/yanbigfeg/p/9546599.html#_label0
51.浏览器缓存机制
以上两点结论就是浏览器缓存机制的关键,它确保了每个请求的缓存存入与读取
根据是否需要向服务器重新发起HTTP请求将缓存过程分为两个部分,分别是强缓存和协商缓存
强缓存:不会向服务器发送请求,直接从缓存中读取资源,在chrome控制台的Network选项中可以看到该请求返回200的状态码,并且Size显示from disk cache或from memory cache。强缓存可以通过设置两种 HTTP Header 实现:Expires 和 Cache-Control
强缓存判断是否缓存的依据来自于是否超出某个时间或者某个时间段,而不关心服务器端文件是否已经更新,这可能会导致加载文件不是服务器端最新的内容,那我们如何获知服务器端内容是否已经发生了更新呢?此时我们需要用到协商缓存策略。
协商缓存:用户发送的请求,发送到服务器后,由服务器判定是否从缓存中获取资源
- 协商缓存生效,返回304和Not Modified
- 协商缓存失效,返回200和请求结果
协商缓存可以通过设置两种 HTTP Header 实现:Last-Modified 和 ETag 。
两者的区别:从名字就可以看出,强缓存不与服务器交互,而协商缓存则需要与服务器交互。
如果什么缓存策略都没设置,那么浏览器会怎么处理?
对于这种情况,浏览器会采用一个启发式的算法,通常会取响应头中的 Date 减去 Last-Modified 值的 10% 作为缓存时间。
https://www.jianshu.com/p/54cc04190252
52.请求头header
HTTP请求中的referrer和referrer-policy :https://juejin.im/post/5cd81b59518825686a06fd05
53.JS中对象和函数的关系
首先什么是对象?根据W3C上面的解释JS中所有事物都是对象,对象是拥有属性和方法的数据,由此可以看出基本值类型不是对象(number、string、Boolean、undefined、null),剩下的引用类型(函数、数组...)都是对象,也有人说对象是若干属性的集合。
函数和对象是什么关系?
1.函数是一种对象。很明显函数是一种对象,但你不能说函数是对象的一种。因为他俩之间是没有包含关系的。
function test() ;
console.log(test instanceof Object); // true
2、对象都是通过函数创建的
先来看一个例子
function test()
this.name="哈哈"
;
var test2=new test();
console.log(test2 instanceof Object); //true
这个例子可以说明对象可以被函数创建。那为什么要说对象都是通过函数创建的,那对象字面量是不是也是通过函数来创建的,答案是肯定的,这是一种语法糖方式。举个简单的例子
var obj=
name:"哈哈",
age:"18"
var obj=new Object()
obj.name="哈哈";
obj.age="18";
上面的对象字面量其实是通过下面的构造函数来创建的。而其中的Object是一种函数:
console.log(typeof Object) //function
通过上面的简单例子我们可以得出一个结论:对象是通过函数创建的,而函数又是一种对象。那么这是为什么呢?这就牵扯到prototype原型
54.null 和 undefeated 的区别?
null是一个表示"无"的对象,转为数值时为0;undefined是一个表示"无"的原始值,转为数值时为NaN
https://www.cnblogs.com/qyzy1024/p/4036661.html
55.es6 class
ES6 的 class 属于一种“语法糖”,所以只是写法更加优雅,更加像面对对象的编程,其思想和 ES5 是一致的
//定义类
class Point
constructor(x, y)
this.x = x;
this.y = y;
toString()
return ‘(‘ + this.x + ‘, ‘ + this.y + ‘)‘;
等同于
function Point(x, y)
this.x = x;
this.y = y;
Point.prototype.toString = function ()
return ‘(‘ + this.x + ‘, ‘ + this.y + ‘)‘;
;
var p = new Point(1, 2);
其中 constructor 方法是类的构造函数,是一个默认方法,通过 new 命令创建对象实例时,class必须要用new命令创建,不然会报错(TypeError: Class constructor Foo cannot be invoked without ‘new’),自动调用该方法。一个类必须有 constructor 方法,如果没有显式定义,一个默认的 consructor 方法会被默认添加。所以即使你没有添加构造函数,也是会有一个默认的构造函数的。一般 constructor 方法返回实例对象 this ,但是也可以指定 constructor 方法返回一个全新的对象,让返回的实例对象不是该类的实例。
class继承中,子类必须在constructor方法中调用super方法,否则新建实例时会报错。这是因为子类自己的this对象,必须先通过父类的构造函数完成塑造,得到与父类同样的实例属性和方法,然后再对其进行加工,加上子类自己的实例属性和方法。如果不调用super方法,子类就得不到this对象。
super 这个关键字,既可以当做函数使用,也可以当做对象使用。这两种情况下,它的用法完全不用。
1.当作函数使用
class A
class B extends A
constructor()
super(); // ES6 要求,子类的构造函数必须执行一次 super 函数,否则会报错。
注:在 constructor 中必须调用 super 方法,因为子类没有自己的 this 对象,而是继承父类的 this 对象,然后对其进行加工,而 super 就代表了父类的构造函数。super 虽然代表了父类 A 的构造函数,但是返回的是子类 B 的实例,即 super 内部的 this 指的是 B,因此 super() 在这里相当于 ```A.prototype.constructor.call(this, props)``。
class A
constructor()
console.log(new.target.name); // new.target 指向当前正在执行的函数
class B extends A
constructor
super();
new A(); // A
new B(); // B
可以看到,在 super() 执行时,它指向的是 子类 B 的构造函数,而不是父类 A 的构造函数。也就是说,super() 内部的 this 指向的是 B
2.当作对象使用
在普通方法中,指向父类的原型对象;在静态方法中,指向父类。
class A
c()
return 2;
class B extends A
constructor()
super();
console.log(super.c()); // 2
let b = new B();
上面代码中,子类 B 当中的 super.c(),就是将 super 当作一个对象使用。这时,super 在普通方法之中,指向 A.prototype,所以 super.c() 就相当于 A.prototype.c()。
通过 super 调用父类的方法时,super 会绑定子类的 this
class A
constructor
this.x = 1;
s()
console.log(this.x);
class B extends A
constructor
super();
this.x = 2;
m()
super.s();
let b = new B();
b.m(); // 2
上面代码中,super.s() 虽然调用的是 A.prototytpe.s(),但是 A.prototytpe.s()会绑定子类 B 的 this,导致输出的是 2,而不是 1。也就是说,实际上执行的是 super.s.call(this)
由于绑定子类的 this,所以如果通过 super 对某个属性赋值,这时 super 就是 this,赋值的属性会变成子类实例的属性
class A
constructor()
this.x = 1;
class B extends A
constructor()
super();
this.x = 2;
super.x = 3;
console.log(super.x); // undefined
console.log(this.x); // 3
let b = new B();
上面代码中,super.x 赋值为 3,这时等同于对 this.x 赋值为 3。而当读取 super.x 的时候,调用的是 A.prototype.x,但并没有 x 方法,所以返回 undefined。
注意,使用 super 的时候,必须显式指定是作为函数,还是作为对象使用,否则会报错。
class A
class B extends A
constructor()
super();
console.log(super); // 报错
上面代码中,console.log(super); 的当中的 super,无法看出是作为函数使用,还是作为对象使用,所以 JavaScript 引擎解析代码的时候就会报错。这是,如果能清晰的表明 super 的数据类型,就不会报错。
最后,由于对象总是继承其他对象的,所以可以在任意一个对象中,使用 super 关键字
https://blog.csdn.net/a419419/article/details/82772412
56.JS的运行机制?
https://baijiahao.baidu.com/s?id=1615713540466951098&wfr=spider&for=pc
57.函数的防抖和节流
https://www.jianshu.com/p/c8b86b09daf0
58.javascript判断一个字符串或者数组里面出现最多的元素及其出现的次数
字符串:
var str = ‘aabbccccdddd‘;
var obj = ;
for (var i = 0; i < str.length; i++)
if (!obj[str.charAt(i)])
obj[str.charAt(i)] = 1;
else
obj[str.charAt(i)]++;
console.log(obj);
var maxVal = 0;
var maxStr = ‘‘;
for (var j in obj)
if (obj[j] > maxVal)
maxVal = obj[j];
maxStr = j;
console.log(‘最多的是:‘ + maxStr + ‘,出现了:‘ + maxVal + ‘次‘);
数组:
var arr = [1, 2, 3, 4, 1, 2, 2, 2, 2, 4];
var obj = ;
for (var i = 0; i < arr.length; i++)
if (!obj[arr[i]])
obj[arr[i]] = 1;
else
obj[arr[i]]++;
var maxVal = 0;
var maxStr = ‘‘;
for (var j in obj)
if (obj[j] > maxVal)
maxVal = obj[j];
maxStr = j;
console.log(‘最多的是:‘ + maxStr + ‘,出现了:‘ + maxVal + ‘次‘);
https://blog.csdn.net/m0_37273490/article/details/80712466
59.获取数组最大值?
var arr = [1,4,2,0,-2,5];
var max = Math.max.apply(null, arr);
var max = Math.max(...arr);
var max = arr.sort().reverse()[0];
var max = arr.sort(function (a, b)
return b - a;
)[0];
60.HTTP协议
当我们在浏览器地址栏上输入要访问的URL后,浏览器会分析出URL上面的域名,然后通过DNS服务器查询出域名映射的IP地址,浏览器根据查询到的IP地址与Web服务器进行通信,而通信的协议就是HTTP协议。
https://blog.csdn.net/aliujiujiang/article/details/81088317
61.DNS服务
通常我们访问一个网站,使用的是主机名或者域名来进行访问的。因为相对于IP地址(一组纯数字),域名更容易让人记住。但TCP/IP协议使用的是IP地址进行访问的,所以必须有个机制或服务把域名转换成IP地址。DNS服务就是用来解决这个问题的,它提供域名到IP地址之间的解析服务。
62.HTTP与TCP/IP、DNS的关系
HTTP协议与它们之间的关系:
HTTP与TCP/IP、DNS的关系
当客户端访问Web站点时,首先会通过DNS服务查询到域名的IP地址。然后浏览器生成HTTP请求,并通过TCP/IP协议发送给Web服务器。Web服务器接收到请求后会根据请求生成响应内容,并通过TCP/IP协议返回给客户端。
63.箭头函数
引入箭头函数有两个方面的作用:更简短的函数并且不绑定this
。
箭头函数表达式的语法比函数表达式更简洁,并且没有自己的this,arguments,super或 new.target。
这些函数表达式更适用于那些本来需要匿名函数的地方,并且它们不能用作构造函数。
箭头函数不会创建自己的this,它只会从自己的作用域链的上一层继承this
。
箭头函数没有prototype属性。
由于 箭头函数没有自己的this指针,通过 call()
或 apply()
方法调用一个函数时,只能传递参数(不能绑定this),他们的第一个参数会被忽略。(这种现象对于bind方法同样成立)
不断完善...