Open libin1991 opened 6 years ago
1) domElem.style.width/height // 只能取到内联样式的宽和高 2) domElem.currentStyle.width/height // 获取到即时的宽和高,但只有IE支持,通用使用第三种 3) window.getComputedStyle(domElem).width/height 4) domElem.getBoundingClientRect().width/height // 根据视窗,计算元素绝对位置,还可获取top和left 复制代码
当两个垂直外边距相遇时,它们将形成一个外边距。合并后的外边距的高度等于两个发生合并的外边距的高度中的较大者
一个元素包含在另一个元素中时(假设没有内边距或边框把外边距分隔开),它们的上和/或下外边距也会发生合并
只有普通文档流中块框的垂直外边距才会发生外边距合并。行内框、浮动框或绝对定位之间的外边距不会合并。
基本概念:块级格式化上下文
渲染规则:
创建BFC:
实例:BFC垂直方向重叠问题(创建包裹div,设置overflow属性为hidden)
<section id="margin"> <p>1</p> <div style="overflow:hidden"> <p>2</p> </div> <p>3</p> </section> * { margin: 0; padding: 0; } #margin { background: pink; overflow: hidden; } #margin > p { background: red; margin: 5px auto 25px; } 复制代码
<section id="layout"> <div class="left"></div> <div class="right"></div> </section> * { margin: 0; padding: 0; } #layout { background: red; } #layout .left { background: pink; width: 100px; height: 100px; float: left; } #layout .right { background: #ccc; height: 110px; overflow: auto; } 复制代码
<section id="float"> <div>我是浮动元素</div> </section> * { margin: 0; padding: 0; } #float { background: red; // overflow: auto; float: left; } #float .float { float: left; font-size: 30px; } 复制代码
var o1 = {name: 'o1'}; var o11 = new Object({name: 'o11'}); 复制代码
var M = function() { this.name = 'o2'; } var o2 = new M(); 复制代码
var P = {name: 'o3'}; var o3 = Object.create(P); 复制代码
Func.prototpye.constructor === Func; // true 复制代码
obj.__proto__ === Func.prototype; // true 复制代码
Func.__proto__ === Function.prototype; // true 复制代码
// 实现方法_new,模拟new的作用 function _new(f) { // 返回一个function return function() { var obj = {"__proto__": f.prototype}; f.apply(obj, arguments); return obj; } } // 测试 function Person(name, age) { this.name = name; this.age = age; } var pp = _new(Person)("Jialin", 25); pp instanceof Person; // true; 复制代码
// 使用Object.create模拟new方法 var _new2 = function(func) { var obj = Object.create(func.prototype); var k = func.call(obj); if(typeof k === 'object') { return k; } else { return obj; } } 复制代码
function Parent() { this.name = 'parent'; this.arr = [1]; } function Child() { Parent.call(this); this.type = 'child'; } 复制代码
function Parent() { this.name = 'parent'; this.arr = [1, 2, 3]; } function Child() { this.type = 'child'; } Child.prototype = new Parent(); var c1 = new Child(); var c2 = new Child(); console.log(c1.arr); // [1, 2, 3] c1.arr.push(4); console.log(c2.arr); // [1, 2, 3, 4] c1.__proto__ === c2.__proto__; // true 复制代码
function Parent() { this.name = 'parent'; this.arr = [1, 2, 3]; } function Child() { Parent.call(this); this.type = 'child'; } Child.prototype = new Parent(); var cc = new Child(); cc.constructor; // Parent(){...} 复制代码
function Parent() { this.name = 'parent'; this.arr = [1, 2, 3]; } function Child() { Parent.call(this); this.type = 'child'; } Child.prototype = Parent.prototype; var cc = new Child(); cc.constructor; // Parent(){...} 复制代码
function Parent() { this.name = 'parent'; this.arr = [1, 2, 3]; } function Child() { Parent.call(this); this.type = 'child'; } Child.prototype = Object.create(Parent.prototype); Child.prototype.constructor = Child; 复制代码
function object(o) { function F(){}; F.prototype = o; return new F(); } 复制代码
function createAnother(originalObj) { var clone = object(originalObj); clone.sayHi = function() { // ... } return clone; } 复制代码
element.onclick = function() {} 复制代码
// W3C element.addEventListener('click', function(){}, false); // IE element.attachEvent('click', function(){}, false); 复制代码
element.addEventListener('keyup', function(){}, false); 复制代码
表示阻止默认事件 复制代码
表示阻止冒泡行为 复制代码
应用于:事件响应的优先级 场景:一个按钮绑定两个click事件A和B,要求让点击A时,不执行B 复制代码
表示:当前绑定事件的对象,恒等于this 特殊情况: 在父子嵌套关系中,父元素绑定了事件,单击子元素 此时,currentTarget指向的是父元素(因为它是绑定事件的对象); 此时,target指向了子元素(因为它是触发事件的具体对象) 复制代码
表示:触发事件的某个具体对象 // IE event.sourceElement 复制代码
var eve = new Event('custom'); ev.addEventListener('custom', function(){ console.log('custom'); }); // 触发 ev.dispatchEvent(eve); 复制代码
// 原始类型 数值 ==> 原始值 字符串 ==> 数值(空字符为0) or NaN 布尔值 ==> true为1,false为0 undefined ==> NaN null ==> 0 // 对象 先调用该对象自身的valueOf方法, - 若返回原始类型的值,则执行Number方法转换; - 若返回符合类型的值,调用自身的toString方法, * 若返回原始类型的值,使用Number方法转换 * 若仍返回复合类型的值,则报错失败 var obj = {a:1}; console.log(Number(obj)); 其中, obj.valueOf()返回{a: 1}, {a: 1}的toString()返回'[object Object]' 复制代码
// 原始类型 数值 ==> 对应字符串 字符串 ==> 原始值 布尔值 ==> true变为'true',false变为'false' undefined ==> 'undefined' null ==> 'null' // 复合类型 先调用该对象自身的toString方法, - 若返回原始类型的值,则执行String方法转换; - 若返回符合类型的值,调用自身的valueOf方法, * 若返回原始类型的值,使用String方法转换 * 若仍返回复合类型的值,则报错失败 复制代码
// 原始类型 undefined, null, -0, +0, NaN, 空字符串 均转为 false 复制代码
[] + [] ==> [] + {} ==> {} + [] ==> 0 - 第一个大括号被作为代码块 - 若被console.log()包括的话结果为 [object Object] {} + {} ==> "[object Object][object Object]" - true + true ==> 1 + {a: 1} ==> 复制代码
补充:cookie,localStorage和sessionStorage区别 复制代码
var xhr = XMLHttpRequest ? new XMLHttpRequest() : new window.ActiveXObject('Microsoft.XMLHTTP'); var data = opt.data; var url = opt.url; var type = opt.type.toUpperCase(); var dataArr = []; for(var k in data) { dataArr.push(k + '=' + data[k]); } if(type === 'GET') { url = url + '?' + dataArr.join('&'); xhr.open(type, url.replace(/\?$/g, ''), true); xhr.send(); } if(type === 'POST') { // 第三个参数:默认为true,表示异步发送 xhr.open(type, url, true); xhr.setRequestHeader('Content-type', 'application/x-www-form-urlencoded'); xhr.send(dataArr.join('&')); } xhr.onload = function(){ if(xhr.status === 200 || xhr.status === 304) { var res; if(opt.success && opt.success instanceof Function) { res = xhr.responseText; if(typeof res === 'string') { res = JSON.parse(res); opt.success.call(xhr, res) } } } else { if(opt.error && opt.error instanceof Function) { opt.error.call(xhr, res); } } } 复制代码
// Promise封装 var getJSON = function(url) { var promise = new Promise((resolve, reject) => { var client = new XMLHttpRequest(); client.open('GET', url); client.onreadystatechange = handler; client.responseType = 'json'; client.setRequestHeader('Accept', 'application/json'); client.send(); function handler() { if(this.readyState !== 4) { return; } if(this.status === 200) { resolve(this.response); } else { reject(new Error(this.statusText)); } }; }); return promise; } 复制代码
util.jsonp = function(url, onsuccess, onerror, charset) { var callbackName = util.getName('name'); window[callbackName] = function() { if(onsuccess && util.isFunction(onsuccess)) { onsuccess(arguments[0]); } } var script = util.createScript(url + '&callback=' + callbackName, charset); script.onload = script.onreadystatechange = function() { if(!script.readyState || /loaded|complete/.test(script.readyState)) { script.onload = script.onreadystatechange = null; if(script.parentNode) { script.parentNode.removeChild(script); } window[callbackName] = null; } }; script.onerror = function() { if(onerror && util.isFunction(onerror)) { onerror(); } } } 复制代码
// 客户端 <script> //动态创建script标签,并请求 function addScriptTag(src){ var script = document.createElement('script'); script.setAttribute('type', 'text/javascript'); script.src = src; document.body.appendChild(script); }; //在onload后,跨域请求 window.onload = function(){ addScriptTag('http://127.0.0.1:8080/jsonp?callback=test'); }; //回调函数,必须为全局,不然会报错 function test(data){ alert(data.name); }; </script> // 服务器端 server.on('request',function(req, res){ var urlPath = url.parse(req.url).pathname; var qs = querystring.parse(req.url.split('?')[1]); if(urlPath === '/jsonp' && qs.callback){ res.writeHead(200,{'Content-Type':'application/json;charset=utf-8'}); var data = { "name": "Monkey" }; data = JSON.stringify(data); var callback = qs.callback+'('+data+');'; res.end(callback); } else{ res.writeHead(200, {'Content-Type':'text/html;charset=utf-8'}); res.end('Hell World\n'); } }); 复制代码
// 当前页面A通过iframe嵌入了跨域页面B // A中: var B = document.getElementsByTagName('iframe'); B.src = B.src + '#' + 'data'; // B中 window.onhashchange = function() { var data = window.location.hash; } 复制代码
// 窗口A(http://A.com)向窗口B(http://B.com)发送消息 Bwindow.postMessage('data', 'http://B.com'); // 在B窗口中 window.addEventListener('message', function(evet) { console.log(event.origin); // http://A.com console.log(event.source); // Bwindow console.log(event.data); // data }, false); 复制代码
var ws = new WebSocket('wss://echo.websocket.org'); ws.onopen = function(evt) { console.log('Connection open...'); ws.send('Hello WebSockets'); } ws.onmessage = function(evt) { console.log('Received Message: ', evt.data); ws.close(); } ws.onclose = function(evt) { console.log('Connection closed.'); } 复制代码
// 客户端 var xhr = new XMLHttpRequest(); xhr.withCredentials = true; // 服务器端 Access-Control-Allow-Credentials: true 复制代码
补充:fetch API
// url必须,options可选 fetch('/some/url', { method: 'get', }).then(function(response) { }).catch(function(err){ }); 复制代码
- 它允许恶意用户将代码注入到网页上,其他用户在观看网页时就会受到影响。 - 这类攻击通常包含了HTML以及用户端脚本语言 举例: 通过客户端脚本(JS)在一个论坛发帖中发布一段恶意的JS代码就是脚本注入,如果这个代码内容有请求外部服务器,那么就叫做XSS! 防护: - 如果您在cookie中设置了HttpOnly属性,那么通过js脚本将无法读取到cookie信息,这样能有效的防止XSS攻击 - 对用户表单输入的数据进行过滤,对javascript代码进行转义 复制代码
- 冒充用户发起请求(在用户不知情的情况下),完成一些违背用户意愿的请求 1.登录受信任网站A,并在本地生成Cookie 。 2.在不退出A的情况下,访问危险网站B。 防护: token防御的整体思路是: 1. 后端随机产生一个token,把这个token保存在SESSION状态中;同时,后端把这个token交给前端页面 2. 下次前端需要发起请求(比如发帖)的时候把这个token加入到请求数据或者头信息中,一起传给后端; 3. 后端校验前端请求带过来的token和SESSION里的token是否一致; 复制代码
请求1 - 响应1 - 请求2 - 响应2 - 请求3 - 响应3 // 管线化,请求打包发送,依次进行响应(单独),并保持顺序 请求1 - 请求2 - 请求3 - 响应1 - 响应2 - 响应3 复制代码
1. HTTP的缺点 - 使用明文(不加密)通信,内容可能被窃听 - 不验证通信方的身份,可能遭遇伪装 - 无法证明报文的完整性,可能已遭篡改 2. HTTPS介绍 - HTTPS = HTTP + 加密 + 认证 + 完整性保护 1)HTTPS使用SSL,先和SSL通信,再由SSL和TCP通信 补充: 对称密钥加密:加密和解密用同一个密钥的方式(共享) - 弊端:加密时密钥也会被发给对方 非对称密钥加密: - 发送方使用接收方的公开密钥进行加密处理,接收方使用自己的私有密钥解密 - 弊端:处理速度过慢 2)HTTPS采用混合加密机制 - 交换密钥时,使用非对称加密方式; - 之后的建立通信交换报文阶段,使用对称加密方式(共享) 3)证明公开密钥的正确性 --> 使用由数字证书认证机构颁发的公开密钥证书 4)HTTPS还可以使用客户端证书 复制代码
- HTTP/2采用二进制格式而非文本格式 - HTTP/2是完全多路复用的,同时发起无数个请求,并且响应可以同时返回 - 使用报头压缩,HTTP/2降低了开销 - HTTP/2让服务器可以将响应主动“推送”到客户端缓存中 复制代码
补充问题:
DTD 文档类型定义
DOCTYPE
常见的DOCTYPE: HTML 5 <!DOCTYPE html> HTML 4.01 Strict 该DTD包含所有HTML元素和属性,但不包括展示型的和弃用的元素(如:font) HTML 4.01 Transitional 该DTD包含所有HTML元素和属性,包括展示型和弃用的元素 复制代码
定义:DOM结构中的各个元素都有自己的盒子模型,这些都需要浏览器根据各种样式计算后将元素放到它该出现的位置,这个过程称为Reflow重排
触发Reflow:
定义:将页面要呈现的内容都画在页面上,这个过程称之为Repaint
触发Repint:
如何减少Repaint的频率?
DocumentFragment: - 表示一个没有父级文件的最小文档对象。它被当做一个轻量版的Document使用,用于存储已排好版的或尚未打理好格式XML片段。 - DocumentFragment不是真实DOM树的一部分,它的变化不会引起DOM树的重新渲染的操作(reflow),且不会导致性能等问题。 - 可以使用document.createDocumentFragment()方法创建一个空的DocumentFragment 复制代码
整个最基本的Event Loop如图所示:
task主要包含:setTimeout、setInterval、setImmediate、I/O、UI交互事件 microtask主要包含:Promise、process.nextTick、MutaionObserver 复制代码
console.log(1) setTimeout(() => { console.log(2) }, 0) Promise.resolve().then(() => { console.log(3) }).then(() => { console.log(4) }) console.log(5) // 1 5 3 4 2 分析如下: 同步运行的代码首先输出:1、5 接着,清空microtask队列:3、4 复制代码
console.log('abc'); setTimeout(() => { console.log('执行啦') },3000);setTimeout(() => { console.log('执行啦123') },0); // 'abc' // 执行啦123 // 执行啦 复制代码
console.log(1) setTimeout(() => { console.log(2) new Promise(resolve => { console.log(4) resolve() }).then(() => { console.log(5) }) }) new Promise(resolve => { console.log(7) resolve() }).then(() => { console.log(8) }) setTimeout(() => { console.log(9) new Promise(resolve => { console.log(11) resolve() }).then(() => { console.log(12) }) }); 分析如下: 同步运行的代码首先输出:1、7 接着,清空microtask队列:8 第一个task执行:2、4 接着,清空microtask队列:5 第二个task执行:9、11 接着,清空microtask队列:12 复制代码
console.log('1'); setTimeout(function() { console.log('2'); process.nextTick(function() { console.log('3'); }) new Promise(function(resolve) { console.log('4'); resolve(); }).then(function() { console.log('5') }) }) process.nextTick(function() { console.log('6'); }) new Promise(function(resolve) { console.log('7'); resolve(); }).then(function() { console.log('8') }) setTimeout(function() { console.log('9'); process.nextTick(function() { console.log('10'); }) new Promise(function(resolve) { console.log('11'); resolve(); }).then(function() { console.log('12') }) }) 复制代码
参考1:juejin.im/post/59e85e… 参考2:juejin.im/post/5a6155…
setTimeout(() => { console.log(2) }, 2) setTimeout(() => { console.log(1) }, 1) setTimeout(() => { console.log(0) }, 0) // 1, 0, 2 复制代码
1. 资源压缩合并,减少HTTP请求 2. 非核心代码的异步加载 --> 异步加载的方式 --> 异步加载的区别 3. 利用浏览器缓存 --> 缓存的分类 --> 缓存的原理 4. 使用CDN 5. 预解析DNS <meta http-equi="x-dns-prefetch-control" content="on"> - a标签自带了预解析的功能,那就不需要link开启预解析的功能,但是如果是https协议的话,a标签就不带有预解析的功能,因此使用meta <link rel="dns-prefetch" href="//host_name_to_prefetch.com"> - 普通方法,link只是规定了包含哪个域名的就对它进行预解析 复制代码
强缓存(直接使用浏览器的缓存,不再请求服务器)
协商缓存(询问服务器,浏览器的缓存是否可以使用)
使用了缓存后, 更改js文件内容但未更改js文件名。 重新发布后,怎么保证浏览器加载的是最新的js文件? A:让浏览器上的强缓存事件变短 复制代码
window.addEventListener('error', (e) => { console.log('捕获', e); }, true); // true表示捕获,false表示冒泡 复制代码
跨域的JS运行错误可以捕获吗?错误提示是什么?应该怎么处理? Answer: 跨域错误只提示Script Error 解决方法:script标签增加crossorigin属性,设置js资源响应头为Access-Control-Allow-Origin: * 复制代码
(new Image()).src = 'http://...'; 该请求可以直接被发出 复制代码
function Foo() { getName = function(){console.log(1);} return this; // 指window对象 } Foo.getName = function(){console.log(2);} Foo.prototype.getName = function(){console.log(3);} var getName = function(){console.log(4);} // 变量定义提升 function getName(){console.log(5);} // 函数定义提升 Foo.getName(); // 2 getName(); // 4 Foo().getName(); // 1,返回window对象,调用window对象的getName方法(已经被从4修改为1) getName(); // 1 new Foo.getName(); // 2 // 等价于 new function(){console.log(2);}(); new Foo().getName(); // 3 // 等价于 新建一个Foo函数的实例,调用该实例上(或原型上)的getName方法 new new Foo().getName();// 3 // 等价于 new foo.getName(); 先执行new Foo(),得到Foo函数的实例; 由于点操作符的优先级高于new(不带参数),故先执行foo.getName(),返回3 复制代码
var flag = function(arr) { let toString = Array.prototype.toString; Array.prototype = function() { return this.join(','); } // 隐式调用toString() let result = arr + ''; Array.prototype.toString = toString; return result; } 复制代码
Array.prototype.valueOf = function() { return this.join(','); } var flat = function(arr) { return arr + ''; }; flat(arr); 复制代码
Array.prototype.[Symbol.iterator] = function() { let arr = [].concat(this); let getFirst = function(array) { let first = array.shift(); return first; } return { next: function() { let item = getFirst(arr); if(item) { return { value: item, done: false }; } else { return { done: true } } } } } var flat = function(arr) { let r = []; for(let i of arr) { r.push(i); } return r.join(','); } 复制代码
function flat (arr) { let jsonArr = JSON.stringify(arr); // 将数组转化成json return jsonArr.replace(/(\[|\])/g, ''); // 用正则去掉json中所有的中括号并返回 }; flat(arr); 复制代码
// 递归实现(深拷贝) function func(arr, str) { str = Array.isArray(str) ? str : JSON.parse(str.replace(/([a-z]+)/g, '"$1"')); let obj = {}; str.forEach((val, index) => { let res = arr[index]; if(Array.isArray(val)) { Object.assign(obj, func(res, val)) } else { obj[val] = res; } }) } 复制代码
CSS三列布局
CSS盒模型
当两个垂直外边距相遇时,它们将形成一个外边距。合并后的外边距的高度等于两个发生合并的外边距的高度中的较大者
一个元素包含在另一个元素中时(假设没有内边距或边框把外边距分隔开),它们的上和/或下外边距也会发生合并
只有普通文档流中块框的垂直外边距才会发生外边距合并。行内框、浮动框或绝对定位之间的外边距不会合并。
基本概念:块级格式化上下文
渲染规则:
创建BFC:
实例:BFC垂直方向重叠问题(创建包裹div,设置overflow属性为hidden)
<section id="margin"> <p>1</p> <div style="overflow:hidden"> <p>2</p> </div> <p>3</p> </section> * { margin: 0; padding: 0; } #margin { background: pink; overflow: hidden; } #margin > p { background: red; margin: 5px auto 25px; } 复制代码
<section id="layout"> <div class="left"></div> <div class="right"></div> </section> * { margin: 0; padding: 0; } #layout { background: red; } #layout .left { background: pink; width: 100px; height: 100px; float: left; } #layout .right { background: #ccc; height: 110px; overflow: auto; } 复制代码
<section id="float"> <div>我是浮动元素</div> </section> * { margin: 0; padding: 0; } #float { background: red; // overflow: auto; float: left; } #float .float { float: left; font-size: 30px; } 复制代码
原型链
var o1 = {name: 'o1'}; var o11 = new Object({name: 'o11'}); 复制代码
var M = function() { this.name = 'o2'; } var o2 = new M(); 复制代码
var P = {name: 'o3'}; var o3 = Object.create(P); 复制代码
Func.prototpye.constructor === Func; // true 复制代码
obj.__proto__ === Func.prototype; // true 复制代码
Func.__proto__ === Function.prototype; // true 复制代码
// 实现方法_new,模拟new的作用 function _new(f) { // 返回一个function return function() { var obj = {"__proto__": f.prototype}; f.apply(obj, arguments); return obj; } } // 测试 function Person(name, age) { this.name = name; this.age = age; } var pp = _new(Person)("Jialin", 25); pp instanceof Person; // true; 复制代码
// 使用Object.create模拟new方法 var _new2 = function(func) { var obj = Object.create(func.prototype); var k = func.call(obj); if(typeof k === 'object') { return k; } else { return obj; } } 复制代码
面向对象
function Parent() { this.name = 'parent'; this.arr = [1]; } function Child() { Parent.call(this); this.type = 'child'; } 复制代码
function Parent() { this.name = 'parent'; this.arr = [1, 2, 3]; } function Child() { this.type = 'child'; } Child.prototype = new Parent(); var c1 = new Child(); var c2 = new Child(); console.log(c1.arr); // [1, 2, 3] c1.arr.push(4); console.log(c2.arr); // [1, 2, 3, 4] c1.__proto__ === c2.__proto__; // true 复制代码
function Parent() { this.name = 'parent'; this.arr = [1, 2, 3]; } function Child() { Parent.call(this); this.type = 'child'; } Child.prototype = new Parent(); var cc = new Child(); cc.constructor; // Parent(){...} 复制代码
function Parent() { this.name = 'parent'; this.arr = [1, 2, 3]; } function Child() { Parent.call(this); this.type = 'child'; } Child.prototype = Parent.prototype; var cc = new Child(); cc.constructor; // Parent(){...} 复制代码
function Parent() { this.name = 'parent'; this.arr = [1, 2, 3]; } function Child() { Parent.call(this); this.type = 'child'; } Child.prototype = Object.create(Parent.prototype); Child.prototype.constructor = Child; 复制代码
function object(o) { function F(){}; F.prototype = o; return new F(); } 复制代码
function createAnother(originalObj) { var clone = object(originalObj); clone.sayHi = function() { // ... } return clone; } 复制代码
DOM事件
element.onclick = function() {} 复制代码
// W3C element.addEventListener('click', function(){}, false); // IE element.attachEvent('click', function(){}, false); 复制代码
element.addEventListener('keyup', function(){}, false); 复制代码
var eve = new Event('custom'); ev.addEventListener('custom', function(){ console.log('custom'); }); // 触发 ev.dispatchEvent(eve); 复制代码
类型转换
// 原始类型 数值 ==> 原始值 字符串 ==> 数值(空字符为0) or NaN 布尔值 ==> true为1,false为0 undefined ==> NaN null ==> 0 // 对象 先调用该对象自身的valueOf方法, - 若返回原始类型的值,则执行Number方法转换; - 若返回符合类型的值,调用自身的toString方法, * 若返回原始类型的值,使用Number方法转换 * 若仍返回复合类型的值,则报错失败 var obj = {a:1}; console.log(Number(obj)); 其中, obj.valueOf()返回{a: 1}, {a: 1}的toString()返回'[object Object]' 复制代码
// 原始类型 数值 ==> 对应字符串 字符串 ==> 原始值 布尔值 ==> true变为'true',false变为'false' undefined ==> 'undefined' null ==> 'null' // 复合类型 先调用该对象自身的toString方法, - 若返回原始类型的值,则执行String方法转换; - 若返回符合类型的值,调用自身的valueOf方法, * 若返回原始类型的值,使用String方法转换 * 若仍返回复合类型的值,则报错失败 复制代码
// 原始类型 undefined, null, -0, +0, NaN, 空字符串 均转为 false 复制代码
[] + [] ==> [] + {} ==> {} + [] ==> 0 - 第一个大括号被作为代码块 - 若被console.log()包括的话结果为 [object Object] {} + {} ==> "[object Object][object Object]" - true + true ==> 1 + {a: 1} ==> 复制代码
设计模式
通信类
补充:cookie,localStorage和sessionStorage区别 复制代码
var xhr = XMLHttpRequest ? new XMLHttpRequest() : new window.ActiveXObject('Microsoft.XMLHTTP'); var data = opt.data; var url = opt.url; var type = opt.type.toUpperCase(); var dataArr = []; for(var k in data) { dataArr.push(k + '=' + data[k]); } if(type === 'GET') { url = url + '?' + dataArr.join('&'); xhr.open(type, url.replace(/\?$/g, ''), true); xhr.send(); } if(type === 'POST') { // 第三个参数:默认为true,表示异步发送 xhr.open(type, url, true); xhr.setRequestHeader('Content-type', 'application/x-www-form-urlencoded'); xhr.send(dataArr.join('&')); } xhr.onload = function(){ if(xhr.status === 200 || xhr.status === 304) { var res; if(opt.success && opt.success instanceof Function) { res = xhr.responseText; if(typeof res === 'string') { res = JSON.parse(res); opt.success.call(xhr, res) } } } else { if(opt.error && opt.error instanceof Function) { opt.error.call(xhr, res); } } } 复制代码
// Promise封装 var getJSON = function(url) { var promise = new Promise((resolve, reject) => { var client = new XMLHttpRequest(); client.open('GET', url); client.onreadystatechange = handler; client.responseType = 'json'; client.setRequestHeader('Accept', 'application/json'); client.send(); function handler() { if(this.readyState !== 4) { return; } if(this.status === 200) { resolve(this.response); } else { reject(new Error(this.statusText)); } }; }); return promise; } 复制代码
util.jsonp = function(url, onsuccess, onerror, charset) { var callbackName = util.getName('name'); window[callbackName] = function() { if(onsuccess && util.isFunction(onsuccess)) { onsuccess(arguments[0]); } } var script = util.createScript(url + '&callback=' + callbackName, charset); script.onload = script.onreadystatechange = function() { if(!script.readyState || /loaded|complete/.test(script.readyState)) { script.onload = script.onreadystatechange = null; if(script.parentNode) { script.parentNode.removeChild(script); } window[callbackName] = null; } }; script.onerror = function() { if(onerror && util.isFunction(onerror)) { onerror(); } } } 复制代码
// 客户端 <script> //动态创建script标签,并请求 function addScriptTag(src){ var script = document.createElement('script'); script.setAttribute('type', 'text/javascript'); script.src = src; document.body.appendChild(script); }; //在onload后,跨域请求 window.onload = function(){ addScriptTag('http://127.0.0.1:8080/jsonp?callback=test'); }; //回调函数,必须为全局,不然会报错 function test(data){ alert(data.name); }; </script> // 服务器端 server.on('request',function(req, res){ var urlPath = url.parse(req.url).pathname; var qs = querystring.parse(req.url.split('?')[1]); if(urlPath === '/jsonp' && qs.callback){ res.writeHead(200,{'Content-Type':'application/json;charset=utf-8'}); var data = { "name": "Monkey" }; data = JSON.stringify(data); var callback = qs.callback+'('+data+');'; res.end(callback); } else{ res.writeHead(200, {'Content-Type':'text/html;charset=utf-8'}); res.end('Hell World\n'); } }); 复制代码
// 当前页面A通过iframe嵌入了跨域页面B // A中: var B = document.getElementsByTagName('iframe'); B.src = B.src + '#' + 'data'; // B中 window.onhashchange = function() { var data = window.location.hash; } 复制代码
// 窗口A(http://A.com)向窗口B(http://B.com)发送消息 Bwindow.postMessage('data', 'http://B.com'); // 在B窗口中 window.addEventListener('message', function(evet) { console.log(event.origin); // http://A.com console.log(event.source); // Bwindow console.log(event.data); // data }, false); 复制代码
var ws = new WebSocket('wss://echo.websocket.org'); ws.onopen = function(evt) { console.log('Connection open...'); ws.send('Hello WebSockets'); } ws.onmessage = function(evt) { console.log('Received Message: ', evt.data); ws.close(); } ws.onclose = function(evt) { console.log('Connection closed.'); } 复制代码
// 客户端 var xhr = new XMLHttpRequest(); xhr.withCredentials = true; // 服务器端 Access-Control-Allow-Credentials: true 复制代码
补充:fetch API
// url必须,options可选 fetch('/some/url', { method: 'get', }).then(function(response) { }).catch(function(err){ }); 复制代码
安全类
HTTP协议类
补充问题:
渲染机制
DTD 文档类型定义
DOCTYPE
定义:DOM结构中的各个元素都有自己的盒子模型,这些都需要浏览器根据各种样式计算后将元素放到它该出现的位置,这个过程称为Reflow重排
触发Reflow:
定义:将页面要呈现的内容都画在页面上,这个过程称之为Repaint
触发Repint:
如何减少Repaint的频率?
JS运行机制 -- 任务队列和EventLoop
整个最基本的Event Loop如图所示:
task主要包含:setTimeout、setInterval、setImmediate、I/O、UI交互事件 microtask主要包含:Promise、process.nextTick、MutaionObserver 复制代码
console.log(1) setTimeout(() => { console.log(2) }, 0) Promise.resolve().then(() => { console.log(3) }).then(() => { console.log(4) }) console.log(5) // 1 5 3 4 2 分析如下: 同步运行的代码首先输出:1、5 接着,清空microtask队列:3、4 复制代码
console.log('abc'); setTimeout(() => { console.log('执行啦') },3000);setTimeout(() => { console.log('执行啦123') },0); // 'abc' // 执行啦123 // 执行啦 复制代码
console.log(1) setTimeout(() => { console.log(2) new Promise(resolve => { console.log(4) resolve() }).then(() => { console.log(5) }) }) new Promise(resolve => { console.log(7) resolve() }).then(() => { console.log(8) }) setTimeout(() => { console.log(9) new Promise(resolve => { console.log(11) resolve() }).then(() => { console.log(12) }) }); 分析如下: 同步运行的代码首先输出:1、7 接着,清空microtask队列:8 第一个task执行:2、4 接着,清空microtask队列:5 第二个task执行:9、11 接着,清空microtask队列:12 复制代码
console.log('1'); setTimeout(function() { console.log('2'); process.nextTick(function() { console.log('3'); }) new Promise(function(resolve) { console.log('4'); resolve(); }).then(function() { console.log('5') }) }) process.nextTick(function() { console.log('6'); }) new Promise(function(resolve) { console.log('7'); resolve(); }).then(function() { console.log('8') }) setTimeout(function() { console.log('9'); process.nextTick(function() { console.log('10'); }) new Promise(function(resolve) { console.log('11'); resolve(); }).then(function() { console.log('12') }) }) 复制代码
参考1:juejin.im/post/59e85e…
参考2:juejin.im/post/5a6155…
setTimeout(() => { console.log(2) }, 2) setTimeout(() => { console.log(1) }, 1) setTimeout(() => { console.log(0) }, 0) // 1, 0, 2 复制代码
页面性能
1. 资源压缩合并,减少HTTP请求 2. 非核心代码的异步加载 --> 异步加载的方式 --> 异步加载的区别 3. 利用浏览器缓存 --> 缓存的分类 --> 缓存的原理 4. 使用CDN 5. 预解析DNS <meta http-equi="x-dns-prefetch-control" content="on"> - a标签自带了预解析的功能,那就不需要link开启预解析的功能,但是如果是https协议的话,a标签就不带有预解析的功能,因此使用meta <link rel="dns-prefetch" href="//host_name_to_prefetch.com"> - 普通方法,link只是规定了包含哪个域名的就对它进行预解析 复制代码
强缓存(直接使用浏览器的缓存,不再请求服务器)
协商缓存(询问服务器,浏览器的缓存是否可以使用)
错误监控
window.addEventListener('error', (e) => { console.log('捕获', e); }, true); // true表示捕获,false表示冒泡 复制代码
(new Image()).src = 'http://...'; 该请求可以直接被发出 复制代码
笔试题
function Foo() { getName = function(){console.log(1);} return this; // 指window对象 } Foo.getName = function(){console.log(2);} Foo.prototype.getName = function(){console.log(3);} var getName = function(){console.log(4);} // 变量定义提升 function getName(){console.log(5);} // 函数定义提升 Foo.getName(); // 2 getName(); // 4 Foo().getName(); // 1,返回window对象,调用window对象的getName方法(已经被从4修改为1) getName(); // 1 new Foo.getName(); // 2 // 等价于 new function(){console.log(2);}(); new Foo().getName(); // 3 // 等价于 新建一个Foo函数的实例,调用该实例上(或原型上)的getName方法 new new Foo().getName();// 3 // 等价于 new foo.getName(); 先执行new Foo(),得到Foo函数的实例; 由于点操作符的优先级高于new(不带参数),故先执行foo.getName(),返回3 复制代码
应用类
算法类
var flag = function(arr) { let toString = Array.prototype.toString; Array.prototype = function() { return this.join(','); } // 隐式调用toString() let result = arr + ''; Array.prototype.toString = toString; return result; } 复制代码
Array.prototype.valueOf = function() { return this.join(','); } var flat = function(arr) { return arr + ''; }; flat(arr); 复制代码
Array.prototype.[Symbol.iterator] = function() { let arr = [].concat(this); let getFirst = function(array) { let first = array.shift(); return first; } return { next: function() { let item = getFirst(arr); if(item) { return { value: item, done: false }; } else { return { done: true } } } } } var flat = function(arr) { let r = []; for(let i of arr) { r.push(i); } return r.join(','); } 复制代码
function flat (arr) { let jsonArr = JSON.stringify(arr); // 将数组转化成json return jsonArr.replace(/(\[|\])/g, ''); // 用正则去掉json中所有的中括号并返回 }; flat(arr); 复制代码
// 递归实现(深拷贝) function func(arr, str) { str = Array.isArray(str) ? str : JSON.parse(str.replace(/([a-z]+)/g, '"$1"')); let obj = {}; str.forEach((val, index) => { let res = arr[index]; if(Array.isArray(val)) { Object.assign(obj, func(res, val)) } else { obj[val] = res; } }) } 复制代码
三面/终面