JavaScript 从入门到实践开发

JavaScript 简介

JavaScript 使用方式

通过 <script></script> 中直接编写

通过 <script src='目标文件的url'></script> 链接外部的 js 文件

作为某个元素的事件属性或者是超链接的 href 属性值

代码屏蔽

1
2
3
4
5
<script>
<!--
js 代码
//-->
</script>

如果浏览器不支持 js ,可以使用 <noscript></noscript> 标签,显示 noscript 中的内容

JavaScript 的基本语法

JavaScript 的执行顺序 :按照在 HTML 文件中出现的顺序依次执行

大小写敏感:JavaScript 严格区分大小写

忽略空白符和换行符

语句分隔符

使用 ; 结束语句

可以把多个语句写在一行

最后一个语句的分号可以省略,但尽量不要省略

可以使用 {} 括成一个语句组,形成一个块 block

通过 \ 对代码进行折行操作:

1
2
3
document.write('hello\

world')

注释

单行注释 //

多行注释 /* 注释内容 */

JavaScript 的保留字

通过 document.write() 向文档书写内容

通过 console.log() 向控制台写入内容

JavaScript 中的错误

语法错误:通过控制台进行调试

逻辑错误:通过 alert() 进行调试

JavaScript 的变量

变量

声明变量

通过 var 关键字声明变量

可以声明变量的同时给变量赋值

可以一次声明一个变量也可以一次声明多个变量

如果只声明变量未对其赋值,默认值为 undefined

如果变量重名会覆盖

变量命名

变量名称严格区分大小写

变量名称最好含义明确,以字母或者下划线开始,跟上数字、字母、下划线,最好遵循驼峰标记法或下划线法。

变量在内存中的存储与释放

收集方式
收集内容
回收算法

数据类型与变量

原始数据类型

数值型

JavaScript中数值包含整数和浮点数,所有数值都以双精度浮点型来表示。

双精度浮点数可以表示 -2 的 53 次方到 2 的 53次方的整数,也可以表示为正负1.7976 的 10 的 308 次方的最大值和正负 2.2250 乘以 10 的 -308 次方的浮点数。

十进制数:12、1.2、-23、.222e33、-1.3e3、3.E-2、12e+20

十六进制:0x0、0XABCDEF、0x1a2b3c4d

八进制:00、0123、0241234

特殊值:Infinity 无穷大(1.79e309~-1.79e309) NaN(0/0、通过 isNaN() 来检测是否是 NaN 值)

字符串型

定界符 “|“

转义符

1
2
3
4
5
\n -> 回车换行
\r -> 换行
\t -> 水平制表符
\' ->'
\\ -> \

布尔类型

true|false

复合数据类型

对象(object)

数组(array)

函数(function)

特殊数据类型

无定义数据类型 undefined(用来表示不存在的值或者未赋值的变量。对一个变量只声明不赋值或者赋予一个不存在的属性值,都会使这个变量的值为 undefined)

空值 null(表示什么都没有,相当于一个占位符。null 和 undefined 的区别是 undefined 表示变量未被赋值,而 null 表示变量被赋予一个空值)

类型转换

隐式转换

转换成布尔类型
1
2
3
4
5
undefined -> false
null -> false
数值 0 或者 0.0 或者 NaN -> false
字符串长度为 0 -> false
其他对象 -> true
转换成数值型
1
2
3
4
5
undefined -> NaN
null -> 0
true -> 1 | false -> 0
内容为数字 -> 数字,否则转换成 NaN
其他对象 -> NaN
转换成字符串型
1
2
3
4
5
undefined -> 'undefined'
null -> null
true -> "true" false -> "false"
数值型 -> NaN、0 或者与数值对应的字符串
其它对象 -> 如果存在这个对象则转换为 toString() 方法的值,否则转换 Undefined

显示转换

转换成布尔类型
1
2
3
4
5
6
通过 Boolean 函数强制转换成布尔值
0、-0 -> false
NaN -> false
空字符串 -> false
undefined -> false
null -> false
转换成数值型
1
2
3
4
5
6
通过 String 函数强制转换成字符串
数值 -> 数值本身
字符串 -> 字符串本身
true -> "true",false -> "false"
undefined -> "undefined"
null -> "null"
转换成字符串型
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
通过 Number 函数强制转换成数值
数值 -> 转换成原来的值
字符串 -> 如果可以解析为数值,则转换成数值;否则转换成 NaN 或者 0
true -> 1,false -> 0
undefined -> NaN
null -> 0

通过 parseInt(string,radix) 返回转换成整数的值
如果 string 以 0X 开头,parseInt()会把 string 的其余部分解析为十六进制的整数
如果 string 以 1~9 的数字开头,parseInt()将把它解析为十进制的整数
字符串如果以合法字符开始,截取合法字符
开头和结尾的空格是允许的
如果字符串的第一个字符不能被转换成数字,parseInt()会返回 NaN
在字符串以 “0” 为开始时旧的浏览器默认使用八进制基数。ECMAScript 5,默认的是十进制的基数

通过 parseFloat() 返回转换成浮点型的值
该函数指定字符串中的首个字符是否为数字,如果是,则对字符串进行解析,直到到达数字的末端为止,然后以数字返回该数字,而不是作为字符串。

编程思想

面向过程

面向对象

面向过程

面向过程就是分析出解决问题所需要的步骤,然后用函数把这些步骤一步一步实现,使用的时候再一个一个的依次调用就可以了。

面向对象

面向对象是把事务分解成一个个对象,然后由对象之间分工与合作。面向对象是以对象功能来划分问题,而不是步骤。

在面向对象程序开发思想中,每个对象都是功能中心,具有明确分工。

面向对象编程具有灵活、代码可复用、容易维护,更适合多人合作的大型软件项目。

面向对象的特性:

封装性

继承性

多态性

面向过程

优点:性能比面向对象高,适合跟硬件联系很紧密的东西,例如单片机就是采用的面向过程编程。

缺点:没有面向对象易维护、易复用、易扩展

面向对象

优点:易维护、易复用、易扩展,由于面向对象有封装、继承、多态性的特点,可以设计出低耦合的系统,使系统更加灵活,更加易于维护

缺点:性能比面向过程低

面向对象更贴近我们的实际生活,可以使用面向对象描述现实事物,但是事物分为具体的事物和抽象的事物

面向对象的思维特点:

1、抽取(抽象)对象共用的属性和行为组织(封装)成一个类(模板)

2、对类进行实例化,获取类的对象

类抽象了对象的公共部分,它泛指某一大类(class)

对象特指某一个,通过类实例化一个具体的对象

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
//  1、创建类 class 创建一个 “明星”类
class Star {
constructor(uname){
this.uname = uname
}

say() {
console.log('我叫' + this.uname)
}

sing(song){
console.log(this.uname + song)
}
}
// 2、利用类创建对象 new
var ldh = new Star('刘德华');
var zxy = new Star('张学友');
console.log(ldh.uname); // 刘德华
console.log(zxy.uname); // 张学友
/*
* (1) 通过 class 关键字创建类,类名习惯性定义首字母大写
* (2) 类里面有个 constructor 函数,可以接受传递过来的参数,同时返回实例对象
* (3) constructor 函数 只要 new 生成实例时,就会自动调用这个函数,不写这个函数,类也会自动生成这个函数
* (4) 生成实例 new 不能省略
* (5) 最后注意语法规范,创建类,类名后面不要加小括号,生成实例 类名后面加小括号,构造函数不需要加 function
*/
// 1. 类的继承
class Father {
constructor(x,y){
this.x = x;
this.y = y;
}

money(){
console.log(100)
}

sum(){
console.log(this.x + this.y) // this 指向的是这个方法的调用者
}

say(){
return '我是爸爸';
}
}

class Son extends Father {
constructor(x,y){ // constructor 里面的 this 指向的是 创建的实例对象
super(x,y); // 调用父类的 constructor 调用普通函数

this.x = x;
this.y = y
}

say(){
console.log('我是儿子');
console.log(super.say() + '的儿子'); // super.say() 调用父类的普通函数
}
}
var son = new Son(1,2)
son.money()
son.sum()
/*
* 1. 继承中,如果实例化子类输出一个方法,先看子类有没有这个方法,如果有就先执行子类的
* 2. 继承中,如果子类里面没有,就去查找父类有没有这个方法,如果有,就执行父类的这个方法(就近原则)
*/
son.say()
/*
* 1. 在 ES6 中类没有变量提升,所以必须先定义类,才能通过类实例化对象
* 2. 类里面的共有的属性和方法一定要加 this 使用。
* 3. constructor 里面的 this 指向实例对象,方法里面的 this 指向这个方法的调用者
*/
// 例子 实现 tab 增删改查
var that;
class Tab {
constructor(id){
that = this;
this.main = document.querySelector(id);
this.add = this.main.querySelector('.tabadd');
this.ul = this.main.querySelector('.fisrstnav ul:first-child');
this.init();
}

updataNode(){
this.lis = this.main.querySelectorAll('li');
this.sections = this.main.querySelectorAll('section');
this.remove = this.main.querySelectorAll('.icon-guanbi');
this.spans = this.main.querySelectorAll('.fisrstnav li span:first-child');
}

init() {
this.updataNode()
this.add.onclick = this.addTab;
for(var i = 0; i < this.lis.length; i++){
this.lis[i].index = i;
this.lis[i].onclick = this.toggleTab;
this.remove[i].onclick = this.removeTab;
this.spans[i].ondblclick = this.editTab;
}
}

clearClass() {
for(var i = 0;i < this.lis.length; i++){
this.lis[i].className = '';
this.sections[i].className = '';
}
}

toggleTab() {
that.clearClass();
this.className = 'liactive'
that.sections[this.index].className = 'conactive'
}

addTab() {
// 以前的做法:动态创建元素 createElement ,但是元素里面内容较多,需要 innerHtml 赋值,在 appendChild 追加到父元素里面
// 现在高级做法:利用 insertAdjacentHTML() 可以直接把字符串格式元素添加到父元素中
// appendChild 不支持追加字符串的子元素, insertAdjacentHTML 支持追加字符串元素
var li = '<li class = "liactive"><span>测试1</span><span class= "iconfont icon-gu"></span></li>'
that.ul.insertAdjacentHTML('beforeend',li);
that.init()
}

removeTab(e) {
e.stopPropagation(); // 阻止冒泡
var index = this.parentNode.index; // 获取父元素的 index
that.lis[index].remove(); // remove() 方法可以直接删除指定的元素
that.sections[index].remove();
that.init();
// 当我们删除的不是选中状态的 li 的时候,原来的选中状态 li 保持不变
if(document.querySelector('.liactive')) return;
// 当我们删除了选中状态的 li 的时候,让它的前一个 li 处于选定状态
index--;
that.lis[index] && that.lis[index].click() // 手动调用点击事件
}

editTab(){
var str = this.innerHTML;
// 双击禁止选定文字
window.getSelection ? window.getSelection().removeAllRanges() : document.selection.empty()
this.innerHTML = '<input type="text">';
var input = this.children[0];
input.value = str;
input.select(); // 文本框里面的文字处于选定状态
input.onblur = function(){
this.parentNode.innerHTML = this.value;
}
input.onkeyup = function(e){
if(e.keyCode === 13){
// 手动调用表单失去焦点事件
this.blur()
}
}
}
}
new Tab("#tab")

构造函数和原型

创建对象可以通过以下三种方式:

1、对象字面量

2、new Object()

3、自定义构造函数

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
var obj1 = new Object();

var obj2 = {}
/*
* 构造函数是一种特殊的函数,主要用来初始化对象,即为对象成员变量初始化赋值,它总与 new 一起使用。
* 我们可以吧对象中一些公共的属性和方法抽取出来,然后封装到这个函数里面。
*
* 在 js 中,使用构造函数时要注意以下两点:
* 1. 构造函数用于创建某一类对象,其首字母要大写
* 2. 构造函数要 new 一起使用 才有意义
*
* new 在执行时会做四件事情
* 在内存中创建一个新的空对象。
* 让 this 指向这个新的对象
* 执行构造函数里面的代码,给这个新对象添加属性和方法。
* 返回这个新对象
*
* 实例成员就是构造函数内部通过 this 添加成员 uname age sing 就是实例成员
* 实例成员只能通过实例化的对象来访问
*/

function Star(uname,age){
this.uname = uname;
this.age = age;
this.sing = function (){ // 这样写会造成内存浪费 构造函数通过原型分配的函数时所有对象所共性的。
console.log('我会唱歌')
}
}

/*
* 构造函数通过原型分配的函数时所有对象所共享的。
* JavaScript规定,每一个构造函数都有一个 prototype 属性,指向另一个对象。
* 注意这个 prototype 就是一个对象,这个对象的所有属性和方法。都会被构造函数所拥有。
* 把一些不变的方法,直接定义在 prototype 对象上,这样所有对象的实例就可以共享这些方法。
*/
Star.prototype.sing = function(){
console.log('我会唱歌');
}

var ldh = new Star('刘德华',18);
ldh.sing();

/*
* 不能通过构造函数来访问实例成员
* 静态成员 在构造函数本身上添加的成员
*/
Star.sex = '男'
// 静态成员只能通过构造函数来访问
console.log(Star.sex)

对象原型 __proto__

对象都会有一个属性 __proto__ 指向构造函数的原型对象 prototype,之所以我们对象可以使用构造函数 prototype 原型对象的属性

和方法,就是因为对象 __proto__ 原型的存在。

__proto__ 对象原型和原型对象 prototype 是等价的。

constructor 构造函数

对象原型(__proto__)和构造函数(prototype) 原型对象 里面都有一个属性 constructor 属性,constructor 称为构造函数

它指回构造函数本身。

constructor 主要用于记录该对象引用于哪个构造函数,它可以让原型对象重新指向原来的构造函数。

1
2
3
4
5
6
7
8
9
10
Star.prototype = {
constructor: Star,
sing: function(){
console.log('我会唱歌')
},

movie: function(){
console.log('我会演电影')
}
}

img

原型链

Star 原型对象 prototype.__proto__ 指向 object 原型对象 prototype

object 原型对象 prototype.__proto__ 指向 null

查找机制

原型链查找

就近原则

this 指向问题

在构造函数中,里面的 this 指向的是对象实例

原型对象函数里 this 指向的是 调用者

扩展内置对象

1
2
3
4
5
6
7
8
9
10
11
console.log(Array.prototype)
Array.prototype.sum = function(){
var sum = 0;
for(var i = 0; i < this.length; i++){
sum += this[i];
}
return sum;
}

var arr = [1,2,3]
console.log(arr.sum())

继承

call 改变 this 指向

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
function fn(x,y){
console.log('我想喝手磨咖啡');
console.log(this)
console.log(x + y)
}
var o = {
name : 'andy'
}
/*
* call() 可以调用函数
* call() 可以改变这个函数的 this 执行
*/
fn.call(o,1,2)
function Father(uname,age){
this.uname = uname;
this.age = age;
}
Father.prototype.money = function(){
console.log(10000);
}

// 继承 构造函数内的属性
function Son(uname,age){
Father.call(this,uname,age);
}
Son.prototype = new Father()
Son.prototype.constructor = Son;
Son.prototype.exam = function(){
console.log('孩子考试')
}

类的本质

class 还是函数

1
2
3
4
5
6
7
ES6 之前通过,构造函数 + 原型实现面向对象 编程
1. 构造函数有原型对象 prototype
2. 构造函数原型对象 prototype 里面有 constructor 指向构造函数本身
3. 构造函数可以通过原型对象添加方法
4. 构造函数创建的实例对象又 __proto__ 原型指向 构造函数的原型对象

ES6 的类其实是语法糖

数组方法

1
2
3
4
5
6
迭代(遍历)方法: forEach()、map()、filter()、some()、every()

forEach(callback(element[, index[, array]])) return 不会终止遍历
filter(callback(element[, index[, array]])) 注意它直接返回一个新数组 return 不会终止遍历
some(callback(element[, index[, array]])) 注意它返回值是布尔值,如果查找到这个元素,就返回 true,如果查找不到就返回 false
唯一的时候 some 更合适 return true 终止遍历 迭代效率更高

字符串方法

1
trim() 方法去除字符串两侧空格

对象方法

1
2
3
4
5
6
7
8
9
Object。defineProperty(obj, prop, descriptor) 定义对象中新属性或修改原有的属性。

var o = {};
Object.defineProperty(o, "a", {
value : 37,
writable : true, // 允许被赋值运算符改变
enumerable : true, // 允许属性出现在对象的枚举属性中
configurable : true // 允许 该属性的描述符才能够被改变,同时该属性也能从对应的对象上被删除。
});

函数进阶

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
/*
* 函数的定义方式
* 1. 自定义函数(命名函数)
* 2. 函数表达式(匿名函数)
* 3. 利用 new Function('参数1','参数2','函数体')
* 所有函数都是 Function 的实例(对象)
* 函数属于对象 万物皆对象
*/
function fn() { // 自定义函数

}

var fun = function() { // 函数表达式 (匿名函数)

}

var f = new Function('a','b','console.log(a+b)') // 利用 new Function
f(1,2)
console.log(f instanceof Object)

img

函数调用方式

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
1. 普通函数
function fn(){
console.log('人生的巅峰');
}
fn();
fn.call();

2. 对象的方法
var o = {
sayHi: function(){
console.log('人生的巅峰');
}
}
o.sayHi();

3. 构造函数
function Star(){

}
new Star()

4. 绑定事件函数
btn.onclick = function () {} // 点击按钮可调用这个函数

5. 定时器函数
setInterval(function(){},1000) // 这个函数是定时器自动调用

6. 立即执行函数
(function(){})()

this 指向

当调用函数的时候确定的,调用方式不同决定了 this 的指向不同,一般指向我们的调用者

调用方式 this 指向
普通函数调用 window
构造函数调用 实例对象 原型对象里的方法也指向实例对象
对象方法调用 该方法所属对象
事件绑定方法 绑定事件对象
定时器函数 window
立即执行函数 window

改变函数内部 this 指向

bind()、call()、apply()

call 方法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
var o = {
name: 'andy'
}

function fn (a,b){
console.log(this.name)
console.log(a + b)
}

fn.call(o,1,2)

// call 主要作用可以实现继承
function Father(uname,age,sex){
this.uname = uname;
this.age = age;
this.sex = sex;
}

function Son(uanme,age,sex){
Father.call(this,uname,age,sex);
}
var son = new Son('刘德华',18,'男');
console.log(son);

apply 方法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
fun.apply(thisArg,[argsArray])

var o = {
name: 'andy'
}

function fn (arr1,arr2){
console.log(this.name)
console.log(arr1) // 1
console.log(arr2) // 2
}

fn.apply(o,[1,2])

求最大值、最小值同理
var arr = [1,66,3,99,4];
// Math.max(value1[,value2, ...])
var max = Math.max.apply(Math,arr);
console.log(max)

bind 方法

不会调用原来的函数 可以改变原来函数内部的 this 指向

返回由指定的 this 值的初始化参数改造的原函数拷贝

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
fun.apply(thisArg,arg1,arg2,...)

var o = {
name: 'andy'
}

function fn (a,b){
console.log(this.name)
console.log(a + b)
}
var f = fn.bind(o,1,2);
f();

应用-按钮禁用后开启
btn.onclick = function(){
this.disabled = true
setTimeout(function(){
this.disabled = false
}.bind(this),3000)
}

// 例子 实现 tab 增删改查
class Tab {
constructor(id){
this.main = document.querySelector(id);
this.add = this.main.querySelector('.tabadd');
this.ul = this.main.querySelector('.fisrstnav ul:first-child');
this.init();
}

updataNode(){
this.lis = this.main.querySelectorAll('li');
this.sections = this.main.querySelectorAll('section');
this.remove = this.main.querySelectorAll('.icon-guanbi');
this.spans = this.main.querySelectorAll('.fisrstnav li span:first-child');
}

init() {
this.updataNode()
this.add.onclick = this.addTab.bind(this.add,this);
for(var i = 0; i < this.lis.length; i++){
this.lis[i].index = i;
this.lis[i].onclick = this.toggleTab.bind(this.list[i],this);
this.remove[i].onclick = this.removeTab.bind(this.remove[i],this);
this.spans[i].ondblclick = this.editTab;
}
}

clearClass() {
for(var i = 0;i < this.lis.length; i++){
this.lis[i].className = '';
this.sections[i].className = '';
}
}

toggleTab(that) {
that.clearClass();
this.className = 'liactive'
that.sections[this.index].className = 'conactive'
}

addTab(that) {
// 以前的做法:动态创建元素 createElement ,但是元素里面内容较多,需要 innerHtml 赋值,在 appendChild 追加到父元素里面
// 现在高级做法:利用 insertAdjacentHTML() 可以直接把字符串格式元素添加到父元素中
// appendChild 不支持追加字符串的子元素, insertAdjacentHTML 支持追加字符串元素
var li = '<li class = "liactive"><span>测试1</span><span class= "iconfont icon-gu"></span></li>'
that.ul.insertAdjacentHTML('beforeend',li);
that.init()
}

removeTab(e,that) {
e.stopPropagation(); // 阻止冒泡
var index = this.parentNode.index; // 获取父元素的 index
that.lis[index].remove(); // remove() 方法可以直接删除指定的元素
that.sections[index].remove();
that.init();
// 当我们删除的不是选中状态的 li 的时候,原来的选中状态 li 保持不变
if(document.querySelector('.liactive')) return;
// 当我们删除了选中状态的 li 的时候,让它的前一个 li 处于选定状态
index--;
that.lis[index] && that.lis[index].click() // 手动调用点击事件
}

editTab(){
var str = this.innerHTML;
// 双击禁止选定文字
window.getSelection ? window.getSelection().removeAllRanges() : document.selection.empty()
this.innerHTML = '<input type="text">';
var input = this.children[0];
input.value = str;
input.select(); // 文本框里面的文字处于选定状态
input.onblur = function(){
this.parentNode.innerHTML = this.value;
}
input.onkeyup = function(e){
if(e.keyCode === 13){
// 手动调用表单失去焦点事件
this.blur()
}
}
}
}
new Tab("#tab")

call、apply、bind 总结

相同点:

都可以改变函数呢你不的 this 指向。

区别点:

  1. call 和 apply 会调用函数,并且改变函数内部 this 指向。
  2. call 和 apply 传递的参数不一样,call 传递参数 aru1,aru2 … 形式,apply 必须数组形式 [arg]
  3. bind 不会调用函数,可以改变函数内部 this 指向。

主要应用场景

  1. call 经常做继承
  2. apply 经常跟数组有关系,比如借助于数学对象实现数组最大值最小值
  3. bind 不调用函数,但是还想改变 this 指向,比如改变定时器内部的 this 指向

严格模式

严格模式在 IE10 以上版本的浏览器中才会被支持,旧版本浏览器中会忽略。

严格模式对正常的 JavaScript 语义做了一些更改:

  1. 消除了 JavaScript 语法的一些不安全之处,保证代码运行的安全。
  2. 消除代码运行的一些不安全之处,保证代码运行的安全
  3. 提高编译器效率,增加运行速度。
  4. 禁用了在 ECMAScript 的未来版本中可能会定义的一些语法,为未来新版本的 JavaScript 做好铺垫。比如一些保留字如:class,enum,export,extends,import,super 不能做变量名

1.变量

变量必须先声明,然后再使用。

严禁删除已经声明变量,例如 delete x; 语法是错误的。

2.this 指向

全局作用域中函数中的 this 是 undefined。

如果构造函数不加 new 调用,this 会报错。

定时器 this 还是指向 window。

3.函数

函数不能有重名的参数。

不允许在非函数的代码内声明函数。

严格模式可以应用到整个脚本或个别函数中。

1.为脚本开启

1
2
3
4
5
'use strict'

(function(){
'use strict'
})()

2.为函数开启严格模式

1
2
3
function fn(){
'use strict'
}

高阶函数

高阶函数是对其他函数进行操作的函数,它接受函数作为参数或将函数作为返回值输出。

1
2
3
4
5
6
7
8
9
function fn(callback){
callback && callback();
}
fn(function()(alert('h1')));

function fn(){
return function
}
fn()

闭包

闭包指有权访问另一个函数作用域中变量的函数。

闭包的主要作用:延伸了变量的作用范围

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
function fn(){
var num = 10;

return function (){
console.log(num)
}
}
var f = fn()
f()

闭包应用 - 点击 li 输出索引号
var lis = document.querySelector('.nav').querySelectorAll('li');
for(var i = 0; i < lis.length; i++){
(function(i){
lis[i].onclick = function(){
console.log(i);
}
})(i)
}

递归

如果一个函数在内部可以调用其本身,那么这个函数就是递归函数。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
function fn(){
console.log('我要打印')
if(num == 6){
return;
}
num++
fn();
}

阶乘
function fn(n) {
if (n == 1){
return 1;
}
return n * fn( n - 1);
}

斐波那契数列
function fb(n){
if(n === 1 || n === 2){
return 1;
}
return fb(n - 1) + fn(n - 2);
}

function getId(json,id){
json.forEach(funcion(item){
if(item.id == id){
console.log(item)
}else if (item.goods && item.goods.length > 0){
getID(item.goods , id);
}
})
}

// 浅克隆
var o = {}
for (var k in obj){
o[k] = obj[k]
}


Object.assign({},obj);

// 深克隆
function deepCopy(newobj,oldobj){
for(var k in oldobj){
var item = oldobj[k];
if(item instanceof Array){
newobj[k] = []
deepCopy(newobj[k],item)
}else if(item instanceof Object){
newobj[k] = {}
deepCopy(newobj[k],item)
}else{
newobj[k] = item
}
}
}

正则表达式

正则表达式是用于匹配字符串中字符组合的模式。在 JavaScript 中,正则表达式也是对象。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
//  1. 利用 RegExp 对象来创建 正则表达式
var regexp = new RegExp(/123/);

// 2. 利用字面量创建 正则表达
var rg = /123/
// 3. test 方法用来检测字符串是否符合正则表达式要求的规范 返回布尔类型
// 边界符 ^ 开头 $ 结尾 [] 字符类 - 范围 [^] 取反
// 量词符 * 相当于 >=0 可以出现 0 次或者很多次
// 量词符 + 相当于 >=1 可以出现 1 次或者很多次
// 量词符 ? 相当于 1 || 0
// {3} 重复 3 次 {3,} 大于等于 3 {3,16} 大于3等于小于等于 16 位
// 预定义类 \d 相当于 [0-9] \D 相当于 [^0-9] \w 任意字母、数字、下划线 \W \s 匹配空格 \S
/^123$/ // 精准匹配 123
[123] // 只要包含其中一个就是 true
/^[123]$/ // 1 2 3 皆为true
/a-z/
/a-zA-Z0-9_-/
[^a-zA-Z0-9_-]
/a*/
/a+/
/a?/
/a{3}/
/^[^a-zA-Z0-9_-]{6,16}/;
/(abc){3}/ abc 重复3次

座机号
/^\d{3}-\d{8}|\d{4}-\d{7}$/
/^\d{3,4}-\d{7,8}$/

替换 replace
var str = 'andy'
var newStr = str.replace(/andy/g,'body'); // g 全局 i 忽略大小写

ES6

let

let 声明的变量只在所处于的块级有效。

不存在变量提升

暂时性死区

const

具有块级作用域

声明常量时必须赋值。

常量赋值后,值不能修改。

解构赋值

1
2
3
4
let [a,b,c] = [1,2,3]
// undefind
let {name,age} = { name:'张三',age:'20'}
let {name: myName, age: myage} = { name:'张三',age:'20'}

箭头函数

箭头函数不绑定 this 关键字,箭头函数中的 this,指向的是函数定义位置的上下文 this。

1
2
3
4
5
6
7
8
( ) => { }

var tor = {
name: '李华'
say: function(){
console.log(this.name) // this 指向 window 对象 {} 不形成作用域
}
}

剩余参数

1
2
3
4
sum(...args){
args.forEach(item => item
}
let [a,...b] = [1,2,3,4,5]

扩展运算符

1
2
3
4
5
6
7
8
let ary = ['a','b','c']
console.log(...ary)
// 数组合并
let ary1 = [1,2,3,...ary ]
console.log(ary1)
// 数组合并
ary1.push(...ary)
// 伪数组转成数组

Array.from()

1
2
3
4
5
6
7
8
9
//  伪数组转成真正的数组
let arrayLike = {
'0': 'a',
'1': 'b',
'2': 'c',
length: 3
}
let arr2 = Array.from(arrayLike);
let arr3 = Array.from(arrayLike,item => item * 2);

实例方法:find()

用于找出第一个符合数组成员,如果没有找到返回 undefined

1
2
3
4
5
6
7
8
9
10
11
12
let ary = [
{
id: 1,
name: '张三'
},
{
id: 2,
name: '李四'
}
]
let target = ary.find(item => item.id === 2)
console.log(target)

实例方法:findIndex()

用于找出第一个符合条件的数组成员的位置,如果没有找到返回 -1

1
2
3
let ary = [1,5,10,15]
let index = ary.findIndex((value,index) => value >9)
console.log(index) // 2

实例方法:includes()

表示某个数组是否包含给定的值,返回布尔值。

1
[1,2,3].includes(2)  // true

模板字符串

1
2
`12345 ${name}`
`12345 ${say()}`

实例方法:startsWith() 和 endsWith()

startsWith():表示参数字符串是否在原字符串的头部,返回布尔值

endsWith():表示参数字符串是否在原字符串的尾部,返回布尔值

1
2
3
let str = 'Hello world!';
str.startsWith('Hello'); // true
str.endWith('!') // true

实例方法:repeat()

repeat 方法表示将原字符串重复 n 次,返回一个新字符串。

1
2
'x'.repeat(3)  //  xxx
'hello'.repeat(2) // 'hellohello'

Set 数据结构

ES6 提供了新的数据结构 Set。它类似于数组,但是成员的值都是唯一的,没有重复的值。

Set 本身是一个构造函数,用来生成 Set 数据结构。

add(value):添加某个值,返回 Set 结构本身

delete(value):删除某个值,返回一个布尔值,表示删除是否成功

has(value):返回一个布尔值,表示该值是否为 Set 的成员

clear():清除所有成员,没有返回值

forEach() 遍历

1
2
3
4
5
6
7
const s = new Set()
const s = new Set([1,2,3,4,4,5])
console.log(s.size);
s.add('a')
s.delete('c')
s.has('a')
s.clear()

Ajax

传统网站中存在的问题

  • 网速慢的情况下,页面加载时间长,用户只能等待
  • 表单提交后,如果内容不合格,需要重新填写所有表单内容
  • 页面跳转,重新加载页面,造成资源浪费,增加用户等待时间

Ajax 概述

它是浏览器提供的一套方法,可以实现页面无刷新更新数据,提高用户浏览网站应用的体验。

应用场景

  • 页面上拉加载更多数据
  • 列表数据无刷新分页
  • 表单填写数据校验
  • 搜索框提示文字下拉列表

运行环境

Ajax 技术需要运行在网站环境中才能生效。

Ajax 的实现步骤

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
//  1、  创建 Ajax 对象
var xhr = new XMLHttpRequest();
// 2、 请求地址以及请求方式
var params = 'username='+ nameValue + '&age=' + ageValue
// get 请求方式
xhr.open('get','http://www.example.com/get?' + params);
// 3、 发送请求
xhr.send();
// 4、 获取服务器端响应数据
xhr.onload = function () {
console.log(xhr.responseText);
}

// post
xhr.open('post','http://www.example.com/post');
// post 必须 设置请求格式
xhr.setRequestHeader('Content-Type','application/x-www-form-urlencoded')
// post 发送请求参数
xhr.send(params)

// JSON 形式
xhr.setRequestHeader('Content-Type','application/json');
xhr.send(JSON.stringify({name: 'lisi',age: 50}))
// node 代码
const bodyParser = require('body-parser')
app.use(bodyParser.urlencoded()); //
app.use(bodyParser.json()) // 解析json

ajax 状态码

0:请求未初始化(还没有调用 open())

1:请求已经建立,但是还没有发送(还没有调用 send())

2:请求已经发送

3:请求正在处理中,通常响应已经有部分数据可以用了

4:响应已经完成,可以获取并使用服务器的响应了

1
2
3
4
5
6
7
8
9
10
xhr.readyState // 获取 Ajax 状态码

var xhr = new XMLHttpRequest(); // 0 已经创建了 ajax 对象
xhr.open('get','http://localhost:3000/readystate'); // 1 已经对 ajax 对象进行配置
xhr.onreadystatechange = function(){ // 当 ajax 状态码变化时触发
if(xhr.readyState === 4){
console.log(xhr.responseText);
}
}
xhr.send();

获取服务器的响应

两种获取服务器响应方式的区别

区别描述 onload事件 onreadystatechange事件
是否兼容 IE 低版本 不兼容 兼容
是否需要判断 Ajax 状态码 不需要 需要
被调用次数 一次 多次

Ajax 错误处理

1
2
3
4
5
6
7
8
9
10
11
12
var xhr= XMLHttpRequest()
xhr.open('get','https://localhost:3000/error');
xhr.send()
xhr.onload = function(){
if(xhr.status === 400){
console.log('请求出错了')
}
}
// 当网络中断会触发 error 事件
xhr.onerror = function(){
console.log('网络中断,无法发送网络请求')
}

低版本 IE 浏览器 的缓存问题

请求地址不变情况下,只有第一次请求会真正发送到服务器端。

ie浏览器切换至低版本

img

解决方案:在请求地址的后面加请求参数,保证每一次请求中的请求参数的值不相同。

1
xhr.open('get','http://www.example.com?t=' + Math.random());  // 我一般比较喜欢加时间戳

Ajax 封装

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
function ajax(options){
var defaults = {
type: 'get',
url: '',
data: {},
header:{
'Content-Type' :'application/x-www.form-urlencoded'
},
success: function(){},
error: function(){}
}

Object.assign(defaults,options)

var xhr = new XMLHttpRequest();

// 序列化
let params = ''

for(var attr in options.data){
params += attr + '=' + options.data[attr] + '&';
}
params = params.substr(0,params.length-1);

if(options.type === 'get'){
options.type = options.url + '?' + params;
}

xhr.open(options.type,options.url);
if(options.type === 'get'){
xhr.send()
}else{
var contentType = options.header['Content-Type']
xhr.setRequestHeader('Content-Type', contentType);
if(contentType === 'application/json'){
xhr.send(JSON.stringify(options.data))
}else{
xhr.send(params)
}
}

xhr.onload = function(){
var contentType = xhr.getResponseHeader('Content-Type') // 获取响应头数据
var responseText = xhr.responseText; // 服务器端返回的数据
if(contentType.includes('application/json')){
responseText = JSON.parse(responseText)
}
if(xhr.status === 200){
options.success(responseText,xhr)
}else{
options.error(responseText,xhr);
}
}
}
ajax(
{
type: 'get',
url: 'http:localhost:3000',
header:{
'Content-Type' :'application/json'
},
data: {
name: '',
age: 20
}
success: function(datas){

}
}
)

模板引擎

art-template

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
<head>
<!-- 1. 将模板引擎的库引入当前页面 -->
<script src="/js/template-web.js"></script>
</head>
<body>
<div id="container">

</div>
<!-- 2. 准备 art-template 模板 -->
<script type="text/html" id="tp1">
<h1 class="{{$value ? 'active' : ''}}">{{username}} {{age}}</h1>
<!-- 循环 -->
{{each target}}
{{$value.name}}
{{/each}}
{{dateFormat($value.name)}}
</script>
<script>
// 3. 告诉模板引擎将数据与模板进行拼接
// 模板 id 数据 对象类型
// 方法的返回值就是拼接好的 html 字符串
var html = template('tp1', {username: 'zhangsan', age: 30,target:[1,2,3,4,5,6]});
document.getElementById('container').innerHTML = html;

function dateFormat(date) {
console.log(data)
return data + 1
}
template.defaults.imports.dateFormat = dateFormat;
</script>
</body>
// 防抖
var timer = null;
searchInput.oninput = function (){
clearTimeout(timer);
timer = setTimeout(function(){
// 发送请求
},200)
}

FormData

FormData 对象的作用

模拟 HTML 表单,相当于将 HTMl 表单映射成表单对象,自动将表单对象中的数据拼接成请求参数的格式。

异步上传二进制文件

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
<form id="form">
<input type="text" name="username"/>
<input type="password" name="password"/>
<input type="button" id="btn" value="提交"/>
</form>
<script>
var btn = document.getElementById('btn');
var form = document.getElementById('form');
btn.onclick = function (){
var formData = new FormData(form)

formData.get('username')

formData.set('username','21312')

formData.delete('username')

formData.append('username','21312') // 追加
var xhr = new XMLHttpRequest();
xhr.open('post','http://localhost:3000/formData');
xhr.send(formData);
xhr.onload = function(){
if(xhr.status === 200){
console.log(xhr.responseText)
}
}
}
</script>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
// 上传文件
<input id="myfiles" multiple type="file">

<script>
var pullfiles=function(){
// love the query selector
var fileInput = document.querySelector("#myfiles");
var files = fileInput.files;
// 获取所选文件数量
var fl = files.length;
var i = 0;

while ( i < fl) {
// localize file var in the loop
var file = files[i];
console.log(文件的名字:file.name,文件的大小:file.size,文件类型:file.type);
i++;
}
var formData = new FormData()
formData.append('attrName',files[0]);

var xhr = new XMLHttpRequest();
xhr.open('post',http://localhost:3000/upload);
xhr.send(formData)
xhr.upload.onprogress = function(e){ // 数据传输进行中
// ev.loaded 文件已上传大小
// ev.total 文件总大小
var result = (ev.loaded / ev.total) * 100 + % // 进度
}
xhr.onload = function (){
if(xhr.status == 200){
console.log(xhr.responseText);
}
}
}

// 设置 change 事件处理函数
document.querySelector("#myfiles").onchange=pullfiles;
</script>

上传预览图片

1
2
3
4
5
6
var img = document.createElement('img');
img.src = result.path;
// 当图片加载完成以后
img.onload = function(){
box.appendChild(img);
}

同源策略

协议、域名、端口相同就属于同源

目的:保证用户信息安全

跨域

JSONP

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
// 客户端
<script>
function fn (data) {
console.log(data)
}
</script>
<script src="https://odn.bootcss.com?callback=fn"></script>

<script>
function jsonp(options){
var script = document.createElement('script');
var params = ''
for(var attr in options.data){
params += '&' + attr + '=' + options.data[attr]
}
var fnName = 'jsonp' + Math.random().toString().replace('.','');
window[fnName] = options.success;
script.src = options.url + '?callback=' + fnName + params;
document.body.appendChild(script);
script.onload = function () {
document.body.removeChild(script)
}
}
jsonp({
url: 'http://localhost:3000/better',
data: {
name: 'list',
age: '30',
},
success: function(data){
console.log(data)
}
})
</script>

服务器端响应必须是一个函数得调用

1
2
// 服务器
res.jsonp({name:'list',age: 20})

CORS 跨域资源共享

1
Access-Control-Allow-Origin: '*'
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
// node
app.get('/cross',(req,res) => {
res.header('Access-Control-Allow-Origin','*')
res.header('Access-Control-Allow-Methods','get,post')
res.send('ok');
})
// 优化 中间件
app.use((req,res,next) => {
// 允许请求地址
res.header('Access-Control-Allow-Origin','*')
// 允许请求方法
res.header('Access-Control-Allow-Methods','get,post')
// 允许跨域携带 cookie 信息
res.header('Access-Control-Allow-Credentials',true);
next()
})

服务器

1
2
3
4
5
6
7
const request = require('request');

app.get('/server',(req,res) => {
request('http://localhost:3001/cross',(err,response,body) =>{
res.send(body);
})
})

cookie

在使用 ajax 技术发送跨域请求时,默认情况下不会在请求中携带 cookie 信息。

withCredentials:指定在涉及到跨域请求时,是否携带 cookie 信息,默认值为 false

Access-Control-Allow-Credentials:true 允许客户端发送时携带 cookie

1
2
3
4
5
6
7
8
var xhr = new XMLHttpRequest()
xhr.open('get','http://localhost:3001/checkLogin');
// 允许跨域携带 cookie 信息
xhr.withCredentials = true
xhr.send()
xhr.onload = function(){
console.log(xhr.responseText)
}

jquery

$.ajax()

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
$.ajax({
type: 'get',
url: 'http://www.example.com',
data: { name:'张三',age:'20' }, // json格式 需要 JSON.stringify()处理
contentType: 'application/x-www-form-urlencoded',
beforeSend: function(){
return false // 取消发送
},
success: function(response){

},
error: function(xhr){

}
})

serialize 方法

作用:将表单中的数据自动拼接成字符串的参数 例如:name=’张三’&age=’18’

1
2
3
4
5
6
7
8
9
10
11
var params = $('form').serialize()
function serializeObject(obj) {
var params = obj.serializeArray(); // [{name: 'b', value: '1'},{name: 'b', value: '2'}]

var result = {}
$.each(params, function(i, field){
// $("#results").append(field.name + ":" + field.value + " ");
result[field.name] = field.value
});
return result // {'b': 1,'b': 2}
}

jsonp

1
2
3
4
5
6
7
8
$.ajax({
url: 'http://www.example.com',
dataType: 'jsonp',
jsonp: 'cb', // 修改 callback 参数名称
jsonCallback: 'fnName', // 指定函数名
success: funciton(response){
}
})
1
2
3
4
5
6
7
$.get('/base',"name=zhangsan&age=30",function(response)=>{
console.log(response)
})

$.post('/base',{name:'list'},function(response)=>{
console.log(response)
})
1
2
3
4
5
6
7
8
9
10
.ajaxStart()    // 当请求开始发送时触发
.ajaxComplete() // 当请求完成时触发

$(document).on('ajaxStart',function (){
NProgress.start(); // 进度条开始
})

$(document).on('ajaxComplete',function (){
NProgress.done(); // 进度条结束
})

RESTful 风格的 API

GET:获取数据

POST:添加数据

PUT:更新数据

DELETE:删除数据

RESTful 风格的接口

1
2
3
4
5
// node
app.get('/users/:id',(req,res) => {
const id = req.params.id;
res.send('获取id为${id}用户信息')
})

XML

1
2
3
4
5
// node
app.get('/xml',(req,res) => {
res.header('content-type', 'text/xml');
res.send('<message><title>消息标题</title><content>消息内容</content></message>')
})
1
2
3
4
5
xhr.onload = function () {
var xmlDocument = xhr.responseXML;
var title = xmlDocument.getElementsByTagName('title')[0].innerHTML;
container.innerHTMl = title
}

fetch

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
postData('http://example.com/answer', {answer: 42})
.then(data => console.log(data)) // JSON from `response.json()` call
.catch(error => console.error(error))

function postData(url, data) {
return fetch(url, {
body: JSON.stringify(data), // must match 'Content-Type' header
cache: 'no-cache', // *default, no-cache, reload, force-cache, only-if-cached
credentials: 'same-origin', // include, same-origin, *omit
headers: {
'user-agent': 'Mozilla/4.0 MDN Example',
'content-type': 'application/json'
},
method: 'POST', // *GET, POST, PUT, DELETE, etc.
mode: 'cors', // no-cors, cors, *same-origin
redirect: 'follow', // manual, *follow, error
referrer: 'no-referrer', // *client, no-referrer
})
.then(response => response.json()) // parses response to JSON
}

Array

Array.prototype.slice()

slice() 方法返回一个新的数组对象,这一对象是一个由 begin 和 end 决定的原数组的浅拷贝(包括 begin,不包括end)。原始数组不会被改变。

1
2
3
4
// JavaScript Demo
const animals = ['ant', 'bison', 'camel', 'duck', 'elephant'];
console.log(animals.slice(2, 4));
// expected output: Array ["camel", "duck"]

描述

  • 如果该元素是个对象引用 (不是实际的对象),slice 会拷贝这个对象引用到新的数组里。两个对象引用都引用了同一个对象。如果被引用的对象发生改变,则新的和原来的数组中的这个元素也会发生改变
  • 对于字符串、数字及布尔值来说(不是 String、Number 或者 Boolean 对象),slice 会拷贝这些值到新的数组里。在别的数组里修改这些字符串或数字或是布尔值,将不会影响另一个数组。
  • 如果向两个数组任一中添加了新元素,则另一个不会受到影响。

slice 方法可以用来将一个类数组(Array-like)对象/集合转换成一个新数组。你只需将该方法绑定到这个对象上。

Object

描述

**Object.keys()** 方法会返回一个由一个给定对象的自身可枚举属性组成的数组,数组中属性名的排列顺序和正常循环遍历该对象时返回的顺序一致 。

1
2
3
4
5
var arr = ['a', 'b', 'c'];
console.log(Object.keys(arr)); // console: ['0', '1', '2']

var obj = { 0: 'a', 1: 'b', 2: 'c' };
console.log(Object.keys(obj)); // console: ['0', '1', '2']

assign


相关资料

[视频]2019全新javaScript进阶面向对象ES6

Array.prototype.slice()

XMLHttpRequest - Web API 接口参考 | MDN (mozilla.org)

FileList - Web API 接口参考 | MDN (mozilla.org)

FileReader.onload - Web API 接口参考 | MDN (mozilla.org))

File.File() - Web API 接口参考 | MDN (mozilla.org)

使用 Fetch - Web API 接口参考 | MDN (mozilla.org)

30-seconds-of-code

数组

arrayMax

返回数组中的最大值。

1
2
const arrayMax = arr => Math.max(...arr);
// arrayMax([10, 1, 5]) -> 10

arrayMin

返回数组中的最小值。

1
2
const arrayMin = arr => Math.min(...arr);
// arrayMin([10, 1, 5]) -> 1

chunk

将数组块划分为指定大小的较小数组。

使用Array.from()**创建新的数组, 这符合将生成的区块数。使用Array.slice()**将新数组的每个元素映射到size长度的区块。如果原始数组不能均匀拆分, 则最终的块将包含剩余的元素。

1
2
3
const chunk = (arr, size) =>
Array.from({length: Math.ceil(arr.length / size)}, (v, i) => arr.slice(i * size, i * size + size));
// chunk([1,2,3,4,5], 2) -> [[1,2],[3,4],[5]]

compact

从数组中移除 falsey 值。

1
2
const compact = (arr) => arr.filter(Boolean);
// compact([0, 1, false, 2, '', 3, 'a', 'e'*23, NaN, 's', 34]) -> [ 1, 2, 3, 'a', 's', 34 ]

countOccurrences

计算数组中值的出现次数。

使用Array.reduce()在每次遇到数组中的特定值时递增计数器。

1
2
const countOccurrences = (arr, value) => arr.reduce((a, v) => v === value ? a + 1 : a + 0, 0);
// countOccurrences([1,1,2,1,2,3], 1) -> 3

deepFlatten

深拼合数组。
使用递归。使用Array.concat()与空数组 ([]) 和跨页运算符 (…) 来拼合数组。递归拼合作为数组的每个元素。

1
2
const deepFlatten = arr => [].concat(...arr.map(v => Array.isArray(v) ? deepFlatten(v) : v));
// deepFlatten([1,[2],[[3],4],5]) -> [1,2,3,4,5]

difference

返回两个数组之间的差异。
从b创建Set, 然后使用Array.filter() on 只保留a b中不包含的值.

1
2
const difference = (a, b) => { const s = new Set(b); return a.filter(x => !s.has(x)); };
// difference([1,2,3], [1,2,4]) -> [3]

distinctValuesOfArray

返回数组的所有不同值。
使用 ES6 Set和…rest运算符放弃所有重复的值。

1
2
const distinctValuesOfArray = arr => [...new Set(arr)];
// distinctValuesOfArray([1,2,2,3,4,4,5]) -> [1,2,3,4,5]

dropElements

移除数组中的元素, 直到传递的函数返回true。返回数组中的其余元素。 在数组中循环, 使用Array.shift()将数组的第一个元素除去, 直到函数的返回值为true。返回其余元素。

1
2
3
4
5
const dropElements = (arr, func) => {
while (arr.length > 0 && !func(arr[0])) arr.shift();
return arr;
};
// dropElements([1, 2, 3, 4], n => n >= 3) -> [3,4]

everyNth

返回数组中的每个第 n 个元素。
使用Array.filter()创建一个包含给定数组的每个第 n 个元素的新数组。

1
2
const everyNth = (arr, nth) => arr.filter((e, i) => i % nth === 0);
// everyNth([1,2,3,4,5,6], 2) -> [ 1, 3, 5 ]

相关资料
30-seconds-of-code
中文翻译 30-seconds-of-code

面闭 — JavaScript

Ajax 请求的步骤

第一步(得到 XMLHttpRequest)

第二步(打开与服务器的链接)

第三步(发送请求)

xmlHttp.send(null);如果括号里不给 null 可能会造成部分浏览器无法发送;

第四步

在 xmlHttp 对象的一个事件上注册监听器: onreadstatechange

得到的 5 个状态

>0状态:刚创建,还没有调用 open() 方法;

>1状态:请求刚开始,调用了open()方法,但是没有调用 send 方法;

>2状态:调用完了 send() 方法;

>3状态:服务器已经开始响应。但不表示响应结束;

>4状态:服务器响应结束!;

得到 xmlHttp 对象状态

var state = xmlHttp.redayState; // 可能是 0,1,2,3,4

得到服务器的响应状态码

var state = xmlHttp.status; // 可能是 200、404、500

得到服务器的响应内容

Ajax 的缺点

(1)、ajax 不支持浏览器 back 按钮

(2)、安全问题 ajax 暴露了与服务器交互的细节。

(3)、对搜索引擎的支持比较弱

(4)、破坏了程序的异常机制

什么是 Ajax 和 JSON,它们的优缺点

Ajax 全称 asynchronous JavaScript and XML,即异步 JavaScript 和 xml,用于在 web 页面中事先异步数据交互,实现页面局部刷新。

优点:可以使得页面不重载全部内容的情况下加载局部内容,降低数据传输量,避免用户不断刷新或者跳转页面,提高用户体验。

缺点:对搜索引擎不友好,要实现 ajax 下的前后退功能成本较大,可能造成请求数量增加跨域问题限制;

json 是一种轻量级的数据交换格式,ECMA 的一个子级

优点:轻量级、易于人的阅读和编写,便于机器(JavaScript)解析,支持复合数据类型(数组、对象、字符串、数字)

Ajax 请求的时候 get 和 post 方式的区别

get 一般用来进行查询操作,url 地址有长度限制,请求的参数都暴露在 url 地址中,如果传递中文参数,需要自己进行编码操作,安全性低

post 请求方式主要提交数据,没有数据长度的限制,提交的数据内容存在于 http 请求体中,数据不会暴露在 url 地址中。

jsonp的原理,以及为什么不是真正的 ajax

jsonp 并不是一种数据格式,而 json 是一种数据格式,jsonp 是用来解决跨域获取数据的一种解决方案,具体是通过动态创建 script 标签,然后通过标签的 src 属性获取 js 文件中的 js 脚本,该脚本的内容是一个函数调用,参数就是服务器返回的数据,为了处理这些返回的数据,需要事先在页面定义好回调函数,本质上不是使用 ajax 技术。

阐述一下异步加载

(1) 异步加载的方案:动态插入 script 标签

(2) 通过 ajax 去获取 js 代码,然后通过 eval 执行

(3) script 标签上添加 defer 或 async 属性

(4) 创建并插入 iframe,让它异步执行 js

post 和 get 的区别,何时使用 post

get:一般用于信息获取,使用 url 传递参数,对所有发送信息的数量也有限制,一般在 2000 个字符,有的浏览器 8000 个字符

post:一般用于修改服务器上的资源,对于发送的信息没有限制

在以下情况中,请使用 post 请求:

(1) 无法使用缓存文件(更新服务器上的文件或数据库)

(2) 向服务器发送大量数据(post 没有数据限制)

(3) 发送包含未知字符的用户输入时,post 比 get 更稳定也更可靠

数组去重的方法

排序方法

1
2
3
4
5
6
7
8
9
10
function distinct(arr){
var result = []
for(var i = 0;i < arr.length; i++){
for(var j = i+1;j<arr.length;j++){
j = ++i;
}
result.push(arr[i])
}
return result;
}

对象方法

1
2
3
4
5
6
7
8
9
10
11
function unique(arr){
var res = []
var json = {}
for(var i=0;i<arr.length;i++){
if(!json[arr[i]]){
res.push(arr[i])
json[arr[i]] = 1;
}
}
return res;
}

js 的作用域

作用域说明:一般理解指一个变量的作用范围

1、全局作用域

(1)全局作用域在页面打开时被创建,页面关闭时被销毁

(2)编写在 script 标签中的变量和函数,作用域为全局,在页面的任意位置都可以访问到

(3)在全局作用域中有全局对象 window,代表一个浏览器窗口,由浏览器创建,可以直接调用

(4)全局作用域中声明的变量和函数会作为 window 对象的属性和方法保存

2、函数作用域

(1)调用函数时,函数作用域被创建,函数执行完毕,函数作用域被销毁

(2)每调用一次函数就会被创建一个新的函数作用域,他们之间是相互独立的。

(3)在函数作用域中可以访问到全局作用域的变量,在函数外无法访问到函数作用域内的变量

(4)在函数作用域中访问变量、函数时,会先在自身作用域中寻找,若没有找到,则会到函数的上一级作用域中寻找,一直到全局作用域

说说你对闭包的理解

说说你对闭包的理解

使用闭包主要是为了设计私有的方法和变量。

闭包的优点是可以避免全局变量的污染,

缺点是闭包会常驻内存,会增大内存使用量,使用不当很容易造成内存泄露。

闭包有三个特性:

1、函数嵌套函数

2、函数内部可以引用外部的参数和比变量

3、参数和变量不会被垃圾回收机制回收

cookie 虽然在持久保存客户数据提供了方便,分担了服务器存储的负担,但是有很多局限性。

1、每个域名下最多生成 20 个 cookie.

2、浏览器会清理 cookie ,IE、Opera 会清理近期最少使用的 cookie,Firefox 会随机清理 cookie.

3、cookie 最大大约为 4096 字节。

优点:极高的扩展性和可用性。

1、通过良好的编程,控制保存在 cookie 中的 session 对象的大小。

2、通过加密和安全传输技术(SSL),减少 cookie 被破解的可能性。

3、只在 cookie 中存放不敏感数据,即使被盗也不会有重大损失。

4、控制 cookie 的生命期,使之不会永远有效。盗窃者很可能拿到一个过期的 cookie。

缺点:

1、cookie 数量和长度的限制。

2、安全性问题。

3、有些状态不可能保存在客户端。

1、cookie 数据存放在客户的浏览器上,session 数据放在服务器上。

2、cookie 不是很安全,可以分析存放在本地的 cookie 并进行 cookie 欺骗,考虑到安全应当使用 session。

3、session 会在一定时间内保存在服务器上。当访问增多,会比较占用服务器的性能。

4、单个 cookie 保存的数据不能超过 4k,一个站点最多保存20个 cookie。

使用 typeof bar === “object” 来确定 bar 是否是对象的潜在陷阱是什么?如何避免这个陷阱?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
// 避免 null 返回 true
var bar = null;
console.log((bar !== null) && (typeof bar === "object")); // logs false

// function 返回 ture
var bar = function(){
};
console.log((bar !== null) && ((typeof bar === "object") || (typeof bar === "function")));

// 数组 返回 false
var bar = [];
console.log((bar !== null) && (typeof bar === "object") && (toString.call(bar) !== "[object Array]"));

// jQ
console.log((bar !== null) && (typeof bar === "object") && (! $.isArray(bar)));
下面的代码将输出什么到控制台,为什么
1
2
3
4
5
6
7
8
9
10
(function(){
var a = b = 3; // b = 3;var a = b;
})();

console.log("a defined? " + (typeof a !== 'undefined'));
console.log("b defined? " + (typeof b !== 'undefined'));

// output
a defined? false
b defined? true
下面输出什么到控制台,为什么
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
var myObject = {
foo: "bar",
func: function() {
var self = this;
console.log("outer func: this.foo = " + this.foo);
console.log("outer func: self.foo = " + self.foo);
(function() {
console.log("inner func: this.foo = " + this.foo);
console.log("inner func: self.foo = " + self.foo);
}());
}
};

myObject.func();
// 在ECMA 5之前,在内部函数中的this 将指向全局的 window 对象;
/*
* outer func: this.foo = bar
* outer func: self.foo = bar
* inner func: this.foo = undefined
* inner func: self.foo = bar
*/
封装JavaScript源文件的全部内容到一个函数块有什么意义及理由

这是一个越来越普遍的做法,被许多流行的JavaScript库(jQuery,Node.js等)采用。这种技术创建了一个围绕文件全部内容的闭包,也许是最重要的是,创建了一个私有的命名空间,从而有助于避免不同JavaScript模块和库之间潜在的名称冲突。

这种技术的另一个特点是,允许一个易于引用的(假设更短的)别名用于全局变量。这通常用于,例如,jQuery插件中。jQuery允许你使用jQuery.noConflict(),来禁用 $ 引用到jQuery命名空间。在完成这项工作之后,你的代码仍然可以使用$ 利用这种闭包技术,如下所示:

1
(function($) { /* jQuery plugin code referencing $ */ } )(jQuery);

在 JavaScript 源文件的开头包含 use strict 意义和好处

use strict 是一种在JavaScript代码运行时自动实行更严格解析和错误处理的方法。

严格模式的一些主要优点包括:

  • 使调试更加容易。
  • 防止意外的全局变量。
  • 消除 this 强制
  • 不允许重复的属性名称或参数值。
  • 使eval() 更安全。
  • 在 delete使用无效时抛出错误。
考虑以下两个函数。它们会返回相同的东西吗? 为什么相同或为什么不相同
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
function foo1()
{
return {
bar: "hello"
};
}

function foo2()
{
return
{
bar: "hello"
};
}
console.log(foo1());
console.log(foo2());

// output
// Object {bar: "hello"};
// undefined

// 代码行上没有其他任何代码,分号会立即自动插入到返回语句之后。
NaN 是什么?它的类型是什么?你如何可靠地测试一个值是否等于 NaN

NaN 属性代表一个“不是数字”的值。这个特殊的值是因为运算不能执行而导致的,不能执行的原因要么是因为其中的运算对象之一非数字(例如, “abc” / 4),要么是因为运算的结果非数字(例如,除数为零)。

NaN 是 Number;

检测
isNaN
Number.isNaN()

下列代码将输出什么?并解释原因

console.log(0.1 + 0.2);
console.log(0.1 + 0.2 == 0.3);

JavaScript中的数字和浮点精度的处理相同,因此,可能不会总是产生预期的结果。

9.讨论写函数 isInteger(x) 的可能方法,用于确定x是否是整数

在ECMAScript规格说明中,整数只概念上存在:即,数字值总是存储为浮点值。

function isInteger(x) { return (x^0) === x; }
function isInteger(x) { return Math.round(x) === x; }
function isInteger(x) { return (typeof x === ‘number’) && (x % 1 === 0);
function isInteger(x) { return parseInt(x, 10) === x; } // 目变得足够大,为指数形式(例如, 1e+21)

Math.ceil() 和 Math.floor() 在上面的实现中等同于 Math.round()。

下列代码行1-4如何排序,使之能够在执行代码时输出到控制台? 为什么

(function() {
console.log(1);
setTimeout(function(){console.log(2)}, 1000);
setTimeout(function(){console.log(3)}, 0);
console.log(4);
})();

output
1
4
3
2

浏览器有一个事件循环,会检查事件队列和处理未完成的事件。例如,如果时间发生在后台(例如,脚本的 onload 事件)时,浏览器正忙(例如,处理一个 onclick),那么事件会添加到队列中。当onclick处理程序完成后,检查队列,然后处理该事件(例如,执行 onload 脚本)。
同样的, setTimeout() 也会把其引用的函数的执行放到事件队列中,如果浏览器正忙的话。
当setTimeout()的第二个参数为0的时候,它的意思是“尽快”执行指定的函数。具体而言,函数的执行会放置在事件队列的下一个计时器开始。但是请注意,这不是立即执行:函数不会被执行除非下一个计时器开始。这就是为什么在上述的例子中,调用 console.log(4) 发生在调用 console.log(3) 之前(因为调用 console.log(3) 是通过setTimeout被调用的,因此会稍微延迟)。

写一个简单的函数(少于80个字符),要求返回一个布尔值指明字符串是否为回文结构

function isPalindrome(str) {
str = str.replace(/W/g, ‘’).toLowerCase();
return (str == str.split(‘’).reverse().join(‘’));
}

console.log(isPalindrome(“level”)); // logs ‘true’
console.log(isPalindrome(“levels”)); // logs ‘false’
console.log(isPalindrome(“A car, a man, a maraca”)); // logs ‘true’

阅读更多

JavaScript 函数及开发技巧

生成一周时间

1
2
3
4
5
function getWeekTime(){
return [...new Array(7)].map((j,i)=> new Date(Date.now()+i*8.64e7).toLocaleDateString())
}
getWeekTime();
// 输出效果 ["2020/7/21", "2020/7/22", "2020/7/23", "2020/7/24", "2020/7/25", "2020/7/26", "2020/7/27"]

判断空对象

1
2
let a = {}
JSON.stringify(a) == '{}';

类型判断

1
2
3
4
5
6
7
8
9
10
11
12
13
14
/**
* @param {any} target
* @param {string} type
* @return {boolean}
*/
function isType(target, type) {
let targetType = Object.prototype.toString.call(target).slice(8, -1).toLowerCase()
return targetType === type.toLowerCase()
}
isType([], 'Array') // true
isType(/\d/, 'RegExp') // true
isType(new Date(), 'Date') // true
isType(function(){}, 'Function') // true
isType(Symbol(1), 'Symbol') // true
阅读更多

Ajax 跨域 一起来吧!

Ajax 简介

Ajax读音:[ˈeɪdʒæks]

Ajax 的全程:Asynchronous JavaScript and XML(异步的 JavaScript 和 XML)。
Ajax 不是某种编程语言,是一种在无需重新加载整个页面的情况之下能够更新网页部分的技术。

解决问题

传统的网页(不使用 Ajax 技术的页面),想要更新内容或者提交一个表单,就需要重新载入页面。
使用了 Ajax 技术的页面,通过在后台跟服务器进行少量的数据交互,网页就可以实现异步局部更新。

阅读更多

九个Console命令,让 JS 调试更简单

显示信息的命令

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>常用console命令</title>
<script>
console.log("hello");
console.info("信息");
console.error("错误");
console.warn("警告");
</script>
</head>
<body></body>
</html>
阅读更多

彻底弄懂bind,apply,call三者的区别

为什么要改变 this 指向?

例子:

1
2
3
4
5
6
7
8
9
var name = 'lucy';
let obj = {
name : "martin",
say : function(){
console.log(this.name);
}
}
obj.say(); // martin this指向obj对象
setTimeout(obj.say,0); // lucy ,this 指向 window 对象

可以观察到,正常情况下 say 方法中的 this 是指向调用它的 obj 对象的,而定时器 setTimeout 中的 say 方法中 this 是指向 window 对象的(浏览器中),这是因为 say 方法在定时器中是作为回调函数来执行的,因此回到主线执行时是在全局执行上下文的环境中执行的,但我们需要的是 say 方法中 this 指向对象 obj 对象,因此我们需要修改 this 的指向。

阅读更多
You need to set client_id and slot_id to show this AD unit. Please set it in _config.yml.