# JavaScript
## 概念
### AJAX
广义上的 AJAX 是指 XMLHttpRequest,也就是 ECMAScript 中用于网络交互的操作。
现在的 AJAX 大多数指的是 JQuery 中的 AJAX 函数。
JQuery 中的 AJAX 封装了大量网络交互操作,使其更加方便快捷。
### this 关键字
首先介绍一下函数不同的调用方法:
普通函数调用;作为方法来调用;作为构造函数来调用;使用apply/call方法来调用;Function.prototype.bind方法;es6箭头函数。
> 谁调用了方法,this 就指向谁。
**普通函数调用**
```javascript
function person() {
this.name = "xiaoming";
console.log(this);
console.log(this.name);
}
person(); //print window, xiaoming
```
在这段代码中,person() 作为普通函数调用,实际上 person 是作为全局对象 window 的一个方法来进行调用的,即 window.person()。
所以在这里,是 window 调用了 person 方法,那么 person() 中的 this 就指向 window,并且 window 还有了一个属性 name,值为 xiaoming。
同样的:
```javascript
var name = "xiaoming"
function person() {
console.log(this);
console.log(this.name);
}
person(); // print window, xiaoming
```
和上边代码的结果是相同的,因为 this 指向 window, 所以 this.name 的值还是 xiaomimg。
**作为方法来调用**
```javascript
var name = "xiao hong";
var person = {
name: "xiao ming",
printName: function() {
console.log(this.name);
},
}
// print xiao ming
// person 对象调用 printName() 方法,所以 this 指向 person 对象,所以打印 xiao ming。
person.printName();
var printName = person.showName; // 注意没有 ()
// print xiao hong
// 将 person 类中的方法 printName() 赋给了 printName 变量,当直接调用 printName() 的时候,this 指向的是 window,所以打印 xiao hong。
printName();
```
**作为构造函数来调用**
```javascript
function Person(name) {
this.name = name;
}
var person1 = Person("xiao ming");
console.log(person1.name); // error:undefine
// 因为没有使用 new 操作,等同于 window 调用了 Person() 方法,因此 this 指向的是 window对象。
console.log(window.name); // xiao ming
var person2 = new Person("xiao hong")
console.log(person2.name); // xiao hong
console.log(window.name); // xiao ming
```
下边讲解一下 new 操作:
```javascript
function Person(name) {
var o = {},
o.__proto__ = Person.prototype; // 原型继承
Person.call(o, name);
return o;
}
// 注意,此处为伪代码,并不能实际运行,如此写是为了让读者更明白 new 操作符的作用。
```
**call/apply 方法的调用**
call、apply 方法的作用是改变 this 的作用域。
```javascript
var name = "xiao hong";
var Person = {
name: "xiao ming",
printName: function() {
console.log(this.name);
}
}
Person.printName(); // xiao ming
Persin.printName.call(); // xiao hong
```
**箭头函数(ES6)**
箭头函数的 this 指向固定化,始终指向外部对象,因为箭头函数没有 this,因此它自身不能进行 new 操作实例化,同时也不能使用 call、apply、bind 来改变 this 的指向。
## 操作符
### == && ===
例:value1 == value2 , value3 === value4,
== :
1. 如果两个值类型相同,再进行三个等号(===)的比较。
2. 如果两个值类型不同,也有可能相等,需根据以下规则进行类型转换在比较:
1. 如果一个是null,一个是undefined,那么相等。
2. 如果一个是字符串,一个是数值,把字符串转换成数值之后再进行比较。
===:
1. 如果类型不同,则一定不相等。
2. 如果两个都是数值并且是同一个值,那么相等。
3. 如果一个是数值一个是 NaN,那么不相等。
4. 如果都是true 或者 false 相等。
5. 如果都是 null 或者 undefined,那么相等。注意,null == undefined => true, null === undefined => false.
总结:
1. string、number 等基础类型,== 与 === 的区别在于,== 会转换类型再进行比较,=== 不会转换类型比较。
2. array、object 等高级类型,== 与 === 没有区别。
3. 基础类型与高级类型比较,区别在于,== 将高级转换为基础类型,再进行比较,=== 因为类型不同,直接返回false
### equals
JavaScript 中字符串没有 equals 方法,但是可以自己实现。
## 函数
### ready() && onload()
- document.ready()
是DOM结构绘制完成之后即可执行,不需要相关联资源下载完成,例如图片DOM结构绘制完成,而图片可能没有下载完成,其函数同样会执行。
- document.onload()
相关资源都加载完毕之后,执行,一个页面中只能有一个onload
- load函数
常用在图片的加载,比如 $('idName').load(),load同样是资源全部加载完成之后才会执行。
> JavaScript 没有 document.ready(),但是有 document.readyState,返回当前页面情况。
### apply && call && bind
#### 相同点:
- 都是用来改变函数的this对象的指向的
- 第一个参数都是this要指向的对象
- 都可以继续传递参数
```javascript
var xb = {
name: '小冰',
gender: '女',
say: function(){
alert(this.name + ',' + this.gender);
}
}
var other = {
name: '小东',
gender: '男',
}
xb.say(); ===> 小冰,女
xb.say.call(other); ==> 小东,男
xb.say.apply(other);
xb.say.bind(other)();
```
#### 三个方法的不同点
- `call`和 `apply`方法,都是对函数的直接调用,但是 `bind()` 方法需要加上()来执行
- `call`和 `apply`方法传参的方法不同
```javascript
xb.say.call(other,'斯坦福','3')。
xb.say.apply(other, 'sitanfu','third');
```
- `bind` 返回的是方法,所以需要后边加上()才能执行。
#### 实现 bind 方法
```javascript
if (!function() {}.bind) {
Function.prototype.bind = function(context) {
var self = this,
args = Array.prototype.slice.call(arguments);
return function() {
return self.apply(context, args.slice(1));
}
};
}
```
### Typeof
Typeof 把参数的类型信息作为字符串返回。
可能的值有:
- number
- string
- boolean
- object
- function
- undefined
> 注意:typeof(null) 返回object。
### 定时器
- `setTimeout(.... , time)` 在指定的时间(time) 后调用函数或者计算表达式。
- `setInterval(code,millisec,lang)`, lang传递给执行函数的其他参数。会在指定的周期(millisec)不断调用函数(code),直到 clearInterval() 清除。
### slice && splice (操作数组)
#### slice (截取数组,不改变原数组)
> 接收一个或两个参数,它可以创建一个由当前数组中的一项或多项组成的新数组,也就是说它不会修改原来数组的值。
用法:
- slice( para1 ),会截取从para1开始的到原数组最后的部分;
- slice(para1,para2)会截取原数组的从para1开始的para2-para1个数组。
> 注意:当两个参数中存在负数时,用原数组的长度加上两个负数的参数作为相应的参数来计算,并且得到的第一个参数必须小于第二个参数,否则返回一个空数组。
```javascript
var a = [1,2,3,4,5,6];
a.slice(-2,-3) //return []
a.slice(-3,-2) //return [4]
```
#### splice (删除/替换 数组中的某几位,返回删除的数组)
用法:
- splice( para1,para2 ) : 删除数组中任意数量的项,从para1开始的para2项。
> 注意的是用splice删除数组中的值会直接将某几项从数组中完全删除,会导致数组length值的改变,这与delete的删除置为undefined是不一样的。
- splice( para1,para2,val1,val2… ):向数组中添加和删除项,para1表示可以添加的项数,para2表示删除的项数,后面的变量表示要添加的项的值,注意是从para1之后开始删除和添加的。
> 参数为负数的问题,如果para1为负数,则会加上数组的长度作为para1的值,而para2为负数或0的话不会执行删除操作,此时从 para1 当前位置开始添加变量。
### split (操作字符串)
根据特定的字符切割字符串并返回切割后生成数组。
> 如果 a.split(''), 则切割每一个字符。
```javascript
var string = 'abcde';
var array = string.split('');
// array = ['a','b','c','d','e']
```
### reverse (反转数组中的元素)
```javascript
var a = [1,2,3,4];
a.reverse();
//a = [4,3,2,1]
```
### join (用于将数组转换为字符串)
> 不改变原数组
```javacript
var a = [1,2,3,4];
a.join('') // return '1234'
a.join('.') // return '1.2.3.4'
//此时,a = [1,2,3,4]
```
## 综合
### 垃圾回收机制
Js具有自动回收垃圾的机制,垃圾收集器会按照固定的时间间隔周期性执行。
- 标记清除
工作原理:当变量进入环境时,将这个变量标记为“进入环境”,当变量离开环境时,则将其标记为“离开环境”。标记为“离开环境”的就会被回收。
- 引用计数
跟踪记录每个值被引用的次数,当一个值被引用的次数为0时,垃圾收集器下次运行时就会将值回收。
### 内存泄漏
| 问题 | 解决办法|
|-------------------------------------------------------|---------------|
|全局变量引起的内存泄漏 |严格使用全局变量|
|闭包引起的内存泄漏。闭包可以维持函数内部变量,使其得不到释放|避免闭包,或者在外部事件处理函数中删除对DOM的引用|
|被遗忘的定时器或者回调函数 |删除定时器|
### JS对象属性遍历
1. 使用 Object.keys(objectName).forEach(function(element,index){ .... } 便利
2. for( var i in object ) {...}
3. Object.getOwnPropertyNames(), 返回一个数组,包含自身的所有属性(不包含symbol属性,但是包含不可枚举属性)。
4. Reflect.ownKeys(obj) ,包含所有属性,包含symbol属性和不可枚举属性
```javascript
var obj = {
a: 'aaaaa',
b: 'bbbbb',
c: 'ccccc',
}
for ( var i in obj ) {
console.log(i, obj[i]);
}
//输出结果
//a aaaaa
//b bbbbb
//c ccccc
Object.keys(obj).forEach(
function(ele,index) {
console.log(ele,obj[ele],index)
})
//输出结果
a dfafda 0
b afdaf 1
c ssssss 2
```
### cookies && storage
`cookies`、`localStorage`、`sessionStorage` 都是在客户端以键值对存储的存储机制,并且只能将值存储为字符串。
`sessionStorage`常用操作方法:
- session.clear(), 清空
- session.setItem('key','value')
- session.getItem('key')
- session.removeItem('key')
- session.length,
| |cookies | localStorage | sessionStorage|
|--------------|--------|--------------|---------------|
|初始化 |客户端或服务器,服务器可使用Set-Cookies请求头|客户端|客户端|
|过期时间 |手动设置|永不过期 |当前页面刷新、关闭|
|与域名关联 |是 |否 |否|
|容量 |4kb |5mb |5mb|
|随请求发给服务器|是 |否 |否|
### 冒泡/捕获/事件委托/事件代理
`事件委托` 就是利用事件冒泡,只指定一个事件处理程序,就可以管理某一类型的所有事件。
举例:
有三个同事预计会在周一收到快递。为签收快递,有两种办法:一是三个人在公司门口等快递;二是委托给前台MM代为签收。现实当中,我们大都采用委托的方案(公司也不会容忍那么多员工站在门口就为了等快递)。前台MM收到快递后,她会判断收件人是谁,然后按照收件人的要求签收,甚至代为付款。这种方案还有一个优势,那就是即使公司里来了新员工(不管多少),前台MM也会在收到寄给新员工的快递后核实并代为签收。
1. 现在委托前台的同事是可以代为签收的,即程序中的现有的dom节点是有事件的;
2. 新员工也是可以被前台MM代为签收的,即程序中新添加的dom节点也是有事件的。
发生事件的元素可以使用 `ev.target` 获取到,利用事件冒泡。
[参考文章](https://www.cnblogs.com/liugang-vip/p/5616484.html)
> 注意:$("ul").on("click","li",function(){});这样写有事件委托。$("ul li").on();这样写法则没有。
### 浏览器事件模型
1. 每个浏览器都有自己的事件模型,W3C为了兼顾标准,定义了三个阶段:捕获阶段,目标阶段,冒泡阶段。
2. elem.addEventListener("eventType", fn , boolean);
3. 参数说明: 事件类型,函数,true,表示在捕获阶段执行,false为在冒泡阶段执行(默认为false).
4. stopPropagation()可以阻止事件的传播,可以是捕获阶段也可以是冒泡阶段。
### 模块化
Commonjs、AMD、CMD、es6 modules
- CommonJS
- 核心思想就是通过 require 方法来同步加载所要依赖的其他模块,然后通过 exports 或者 module.exports 来导出需要暴露的接口
- 同步加载
- 用于nodejs服务器端
- AMD规范
- require.js
- 非同步加载,允许指定回调函数
- require([module],callback),引用函数
- define(id, [dependences], callback), 定义函数
- 支持 Commonjs 的导出方式
- 只是提前加载所有依赖,不是按需加载
- CMD规范
- sea.js
- 按需加载
- 依赖spm打包
- ES6模块化
- import
- es6 modules
- 前端框架中常用
### 闭包
简答的说,如果一个function能除了能访问它的参数、局部变量或函数之外,还能访问外部环境变量(包括全局变量、DOM、外部函数的变量或者函数),那么它就可以成为一个闭包。
```javascript
// 看代码猜结果:
function fun(n,o) {
console.log(o);
return {
fun:function(m){
return fun(m,n);
}
};
}
1. var a = fun(0); a.fun(1); a.fun(2); a.fun(3);//undefined,0,0,0
2. var b = fun(0).fun(1).fun(2).fun(3);//undefined,0,1,2,
3. var c = fun(0).fun(1); c.fun(2); c.fun(3);//undefined,0,1,1
```
> [参考文章](https://segmentfault.com/a/1190000009886713)
## 元素获取
### 原生
- getElementById()
- getElementsByName()
- getElementsByTagName()
### 模拟复杂元素获取方式
- getElementByClassNameAndTag
根据 `tagName` 和 `className`获取元素
```javascript
function getElementsByClassNameAndTag(tagName, className){
var tags = document.getElemetsByTagName(tagName);
var resultTags = [];
for(var i = 0; i < tags.length; i++){
if(tags[i].classname.indexOf(className) != -1){ /* important, indexOf */
resultTags[resultTags.length] = tags[i];
}
}
return resultTags;
}
```
- getElementsByClassName
只根据 `className` 来获取元素
```javascript
function getElementsByClassName(className){
var resultTags = []
if( !document.getElementsByClassName ) {
var allTags = document.getElementsByTagName('*');
for ( var i = 0; i < allTags.length; i++ ) {
if( allTags[i].className.indexOf(className) != -1 ) {
resultTags[resultTags.length] = allTags[i];
}
}
}else {
var tags = document.getElementsByClassName(className);
for ( var i = 0; i < tags.length; i++) {
resultTags.push(tags[i]);
}
}
return resultTags;
}
```
- 1. HTML
- 1.1 HTML 标签
- 1.2 HTML 属性
- 1.3 HTML5
- 2. CSS/CSS3
- 2.1 CSS3
- 2.2 Less
- 2.3 Sass
- 3. JavaScript
- 3.1 JQuery
- 3.2 javascript code
- 3.3 es6
- 4. 前端框架
- 4.1 Angular4+
- 4.2 React
- 4.3 Vue
- 5. 综合知识
- 5.1 HTTP
- 5.2 websocket
- 5.3 综合问题集合
- 5.4 前端优化
- 6. 附加知识
- 6.1 TCP/IP
- 6.2 数据结构
- 6.3 前端开发
- 7. 相关工具
- 7.1 Git
- 7.2 调试
- 7.3 Linux
- 8. 其他需要了解的内容
- 8.1 Python3
- 8.2 Java
- 8.3 数据库