总结54个JavaScript基础知识
1.如何理解this?
this表示当前对象,this的指向是根据调用的上下文来决定的,默认指向window对象。
- 在全局环境作用域下直接调用函数,this指向window。
- 在对象函数调用,哪个对象调用就指向哪个对象。
- 使用new实例化对象,在构造函数中的this指向实例化对象。
- 使用bind,apply,call可以改变this指向。bind接收一个参数,这个参数用来设置this的指向,但是函数不会立刻执行。apply和call除了改变this指向,后面可以接收参数作为函数行参,并且函数会立刻执行。apply和call的区别是apply第一个参数用来定义this指向,第二个参数接收一个数组作为函数的行参,而call第一个参数定义this指向,后面的所有参数都是函数行参。
- 在箭头函数中this的指向,箭头函数没有自己的this,他的this是继承而来,默认指向在定义它时所处的对象(宿主对象)而不是执行时的对象。定义它的时候,可能环境时window,箭头函数可以让我们很方便的在setTimeout, setInterval中使用this。箭头函数中,this指向的固定化,并不是因为箭头韩式内部有this绑定机制,实际原因时箭头函数没有自己的this,导致内部的this就是外层代码块的this。
2.严格模式与非严格模式下this指向?
- 非严格模式自执行函数中的this永远都是window(严格模式下指向是undefined)
- 方法执行非严格模式this指向window(严格模式下指向undefind)
- 非严格模式bind,call,apply的第一个参数传null,undifined也会指向window(严格模式指向什么就是什么)
3. 什么是严格模式?
ES5的严格模式是采用具有限制性的Javascript变体的一种方式,从而使代码显式地脱离“马虎模式,懒散模式,稀松模式”,
严格模式对正常的Javascript语义做了一些修改。
- 严格模式通过抛出错误来消除了一些原有的静默错误。
- 严格模式修复了一些导致Javascript引擎难以执行优化的缺陷,有时候严格模式比非严格模式代码执行更快。
- 严格模式禁用了ECMAScript的未来版本中可能会定义的语法。
调用严格模式:
严格模式可以应用到整个脚本或个别函数中。不能在{}中这样做,在这样的上下文中这么做会无效。
为整个脚本开启严格模式 ,需要在所有语句之前放一个特定语句“use strict”
// 脚本中的严格模式
"use strict";
var a = 'this is a strict mode script';
要给某个函数开启严格模式,得把 "use strict"; (或 'use strict'; )声明一字不漏地放在函数体所有语句之前。
// 函数中的严格模式
function strict(){
"use strict";
console.log("this is a strict mode script")
}
4.JavaScript中的作用域(scope)是指什么?
在JavaScript中,每个函数都有自己的作用域。只有函数内部才能访问函数作用域内部的申明的变量,如果在函数内部使用没有申明的变量会产生隐式全局变量。同一个作用域的变量名必须是唯一的,一个作用域可以嵌套在另一个作用域内,如果当前作用域内没找到变量,则会向外层查找,如果在最外层也没有找到就会隐式创建全局变量。
5.什么是闭包?
在javascript中,只有函数内部的子函数才能读取局部变量,所以闭包可以理解成“定义在一个函数内部的函数“。在本质上,闭包是将函数内部和函数外部连接起来的桥梁。
6. 如何理解事件委托?
利用事件冒泡机制处理指定一个事件处理程序,来管理某一类型的所有事件。
页面中一个li被点击,相当于其父元素ul,ul的父元素body,body的父元素都被点击。
既然如此,与其给100个li绑定100个事件,还不如给一个ul绑定一个事件,通过event.target来获取具体被点击的元素。
这就是事件委托。
7.解释JavaScript中的null和undefined
null代表空值,undefined代表尚未初始化。
8.JavaScript中的值 和类型
值类型:
- number
- string
- boolean
- null
- undefined
- symbol
引用类型:
- array
- object
- functon
9.解释事件冒泡以及如何阻止
事件冒泡就是嵌套在深层的元素触发一个事件,然后这个事件会顺着嵌套顺序在父元素上跟着触发。防止事件冒泡的方法是:Event.cancel.Bubble 或Event.stopPropagation
10. window.onload的作用
window.onload 是等文档和资源都加载完成后调用的事件,js获取元素的时候,已经加载。
11.说说你对作用域链的理解
作用域链是js中的一种查找机制,从当前作用域查找,当前作用域没有往上一级作用域查找,一直到最外层,如果都找不到则是is not define。
12.冒泡排序算法?
var nums = [223, 432432, 324, 34, 2, 3, 2432, 4, 23424, -3, 43, 3]
for(var i = 0, l = nums.length; i < l; i++){
for(var k = 0, g = nums.length; k < g; k++){
if(nums[k] > nums[k+1]){
var temp = nums[k]
nums[k] = nums[k +1]
nums[k+1] = temp
}
}
}
13.DOM怎么添加,移除,移动,复制,创建和查找节点?
- 获取子节点所有元素 (父节点.children)
<ol>
<li></li>
<li></li>
<li></li>
<li></li>
<li></li>
</ol>
console.log(document.querySelector('ol').children)
- 获取父节点 (子节点.parentNode)
<div>
<span></span>
</div>
console.log(document.querySelector('span').parentNode)
- 创建节点 document.createElement('标签名')
document.createElement('div)
document.TextNode('文本内容')
- 添加节点 (父节点.appendChild(子节点))
var div = document.createElement('div')
box.appendChild(div)
- 复制 (被复制的节点.cloneNode(true))
div.cloneNode(true)
- 删除 (节点.remove() , 父节点.removeChild(子节点))
div.remove()
parentNode.removeChild(div)
- 替换
父节点.replaceChild(newChild, refChild)
- 查找
document.querySelector('#root')
document.querySelectorAll('li')
14.什么是window对象,什么是document对象
window对象是js最大的对象,表示窗口包含document对象。
document是文档对象,,表示html
15.offsetWidth,clientWidth,scrollTop的区别
- offsetWidth:占位宽,包含内容宽度+ 左右padding+ 左右border
- clientWidth:可视宽度,包含内容+ 左右padding
- scrollTop: 页面被卷去的高
16.如何获取url地址中搜索内容?
window.location.search
17.cookie的利弊
优点:
- 通过良好的编程,控制保存在cookie中的session对象的大小。
- 通过加密和安全传输技术(SSL),减少cookie被破解的可能性
- 只在cookie中存放不敏感数据,即使被盗也不会有重大损失。
- 控制cookie的生命周期,使之不回永远有效,偷盗者很可能拿到一个过期的cookie
缺点:
- cookie数量和长度的限制,每个domain最多只能有20条cookie,每个cookie长度不能超过4K,否则会被截掉。
- 安全性问题,如果cookie被人拦截了,那人就可以去的所有的session信息,技师加密也与事无关,因为拦截者并不需要知道cookie的意义,他只要原样转发cookie就可以达到目的了。
- 有些状态不可能保存在客户端,例如,为了防止重复提交表单,我们需要在服务器端保存一个计数器,如果我们把这个计数器保存在客户端,那么他就起不到作用。cookie需要指定作用域,不可以跨域使用。
18 说说创建Ajax的过程
// 1.创建对象
const ajax = new XMLHttpRequest()
// 2.建立链接
ajax.open('GET', 'http://status.164.red/200', true)
// 3.发送请求
ajax.send()
// ajax.send(json.data) // post请求时
// 4.监听结果
ajax.onreadystatechange = function(){
if(ajax.readyState === 4 || ajax.readyState === 200){
console.log(ajax.response)
}
}
19.常见的http状态码有哪些?
- 200:表示请求已成功,请求所希望响应头或数据体将随此响应返回。
- 201: 表示请求成功并且服务器创建了新的资源,且其URI已经随Location头信息返回。假如需要的资源无法技师建立的话,应当返回202 Accepted。
- 301: 被请求的资源已永久移到新位置。服务器返回次响应(对GET或HEAD请求的响应)时,会自动将请求者转到新位置。
- 302: 请求的资源临时从不同的URI响应请求,但请求者应继续使用原有位置来进行以后的请求。
- 304: 自从上次请求后,请求的网页未修改过。服务器返回此响应时,不回返回网页内容,如果网页自请求上次请求后再也没有更改过。你应将服务器配置为返回此响应(称为If-Modified-Since HTTP标头)。
- 401: 请求要求验证身份,对于需要登陆的网页,服务器可能返回此响应。
- 403: 服务器已经理解请求,但是拒绝执行它。与401响应不同的是,身份验证并不能任何帮助,而且这个请求也不应该被重复提交。
- 404: 请求失败,请求所希望的得到的资源未被在服务器上发现。没有信息能够告诉用户这个状态到底是暂时的还是永久的。假如服务器知道情况的话,应当使用410状态码来告诉旧资源因为某些内部的配置机制问题,已经永久不可用,而且没有任何可以跳转的地址。404这个状态码被广泛应用于服务器不像揭示到底为何请求被拒绝或者没有其他适合的响应可用的情况下。
- 500: 服务器遇到了一个未曾预料到的状况,导致了它无法完成对请求的处理,一般来说,这个问题都会在服务器的程序码出错是出现。
- 503: 由于临时的服务器维护或者过载,服务当前无法处理请求。通常,这个是暂时状态,一段时间会恢复。
20.js哪些操作会造成内存泄漏
- 意外全局变量引起的内存泄漏 function leak(){ leak = 'xxx' } // leak称为一个全局变量,不会被回收。
- 被遗忘的定时器或者回掉。
- 闭包英气的内存泄漏。
21.判断一个值是什么类型有哪些方法?
- typeof 运算符 (不能判断array null object)
- instanceof
- Object.prototype.toString.call(arr) (判断是否为数组)
- Array.prototype.toString.call(arr)
console.log(Object.prototype.toString.call(123)); //[object Number]
console.log(Object.prototype.toString.call('123')); //[object String]
console.log(Object.prototype.toString.call(undefined)); //[object Undefined]
console.log(Object.prototype.toString.call(true)); //[object Boolean]
console.log(Object.prototype.toString.call({})); //[object Object]
console.log(Object.prototype.toString.call([])); //[object Array]
console.log(Object.prototype.toString.call(function(){})); //[object Function]
console.log(Object.prototype.toString.call(null)); //[[object Null]]
22.怎么判断一个变量是否为数组?
- arr instanceof Array
var arr = [1, 3, 5, 7, 10]
console.log( arr instanceof Array)
- arr.constructor == Array
- Object.protype.toString.call(arr) == '[Object Array]'
23.“ ===”、“ ==”的区别?
- ==,当且仅当两个运算数相等时,它返回 true,即不检查数据类型
- ===,只有在无需类型转换运算数就相等的情况下,才返回 true,需要检查数据类型
24."eval" 是做什么的?
- 它的功能是把对应的字符串解析成 JS 代码并运行;
- 应该避免使用 eval,不安全,非常耗性能(2次,一次解析成 js 语句,一次执行)。
25.箭头函数有哪些特点?
- 不需要function关键字来创建函数
- 单行可以省略return关键字
- 改变this指向
26.var、let、const 区别?
- var 存在变量提升
- let 只能在块级作用域内使用
- const 用来定义常量,必须初始化,不能修改(对象特殊)
27.new 操作符到底干了什么?
- 创建一个空对象,并且this变量引用该对象,同时还继承了该函数的原型。
- 属性和方法加入到this应用的对象中
- 新创建的对象由this所引用,并且最后隐式的返回this
28. document.write 与 innerHTML 的区别是什么?
- document.write只能重绘整个页面
- innerHTML 可以重绘页面的一部分
29.请解释下Javascript的同源策略?
同源策略指的是端口、域名、协议相同,同源策略是一种安全协议。
30.JavaScript的内存垃圾回收机制
- 垃圾回收器会每隔一段时间找出哪些不再使用的内存,然后为其释放内存。
- 一般使用标记清除法(mark and sweep),当变量进入环境标记为进入环境,离开环境标记为离开环境
- 垃圾回收器会在运行时给存储在内存中的所有变量添加标记,然后去掉环境中变量以及被环境中变量所引用的变量(闭包),在这些完成之后仍存在标记的就是要删除的变量了
- 还有引用计数器方法(reference counting),在低版本IE中经常会出现内存泄漏,很多时候就是因为其采用引用计数器进行垃圾回收。引用计数器的策略是跟踪记录每个值被使用的次数,当申明了一个变量并将一个引用类型赋值给该变量的时候,这个值的引用次数就加1,如果该变量的值变成了另外一个则这个值的引用次数减1,当这个值的引用次数变为0的时候,说明没有变量在使用,这个值没法被访问了,因此可以将其占用的空间回收。这样垃圾回收器会在运行的时候清理掉引用次数为0的值占用的空间。
- 在IE中虽然JavaScript对象通过标记清除的方式进行回收,但是BOM和DOM对象却是通过引用计数器回收垃圾的,也就是说只要涉及BOM及DOM就会出现循环引用的问题。
31.Javascript原型、原型链有什么特点?
- 每个对象都会在其内部初始化一个属性,就是prototype(原型),当我们访问一个对象的属性时,如果这个对象内部不存在这个属性,那么他就会去prototype里找这个属性,这个prototype又会有自己的prototype,于是就这样找下去,这就是原型链的概念。
- JavaScript是通过引用来传递的,我们新创建的对象实体中并没有一份自己的原型副本,当我们修改原型时,与之相关的对象也会继承这一改变。
32.对MVVM的理解
- MVVM分为: Model、View、ViewModel 三者。
- Model代表数据模型,数据和业务逻辑都在Model层定义。
- View代表UI视图,负责数据展示。
- ViewModel负责监听Model中数据的变化并控制数据的更新,处理用户交互操作。
- Model和View并无直接关联,而是通过ViewModel来联系的,Model和ViewModel存在数据双向绑定的联系。因此Model中数据改变会出发View层的刷新,View中由于用户交互操作而改变的数据也会在Model中同步。
- 这种模式实现了Model和View的数据自动同步,因此开发者只需要专注对数据的维护即可,而不需要自己操作DOM。
33.var与let、const的区别
他们的作用都是用来申明变量的, let和const是ES6的规范。
区别如下:
- let和const不存在变量提升。
- 同一个作用域下,不能重复定义同一个名称。
- 有严格的作用域。var属于函数作用域,let属于块级作用域。
- const 申明的是只读的常量,一旦申明无法改变。(数组和对象属于复杂引用类型,因此可以改变)。
- const 在申明的时候必须初始化,不能只申明不赋值。
34. Set 与 Map
-
Set类似于数组,成员是唯一的。是一种叫集合的数据结构。
- 集合是一组无序且唯一(即不能重复)的项组成,可以想象成集合是一个既没有重复元素也没有顺序概念的数组。
- Set和Map的主要应用场景在于数组去重和数据存储
- ES6提供了新的数据结构Set,Set本身是一个构造函数,用来生成Set数据结构。
- Set的属性和方法:
var a = new Set() a.add('hello') // Set { 'hello' } // 去除重复 var arr = [1,1,2,3,4,6,5,2,4,0,4,2] var arr2 = [... new Set(arr)] // [1, 2, 3, 4,6, 5, 0]
- size属性:返回集合所包含元素的数量。
- add(value)方法:向集合添加新的一项。add方法支持链式操作,s.add(1).add(2).add(3)。
- delete(value)方法:向集合移除一个值。
- has(value)方法:如果值在一个集合中存在,返回true,返回false。
- clear()方法:移除集合的所有项。
- keys():返回一个包含集合所有键的数组。
- values():返回一个包含集合所有值的数组。
- entries():返回一个集合中所有键值对的数组。
- forEach():遍历用于对集合成员执行某种操作,没有返回值。
// has方法的实现 function Set() { let items = {}; this.size = 0; // has(val)方法 this.has = function(val) { // 对象都有hasOwnProperty方法,判断是否拥有特定属性 return items.hasOwnProperty(val); }; } // add方法的实现 this.add = function(val) { let items = {}; this.size = 0; if (!this.has(val)) { items[val] = val; this.size++; // 累加集合成员数量 return true; } return false; }; // delete(val)方法的实现 this.delete = function(val) { if (this.has(val)) { delete items[val]; // 将items对象上的属性删掉 this.size--; return true; } return false; }; // clear方法的实现 this.clear = function() { items = {}; // 直接将集合赋一个空对象即可 this.size = 0; }; // keys()方法的实现 this.keys = function() { return Object.keys(items); // 返回遍历集合的所有键名的数组 }; // values()方法的实现 this.values = function() { return Object.values(items); // 返回遍历集合的所有键值的数组 }; // forEach(fn, context)方法 this.forEach = function(fn, context = this) { for (let i = 0; i < this.size; i++) { let item = Object.keys(items)[i]; fn.call(context, item, item, items); } };
-
Map类似于对象,以键值对的形式存在。是一种叫字典的数据结构(key是有序的)。
-
Map的属性和方法:
- size:返回字典所包含的元素的个数。
- set(key,value):向字典添加元素。
- get(key):通过健值查找特定的数值并返回。
- has(key):如果键存在字典中返回true,否则false。
- delete(key):通过键值从字典中移除对应的数据。
- clear():这个字典中的所有元素删除。
- keys():将字典中包含的所有键名以数组形式返回。
- values():将字典中包含的所有数值以数组形式返回。
- forEach():遍历字典的所有成员。
35.堆栈的概念
36.EventLoop事件循环机制
37.比较排序的时间复杂度,为什么是1*1ng
38.讲一个Generator的具体实现场景?
39. URL请求原理
40.栈和队列的区别
41.两个栈实现一个队列
42.讲解一下diff算法,它是深度优先遍历还是广度优先遍历
43.浏览器缓存、操作系统缓存、路由器缓存、ISP缓存
44.git的具体特点,pull和fetch的区别。git于svn的区别
45. JavaScript与Java的区别
46.讲一下预渲染
47.浏览器引擎,V8是基于什么写的?
48.跨域的原因及方法
49.http、http1.0、http1.1、http2.0
50.JavaScript如何实现继承?
- 借用构造函数继承
- 原型链+构造函数组合继承
- 寄生式继承
- 原型链继承
51.数组的方法及操作
map: 遍历数组,返回回调返回值组成的新数组
forEach: 无法break,可以用try/catch中throw new Error来停止
filter: 过滤
some: 有一项返回true,则整体为true
every: 有一项返回false,则整体为false
join: 通过指定连接符生成字符串
push / pop: 末尾推入和弹出,改变原数组, 返回推入/弹出项
unshift / shift: 头部推入和弹出,改变原数组,返回操作项
sort(fn) / reverse: 排序与反转,改变原数组
concat: 连接数组,不影响原数组, 浅拷贝
slice(start, end): 返回截断后的新数组,不改变原数组
splice(start, number, value...): 返回删除元素组成的数组,value 为插入项,改变原数组
indexOf / lastIndexOf(value, fromIndex): 查找数组项,返回对应的下标
reduce / reduceRight(fn(prev, cur), defaultPrev): 两两执行,prev 为上次化简函数的return值,cur 为当前值(从第二项开始)
52. JavaScript有哪些内置对象?
Object是JavaScript中所有对象的父对象
数据封装对象:Object、Array、Boolean、Number和String
其他对象:Function、Arguments、Math、Date、RegExp、Error
53.组件化和模块化
为什么要组件化?
有时候页面代码量太大,逻辑太多或者同一个功能组件在许多页面均有使用,维护起来相当复杂,这个时候,就需要组件化开发来进行功能拆分、组件封装,已达到组件通用性,增强代码可读性,维护成本也能大大降低。
组件化开发的优点
很大程度上降低系统各个功能的耦合性,并且提高了功能内部的聚合性。这对前端工程化及降低代码的维护来说,是有很大的好处的,耦合性的降低,提高了系统的伸展性,降低了开发的复杂度,提升开发效率,降低开发成本。
组件化开发的原则
- 专一
- 可配置性
- 标准性
- 复用性
- 可维护性
模块化的好处
- 避免变量污染,命名冲突
- 提高代码复用率
- 提高了可维护性
- 方便依赖关系管理
模块化的几种方法
- 函数封装
var myModule = {
var1: 1,
var2: 2,
fn1: function(){
},
fn2: function(){
}
}
复制代码
总结:这样避免了变量污染,只要保证模块名唯一即可,同时同一模块内的成员也有了关系
缺陷:外部可以随意修改内部成员,这样就会产生意外的安全问题
复制代码
- 立即执行函数表达式(IIFE)
var myModule = (function(){
var var1 = 1;
var var2 = 2;
function fn1(){
}
function fn2(){
}
return {
fn1: fn1,
fn2: fn2
};
})();
复制代码
总结:这样在模块外部无法修改我们没有暴露出来的变量、函数
缺点:功能相对较弱,封装过程增加了工作量,仍会导致命名空间污染可能、闭包是有成本的
54、mouseover和mouseenter的区别
mouseover:当鼠标移入元素或其子元素都会触发事件,所以有一个重复触发,冒泡的过程。对应的移除事件是mouseout
mouseenter:当鼠标移除元素本身(不包含元素的子元素)会触发事件,也就是不会冒泡,对应的移除事件是mouseleave