2.DOM相关
1.target与currentTarget区别
target在事件流的目标阶段;currentTarget在事件流的捕获,目标及冒泡阶段。只有当事件流处在目标阶段的时候,两个的指向才是一样的, 而当处于捕获和冒泡阶段的时候,target指向被单击的对象而currentTarget指向当前事件活动的对象(一般为父级)。
2. DOM的增删改查操作?
(1)创建新节点
createDocumentFragment() //创建一个DOM片段
createElement() //创建一个具体的元素
createTextNode() //创建一个文本节点
(2)添加、移除、替换、插入
appendChild()
removeChild()
replaceChild()
insertBefore() //在已有的子节点前插入一个新的子节点
(3)查找
getElementsByTagName() //通过标签名称
getElementsByName() //通过元素的Name属性的值(IE容错能力较强,会得到一个数组,其中包括id等于name值的)
getElementById() //通过元素Id,唯一性
请描述DOM事件流的过程?
DOM事件流包括三个阶段:事件捕获阶段、处于目标阶段、事件冒泡阶段。首先发生的事件捕获,为截获事件提供机会。然后是实际的目标接受事件。最后一个阶段是时间冒泡阶段,可以在这个阶段对事件做出响应。
事件委托的原理,作用,和触发事件的对象是谁?
因为事件具有冒泡机制,因此我们可以利用冒泡的原理,把事件加到父级上,触发执行效果。这样做的好处当然就是提高性能了
最重要的是通过event.target判断触发事件的对象是谁
5.documen.write和 innerHTML的区别
document.write只能重绘整个页面
innerHTML可以重绘页面的一部分
3.BOM相关
4.JS高级
1.函数高级
1.闭包
特性: 1.函数嵌套函数 2.函数内部可以引用外部的参数和变量 3.参数和变量不会被垃 圾回收机制回收
闭包的缺点就是常驻内存,会增大内存使用量,使用不当很容易造成内存泄露。
为什么要使用闭包:
为了设计私有方法和变量,避免全局变量污染 希望一个变量长期驻扎在内存中
view detail: https://segmentfault.com/a/1190000000652891
2.原型与原型链
当从一个对象那里调取属性或方法时,如果该对象自身不存在这样的属性或方法,就会去自己关联的prototype对象那里寻找,如果prototype没有,就会去prototype关联的前辈prototype那里寻找,如果再没有则继续查找Prototype.Prototype引用的对象,依次类推,直到Prototype.….Prototype为undefined(Object的Prototype就是undefined)从而形成了所谓的“原型链”。
其中foo是Function对象的实例。而Function的原型对象同时又是Object的实例。这样就构成了一条原型链。
instanceof 确定原型和实例之间的关系
用来判断某个构造函数的prototype属性是否存在另外一个要检测对象的原型链上
对象的__proto__指向自己构造函数的prototype。obj.__proto__.__proto__…的原型链由此产生,包括我们的操作符instanceof正是通过探测obj.__proto__.__proto__… === Constructor.prototype来验证obj是否是Constructor的实例。
function C(){}
var o = new C(){}
//true 因为Object.getPrototypeOf(o) === C.prototype
o instanceof C
instanceof只能用来判断对象和函数,不能用来判断字符串和数字
isPrototypeOf
用于测试一个对象是否存在于另一个对象的原型链上。
判断父级对象 可检查整个原型链
3.作用域与作用域链
作用域链的作用是保证执行环境里有权访问的变量和函数是有序的,作用域链的变量只能向上访问,变量访问到window对象即被终止,作用域链向下访问变量是不被允许的。
全局函数无法查看局部函数的内部细节,但局部函数可以查看其上层的函数细节,直至全局细节。
当需要从局部函数查找某一属性或方法时,如果当前作用域没有找到,就会上溯到上层作用域查找,
直至全局函数,这种组织形式就是作用域链。
4.apply, call和bind有什么区别?
参考答案:三者都可以把一个函数应用到其他对象上,call、apply是修改函数的作用域(修改this指向),并且立即执行,而bind是返回了一个新的函数,不是立即执行.apply和call的区别是apply接受数组作为参数,而call是接受逗号分隔的无限多个参数列表,
Array.prototype.slice.call(null, args)
function getMax(arr){
return Math.max.apply(null, arr);
}
//call
function foo() {
console.log(this);//{id: 42}
}
foo.call({ id: 42 });
如果该方法是非严格模式代码中的函数,则null和undefined将替换为全局对象,并且原始值将被包装。 当你调用apply传递给它null时,就像是调用函数而不提供任何对象
5.谈谈对this的理解
this总是指向函数的直接调用者(而非间接调用者);
如果有new关键字,this指向new出来的那个对象;
在事件中,this指向触发这个事件的对象,特殊的是,IE中的attachEvent中的this总是指向全局对象Window;
6.那些操作会造成内存泄漏?
内存泄漏指任何对象在您不再拥有或需要它之后仍然存在。
垃圾回收器定期扫描对象,并计算引用了每个对象的其他对象的数量。如果一个对象的引用数量为 0(没有其他对象引用过该对象),或对该对象的惟一引用是循环的,那么该对象的内存即可回收。
setTimeout 的第一个参数使用字符串而非函数的话,会引发内存泄漏。
闭包、控制台日志、循环(在两个对象彼此引用且彼此保留时,就会产生一个循环)
7.深入贯彻闭包思想,全面理解JS闭包形成过程
https://segmentfault.com/a/1190000009886713
8.下面这个ul,如何点击每一列的时候alert其index?(闭包)
<ul id=”test”>
<li>这是第一条</li>
<li>这是第二条</li>
<li>这是第三条</li>
</ul>
// 方法一:
var lis=document.getElementById(‘2223’).getElementsByTagName(‘li’);
for(var i=0;i<3;i++)
{
lis[i].index=i;
lis[i].οnclick=function(){
alert(this.index);
};
}
//方法二:
var lis=document.getElementById(‘2223’).getElementsByTagName(‘li’);
for(var i=0;i<3;i++){
lis[i].index=i;
lis[i].οnclick=(function(a){
return function() {
alert(a);
}
})(i);
}
9 函数节流和函数防抖
前言: 函数节流和函数防抖都是优化高频率执行js代码的一种手段
9.1 函数节流
定义:是指一定时间内js方法只跑一次。比如人的眨眼睛,就是一定时间内眨一次。这是函数节流最形象的解释。
应用场景
在监听页面元素滚动事件的时候会用到。因为滚动事件,是一个高频触发的事件。
代码实例:
// 函数节流
var canRun = true;
document.getElementById(“target”).onscroll = function(){
if(!canRun){
// 判断是否已空闲,如果在执行中,则直接return
return;
}
canRun = false;
setTimeout(function(){
console.log(“函数节流”);
canRun = true;
}, 300);
};
9.2 函数防抖
定义:是指频繁触发的情况下,只有足够的空闲时间,才执行代码一次。比如生活中的坐公交,就是一定时间内,如果有人陆续刷卡上车,司机就不会开车。只有别人没刷卡了,司机才开车。
应用场景
最常见的就是用户注册时候的手机号码验证和邮箱验证了。只有等用户输入完毕后,前端才需要检查格式是否正确,如果不正确,再弹出提示语。
代码示例:
// 函数防抖
var timer = false;
document.getElementById(“debounce”).onscroll = function(){
clearTimeout(timer); // 清除未执行的代码,重置回初始化状态
timer = setTimeout(function(){
console.log(“函数防抖”);
}, 300);
};
2.对象高级
1.js继承方式及其优缺点
原型链继承的缺点
一 是字面量重写原型会中断关系,使用引用类型的原型,并且子类型还无法给超类型传递参数。
借用构造函数(类式继承)
借用构造函数虽然解决了刚才两种问题,但没有原型,则复用无从谈起。所以我们需要原型链+借用构造函数的模式,这种模式称为组合继承
组合式继承
组合式继承是比较常用的一种继承方法,其背后的思路是 使用原型链实现对原型属性和方法的继承,而通过借用构造函数来实现对实例属性的继承。这样,既通过在原型上定义方法实现了函数复用,又保证每个实例都有它自己的属性。
2.上下文环境对象
3.new操作符具体做了什么
1、创建一个空对象,并且this变量引用该对象,同时继承了该函数的原型(实例对象通过__proto__属性指向原型对象;obj.__proto__ = Base.prototype;) 2、属性和方法被加入到 this 引用的对象中。
function Animal(name) {
this.name = name;
}
Animal.prototype.run = function() {
console.log(this.name + ‘can run…’);
}
var cat = new Animal(‘cat’);
//模拟过程
new Animal(‘cat’)=function(){
let obj={}; //创建一个空对象
obj.__proto__=Animal.prototype;
//把该对象的原型指向构造函数的原型对象,就建立起原型了:obj->Animal.prototype->Object.prototype->null
return Animal.call(obj,’cat’);// 绑定this到实例化的对象上
}
4.创建对象的几种方式
javascript创建对象简单的说,无非就是使用内置对象或各种自定义对象,当然还可以用JSON;但写法有很多种,也能混合使用。
1、对象字面量的方式
person={firstname:”Mark”,lastname:”Yun”,age:25,eyecolor:”black”};
2、用function来模拟无参的构造函数
function Person(){}
var person=new Person();//定义一个function,如果使用new”实例化”,该function可以看作是一个Class
person.name=”Mark”;
person.age=”25″;
person.work=function(){
alert(person.name+” hello…”);
}
person.work();
3、用function来模拟参构造函数来实现(用this关键字定义构造的上下文属性)
function Pet(name,age,hobby){
this.name=name;//this作用域:当前对象
this.age=age;
this.hobby=hobby;
this.eat=function(){
alert(“我叫”+this.name+”,我喜欢”+this.hobby+”,是个程序员”);
}
}
var maidou =new Pet(“麦兜”,25,”coding”);//实例化、创建对象
maidou.eat();//调用eat方法
4、用工厂方式来创建(内置对象)
var wcDog =new Object();
wcDog.name=”旺财”;
wcDog.age=3;
wcDog.work=function(){
alert(“我是”+wcDog.name+”,汪汪汪……”);
}
wcDog.work();
5、用原型方式来创建
function Dog(){
}
Dog.prototype.name=”旺财”;
Dog.prototype.eat=function(){
alert(this.name+”是个吃货”);
}
var wangcai =new Dog();
wangcai.eat();
5、用混合方式来创建
function Car(name,price){
this.name=name;
this.price=price;
}
Car.prototype.sell=function(){
alert(“我是”+this.name+”,我现在卖”+this.price+”万元”);
}
var camry =new Car(“凯美瑞”,27);
camry.sell();
3.线程机制
1.同步和异步的区别?
同步:阻塞的
-张三叫李四去吃饭,李四一直忙得不停,张三一直等着,直到李四忙完两个人一块去吃饭
=浏览器向服务器请求数据,服务器比较忙,浏览器一直等着(页面白屏),直到服务器返回数据,浏览器才能显示页面
异步:非阻塞的
-张三叫李四去吃饭,李四在忙,张三说了一声然后自己就去吃饭了,李四忙完后自己去吃
=浏览器向服务器请求数据,服务器比较忙,浏览器可以自如的干原来的事情(显示页面),服务器返回数据的时候通知浏览器一声,浏览器把返回的数据再渲染到页面,局部更新
5.ES6相关
1.谈一谈let与var和const的区别?
let为ES6新添加申明变量的命令,它类似于var,但是有以下不同:
let命令不存在变量提升,如果在let前使用,会导致报错
暂时性死区的本质,其实还是块级作用域必须“先声明后使用”的性质。
let,const和class声明的全局变量不是全局对象的属性。
const声明的变量与let声明的变量类似,它们的不同之处在于,const声明的变量只可以在声明时赋值,不可随意修改,否则会导致SyntaxError(语法错误)。
const只是保证变量名指向的地址不变,并不保证该地址的数据不变。const可以在多个模块间共享 let 暂时性死区的原因:var 会变量提升,let 不会。
2.箭头函数
箭头函数不属于普通的 function,所以没有独立的上下文。箭头函数体内的this对象,就是定义时所在的对象,而不是使用时所在的对象。 由于箭头函数没有自己的this,函数对象中的call、apply、bind三个方法,无法”覆盖”箭头函数中的this值。 箭头函数没有原本(传统)的函数有的隐藏arguments对象。 箭头函数不能当作generators使用,使用yield会产生错误。
在以下场景中不要使用箭头函数去定义:
1. 定义对象方法、定义原型方法、定义构造函数、定义事件回调函数。
2. 箭头函数里不但没有 this,也没有 arguments, super ……
3. Map和Set
Symbol,Map和Set
Map 对象保存键值对。一个对象的键只能是字符串或者 Symbols,但一个 Map 的键可以是任意值。 Set 对象允许你存储任何类型的唯一值,Set对象是值的集合,Set中的元素只会出现一次 Symbol 是一种特殊的、不可变的数据类型,可以作为对象属性的标识符使用(Symbol([description]) )
let mySet = new Set()
mySet.add(1)
mySet.add(‘hello’)
mySet.add(‘hello’)
console.log(mySet.size);//2
console.log(mySet);//Set {1,’hello’}
//Map保存键值对也不能有重复的
let myMap = new Map();
let key1 = ‘China’,key2 = ‘America’;
myMap.set(key1,’welcome’)
myMap.set(key2,’gold bless you’)
console.log(myMap);//Map { ‘China’ => ‘welcome’, ‘America’ => ‘gold bless you’ }
console.log(myMap.get(key1));//welcome
console.log(myMap.get(key2));//gold bless you
let mySymbol = Symbol(‘symbol1’);
let mySymbol2 = Symbol(‘symbol1’);
console.log(mySymbol == mySymbol2);//false
//Symbols 在 for…in 迭代中不可枚举。
let obj = {}
obj[‘c’] = ‘c’
obj.d =’d’
obj[Symbol(‘a’)] = ‘a’
obj[Symbol.for(‘b’)] = ‘b’
for(let k in obj){
console.log(k);//logs ‘c’ and ‘d’
}
for…of可以用来遍历数组,类数组对象,argument,字符串,Map和Set,for…in用来遍历对象
4.Promise实现原理
现在回顾下Promise的实现过程,其主要使用了设计模式中的观察者模式:
通过Promise.prototype.then和Promise.prototype.catch方法将观察者方法注册到被观察者Promise对象中,同时返回一个新的Promise对象,以便可以链式调用。
被观察者管理内部pending、fulfilled和rejected的状态转变,同时通过构造函数中传递的resolve和reject方法以主动触发状态转变和通知观察者。
Promise.then()是异步调用的,这也是Promise设计上规定的,其原因在于同步调用和异步调用同时存在会导致混乱。
为了暂停当前的 promise,或者要它等待另一个 promise 完成,只需要简单地在 then() 函数中返回另一个 promise。
Promise 也有一些缺点。首先,无法取消 Promise,一旦新建它就会立即执行,无法中途取消。其次,如果不设置回调函数,Promise 内部抛出的错误,不会反应到外部。第三,当处于 Pending 状态时,无法得知目前进展到哪一个阶段(刚刚开始还是即将完成)。
一般来说,不要在then方法里面定义Reject状态的回调函数(即then的第二个参数),总是使用catch方法,理由是更接近同步的写法。 then的第二个函数参数和catch等价
Promise.all和Promise.race的区别?
Promise.all 把多个promise实例当成一个promise实例,当这些实例的状态都发生改变时才会返回一个新的promise实例,才会执行then方法。 Promise.race 只要该数组中的 Promise 对象的状态发生变化(无论是resolve还是reject)该方法都会返回。
5.Object.is() 与原来的比较操作符“ ===”、“ ==”的区别?
两等号判等,会在比较时进行类型转换;
三等号判等(判断严格),比较时不进行隐式类型转换,(类型不同则会返回false);
Object.is 在三等号判等的基础上特别处理了 NaN 、-0 和 +0 ,保证 -0 和 +0 不再相同,
但 Object.is(NaN, NaN) 会返回 true.
Object.is 应被认为有其特殊的用途,而不能用它认为它比其它的相等对比更宽松或严格。
6.前端面试之ES6篇
https://juejin.im/post/59c8aec0f265da065c5e965e
6.跨域
1.JSONP
回调函数+数据就是 JSON With Padding,简单、易部署。(做法:动态插入script标签,设置其src属性指向提供JSONP服务的URL地址,查询字符串中加入 callback 指定回调函数,返回的 JSON 被包裹在回调函数中以字符串的形式被返回,需将script标签插入body底部)。缺点是只支持GET,不支持POST(原因是通过地址栏传参所以只能使用GET)
2.document.domain 跨子域
document.domain 跨子域 ( 例如a.qq.com嵌套一个b.qq.com的iframe ,如果a.qq.com设置document.domain为qq.com 。b.qq.com设置document.domain为qq.com, 那么他俩就能互相通信了,不受跨域限制了。 注意:只能跨子域)
3. iframe
window.name + iframe ==> //www.tuicool.com/articles/viMFbqV,支持跨主域。不支持POST
4.postMessage()
HTML5的postMessage()方法允许来自不同源的脚本采用异步方式进行有限的通信,可以实现跨文本档、多窗口、跨域消息传递。适用于不同窗口iframe之间的跨域
5.CORS
CORS(Cross Origin Resource Share)对方服务端设置响应头
设置相应头:”Access-Control-Allow-Origin“
CORS请求默认不发送Cookie和HTTP认证信息。如果要把Cookie发到服务器,一方面要服务器同意,指定Access-Control-Allow-Credentials字段。
6.服务端代理
服务端代理 在浏览器客户端不能跨域访问,而服务器端调用HTTP接口只是使用HTTP协议,不会执行JS脚本,不需要同源策略,也就没有跨越问题。简单地说,就是浏览器不能跨域,后台服务器可以跨域。(一种是通过http-proxy-middleware插件设置后端代理;另一种是通过使用http模块发出请求)
7.Ajax
1.ajax请求和原理
var xhr = new XMLHTTPRequest();
// 请求 method 和 URI
xhr.open(‘GET’, url);
// 请求内容
xhr.send();
// 响应状态
xhr.status
// xhr 对象的事件响应
xhr.onreadystatechange = function() {}
xhr.readyState
// 响应内容
xhr.responseText
AJAX的工作原理
Ajax的工作原理相当于在用户和服务器之间加了—个中间层(AJAX引擎),使用户操作与服务器响应异步化。 Ajax的原理简单来说通过XmlHttpRequest对象来向服务器发异步请求,从服务器获得数据,然后用javascript来操作DOM而更新页面。
ajax优缺点
优点:无刷新更新数据 异步与服务器通信 前后端负载均衡
缺点:
1)ajax干掉了Back和history功能,对浏览器机制的破坏 2)对搜索引擎支持较弱 3)违背了URI和资源定位的初衷
2.fetch和Ajax有什么不同
XMLHttpRequest 是一个设计粗糙的 API,不符合关注分离(Separation of Concerns)的原则,配置和调用方式非常混乱,而且基于事件的异步模型写起来也没有现代的 Promise,generator/yield,async/await 友好。
fetch 是浏览器提供的一个新的 web API,它用来代替 Ajax(XMLHttpRequest),其提供了更优雅的接口,更灵活强大的功能。 Fetch 优点主要有:
语法简洁,更加语义化
基于标准 Promise 实现,支持 async/await
fetch(url).then(response => response.json())
.then(data => console.log(data))
.catch(e => console.log(“Oops, error”, e))
3.GET和POST的区别
GET和POST的区别
GET使用URL或Cookie传参,而POST将数据放在BODY中,这个是因为HTTP协议用法的约定。并非它们的本身区别。
GET方式提交的数据有长度限制,则POST的数据则可以非常大,这个是因为它们使用的操作系统和浏览器设置的不同引起的区别。也不是GET和POST本身的区别。
POST比GET安全,因为数据在地址栏上不可见,这个说法没毛病,但依然不是GET和POST本身的区别。
GET和POST最大的区别主要是GET请求是幂等性的,POST请求不是。(幂等性:对同一URL的多个请求应该返回同样的结果。)因为get请求是幂等的,在网络不好的隧道中会尝试重试。如果用get请求增数据,会有重复操作的风险,而这种重复操作可能会导致副作用
4.GET,POST,PUT,Delete
1. GET请求会向数据库获取信息,只是用来查询数据,不会修改,增加数据。使用URL传递参数,对所发送的数量有限制,一般在2000字符
2. POST向服务器发送数据,会改变数据的种类等资源,就像insert操作一样,会创建新的内容,大小一般没有限制,POST安全性高,POST不会被缓存
3. PUT请求就像数据库的update操作一样,用来修改数据内容,不会增加数据种类
4. Delete用来删除操作
5.缓存,存储相关(cookie,web storage和session)
cookie优点: 1.可以解决HTTP无状态的问题,与服务器进行交互 缺点: 1.数量和长度限制,每个域名最多20条,每个cookie长度不能超过4kb 2.安全性问题。容易被人拦截 3.浪费带宽,每次请求新页面,cookie都会被发送过去
cookie和session区别
1.cookie数据存放在客户的浏览器上,session数据放在服务器上。 2.session会在一定时间内保存在服务器上。当访问增多,会比较占用你服务器的性能。考虑到减轻服务器性能方面,应当使用COOKIE。
sessionStorage是当前对话的缓存,浏览器窗口关闭即消失,localStorage持久存在,除非清除浏览器缓存。
页面缓存原理
页面缓存状态是由http header决定的,一个浏览器请求信息,一个是服务器响应信息。主要包括Pragma: no-cache、Cache-Control、 Expires、 Last-Modified、If-Modified-Since。
6.什么是Cookie 隔离?(或者说:请求资源的时候不要让它带cookie怎么做)
如果静态文件都放在主域名下,那静态文件请求的时候都带有的cookie的数据提交给server的,非常浪费流量,
所以不如隔离开。
因为cookie有域的限制,因此不能跨域提交请求,故使用非主要域名的时候,请求头中就不会带有cookie数据,
这样可以降低请求头的大小,降低请求时间,从而达到降低整体请求延时的目的。
同时这种方式不会将cookie传入Web Server,也减少了Web Server对cookie的处理分析环节,
提高了webserver的http请求的解析速度。
7. Ajax 解决浏览器缓存问题?
1、在ajax发送请求前加上 anyAjaxObj.setRequestHeader(“If-Modified-Since”,”0″)。
2、在ajax发送请求前加上 anyAjaxObj.setRequestHeader(“Cache-Control”,”no-cache”)。
3、在URL后面加上一个随机数: “fresh=” + Math.random();。
4、在URL后面加上时间戳:”nowtime=” + new Date().getTime();。
5、如果是使用jQuery,直接这样就可以了 $.ajaxSetup({cache:false})。这样页面的所有ajax都会执行这条语句就是不需要保存缓存记录