console.log([] == ![]) // true
console.log([] == []) // false
console.log([] == {}) // false
console.log(new String('a') == new String('a')) // false
console.log(isNaN(NaN)) // true
console.log(isNaN('32131dsafdas')) // true
console.log(NaN === NaN) // false
console.log(NaN === undefined) // false
console.log(undefined === undefined) // true
console.log(typeof NaN) // number
console.log({} + []) // 0
console.log([] + {}) // "[object Object]"
console.log({} + {}) // "[object Object][object Object]"
console.log([]+ []) // ""
console.log({} + 1) // 1
console.log([] + 1) // "1"
隐式类型转换参考:https://blog.csdn.net/m0_37793545/article/details/87862847
var name = 'world';
(function () {
if (typeof name === 'undefined') {
var name = 'jack'
console.log('hi' + name)
} else {
console.log('hello' + name)
}
})()
// hijack
var test = (function (a) {
this.a = a;
return function (b) {
return this.a + b
}
}(function (a, b) {
return a
}(1, 2)))
test(4)
// 5
var x = 1,
y = z = 0
function add(n) {
return n = n + 1
}
y = add(x)
function add(n) {
return n = n + 3
}
z = add(x)
console.log(x, y, z)
// 1 4 4
function bar() {
return foo;
foo = 10;
function foo() {}
var foo = 11
}
alert(typeof bar())
// function
(2)
var a = 1
function a() {}
console.log(a) // 1
(3)
var b = 1
if (true) {
console.log(b) // 1
var b = 2
var c = 3
console.log(c) // 3
}
console.log(b) // 2
console.log(c) // 3
var x = 3;
var foo = {
x: 2,
baz: {
x: 1,
bar: function () {
return this.x
}
}
}
var go = foo.baz.bar
go()
foo.baz.bar()
// 3
// 1
console.log([1, 2] + [2, 1])
// 1,2,2,1
var test = '12344321'
var test1 = test.split('').reverse().join('')
console.log(test === test1)
//去重:
(1)Array.from(new Set([1,2,3,5,2,1,22,'1']))
(2)
var array = [1, 2, 1, 1, '1'];
function unique(array) {
var res = array.filter(function(item, index, array){
return array.indexOf(item) === index;
})
return res;
}
console.log(unique(array));
(3)
function unique(arr) {
arr = arr.sort()
let res = []
for (let i = 0; i < arr.length; i++) {
if (arr[i] !== arr[i-1]) {
res.push(arr[i])
}
}
return res
}
更多去重方式:https://juejin.im/post/5949d85f61ff4b006c0de98b
数组排序总结:https://juejin.im/post/57dcd394a22b9d00610c5ec8#heading-24
// (1) 借助构造函数实现继承
function Parent() {
this.name = 'parent'
}
function Child() {
Parent.call(this)
this.age = 18
}
// 这种方式只能实现部分继承,即父类的构造方法中的属性,子类可以继承,其缺点是,父类原型上的属性或方法,子类无法继承。
// (2)借助原型链实现继承
function Parent() {
this.name = 'parent'
this.play = [1, 2, 3]
}
function Child() {
this.age = 18
}
Child.prototype = new Parent()
// 这种继承方式的缺点是用子类Child实例化两个对象后,var s1 = new Child(); var s2 = new Child(); s1.play.push(4); console.log(s2.play); 也会打印出[1,2,3,4],其原因是两个对象的__proto__指向了同一个原型对象。
// (3)组合方式(继承的完美实现)
function Parent() {
this.name = 'parent'
}
function Child() {
// 将父对象的构造函数绑定在子对象上
Parent.call(this)
this.age = 18
}
// 将Parent的prototype拷贝给Child
Child.prototype = Object.create(Parent.prototype)
// 修正Child的prototype对象指向的构造函数
Child.prototype.constructor = Child
// 请写出以下代码执行的顺序
setTimeout(function () {
console.log(1);
});
new Promise(function(resolve,reject){
console.log(2)
resolve(3)
}).then(function(val){
console.log(val);
})
console.log(4);
// 2 4 3 1
在同步代码执行完成后才回去检查是否有异步任务完成,并执行对应的回调,而微任务又会在宏任务之前执行。
同步代码=>异步代码(微任务=>宏任务)
宏任务: setTimeout
微任务: Promise.then
var a={length:2,0:'aaa',1:'bbb'};
// ES6:
Array.from(a)
// ES5:
Array.prototype.slice.call(a);// ["aaa", "bbb"]
var a={length:2};
Array.prototype.slice.call(a);// [undefined, undefined]
function Foo() {
getName = function() {
alert(1)
}
return this
}
Foo.getName = function() {
alert(2)
}
Foo.prototype.getName = function() {
alert(3)
}
var getName = function() {
alert(4)
}
function getName() {
alert(5)
}
//请写出以下输出结果:
Foo.getName() // 2
getName() // 4
Foo().getName() // 1
getName() // 1
new Foo.getName() // 2
new Foo().getName() // 3
new new Foo().getName() // 3
// 详细解释 https://www.jb51.net/article/79461.htm
(1) 函数防抖
function debounce(func, delay) {
var timeout
return function(e) {
console.log('清除', timeout, e.target.value)
clearTimeout(timeout)
var context = this,
args = arguments
console.log('新的', timeout, e.target.value)
timeout = setTimeout(function() {
console.log('----')
func.apply(context, args)
}, delay)
}
}
var validate = debounce(function(e) {
console.log('change', e.target.value, new Date() - 0)
}, 380)
// 绑定监听
document.querySelector('input').addEventListener('input', validate)
(2) 函数节流
// 函数节流
function throttle(fn, threshhold) {
var timeout
var start = new Date()
var threshhold = threshhold || 160
return function() {
var context = this,
args = arguments,
curr = new Date() - 0
clearTimeout(timeout) //总是干掉事件回调
if (curr - start >= threshhold) {
console.log('now', curr, curr - start) //注意这里相减的结果,都差不多是160左右
fn.apply(context, args) //只执行一部分方法,这些方法是在某个时间段内执行一次
start = curr
} else {
//让方法在脱离事件后也能执行一次
timeout = setTimeout(function() {
fn.apply(context, args)
}, threshhold)
}
}
}
var mousemove = throttle(function(e) {
console.log(e.pageX, e.pageY)
})
// 绑定监听
document.querySelector('#panel').addEventListener('mousemove', mousemove)
hash(散列、杂凑)函数,是将任意长度的数据映射到有限长度的域上。直观解释起来,就是对一串数据m进行杂糅,输出另一段固定长度的数据h,作为这段数据的特征(指纹)。也就是说,无论数据块m有多大,其输出值h为固定长度。
hashmap,hash值(key)存在的目的是加速键值对的查找
详解:https://www.zhihu.com/question/26762707
每个实例可以维护一份被返回对象的独立的拷贝,否则;将导致多个实例共享一个对象,其中一个组件改变data属性值,其它实例也会受到影响。
jQuery.extend()为扩展jQuery类本身.为类添加新的方法。如 $.wang()
jquery.fn.extend(object);给jQuery对象添加方法。如 $('#test').wang()
(1)所谓的virtual dom,也就是虚拟节点。它通过JS的Object对象模拟DOM中的节点,然后再通过特定的render方法将其渲染成真实的DOM节点
dom diff 则是通过JS层面的计算,返回一个patch对象,即补丁对象,在通过特定的操作解析patch对象,完成页面的重新渲染
(2)DOM DIFF
比较两棵DOM树的差异是Virtual DOM算法最核心的部分.简单的说就是新旧虚拟dom 的比较,如果有差异就以新的为准,然后再插入的真实的dom中,重新渲染。
比较只会在同层级进行, 不会跨层级比较。新旧虚拟dom比较的时候,是先同层比较,同层比较完看看时候有儿子,有则需要继续比较下去,直到没有儿子。
比较后会出现四种情况:
1、此节点是否被移除 -> 添加新的节点
2、属性是否被改变 -> 旧属性改为新属性
3、文本内容被改变-> 旧内容改为新内容
4、节点要被整个替换 -> 结构完全不相同 移除整个替换
详解:https://juejin.im/post/5ad6182df265da23906c8627
// 100 Continue 继续,一般在发送post请求时,已发送了http header之后服务端将返回此信息,表示确认,之后发送具体参数信息
// 200 OK 正常返回信息
// 201 Created 请求成功并且服务器创建了新的资源
// 202 Accepted 服务器已接受请求,但尚未处理
// 301 Moved Permanently 请求的网页已永久移动到新位置。
// 302 Found 临时性重定向。
// 303 See Other 临时性重定向,且总是使用 GET 请求新的 URI。
// 304 Not Modified 自从上次请求后,请求的网页未修改过。
// 400 Bad Request 服务器无法理解请求的格式,客户端不应当尝试再次使用相同的内容发起请求。
// 401 Unauthorized 请求未授权。
// 403 Forbidden 禁止访问。
// 404 Not Found 找不到如何与 URI 相匹配的资源。
// 500 Internal Server Error 最常见的服务器端错误。
// 503 Service Unavailable 服务器端暂时无法处理请求(可能是过载或维护)。
同源策略是浏览器的一个安全功能,不同源的客户端脚本在没有明确授权的情况下,不能读写对方资源。若地址里面的协议、域名和端口号均相同则属于同源。
jsonp跨域、nginx反向代理、node.js中间件代理跨域、后端设置http header、后端在服务器上设置cors。
重绘:当页面中元素样式的改变并不影响它在文档流中的位置时(例如:color、background-color、visibility等),浏览器会将新样式赋予给元素并重新绘制它,这个过程称为重绘。
回流:当Render Tree(DOM)中部分或全部元素的尺寸、结构、或某些属性发生改变时,浏览器重新渲染部分或全部文档的过程称为回流。
回流要比重绘消耗性能开支更大。
回流必将引起重绘,重绘不一定会引起回流。
详解:https://juejin.im/post/5a9923e9518825558251c96a
加载过程:
渲染过程:
时,会执行并阻塞渲染强制缓存:catch-control,expires,catch-control优先级高。
协商缓存:Last-Modified / If-Modified-Since和Etag / If-None-Match,其中Etag / If-None-Match的优先级比Last-Modified / If-Modified-Since高。
详解:https://juejin.im/entry/5ad86c16f265da505a77dca4
https://segmentfault.com/a/1190000008377508
详解:https://juejin.im/post/5acd0c8a6fb9a028da7cdfaf#heading-9
首页可以控制导航跳转,beforeEach,afterEach等,一般用于页面title的修改。一些需要登录才能调整页面的重定向功能。
beforeEach主要有3个参数to,from,next:
afterEach和beforeResolve(全局解析守卫)没有next参数
路由独享的守卫:beforeEnter,参数同全局前置守卫
组件内的守卫:beforeRouteEnter(组件实例未创建,不能使用this),beforeRouteUpdate,beforeRouteLeave
详解:https://router.vuejs.org/zh/guide/advanced/navigation-guards.html
hash模式:在浏览器中符号“#”,#以及#后面的字符称之为hash,用window.location.hash读取;
特点:hash虽然在URL中,但不被包括在HTTP请求中;用来指导浏览器动作,对服务端安全无用,hash不会重加载页面。
hash 模式下,仅 hash 符号之前的内容会被包含在请求中,如 http://www.xxx.com,因此对于后端来说,即使没有做到对路由的全覆盖,也不会返回 404 错误。
history模式:history采用HTML5的新特性;且提供了两个新方法:pushState(),replaceState()可以对浏览器历史记录栈进行修改,以及popState事件的监听到状态变更。
history 模式下,前端的 URL 必须和实际向后端发起请求的 URL 一致,如 http://www.xxx.com/items/id。后端如果缺少对 /items/id 的路由处理,将返回 404 错误。Vue-Router 官网里如此描述:“不过这种模式要玩好,还需要后台配置支持……所以呢,你要在服务端增加一个覆盖所有情况的候选资源:如果 URL 匹配不到任何静态资源,则应该返回同一个 index.html 页面,这个页面就是你 app 依赖的页面。”
防范:所有用户输入的都是不可信的,输入过滤和转义,重要cookie设置为HttpOnly
防范:
详解:https://www.jianshu.com/p/64a413ada155
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
简单方法:
var add = function(a){
return function(b){
return function(c){
return a+b+c;
};
};
};
但是这样的代码在add(1)(2)或者add(1)(2)(3)(4)时便不对了。
正确实现:
function add () {
var args = Array.prototype.slice.call(arguments);
var fn = function () {
var arg_fn = Array.prototype.slice.call(arguments);
return add.apply(null, args.concat(arg_fn));
}
// 重写valueOf方法,是为了处理隐式类型转换
fn.valueOf = function () {
return args.reduce(function(a, b) {
return a + b;
})
}
return fn;
}
详解:https://www.cnblogs.com/coco1s/p/6509141.html 或
https://blog.csdn.net/m0_37793545/article/details/87862847
function log(){
console.log.apply(console, arguments);
}
手写一个new实现
function create() {
// 创建一个空的对象
var obj = new Object(),
// 获得构造函数,arguments中去除第一个参数
Con = [].shift.call(arguments);
// 链接到原型,obj 可以访问到构造函数原型中的属性
obj.__proto__ = Con.prototype;
// 绑定 this 实现继承,obj 可以访问到构造函数中的属性
var ret = Con.apply(obj, arguments);
// 优先返回构造函数返回的对象
return ret instanceof Object ? ret : obj;
};
详解:《JS高程》 6.2.2 《你不知道的Javascript》2.2.4
相对于父级宽度的:
max-width、min-width、width、left、right、text-indent、padding、margin、grid-template-columns、grid-auto-columns、column-gap 等;
相对于父级高度的:
max-height、min-height、height、top、bottom、grid-template-rows、grid-auto-rows、row-gap 等;
相对于主轴长度的:
flex-basis 等;
相对于继承字号的:
font-size 等;
相对于自身字号的:
line-height 等;
相对于自身宽高的:
border-radius、background-size、border-image-width、transform: translate()、transform-origin、zoom、clip-path 等;
相对于行高的:
vertical-align 等;
特殊算法的:
background-position (方向长度 / 该方向除背景图之外部分总长度)、border-image-slice (相对于图片尺寸)、filter 系列函数等;
如果自身设置 position: absolute,“父级”指:Boring:破坏文档流的div高度设为百分比是相对谁而言的?;
如果 position: fixed,“父级”指视口(父级不存在 transform 为非 none 值的情况下)。
来源:知乎
链接:https://www.zhihu.com/question/36079531/answer/65809167
闭包:就是能够读取其他函数内部变量的函数。
堆栈溢出:就是不顾堆栈中分配的局部数据块大小,向该数据块写入了过多的数据,导致数据越界,结果覆盖了别的数据。经常会在递归中发生。
内存泄露是指:用动态存储分配函数内存空间,在使用完毕后未释放,导致一直占据该内存单元。直到程序结束。指任何对象在您不再拥有或需要它之后仍然存在。
造成内存泄漏:
setTimeout 的第一个参数使用字符串而非函数的话,会引发内存泄漏。
闭包、控制台日志、循环(在两个对象彼此引用且彼此保留时,就会产生一个循环)
防止内存泄露:
1、不要动态绑定事件;
2、不要在动态添加,或者会被动态移除的dom上绑事件,用事件冒泡在父容器监听事件;
3、如果要违反上面的原则,必须提供destroy方法,保证移除dom后事件也被移除,这点可以参考Backbone的源代码,做的比较好;
4、单例化,少创建dom,少绑事件。
点击穿透现象有3种:
点击穿透问题:点击蒙层(mask)上的关闭按钮,蒙层消失后发现触发了按钮下面元素的click事件跨页面点击穿透问题:如果按钮下面恰好是一个有href属性的a标签,那么页面就会发生跳转另一种跨页面点击穿透问题:这次没有mask了,直接点击页内按钮跳转至新页,然后发现新页面中对应位置元素的click事件被触发了
解决方案:
1、只用touch
最简单的解决方案,完美解决点击穿透问题
把页面内所有click全部换成touch事件( touchstart 、’touchend’、’tap’)
2、只用click
下下策,因为会带来300ms延迟,页面内任何一个自定义交互都将增加300毫秒延迟
3、tap后延迟350ms再隐藏mask
改动最小,缺点是隐藏mask变慢了,350ms还是能感觉到慢的
4、pointer-events
比较麻烦且有缺陷, 不建议使用mask隐藏后,给按钮下面元素添上 pointer-events: none; 样式,让click穿过去,350ms后去掉这个样式,恢复响应缺陷是mask消失后的的350ms内,用户可以看到按钮下面的元素点着没反应,如果用户手速很快的话一定会发现
TCP是主机对主机层的传输控制协议,提供可靠的连接服务,采用三次握手确认建立一个连接,四次挥手断开连接
位码即tcp标志位,有6种标示:SYN(synchronous建立联机) ACK(acknowledgement 确认) PSH(push传送) FIN(finish结束) RST(reset重置) URG(urgent紧急)
Sequence number(顺序号码) Acknowledge number(确认号码)
第一次握手:主机A发送位码为syn=1,随机产生seq number=1234567的数据包到服务器并进入SYN_SEND状态,主机B由SYN=1知道,A要求建立联机;
第二次握手:主机B收到请求后要确认联机信息,向A发送ack number=(主机A的seq+1),syn=1,ack=1,随机产生seq=7654321的包并进入SYN_RECV状态
第三次握手:主机A收到后检查ack number是否正确,即第一次发送的seq number+1,以及位码ack是否为1,若正确,主机A会再发送ack number=(主机B的seq+1),ack=1,主机B收到后确认seq值与ack=1则连接建立成功,客户端和服务器进入ESTABLISHED状态。
完成三次握手,主机A与主机B开始传送数据。
中断连接可以是Client端发起,也可以是Server端发起
假设Client端发起中断连接请求,也就是发送FIN报文。Server端接到FIN报文后,意思是说"我Client端没有数据要发给你了",但是如果你还有数据没有发送完成,则不必急着关闭Socket,可以继续发送数据。所以你先发送ACK,“告诉Client端,你的请求我收到了,但是我还没准备好,请继续你等我的消息”。这个时候Client端就进入FIN_WAIT状态,继续等待Server端的FIN报文。当Server端确定数据已发送完成,则向Client端发送FIN报文,“告诉Client端,好了,我这边数据发完了,准备好关闭连接了”。Client端收到FIN报文后,"就知道可以关闭连接了,但是他还是不相信网络,怕Server端不知道要关闭,所以发送ACK后进入TIME_WAIT状态,如果Server端没有收到ACK则可以重传。“,Server端收到ACK后,“就知道可以断开连接了”。Client端等待了2MSL后依然没有收到回复,则证明Server端已正常关闭,那好,我Client端也可以关闭连接了。Ok,TCP连接就这样关闭了!
相关问题:
1.为什么建立连接协议是三次握手,而关闭连接却是四次握手呢?
这是因为服务端的LISTEN状态下的SOCKET当收到SYN报文的建连请求后,它可以把ACK和SYN(ACK起应答作用,而SYN起同步作用)放在一个报文里来发送。但关闭连接时,当收到对方的FIN报文通知时,它仅仅表示对方没有数据发送给你了;但未必你所有的数据都全部发送给对方了,所以你可以未必会马上会关闭SOCKET,也即你可能还需要发送一些数据给对方之后,再发送FIN报文给对方来表示你同意现在可以关闭连接了,所以它这里的ACK报文和FIN报文多数情况下都是分开发送的.
2.为什么TIME_WAIT状态还需要等2MSL后才能返回到CLOSED状态?
这是因为虽然双方都同意关闭连接了,而且握手的4个报文也都协调和发送完毕,按理可以直接回到CLOSED状态(就好比从SYN_SEND状态到ESTABLISH状态那样);但是因为我们必须要假想网络是不可靠的,你无法保证你最后发送的ACK报文会一定被对方收到,因此对方处于LAST_ACK状态下的SOCKET可能会因为超时未收到ACK报文,而重发FIN报文,所以这个TIME_WAIT状态的作用就是用来重发可能丢失的ACK报文。
详解解释:http://www.cnblogs.com/cy568searchx/p/3711670.html
// 坑人的连续赋值问题
var a = {n: 1};
var b = a;
a.x = a = {n: 2};
console.log(a.x); // undefinde
console.log(b); // {n:1,x:{n:2}}
详解:https://www.zhihu.com/question/41220520
function f1(){
var n=999
nAdd=function(){
n+=1
console.log(n)
}
function f2(){
console.log(n)
}
return f2
}
f1()() // 999
nAdd() // 1000
nAdd() // 1001
require/exports 的用法只有以下三种简单的写法:
const fs = require('fs')
exports.fs = fs
module.exports = fs
而 import/export 的写法就多种多样:
import fs from 'fs'
import {default as fs} from 'fs'
import * as fs from 'fs'
import {readFile} from 'fs'
import {readFile as read} from 'fs'
import fs, {readFile} from 'fs'
export default fs
export const fs
export function readFile
export {readFile, read}
export * from 'fs'
详解:https://www.zhihu.com/question/56820346