- let与var的区别?
Let为ES6新添加申明变量的命令,它类似于var,但是有以下不同:
1、var声明的变量,其作用域为该语句所在的函数内,且存在变量提升现象
2、let声明的变量,其作用域为该语句所在的代码块内,不存在变量提升
3、let不允许重复声明. 封装一个函数,参数是定时器的时间,.then执行回调函数。
function sleep (time) { return new Promise((resolve) => setTimeout(resolve, time)); }
- 项目做过哪些性能优化?
1、减少HTTP请求数
2、减少DNS查询
3、使用CDN
4、避免重定向
5、图片懒加载
6、减少DOM元素数量
7、减少DOM操作
8、使用外部JavaScript和CSS
9、压缩JavaScript、CSS、字体、图片等
10、优化CSS Sprite
11、使用iconfont
12、字体裁剪
13、多域名分发划分内容到不同域名
14、尽量减少iframe使用
15、避免图片src为空
16、把样式表放在 中
17、把脚本放在页面底部 怎么判断两个对象相等?
1、转化成字符串后比较字符串是否一致:JSON.stringify(obj)===JSON.stringify(obj2);
2、Object.is(obj1,obj2):判断两个值是否 [相同]。如果下列任何一项成立,则两个值相同。
* 两个值都是 [undefined] * 两个值都是 [null] * 两个值都是true或者都是false * 两个值是由相同个数的字符按照相同的顺序组成的字符串 * ~两个值指向同一个对象~ * 两个值都是数字并且 * 都是正零+0 * 都是负零-0 * 都是 [NaN] * 都是除零和[NaN]外的其它同一个数字
- 什么是模块化开发?
实现mvvm主要包含两个方面,数据变化更新视图,视图变化更新数据:
关键点在于data如何更新view,因为view更新data其实可以通过事件监听即可,比如input标签监听 'input' 事件就可以实现了。所以我们着重来分析下,当数据改变,如何更新视图的。
数据更新视图的重点是如何知道数据变了,只要知道数据变了,那么接下去的事都好处理。如何知道数据变了,其实上文我们已经给出答案了,就是通过Object.defineProperty( )对属性设置一个set函数,当数据改变了就会来触发这个函数,所以我们只要将一些需要更新的方法放在这里面就可以实现data更新view了。Vue是通过Object.defineProperty()来实现数据劫持的。它可以来控制一个对象属性的一些特有操作,比如set()、get()、是否可以枚举。
实现数据的双向绑定,首先要对数据进行劫持监听,所以我们需要设置一个监听器Observer,用来监听所有属性。如果属性发上变化了,就需要告诉订阅者Watcher看是否需要更新。因为订阅者是有很多个,所以我们需要有一个消息订阅器Dep来专门收集这些订阅者,然后在监听器Observer和订阅者Watcher之间进行统一管理的。接着,我们还需要有一个指令解析器Compile,对每个节点元素进行扫描和解析,将相关指令对应初始化成一个订阅者Watcher,并替换模板数据或者绑定相应的函数,此时当订阅者Watcher接收到相应属性的变化,就会执行对应的更新函数,从而更新视图。因此接下去我们执行以下3个步骤,实现数据的双向绑定:
1.实现一个监听器Observer,用来劫持并监听所有属性,如果有变动的,就通知订阅者。
2.实现一个订阅者Watcher,可以收到属性的变化通知并执行相应的函数,从而更新视图。
3.实现一个解析器Compile,可以扫描和解析每个节点的相关指令,并根据初始化模板数据以及初始化相应的订阅器。流程图如下:
浏览器兼容性问题
图片描述
1、Normalize.css
2、不同浏览器的标签默认的外边距和内边距不同:
3、IE6双边距问题:在 IE6中设置了float , 同时又设置margin , 就会出现边距问题:设置display:inline;
4、图片默认有间距:使用float 为img 布局
5、IE9以下浏览器不能使用opacity:opacity: 0.5; filter: alpha(opacity = 50); filter: progid:DXImageTransform.Microsoft.Alpha(style = 0, opacity = 50);
6、cursor:hand 显示手型在safari 上不支持:统一使用 cursor:pointer
7、当标签的高度设置小于10px,在IE6、IE7中会超出自己设置的高度:超出高度的标签设置overflow:hidden,或者设置line-height的值小于你的设置高度
8、CSS HACK的方法:height: 100px; // 所有浏览器 通用 _height: 100px; // IE6 专用 *+height: 100px; // IE7 专用 *height: 100px; // IE6、IE7 共用 height: 100px !important; // IE7、FF 共用
代码的顺序一定不能颠倒了,要不又前功尽弃了。因为浏览器在解释程序的时候,如果重名的话,会用后面的覆盖前面的,就象给变量赋值一个道理,所以我们把通用的放前面,越专用的越放后面
前端跨域
什么是跨域?
> 只要协议、域名、端口有任何一个不同,都被当作是不同的域。 > 同源策略是浏览器的行为,是为了保护本地数据不被JavaScript代码获取回来的数据污染,因此拦截的是客户端发出的请求回来的数据接收,即请求发送了,服务器响应了,但是无法被浏览器接收。 > 之所以会跨域,是因为受到了同源策略的限制,同源策略要求源相同才能正常进行通信,即协议、域名、端口号都完全一致。 > > 注意点: > 如果是协议和端口造成的跨域问题“前台”是无能为力的; > 在跨域问题上,域仅仅是通过“URL的首部”来识别而不会去尝试判断相同的ip地址对应着两个域或两个域是否在同一个ip上。 (“URL的首部”指window.location.protocol +window.location.host,也可以理解为“Domains, protocols and ports must match”。)
什么是同源策略?
> 所谓同源是指,域名,协议,端口相同。浏览器采用同源策略,就是禁止页面加载或执行与自身来源不同的域的任何脚本。
通过document.domain跨域(只适用于不同子域的框架间的交互)
浏览器有一个同源策略,其限制之一是不能通过ajax的方法去请求不同源中的文档。第二个限制是浏览器中不同域的框架之间是不能进行js的交互操作的。不同的框架之间是可以获取window对象的,但却无法获取相应的属性和方法。比如,有一个页面,它的地址是 [www.damonare.cn/a.html], 在这个页面里面有一个iframe,它的src是 [damonare.cn/b.html], 很显然,这个页面与它里面的iframe框架是不同域的,所以我们是无法通过在页面中书写js代码来获取iframe中的东西的:
- 通过location.hash跨域
- 通过HTML5的postMessage方法跨域
通过jsonp跨域
JSONP的优缺点 * JSONP的优点是:它不像XMLHttpRequest对象实现的Ajax请求那样受到同源策略的限制;它的兼容性更好,在更加古老的浏览器中都可以运行,不需要XMLHttpRequest或ActiveX的支持;并且在请求完毕后可以通过调用callback的方式回传结果。 * JSONP的缺点则是:它只支持GET请求而不支持POST等其它类型的HTTP请求;它只支持跨域HTTP请求这种情况,不能解决不同域的两个页面之间如何进行JavaScript调用的问题。
通过CORS跨域
通过window.name跨域
前端优化:浏览器缓存
前端优化:浏览器缓存技术介绍 - 掘金
浏览器缓存分为强缓存和协商缓存。jsonp的原理与实现
Jsonp是一种跨域通信的手段,它的原理其实很简单:
> 1. 首先是利用script标签的src属性来实现跨域。 > 2. 通过将前端方法作为参数传递到服务器端,然后由服务器端注入参数之后再返回,实现服务器端向客户端通信。 > 3. 由于使用script标签的src属性,因此只支持get方法
实现流程:
1、设定一个script标签
<script src="http://jsonp.js?callback=xxx"></script>
2、callback定义了一个函数名,而远程服务端通过调用指定的函数并传入参数来实现传递参数,将function(response)传递回客户端
$callback = !empty($_GET['callback']) ? $_GET['callback'] : 'callback'; echo $callback.'(.json_encode($data).)';
3、客户端接收到返回的js脚本,开始解析和执行function(response)
简单的实例:
一个简单的jsonp实现,其实就是拼接url,然后将动态添加一个script元素到头部。function jsonp(req){ var script = document.createElement('script'); var url = req.url + '?callback=' + req.callback.name; script.src = url; document.getElementsByTagName('head')[0].appendChild(script); }
前端js示例:
function hello(res){ alert('hello ' + res.data); } jsonp({ url : '', callback : hello });
服务器端代码:
var http = require('http'); var urllib = require('url'); var port = 8080; var data = {'data':'world'}; http.createServer(function(req,res){ var params = urllib.parse(req.url,true); if(params.query.callback){ console.log(params.query.callback); //jsonp var str = params.query.callback + '(' + JSON.stringify(data) + ')'; res.end(str); } else { res.end(); } }).listen(port,function(){ console.log('jsonp server is on'); });
可靠的jsonp实例:
(function (global) { var id = 0, container = document.getElementsByTagName("head")[0]; function jsonp(options) { if(!options || !options.url) return; var scriptNode = document.createElement("script"), data = options.data || {}, url = options.url, callback = options.callback, fnName = "jsonp" + id++; // 添加回调函数 data["callback"] = fnName; // 拼接url var params = []; for (var key in data) { params.push(encodeURIComponent(key) + "=" + encodeURIComponent(data[key])); } url = url.indexOf("?") > 0 ? (url + "&") : (url + "?"); url += params.join("&"); scriptNode.src = url; // 传递的是一个匿名的回调函数,要执行的话,暴露为一个全局方法 global[fnName] = function (ret) { callback && callback(ret); container.removeChild(scriptNode); delete global[fnName]; } // 出错处理 scriptNode.onerror = function () { callback && callback({error:"error"}); container.removeChild(scriptNode); global[fnName] && delete global[fnName]; } scriptNode.type = "text/javascript"; container.appendChild(scriptNode) } global.jsonp = jsonp; })(this);
使用示例:
jsonp({ url : "www.example.com", data : {id : 1}, callback : function (ret) { console.log(ret); } });
欢迎阅读:
2019年前端面试题-01
2019年前端面试题-02
2019年前端笔试题