var a = 1;
function changeA() {
console.log(a);
var a;
}
changeA();
复制代码
猜猜输出啥,undefined,由于函数做用域内发生了变量提高,函数内的代码至关于
var a; console.log(a);
html
var a = 1;
function changeA() {
a = 2;
console.log(a);
var a;
}
changeA();
复制代码
猜猜输出啥,a=2,由于变量提高是直接到函数做用域顶部,在输出a以前,a已经赋值2,且函数做用域内a是局部变量web
var a = 1;
function changeA(a) {
console.log(a);
var a;
}
changeA(a);
复制代码
输出 1,由于a是传参进来的,至关于在函数做用域内a已经声明和赋值了 另外:ajax
(function () {
a=5;
console.log(window.a) // undefined,发生了变量提高
var a = 10;
})() // 这个自执行保证了 `var a` 发生变量提高时,不会污染到全局window.a
复制代码
函数的声明优先级低于变量:算法
var sum = function () {
console.log(1);
}
function sum() {
console.log(2);
}
sum() // 1
复制代码
函数声明提高会被后面的覆盖chrome
foo(); // 3
function foo() {
console.log( 1 );
}
function foo() {
console.log( 3 );
}
复制代码
if-else语句里面的函数声明都有效json
foo(); // "b"
var a = true;
if (a) {
function foo() { console.log("a"); }
} else {
function foo() { console.log("b"); }
}
复制代码
函数调用中,传递是一个数值,咱们称为 “值传递”。 函数调用中,传递是对象,通常称为 “引用传递”。引用类型:Object Array Fucntion;对于传参而言,传入的东西是不变的。segmentfault
var a = 1;
function changeA(a) {
a++;
}
changeA(a);
console.log(a);
复制代码
这里会输出1,a的值没有改变跨域
var a = [1, 2, 3];
function changeA(a) {
a[0] = 2;
}
changeA(a);
console.log(a);
复制代码
这里输出[2, 2, 3],传入的是指针,指针没有改变,而值发生了变化
再看看下面的例子,若是数组以...arr的方式传递,会发生什么数组
var a = [1, 2, 3];
function changeA(...a) {
a[0] = 2;
}
changeA(a);
console.log(a); // [1, 2, 3]
复制代码
引用类型全等于永远返回false,由于变量存储的是地址值,好比new Object浏览器
NaN === NaN // false
+0 === -0 // true
+0 === 0 // true
-0 === 0 // true
+Infinity === Infinity // true
+Infinity === -Infinity // false
-Infinity === Infinity // false
null === undefined // false
[] === [] // false
{} === {} // false
复制代码
Javascript是弱类型语言,之因此不一样的数据类型之间能够作运算,是由于JavaScript引擎在运算以前会悄悄的把他们进行了隐式类型转换的。
如下假设为比较 x == y的状况,Type(x)指的是x的数据类型,Type(y)指的是y的类型,最终返回值只有true或false。
参考教程
其实第一次读的时候,仍是有点理解不了。仍是系统的剖析一下吧,简单点来讲,比较x == y
这个表达式,当 x 和 y 相同,直接进行值比较。当 x 和 y 不一样,最终必定会把 x 和 y 转换为要么number、要么string再作值比较。
当 x 和 y 都是 基础类型 时,会转化为相同的基础类型再比较,boolean类型会转化为Number类型再作比较,当有一方是Number时,最终会转化为Number类型再比较,Number类型的优先级是最高的,因此须要熟悉一下Number(x)
这个函数,Number(x)
和+x
效果是同样的,枚举一下可能碰见的状况吧:
Number('1.5') //1.5
Number('1,2') //NaN
Number({}) //NaN
Number([]) //0
Number([2]) //2
Number(true) //1
Number(null) //0
Number(undefined) //NaN
当 x 和 y 两个都是引用类型,好比数组、对象、Function,会直接返回false,由于 x 和 y 储存的是地址。
当 x 和 y 一方是引用类型时,会进行 ToPrimitive(x || y)
的转化。若是 x.valueOf
能返回基础数据类型,则会优先调用 valueOf
方法,若是不能返回基础数据类型,则会调用 toString
方法。toString
必定会返回基础数据类型。
找几个例子说说:
true == true
Number(true) == true
=>1 == Number(true)
=>1 == 1
左值true
先转化为数字,右值true
再转化为数字,而后进行值比较
答案:true
true == '123'
Number(true) == '123'
=>1 == Number('123')
=>1 == 123
true先转化为数字,再把'123'转化为数字123,再比较左值和右值
答案:false
'123' == 2
Number('123') == 2
=>123 == 2
'123'先转化为数字,再比较左值和右值
答案:false
[1,2] == 2
toToPrimitive([1,2]) == 2
=>'1,2' == 2
=>Number('1,2') == 2
=>NaN == 2
只解析一项,NaN也是Number类型的一种,NaN与任何数字比较,返回false
答案:false
[1,2,3].valueOf() //[1,2,3] 不是基础数据类型
[1,2,3].toString() //1,2,3,数组类型toPrimitive会调用这个方法
复制代码
var c = {a:1}; //必须赋值,{a:1}.valueOf()会报错
c.valueOf() //{a:1}不是基础数据类型
c.toString() //[object Object],toPrimitive会调用这个方法
c == '[object Object]' //true
复制代码
解释一下为啥 c == '[object Object]'
返回true
toPrimitive(c) == '[object Object]'
=>'[object Object]' == '[object Object]'
function b() { return 2;}
b.valueOf() //返回函数自己,即function b() { return 2;}
b.toString() //返回字符串,即'function b() { return 2;}',toPrimitive采用这个
b == 'function b() { return 2;}' //true
复制代码
不介绍常见的状态码,主要针对1XX到5XX能加分的详细说明
100 - Continue 初始的请求已经接受,客户应当继续发送请求的其他部分
101 - 服务器已经理解了客户端的请求,并将经过Upgrade消息头通知客户端采用不一样的协议来完成这个请求
202 - 接受和处理、但处理未完成
204 - 服务器成功处理了请求,但没有返回任何内容
206 - 服务器已经完成了部分用户的GET请求
303 - 临时重定向,和302状态码有着相同的功能,可是303明确表示客户端应当采用get方式请求资源
307 - 临时重定向,和302状态码有着相同的功能,当30一、30二、303响应状态码返回时,几乎全部浏览器都会把post改为get,并删除请求报文内的主体,以后请求会自动再次发送。307会遵守浏览器标准,不会从post变为get。可是对于处理响应时的行为,各类浏览器有可能出现不一样的状况。
400 - 语义有误,当前请求没法被服务器理解。除非进行修改,不然客户端不该该重复提交这个请求
401 - 当前请求须要用户验证。该响应必须包含一个适用于被请求资源的 WWW-Authenticate 信息头用以询问用户信息,浏览器据此显示用户名字/密码对话框,而后在填写合适的Authorization头后再次发出请求
403 - Forbidden,服务器已经理解请求,可是拒绝执行它
405 - 请求行中指定的请求方法不能被用于请求相应的资源。该响应必须返回一个Allow 头信息用以表示出当前资源可以接受的请求方法的列表
410 - Gone,被请求的资源在服务器上已经再也不可用,并且没有任何已知的转发地址。这样的情况应当被认为是永久性的
414 - Request-URI Too Long,请求的URI 长度超过了服务器可以解释的长度,所以服务器拒绝对该请求提供服务
415 - Unsupported Media Type,对于当前请求的方法和所请求的资源,请求中提交的实体并非服务器中所支持的格式,所以请求被拒绝
421 - too many connections,从当前客户端所在的IP地址到服务器的链接数超过了服务器许可的最大范围。一般,这里的IP地址指的是从服务器上看到的客户端地址(好比用户的网关或者代理服务器地址)
500 - Internal Server Error,做为网关或者代理工做的服务器尝试执行请求时,从上游服务器接收到无效的响应
502 - Bad Gateway,做为网关或者代理工做的服务器尝试执行请求时,从上游服务器接收到无效的响应
503 - Service Unavailable,因为临时的服务器维护或者过载,服务器当前没法处理请求
504 - Gateway Timeout,做为网关或者代理工做的服务器尝试执行请求时,未能及时从上游服务器(URI标识出的服务器,例如HTTP、FTP、LDAP)或者辅助服务器(例如DNS)收到响应
一个域名的DNS记录会在本地有两种缓存:浏览器缓存和操做系统(OS)缓存,会优先访问浏览器缓存,若是未命中则访问OS缓存,最后再访问DNS服务器(通常是ISP提供),而后DNS服务器会递归式的查找域名记录,而后返回。
DNS记录会有一个ttl值(time to live),单位是秒,意思是这个记录最大有效期是多少。通过实验,OS缓存会参考ttl值,可是不彻底等于ttl值,而浏览器DNS缓存的时间跟ttl值无关,每种浏览器都使用一个固定值。 修改hosts文件以后,为啥有时会马上生效,有时却一直不生效呢?在修改hosts文件后,全部OS中DNS缓存会被清空,而浏览器缓存则不发生变化,在 chrome://net-internals/#dns
中点击 Clear Host Cache
会清空OS缓存。若是发现DNS更改不成功,能够静待几十秒。
浏览器在获取网站域名的实际IP地址后会对其IP进行缓存,减小网络请求的损耗。每种浏览器都有一个固定的DNS缓存时间,其中Chrome的过时时间是1分钟,在这个期限内不会从新请求DNS。chrome://net-internals/#dns
OS缓存会参考DNS服务器响应的TTL值,可是不彻底等于TTL值
先检查一下本身的缓存中有没有这个地址,有的话就直接返回,没有的话就去根域找,从根域开始递归查询IP
根域的地址是写死在ISP DNS服务器上的,根域便是/;好比www.a.com这样的域名,先去根域找.com的服务器对应IP,而后.com的服务器对应IP找到a.com的服务器IP...
先通关正则,打好基础,不然会看不明白,戳我。在正则优化的状况下,使用new RegExp会比较快。缘由是new RegExp会在代码执行的过程当中编译正则,编译就是在内存中开辟一个空间存放变量或函数,字面量有个废弃的compile方法也能够作到这个事情。
腾讯QQ号从10000开始,最少5位
[1-9][0-9]{4,}
复制代码
\s表示匹配一个空白符,包括空格、制表符、换页符、换行符和其余 Unicode 空格,具体点就是[ \f\n\r\t\v\u00a0\u1680\u180e\u2000\u2001\u2002\u2003\u2004 \u2005\u2006\u2007\u2008\u2009\u200a\u2028\u2029\u202f\u205f \u3000
/(^\s+)|(\s+$)/g
复制代码
将阿拉伯数字每三位一逗号分隔,如:15000000转化为15,000,000
'1500000000000'.replace(/\B(?=(\d{3})+$)/g,',')
复制代码
[\u4e00-\u9fa5]
复制代码
DNS规定,域名中的标号都由英文字母和数字组成,每个标号不超过63个字符,也不区分大小写字母。标号中除连字符(-)外不能使用其余的标点符号。级别最低的域名写在最左边,而级别最高的域名写在最右边。由多个标号组成的完整域名总共不超过255个字符。
/[a-zA-Z0-9][-a-zA-Z0-9]{0,62}(/.[a-zA-Z0-9][-a-zA-Z0-9]{0,62})+/
复制代码
分析:第一,不区分大小写;第二,带'的也算在里面,好比don't;第三,带-的也算在里面,好比x-ray
var str = "When you are old and grey and full of sleep, don't make me think. And hid his face amid a crowd of stars.";
str.match(/[a-z]+([-'][a-z]+)?/ig);
复制代码
分析:参数名=值,不一样参数间用&分隔开
var name= 'rqlang';
reg = new RegExp('[&?]'+name+'=([^& ]+)')
str = 'https://www.baidu.com/s?ie=utf-8&f=8&rsv_bp=1&rsv_idx=1&tn=baidu&wd=%E6%AD%A3%E5%88%99%E8%8E%B7%E5%8F%96%E6%9F%90%E4%B8%AA%E5%8F%82%E6%95%B0&oq=%25E6%25AD%25A3%25E5%2588%2599%25E8%258E%25B7%25E5%258F%2596%25E6%259F%2590%25E4%25B8%25AA%25E5%258F%2582%25E6%2595%25B0&rsv_pq=baac736e00031a97&rsv_t=515dwC%2Bf5ZRACPl1sr7KbYkxhUwe9G1VxfTPLWRBaQ9vh4Oa8jc6nfh0pQg&rqlang=cn&rsv_enter=1&inputT=2550&rsv_sug3=107&rsv_sug1=103&rsv_sug7=100&rsv_sug2=0&rsv_sug4=3469'
val = str.match(reg)[1];
复制代码
升级一下,获取url中全部的参数名
var propReg = /([?&][^=]+)/g,
str = location.search,
arr = str.match(propReg);
arr.forEach(function(val, index, arr) {
arr[index] = arr[index].replace(/^(&|\?)/, '');
});
console.log(arr); // 全部的参数名
复制代码
再升级一下,获取url中全部参数对键值,以对象的方式展示
var reg = /([&\?][^=]+)=([^& ]+)/g,
str = location.search,
arr = str.match(reg);
arr.reduce(function(obj, val, index, arr) {
var reg = /[&?]([^=]+)=([^& ]+)/,
tmpArr = val.match(reg);
obj[tmpArr[1]] = tmpArr[2];
return obj;
}, {});
复制代码
cookie的特征,以';'为分隔,末字段也许不带';',prop值前面匹配一个或0个空格
function getCookie(name) {
var arr,reg=new RegExp("(^| )"+name+"=([^;]*)(;|$)");
if(arr=document.cookie.match(reg))
return unescape(arr[2]);
else
return null;
}
复制代码
readyState各类值,记不住不要紧,记住这几个状态都是围绕着XMLHttpRequest对象自己,且最后一个状态是4就能够了:
var xmlHttp = new XMLHttpRequest();
xmlHttp.onreadystatechange = function () {
// readyState
if (this.readyState == 4 && this.status == 200) {
console.log(1);
}
}
xmlHttp.open('GET', 'https://www.afea.com', false);
xmlHttp.setRequestHeader('X-Requested-With');
xmlHttp.withCredentials = true; // 跨域请求携带cookie
xmlHttp.send(null);
复制代码
var data = {};
object.defineProperty(data, 'text', {
set(value) {
$input.value = value;
this.value = value;
}
});
$input.onChange = function () {
data.text = e.target.value;
}
复制代码
形如sum(1,2)(3)(4,5)
的样子:
sum(1,2) // 返回3
sum(3)(1,2) // 返回6
sum(1,3)(5)(2) // 返回11
复制代码
思路是利用闭包,改写toString方法。
function add () {
var total = 0;
var args1 = [...arguments];
var sum = function (...args) {
total = args1.concat(args).reduce((total, a) => total += a, total)
return sum;
}
sum.toString = function() { // sum方法调用,return必然会调用toString方法
return total;
}
return sum;
}
复制代码
bind原是Function.prototype上的方法,能够修改函数的this指向
Function.prototype.bind = function (context) {
// this指向调用函数
if (typeof this !== 'function') {
throw new TypeError('not a function');
}
var args = Array.prototype.slice.call(arguments, 1);
return function () {
return this.apply(context, args.concat(Array.prototype.slice.call(arguments)))
}
}
复制代码
浅拷贝只复制指向某个对象的引用地址,而不复制对象自己,新旧对象仍是共享同一块内存。但深拷贝会另外创造一个如出一辙的对象,新对象跟原对象不共享内存,修改新对象不会改到原对象。浅复制只复制一层对象的属性,而深复制则递归复制了全部层级。
function deepClone(target) {
if (typeof target !== 'object' ) return target;
var arr_tmp = target instanceof Array ? [] : {};
for (var i in target) {
arr_tmp[i] = typeof target[i] !== 'object' ? target[i] : deepClone(target[i]);
}
return arr_tmp;
}
复制代码
递归实现,若是当前元素是数组,则继续递归,不然加入最终返回的扁平化数组
const flattern = (arr) => arr.reduce((a, item) => Array.isArray(item) ? a.concat(flattern(item)) : a.concat(item), [])
复制代码
循环利用队列
const flattern = (arr) => {
var finalArray = [];
if (!Array.isArray(arr)) return;
while(arr.length) {
var target = arr.shift();
if (Array.isArray(target)) {
arr = target.concat(arr);
} else {
finalArray.push(target)
}
}
return finalArray;
}
复制代码
防抖主要是为了限制函数的执行频次,以优化函数执行频次太高致使的响应速度跟不上触发频率的问题。若是倒计时没有结束,则清空倒计时,再从新计时。有个弊端,若是事件不断循环触发,而且小于等待时间,则不可能执行回调事件,因此后来又催生了节流。
function debounce(fn, wait, immediate) {
var timer;
return function() {
var that = this, args = arguments;
if (immediate) {
fn.apply(that, args);
immediate = false;
}
clearTimeout(timer);
timer = setTimeout(function() {
fn.apply(that, args);
}, wait);
}
}
复制代码
节流主要是为了限制函数的执行频次,以优化函数执行频次太高致使的响应速度跟不上触发频率的问题。要记住这个概念,能够联想水龙头滴水,水的必定量的,会一滴一滴的流出去,可是不必定会流光,时间间隔是必定的。直接上代码:
function throttle(fn, wait, immediate) {
var timer, previous = new Date().getTime();
return function() {
var that = this, args = arguments, now = new Date().getTime();
if (immediate) {
fn.apply(that, args);
immediate = false;
}
if (wait >= now-previous) { // 到必定时间必定执行一次
previous = now;
fn.apply(that, args);
clearTimeout(timer);
} else {
timer = setTimeout(function() {
previous = new Date().getTime();
fn.apply(that, args);
}, wait);
}
}
}
复制代码
要求:写一个对象,实现调用a.work().sleep(20).lunch()
,会输出
work
sleep 20 seconds
(wait 20 seconds)
lunch
复制代码
用队列实现链式调用,当调用链很长的时候递归,实际上是会报栈溢出的,最好在run里加个setTimeout 0
let a = {
query: [],
status: false,
run: function () { // 关键点,递归执行函数
setTimeout(() => {
if (this.status) return; // 若是队列还在运行中,则返回
if (this.query.length > 0) {
this.query.shift()(this.run.bind(this))
}
}, 0)
},
work: function () {
this.query.push((fn) => {
console.log('work');
fn();
});
this.run();
return this;
},
lunch: function () {
this.query.push((fn) => {
console.log('lunch');
fn();
});
this.run();
return this;
},
sleep: function (time) {
this.query.push((fn) => {
this.status = true; // 只有异步会阻塞队列执行而已,因此status的更新放在这里
console.log(`sleep ${time} seconds`);
setTimeout(() => {
this.status = false;
fn();
}, time * 1000);
});
this.run();
return this;
}
}
复制代码
假设有一个有序数组,须要查找一个数值为3的元素,若是存在,返回第一个元素的下标,不然返回-1。
function binarySearch(arr, target) {
var low = 0, high = arr.length-1, mid;
while(low < high) {
mid = ~~((low + high)/2);
if(arr[mid] < target) {
low = mid+1;
} else {
high = mid;
}
}
if (arr[low] === target) return low;
else return -1;
}
复制代码
相似整理扑克牌,将每一张牌插到其余已经有序的牌中适当的位置。冒泡和选择就不用说了,一个正方向一个反方向,两个for循环搞定的。
for (var i = 1; i < arr.length; i++) {
for(var j=i;j > 0 && arr[j] < arr[j-1]; j--;) {
[arr[j], arr[j-1]] = [arr[j-1], arr[j]]
}
}
复制代码
原理戳这,插入排序的晋级版,以gap为界限分为一组,每一组进行插入排序计算,一开始时,通常来讲gap=length/2,因此稳定的复杂度为nlogn
function heerSort(arr) {
var gap = ~~(arr.length/2); // 取整居然比/优先级高,只能用括号补了
for (var i=gap;i>0;i=~~(i/2))
for (var j=i;j<arr.length;j+=i)
for(var k=j;k>=i&&arr[k]<arr[k-i];k-=i)
[arr[k],arr[k-i]]=[arr[k-i],arr[k]];
}
复制代码
原理戳这,是利用归并的思想实现的排序方法,该算法采用经典的分治(divide-and-conquer)策略(分治法将问题分(divide)成一些小的问题而后递归求解,而治(conquer)的阶段则将分的阶段获得的各答案"修补"在一块儿,即分而治之)。
function mergeSort(arr, low, high, temp) {
if (low < high) {
var mid = parseInt((low+high)/2);
mergeSort(arr, low, mid, temp);
mergeSort(arr, mid+1, high, temp);
merge(arr, low, high, temp);
}
}
function merge(arr, low, high, temp) {
var mid = parseInt((low+high)/2),
i = low,
j = mid+1,
k = 0;
while (i<=mid&&j<=high) {
if (a[i] < a[j]) {
temp[k++] = a[i++];
} else {
temp[k++] = a[j++];
}
}
while(i<=mid) {
temp[k++] = a[i++];
}
while(j<=high) {
temp[k++] = a[j++]
}
for (var i=0;i<k;i++) {
a[low+i] = temp[i];
}
}
复制代码
简单点来讲,就是以一个数为基准(一般是最左边的数),把这个序列小于这个数的数放在这个数的左边,若是大于这个数,则放在右边。平均性能O(nlogn),最坏性能是O(n2)至关于插入排序,在正序和逆序的时候出现,递归划分为有一边为0个
function quickSort(arr, low, high) {
var i = low,
j = high,
temp = arr[i];
if(i>=j) return;
while(i<j) {
while(i<j&&arr[j]>=temp)
j--;
if(i<j)
arr[i]=arr[j];
while(i<j&&arr[i]<=temp)
i++;
if(i<j)
arr[j]=arr[i];
}
arr[i]=temp;
quickSort(arr, low,i-1);
quickSort(arr, i+1, high);
}复制代码