前端面试题总结
1. 使用事件代理是避免什么性能问题?
js绑定事件对性能的影响:
- 在js中每个函数都是对象,都会占用内存;内存中对象越多,性能越差。
- 必须事先指定所有事件处理程序而导致的DOM访问次数,会延迟整个页面的交互就绪事件。
- 而且如果使用遍历字节点去绑定事件处理程序,如果删除子节点,那些绑定在子节点上的事件处理程序还是没有移除的,必须得手动移除,如:element.onclick=null,而采用事件委托也没有这烦恼。
向父元素中加入子元素还是有之前的事件的。一般,因为遍历是在新添加元素之前完成的,所以当我们新添加的元素进去后,事件处理程序是不可能绑定进去的。
2. 写一个requirejs的加载机制
function loadScript(url, callback){
var script = document.createElement ("script")
script.type = "text/javascript";
if (script.readyState){ //IE
script.onreadystatechange = function(){
if (script.readyState == "loaded" || script.readyState == "complete"){
script.onreadystatechange = null;
callback();
}
};
} else { //Others
script.onload = function(){
callback();
};
}
script.src = url;
document.getElementsByTagName_r("head")[0].appendChild(script);//即插即执行
}
4. cookie expire有几种设置
https://developer.mozilla.org/zh-CN/docs/Web/API/Document/cookie
过期:
//永久cookie
; expires=Fri, 31 Dec 9999 23:59:59 GMT
>Note: 对于永久cookie我们用了Fri, 31 Dec 9999 23:59:59 GMT
作为过期日。如果你不想使用这个日期,可使用*[世界末日](http://en.wikipedia.org/wiki/Year_2038_problem)*Tue, 19 Jan 2038 03:14:07 GMT,
它是32位带符号整数能表示从1 January 1970 00:00:00 UTC开始的最大秒长(即01111111111111111111111111111111
, 是 new Date(0x7fffffff * 1e3)
).
//删除cookie
function deleteCookie(name){
var name = escape(name);
var expires = new Date(0);//1970
var path = ";path=/";
document.cookie = name + "="+ ";expires=" + expires.toUTCString() + path;
}
5. 本地存储
特性 | Cookie | localStorage | sessionStorage |
---|---|---|---|
数据的生命期 | 一般由服务器生成,可设置失效时间。如果在浏览器端生成Cookie,默认是关闭浏览器后失效 | 除非被清除,否则永久保存 | 仅在当前会话下有效,关闭页面或浏览器后被清除 |
存放数据大小 | 4K左右 | 一般为5MB | - |
与服务器端通信 | 每次都会携带在HTTP头中,如果使用cookie保存过多数据会带来性能问题 | 仅在客户端(即浏览器)中保存,不参与和服务器的通信 | - |
易用性 | 需要程序员自己封装,源生的Cookie接口不友好 | 源生接口可以接受,亦可再次封装来对Object和Array有更好的支持 | - |
两个storage其他属性方法都一样
特性 | Cookie | localStorage | sessionStorage |
---|---|---|---|
数据的生命期 | 一般由服务器生成,可设置失效时间。如果在浏览器端生成Cookie,默认是关闭浏览器后失效 | 除非被清除,否则永久保存 | 仅在当前会话下有效,关闭页面或浏览器后被清除 |
存放数据大小 | 4K左右 | 一般为5MB | - |
与服务器端通信 | 每次都会携带在HTTP头中,如果使用cookie保存过多数据会带来性能问题 | 仅在客户端(即浏览器)中保存,不参与和服务器的通信 | - |
易用性 | 需要程序员自己封装,源生的Cookie接口不友好 | 源生接口可以接受,亦可再次封装来对Object和Array有更好的支持 | - |
有了对上面这些差别的直观理解,我们就可以讨论三者的应用场景了。
-
因为考虑到每个 HTTP 请求都会带着 Cookie 的信息,所以 Cookie 当然是能精简就精简啦,比较常用的一个应用场景就是判断用户是否登录。针对登录过的用户,服务器端会在他登录时往 Cookie 中插入一段加密过的唯一辨识单一用户的辨识码,下次只要读取这个值就可以判断当前用户是否登录啦。曾经还使用 Cookie 来保存用户在电商网站的购物车信息,如今有了 localStorage,似乎在这个方面也可以给 Cookie 放个假了~
-
而另一方面 localStorage 接替了 Cookie 管理购物车的工作,同时也能胜任其他一些工作。比如HTML5游戏通常会产生一些本地数据,localStorage 也是非常适用的。
-
如果遇到一些内容特别多的表单,为了优化用户体验,我们可能要把表单页面拆分成多个子页面,然后按步骤引导用户填写。这时候 sessionStorage 的作用就发挥出来了(关闭浏览器就失效)。
6. 使用CORS还会带cookie么
JavaScript出于安全方面的考虑,不允许跨域调用其他页面的对象,当然无法读取cookie了。
使用CORS是可以的:跨域资源共享 CORS 详解
Access-Control-Allow-Credentials
该字段可选。它的值是一个布尔值,表示是否允许发送Cookie。默认情况下,Cookie不包括在CORS请求之中。设为true
,即表示服务器明确许可,Cookie可以包含在请求中,一起发给服务器。这个值也只能设为true
,如果服务器不要浏览器发送Cookie,删除该字段即可。
7. 介绍原型链原理
JavaScript核心 - 博客 - 伯乐在线
如果一个属性或者一个方法在对象自身中无法找到(也就是对象自身没有一个那样的属性),然后它会尝试在原型链中寻找这个属性/方法。如果这个属性在原型中没有查找到,那么将会查找这个原型的原型,以此类推,遍历整个原型链(当然这在类继承中也是一样的,当解析一个继承的方法的时候-我们遍历class链( classchain))。
- 每个实例都有一个隐含的proto属性,这个属性是对原型对象的引用
o.proto === Object.prototype(构造函数的原型) - 每一个实例也有一个constructor属性,默认调用prototype对象的constructor属性。
o.constructor === Object.prototype.constructor
注意: ES5标准化了一个实现原型继承的可选方法,即使用Object.create函数:
1 var b = Object.create(a, {y: {value: 20}});
2 var c = Object.create(a, {y: {value: 30}});
你可以在对应的章节获取到更多关于ES5新API的信息。 ES6标准化了 proto属性,并且可以在对象初始化的时候使用它。
8. this的几种指向
- this指向的是当前调用该函数的对象
function test(){ console.log(this) } //直接调用 test()// 输出对象为window,在nodejs中输出为global
- 作为对象方法的调用:函数还可以作为某个对象的方法调用,这时this就指这个上级对象。
- 作为构造函数调用: this指向实例
- apply调用: apply()是函数对象的一个方法,它的作用是改变函数的调用对象,它的第一个参数就表示改变后的调用这个函数的对象。因此,this指的就是这第一个参数。
8.1 构造函数中有return 的话,this的指向是?
使用new关键字实例化的时候发生了什么?
function Prince(name,age){
this.gender="male";
this.kind=true;
}
Prince.prototype.toFrog=function(){
console.log("Prince "+this.name+" turned into a frog.");
}
var prince=new Prince("charming",25);
以上文中的Prince()函数举个栗子[重要]:
- 第一步,创建一个空对象。var prince={}
- 第二步,将构造函数Prince()中的this指向新创建的对象prince。
- 第三步,将prince的proto属性指向Prince函数的prototype,创建对象和原型间关系
- 第四步,执行构造函数Prince()内的代码。
构造函数有return值怎么办?
构造函数里没有显式调用return时,默认是返回this对象,也就是新创建的实例对象。
当构造函数里调用return时,分两种情况:
- return的是五种简单数据类型:String,Number,Boolean,Null,Undefined。
这种情况下,忽视return值,依然返回this对象。[和没有return一样]
其中的this指向如下所示:
function O(name){
console.log(this);//{}
this.name = name;
console.log(this);//{name:"f"}
}
var oo = new O("f");
console.log(oo);//{name:"f"}
- return的是Object: 这种情况下,不再返回this对象,而是返回return语句的返回值。
其中的this,如注释所示:
function O(name){
console.log(this);//{}
this.name = name;
console.log(this);//{name:"f"}
return {};
}
var oo = new O("f");
console.log(oo);//{}
4. call 和 apply
call调用的是apply.
5. jsbridge
我们使用的是jsbridge:使用WebViewJavascriptBridge与JS交互
WebViewJavascriptBridge是一个轻量的用于OC与JS交互的第三方库
参考:JSBridge——Web与Native交互之iOS篇
下面是jsbridge的原理:
UIWebView是iOS内置的浏览器控件,UIWebView用于在APP中嵌入网页,通常是html网页,也可以是PDF、txt文档等。但需要注意的是,Safari浏览器使用的浏览器控件和UIwebView组件并不是同一个,两者在性能上有很大的差距。幸运的是,苹果发布iOS8的时候,新增了一个WKWebView组件,如果你的APP只考虑支持iOS8及以上版本,那么你就可以使用这个新的浏览器控件了。
- Native(Objective-C或Swift)调用Javascript方法
Native调用Javascript语言,是通过UIWebView组件的stringByEvaluatingJavaScriptFromString方法来实现的,该方法返回js脚本的执行结果。
// Swiftwebview.stringByEvaluatingJavaScriptFromString("Math.random()")// OC[webView stringByEvaluatingJavaScriptFromString:@"Math.random();"]; - Javascript调用Native(Objective-C或Swift)方法
“Native调用Web”本质上是JavaScript脚本的动态执行,在“Web调用Native”的场景下由于目前Native语言(Java和Objective-C)不容易像JavaScript那样便于动态执行,所以需要另辟蹊径。
Javascript调用Native,并没有现成的API可以直接拿来用,而是需要间接地通过一些方法来实现。UIWebView有个特性:在UIWebView内发起的所有网络请求,都可以通过delegate函数在Native层得到通知。这样,我们就可以在UIWebView(嵌的我们的html)内发起一个自定义的网络请求,通常是这样的格式:jsbridge://methodName?param1=value1¶m2=value2
于是在UIWebView的delegate函数中,我们只要发现是jsbridge://开头的地址,就不进行内容的加载,转而执行相应的调用逻辑。
【Js 使用了两种方式来与 Objective-C 通信,一种是使用 XMLHttpRequest 发起请求的方式,另一种则是通过设置透明的 iframe 的 src 属性。】
首先通过iframe发起一个网络请求,这个请求的作用是唤起Native APP的分享组件,将网页分享到朋友圈或分享给其他好友。
然后Webview就可以拦截这个请求,并且解析出相应的方法和参数,执行相应的操作
在 native 执行完相应调用后,可以用stringByEvaluatingJavaScriptFromString 方法,将执行结果返回给 js.
更好的总结: http://www.blogs8.cn/posts/Wijg6e6
- 第1步. 匹配url格式
- 第2步 协商url格式以及参数传递方式
同步和异步
因为 iOS SDK 没有天生支持 js 和 native 相互调用,大家的技术方案都是自己实现的一套调用机制,所以这里面有同步异步的问题。细心的同学就能发现,js 调用 native 是通过插入一个 iframe,这个 iframe 插入完了就完了,执行的结果需要 native 另外用 stringByEvaluatingJavaScriptFromString 方法通知 js,所以这是一个异步的调用。
而 stringByEvaluatingJavaScriptFromString 方法本身会直接返回一个 NSString 类型的执行结果,所以这显然是一个同步调用。
所以 js call native 是异步,native call js 是同步。在处理一些逻辑的时候,不可避免需要考虑这个特点。
Ios Android Hybrid app 与 Js Bridge
将要发送的消息存放在Js端——>调用iframe.src,触发通知Native——>Native拦截请求,调用Js bridge里面的fetchQueue并获取返回的消息内容,处理消息——>将需要返回的数据通过直接调用Js的方式,让Js处理
重点: 最后一步我觉得可能是js发的调用native的请求里,还js里定义的callback。然后native直接执行这个callback。