一、基础知识
1. 数据类型
js 中的数据类型包含两部分:
1)基本数据类型(String、Number、Boolean、Null、Undefined、Symbol)
2)复杂数据类型(Object)
2. typeof 的作用
用于判断是否是某个数据类型:
var
a = 'a',
b = 1,
c = [1, 2, 3],
d = {a: 'a'},
e = new Date(),
f = new RegExp(),
g = true,
h = new Function(),
i = null,
j = undefined;
console.log(typeof a, typeof b, typeof c, typeof d, typeof e, typeof f, typeof g, typeof h, typeof i, typeof j)
// 输出
// string number object object object object boolean function object undefined
3. 引用类型
引用类型包括:Object、Array、RegExp、Date、Function、特殊的基本包装类型(String、Number、Boolean)以及单体内置对象(Global、Math)。
4. instanceof 的作用
用于判断是否是某个对象类型
var a = [1,2,3];
console.log(a instanceof Object); //true
console.log(a instanceof Array); //true
console.log(a instanceof RegExp); //false
5. 类型转换
js中有两种强制类型转换:
1)显示类型转换
-
转换为数值类型:Number(mix)、parseInt(string,radix)、parseFloat(string)
-
转换为字符串类型:toString(radix)、String(mix)
-
转换为布尔类型:Boolean(mix)
2)隐式类型转换
-
用于检测是否为非数值的函数:isNaN(mix);
-
递增递减操作符(包括前置和后置)、一元正负符号操作符
-
加法运算操作符
-
乘除、减号运算符、取模运算符
-
逻辑操作符(!、&&、||)
-
关系操作符(<, >, <=, >=)
-
相等操作符(==)
6. 原型链
推荐文章:https://www.jianshu.com/p/dee9f8b14771
7. 闭包
闭包是指有权访问另一个函数作用域中的变量的函数。
可以看下我写的另一篇关于闭包的文章:闭包的理解
8. 类和继承
五条规则:
-
所有的引用类型(数组、函数、对象)都具有对象特性,即可自由扩展属性
-
所有的引用类型(数组、对象、函数)有一个
__proto__
属性,属性值是一个普通的对象 -
所有的函数都有一个
prototype
属性,属性值也是一个普通的对象 -
所有的引用类型(数组、对象、函数),
__proto__
属性指向它的构造函数的prototype
属性 -
当试图得到一个对象的某个属性时,如果这个对象本身没有这个属性,那么回去他的
__proto__
(即它的构造函数的prototype
中寻找)
详情传送门:https://www.cnblogs.com/chengzp/p/extends.html
9. 变量提升与函数提升
这个很好理解,就是在某个 作用域 内,用 var 声明的变量以及函数声明会提升到作用域顶部。
console.log(a) // undefined
var a = 'haha';
console.log(a) // haha
由于变量提升,实际上是这样执行的:
var a;
console.log(a)
a = 'haha';
console.log(a)
函数也是一样,但要注意一点:只有函数声明才会提升整个函数代码块,函数表达式则只会提升标识符。
console.log(f1); // [Function: f1]
console.log(f2); // undefined
//函数声明
function f1() {
}
//函数表达式
var f2 = function () {
}
10. this 指向问题
可参考我的一篇文章:关于this的指向问题
11. new 操作符到底干了什么
就干了这些:
- 创建了一个新对象
- 将构造函数的作用域赋给新对象(因此 this 就指向了这个新对象)
- 执行构造函数中的代码(为这个新对象添加属性)
- 返回新对象
用代码表示就是:
var obj = {};
obj.__proto__ = Person.prototype;
Person.call(obj);
- 第一行,我们创建了一个空对象obj
- 第二行,我们将这个空对象的__proto__成员指向了Person函数对象prototype成员对象
- 第三行,我们将Person函数对象的this指针替换成obj,然后再调用Person函数,于是我们就给obj对象赋值了一个id成员变量,这个成员变量的值是”Person”
12. script标签的async和defer属性
有了async属性,表示后续文档的加载和渲染与js脚本的加载和执行是并行进行的,即异步执行;加载完立即执行。
有了defer属性,加载后续文档的过程和js脚本的加载(此时仅加载不执行)是并行进行的(异步),js脚本的执行需要等到文档所有元素解析完成之后,DOMContentLoaded事件触发执行之前。解析完执行。
如果存在多个有defer属性的脚本,那么它们是按照 加载顺序 执行脚本的;而对于async,它的加载和执行是紧紧挨着的,无论声明顺序如何,只要加载完成就立刻执行,它对于应用脚本用处不大,因为它完全不考虑依赖。
13. 为什么TCP连接三次握手,断开4次挥手
三次握手
- 第一次握手:客户端发送请求报文。
- 第二次握手:服务端接受报文并发送应答报文。
- 第三次握手:客户端发送应答报文
相当于
- 第一次握手: A给B打电话说,你可以听到我说话吗?
- 第二次握手: B收到了A的信息,然后对A说: 我可以听得到你说话啊,你能听得到我说话吗?
- 第三次握手: A收到了B的信息,然后说可以的,我要给你发信息啦!
四次挥手
- 主动方:server我要断开连接了。
- 被动方:等会,我还有一些响应的信息还没发给你。
。。。过了一会
- 被动方 :响应信息发好了,我要断开连接了。
虽然server信息发好了,但是要考虑到client还在接受信息于是server就在等client的确认信息。
- 主动方:接收好了,断开连接了。
14. 跨域问题
跨域:
浏览器对于javascript的同源策略的限制,例如a.cn下面的js不能调用b.cn中的js,对象或数据(因为a.cn和b.cn是不同域),所以跨域就出现了,上面提到的,同域的概念又是什么呢?简单的解释就是相同域名,端口相同,协议相同,请求的url地址,必须与浏览器上的url地址处于同域上,也就是域名,端口,协议相同。
jsonp的产生:
1. AJAX 直接请求普通文件存在跨域无权限访问的问题,不管是静态页面也好。
2. 不过我们在调用 js 文件的时候又不受跨域影响,比如引入 jquery 框架的,或者是调用相片的时候。
3. 凡是拥有 src 这个属性的标签都可以跨域例如 <script> <img> <iframe>。
4. 如果想通过纯 web 端跨域访问数据只有一种可能,那就是把远程服务器上的数据装进js格式的文件里。
5. 而 json 又是一个轻量级的数据格式,还被js原生支持。
6.为了便于客户端使用数据,逐渐形成了一种非正式传输协议,人们把它称作JSONP,该协议的一个要点就是允许用户传递一个 callback 参数给服务端。
基于iframe实现的跨域
要求两个域具有 aa.xx.com,bb.xx.com 这种特点;也就是两个页面必须属于一个基础域(例如都是 xxx.com ),使用同一协议和同一端口,这样在两个页面中同时添加 document.domain,就可以实现父页面调用子页面的函数,要点就是:通过修改document.domain 来跨子域。
15. 数组的用法与字符串的用法
我的另两篇文章 数组的一些用法总结 字符串的一些用法总结
二、算法知识
1. 数组去重
function arrayUnique(arr) {
let a = [];
let len = arr.length;
for (let i = 0; i < len; i++) {
if (a.indexOf(arr[i]) < 0) {
a.push(arr[i])
}
}
return a
}
let newArr = arrayUnique([1, 2, 3, 2, 3, 1, 23, 4]);
console.log(newArr) //[ 1, 2, 3, 23, 4 ]
function arrayUnique(arr) {
let json = {}
let newArr = []
let len = arr.length
for (let i = 0; i < len; i++) {
if (!json[arr[i]]) {
json[arr[i]] = true
newArr.push(arr[i])
}
}
return newArr
}
let newArr = arrayUnique([1, 2, 3, 2, 3, 1, 23, 4]);
console.log(newArr) //[ 1, 2, 3, 23, 4 ]
function arrayUnique(arr) {
return Array.from(new Set(arr));
}
let newArr = arrayUnique([1, 2, 3, 2, 3, 1, 23, 4]);
console.log(newArr) //[ 1, 2, 3, 23, 4 ]
function arrayUnique(arr) {
return [...new Set(arr)];
}
let newArr = arrayUnique([1, 2, 3, 2, 3, 1, 23, 4]);
console.log(newArr) //[ 1, 2, 3, 23, 4 ]
2. 数组降维
var arr = [1, [2, [3, [4, [5, [6, [7]]]]]]]
function dimensionalityReduction(arr){
var tempArr = Array.from(arr.join('-'));
for(var i = 0;i<tempArr.length;i++){
if(isNaN(tempArr[i])){
tempArr.splice(i,1)
}
}
return tempArr; // 1,2,3,4,5,6,7
}
let flatten = arr => arr.reduce((pre,cur)=>{
Array.isArray(cur)?pre.push(...flatten(cur)):pre.push(cur);
return pre //1,2,3,4,5,6,7
},[])
3. 输出字符中最长的没有重复字符的字串
var str = 'aadacd'
function getMaxUniquePart(str) {
let res = 0;
let unqiueStr = '';
let maxStr = '';
let len = str.length;
for (let i = 0; i < len; i++) {
let a = str.charAt(i);
let index = unqiueStr.indexOf(a)
if (index === -1) {
unqiueStr += a
if (res <= unqiueStr.length) {
res = unqiueStr.length
maxStr = unqiueStr
}
} else {
unqiueStr = unqiueStr.substr(index + 1) + a
}
}
return maxStr
}
console.log(getMaxUniquePart(str)) //dac
这种方法有一个不足,如果有多个长度相同的子串则无法得到多个字串,只会输出第一个。
var str = 'aadacd'
function getMax(data) {
data = data.trim();
let obj = {
start: 0
};
let nowStr = '';
let maxStr = '';
let strGroup = []
let maxLen = 0;
let len = data.length;
for (let i = 0; i < len; i++) {
let char = data[i];
if (!(obj[char] >= 0)) { // 若不重复
obj[char] = i;
nowStr += char;
} else { // 若重复
if (obj.start <= obj[char]) { // 若start指针在重复字符(重复字符的第一个字符)之前
obj.start = obj[char] + 1; // 改变start指针位置
obj[char] = i; // 改变这个字符的位置
if (nowStr.length > maxLen) { // 比较当前子串跟最长子串的长度
maxStr = nowStr; // 若大于,则替换
maxLen = nowStr.length;
nowStr = data.substring(obj.start, i + 1);
} else if (nowStr.length === maxLen) { // 若等于,则隔一个空格加在末尾
maxStr += ' ' + nowStr;
nowStr = data.substring(obj.start, i + 1);
} else { // 若小于,仅更新nowStr
nowStr = data.substring(obj.start, i + 1);
}
} else { // 若start指针在重复字符(重复字符的第一个字符)之后
obj[char] = i; // 改变这个字符的位置
nowStr = data.substring(obj.start, i + 1);
}
}
}
if (nowStr.length > maxLen) {
maxStr = nowStr;
maxLen = nowStr.length;
} else if (nowStr.length === maxLen) {
maxStr += ' ' + nowStr;
}
return maxStr
}
console.log(getMax(str)) //dac acd
不知是否还有更为简单的方式,望告知。
4. 输入一个数组和一个数,找出数组中两个值之和为该数,返回该俩个值的位置
function getOrder(arr, sum) {
let a = {}
let b = []
for (let i = 0; i < arr.length; i++) {
let c = sum - arr[i]
if (a[c] === undefined) {
a[arr[i]] = i
} else {
b.push([a[c], i])
}
}
return b
}
console.log(getOrder([1, 17, 8, 20, 5, 9, 0], 25)) //[ [ 1, 2 ], [ 3, 4 ] ]