个人学习笔记汇总
面向对象和面向过程
- 面向过程就是分析出解决问题所需要的步骤,然后用函数把这些步骤一步一步实现,使用的时候再一个一个的依次调用就可以了。
- 面向对象是把事务分解成为一个个对象,然后由对象之间分工与合作。
两者比较 - 面向过程性能比面向对象高,适合跟硬件联系很紧密的东西,但是不易维护、不易复用、不易扩展。
- 面向对象易维护、易复用、易扩展,由于面向对象有封装、继承、多态性的特性,可以设计出低耦合的系统,使系统 更加灵活、更加易于维护,但是性能比面向过程低。
ES6中的类和对象
类和对象的区别
类的创建(创建类并生成实例)
类继承extends来继承
- extends关键字可以实现子类继承父类
super关键字:
- 使用super调用父类的构造函数
- super调用父类普通函数以及继承中属性方法的查找 的
- 子类调用父类两种方式
- 通过构造函数的方式(在congstructor中调用super)
- 调用普通函数(如果子类的constructor没有使用super,则采用super.普通函数()的方式调用父类的普通函数)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15class Father{
say(){
return ("我是父亲");
/**
* 在函数中有return调用函数才会有直接输出数值,没有return关键字则会返回undefined
*/
}
}
class Son extends Father{
say(){
// console.log("我是儿子");
console.log(super.say()+"的儿子");
}
}
- 调用普通函数(如果子类的constructor没有使用super,则采用super.普通函数()的方式调用父类的普通函数)
- super关键字可以调用父类普通函数和构造函数
- 继承中的属性或者方法查找原则(就近原则)
- super必须放在子类的this之前
对象和类使用注意事项
- 在Es6中没有变量的提升,必须先定义类,才能通过类实例化
- 类里面的共有属性和方法必须要加this使用
- this:
- constructor里面的this指向的创建的实例化对象
- 方法中的this指向不清楚,只有在调用的时候才能清楚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
27class Father{
constructor(x,y){
that = this;//that存储的是当前父元素的实例化对象
this.x = x;
this.y = y;
//函数调用就执行自身的方法
this.sing();
//button按钮点击就执行sing方法
this.btn = document.querySelector("button");
this.btn.onclick = this.sing;//undefined这里的this指向的是button对象而不是father实例化对象,button调用了这个方法
//注意这里传的是函数对象,不是函数对象的方法调用所以不需要添加(),这里的this指向的是button对象而不是father实例化对象
}
sing(){
//如果在构造函数中要使用constructor中的this解决方案:声明全局变量保存
console.log(that.x);
}
dance(){
console.log(this.y);
}
}
var f = new Father("刘德华",23);
f.dance();
/**
* 在ES6中,没有变量的提升,必须先定义类,在通过类进行实例化
* 类里面共有的属性和方法都需要使用this
*/
事件知识点总结:
- appendChild()和insertAdjacentHTML()的区别
- 前者用于给添加节点,不能直接添加字符串
- insertAajacentHTML可以添加字符串到对应的父元素中
1
2var li = '<li class="liactive"><span>新标签</span><span class="iconfont icon-guanbi"></span></li>';
that.ul.insertAdjacentHTML('beforeend', li);
- 冒泡事件取消的方式
- IE中通过设置e.cancleBubble=true组织事件传播这里调用的是属性
- e.stopPropagation() 方法通知不在派发事件
- 组织浏览器的默认行为通过:e.preventDefault()
- 双击静止选中文字的方法:
1
2//涉及到浏览器兼容的问题
window.getSelection ? window.getSelection().removeAllRanges() : document.selection.empty(); - element.onblur和element.blur()的区别
- 二者都是释放焦点
- 前者是焦点释放事件,需要绑定函数,进行事件的操作才会执行
- 后者是函数,会自动执行
- input对象中的方法
- input.select():选中文本框中的内容
- 条件执行语句:存在则执行后面操作的渐变写法:
click()方法和blur()方法一样会自动执行1
that.lis[index] && that.lis[index].click();
- 函数执行过程注意this的指向,保存父元素的this可以开启全局变量进行保存
构造函数和原型
- appendChild()和insertAdjacentHTML()的区别
ES6之前是不支持类的构建的,通过构造函数的方式进行对象的创建
对象的创建方式有三种:
- 通过new Object()关键字的方式,ps:Object()是一个对象
- 通过字面量的方式
- 通过构造函数的方式
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18//构造函数创建的三种方式
// 1.字面量的方式
var obj = {};
// 2.new关键字创建,这里的Object是一个对象
var obj1 = new Object();
// 3.通过构造函数来创建
function Star(name,age){
this.name = name;
this.age = age ;
this.sing = function(){
alert(this.name+"我会唱歌");
}
}
// 通过构造函数来创建实例
var ldh = new Star("刘德华",23);
var zxy = new Star("张学友",23);
ldh.sing();
zxy.sing();构造函数
构造函数是一种特殊的函数,主要用来初始化对象,即为对象成员变量赋初始值,
与new一起使用,可以把对象中的一些公共方法抽取出来,封装在函数里构造函数创建某一类对象时,首字母大写 构造函数要和new一起使用
new在执行过程中操作:
- 在内存中创建了一个空的对象
- 让this指向这个新的对象
- 执行构造函数里面的代码,给新的对象添加属性和方法
- 返回这个心对象,不用return
- 构造函数中有两类成员
- 实例成员:在构造函数内部通过this添加的成员就是实例成员
- 实例成员只能通过实例化对象来访问,不能通过构造函数来访问
- 静态成员:在构造函数上直接添加的成员
- 静态成员只能通过构造函数来访问,不能通过实例化对象来添加
1
2
3
4
5
6
7
8
9
10
11
12
13
14function Star(name,age){
this.name = name;
this.age = age ;
this.sing = function(){
alert(this.name+"我会唱歌");
}
}
var ldh = new Star("刘德华",23);
var zxy = new Star("张学友",23);
ldh.sing();//实例成员通过对象访问
console.log(Star.name);//不可以通过构造函数来访问
Star.sex = "男";
console.log(Star.sex);//通过构造函数来访问
console.log(ldh.sex);//undefiined
- 静态成员只能通过构造函数来访问,不能通过实例化对象来添加
- 实例成员:在构造函数内部通过this添加的成员就是实例成员
构造函数弊端
- 构造函数存在浪费内存的问题,new一个对象就会在内存中分配一个地址,每个实例对象是放在不同的内存空间,所以都是不相等的,
- 解决方案:通过原型共享空间
原型(共享)
- 构造函数通过原型分配的方法是所有对象共享的
- js中规定,每一个构造函数都有一个prototype属性,这个属性是一个对象,这个对象所有的属性和方法都会被构造函数所拥有.可以把共有的方法定义在构造函数prototype对象上,以便所有的实例都可以共享这些方法
- 构造函数原型
- 一般情况下公共的属性定义在构造函数里面.公共的方法定义在原型对象中
- console.dir(Star)可以查看对象中的属性和方法
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16console.dir(Star);
/*
查看Star中的属性和方法
*/
//通过原型来添加方法,共享内存空间
Star.prototype.sing = function(){
console.log(this.name+'我会唱歌');
}
// 通过构造函数来创建实例
var ldh = new Star("刘德华",23);
var zxy = new Star("张学友",23);
//实例化对象都可以访问
alert(ldh.sing==zxy.sing);//不同实例属性方法的比较返回true
ldh.sing();
zxy.sing();
// ps:一般情况下公共的属性定义在构造函数里面.公共的方法定义在原型对象中
原型是对象,作用是共享内存空间
- 原型对象proto
- 实例化对象中的属性proto指向构造函数的prototype属相,之所以实例化对象可以调用构造函数原型prototype属性对象中的属相和方法就是实例化对象与proto属相的存在
- 即console.log(ldh.proto==Star.prototype);//true
- proto是一个非标准的,实际开发中不能使用
constructor构造函数
- 对象原型(proto)和构造函数(prototype)原型对象里面都有一个属性conostructor,他是构造函数,因为他指回构造函数本身
- constructor主要用于记录该对象引用于哪个构造函数,他可以让原型对象重新指回原来的构造函数(当给原型对象传入的是一个对象时)
1
2
3
4
5
6
7
8
9
10//当原型对象中定义多个方法时,这是是将一个方法传给了原型对象,需要将constructor指向Star
Star.prototype = {
constructor:Star,//指回原来的构造函数
sing: function(){
console.log("我会唱歌");
},
dance: function(){
console.log("我会跳舞");
}
}
构造函数,实例和原型对象之间的关系
原型链
每个实例对象都会有一个原型对象,原型对象中也会有原型对象,直到object的原型对象,object的原型对象为null,在方法调用查找过程中就是按照原型链进行查找的原型链:每一个实例对象又有一个proto属性,指向的构造函数的原型对象,构造函数的原型对象也是一个对象,也有proto属性,这样一层一层往上找就形成了原型链。
js中成员的查找机制是按照原型链的方式进行的
- 当访问一个对象的属性时,先在先看对象自身有没有该属性
- 没有则找他的原型(prototype原型对象)
- 如果还没有就查找原型对象的属性(Object)
- 直到找到Object的原型对象null
this指向:构造函数中的this和原型对象中的this都指向的实例对象
原型的应用
- 扩展内置对象的方法(通过原型对象的方式)
- 这里只能通过原型对象点的方式进行扩展[Array.prototype.sum = function(){}]
- 数组字符串内置对象不能使用原型对象中赋值原型对象的方式,会覆盖原先的原型对戏哪个[Array.prototype={}]不可用会报错
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19console.log(Array.prototype);//查看原型对象中的方法
//1.通过原型对象的方式扩展sum方法(点的方式)
Array.prototype.sum = function(){
var sum = 0;
for(var i = 0;i<this.length;i++){
sum += this[i];
}
return sum;
}
//2. 对象的方式,会覆盖掉原先原型对象,不能使用,会报错
Array.prototype = {
sum(){
var sum = 0;
for(var i = 0;i<this.length;i++){
sum += this[i];
}
return sum;
}
}构造函数的继承
继承函数call()
可以用来调用函数
可以改变函数的指针this的指向
借用构造函数继承父类的属性
ES6之前继承通过借用构造函数来实现继承,[通过函数方法call(),子对象借用call()调用父对象函数的方式]
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
39call()函数的调用
function fn(){
console.log(this.name);
}
var obj = {
name: "刘德华",
age: 15,
}
fn.call(obj);//返回obj中的name
-----------------------------------------------
/// 在ES6之前的构造函数的继承是通过call()函数来执行的
function Father(name, age) {
this.name = name;
this.age = age;
}
//父元素的原型对象中添加mone方法
Father.prototype.money = function () {
console.log(10000000);
}
function Son(name, age, score) {
//继承父元素的属性,子对象调用父对象来继承父元素的属性和方法
Father.call(this, name, age);
// 构造函数中定义自己的属性
this.score = score;
}
//子元素要继承父元素中的方法通过将子元素的原型对象指向父元素的实例对象,而父元素的实例对象可以通过__proto__属性对象找到父元素的原型对象,从而将父元素的方法继承过来
Son.prototype = new Father();
//这里将子元素的原型对象中的属性通过父元素的实例对象覆盖了,需要将子元素的constructor属性指回原构造函数,避免覆盖原构造函数的方法
Son.prototype.constructor = Son;
// 子构造函数特有的方法,不会修改到父元素的原型对象
Son.prototype.exam = function(){
console.log("孩子要考试");
}
var son = new Son("lilei", 23, 100);
console.log(son);
console.log(son.money());
console.log(son.__proto__);
console.log(Son.prototype.constructor);借用原型对象继承方法
子元素要继承父元素中的方法通过将子元素的原型对象指向父元素的实例对象,而父元素的实例对象可以通过proto属性对象找到父元素的原型对象,从而将父元素的方法继承过来,这样修改子元素原型对象中的方法不会影响父元素的原型对象
1
Son.prototype = new Father();
这里将子元素的原型对象中的属性通过父元素的实例对象覆盖了,需要将子元素的constructor属性指回原构造函数,避免覆盖原构造函数的方法,详细使用见上段代码
1
Son.prototype.constructor = Son;
详情见下图
- 注意事项
- 定义父构造函数,为父元素添加方法,定义子构造函数,子构造函数要继承父构造函数中的方法
- 在子构造函数中通过 call() 把父类型的 this 指向子类型的 this ,这样就可以实现子类型继承父类型的属性
- 将子构造函数的原型对象指向父构造函数的实例,因为修改了子构造函数原型对象,一定要利用constructor 指回子构造函数
ES5中新增的方法
数组方法
- foreach()
- map()
- filter()
- some():遇到return true就会终止循环,如果要查询唯一的元素使用some是最好的选择,效率更高
- every()
- array.foreach(function(value,index,array))
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/*
1.foreach()
数组.forEach(function(value,index,arr){})
*/
var arr = [1,2,4,5];
arr.forEach(function(value,index,arr){
console.log(value);
console.log(index);
console.log(arr);
})
console.log("--------------");
/*
2.filter(function()),过滤符合条件的元素放在新的数组中而不会影响原数组
*/
var newarr =arr.filter(function(value,index,array){
console.log(value);
console.log(index);
console.log(array);
return value%2==0;
})
console.log(newarr);
console.log("-----------------");
/*
3.some(),查找数组中是否有满足条件的元素,有则返回true,否则返回false,只要找到满足条件的元素则会直接返回不会再进行查找,some里面遇到return true就会终止迭代
*/
var flag = arr.some(function(value,index,arr){
// return value>4;
return value>10;
});
console.log(flag);
console.log("---------");
/*
4. map()对数组中的每个元素调用匿名函数中的方法并返回一个新的数组,不会改变原数组
5. every()判断数组中的元素是否满足条件,所有的都满足才会返回true,some还要有一个满足就会返回true
*/
var newarr1 = arr.map(function(value,index,arr){
return value*value;
})
console.log(newarr1);
console.log("-----------");
var flage = arr.every(function(value,index,arr){
return value>3;
})
console.log(flage);
- array.foreach(function(value,index,array))
字符串方法
trim():去除字符串两端的空格返回一个新的字符串,原先的字符串不改变
对象方法
Object.keys(obj)返回所有属性名并放在一个新的数组中
1
2
3
4
5
6
7
8
9var data = {
id: 1,
pname: '小米',
price: 3999
};
var arr = Object.keys(data);
for(var i in arr){
console.log(arr[i]);
}Object.defineProperty()设置或修改对象的属性
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
41Object.defineProperty() 定义新属性或修改原有的属性。
参数设置
参数一是当前设置的对象。
参数二是需定义或修改的属性的名字。
参数三是一个对象。
属性一是value:: 设置属性的值
属性二是writable: false/true;如果值为false 不允许修改这个属性值 默认值也是false
属性三是enumerable: false/true;如果值为false 则不允许遍历, 默认的值是 false
属性四是configurable: false/true;如果为false 则不允许删除这个属性 不允许在修改第三个参数里面的特性 默认为false
var obj = {
id: 1,
pname: '小米',
price: 3999
};
//1.原先修改属性的方法
// obj.pname = "华为";
// obj.num = 2;
// console.log(obj);
// 新的方法添加属性的方法
Object.defineProperty(obj,'num',{
value: 9,
writable: true,//是否允许修改属性
enumerable: false,//是否允许遍历属性
configurable: false, //是否允许修改特性
})
Object.defineProperty(obj,'address',{
value: "湖南长沙",
writable: true,//是否允许修改属性
enumerable: true,//是否允许遍历属性
configurable: false, //是否允许修改特性
})
console.log(obj);
// obj.num = 4;
// console.log(Object.keys(obj));
delete obj.price;//设置为false则不可以删除
console.log(obj);
// Object.defineProperty(obj, 'price', {
// value: 9.9
// });函数进阶
函数的定义和调用
函数的声明
函数定义的三种方式
- 函数声明
- 函数表达式
- new Function(‘参数’,’参数’,’函数体’)
在开发过程中我们使用前两种方式比较多,方式三书写繁琐,指向效率较低,使用频率较少;
函数也是属于对象;
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18// 函数声明的三种方式
// 1.函数声明方式
function test(){
console.log("我是函数声明函数");
}
// 2.函数表达式
var fun = function(){
console.log("我是函数表达式");
}
// 3.new Function
var f = new Function('a','b','console.log(a+b)');
test();
fun();
f(1,2);
//所有的函数都是function的实例
console.dir(f);
// 函数也属于对象
console.log(f instanceof Object);函数的调用
函数调用方式
- 普通函数的调用
- 对象中的函数调用
- 构造函数的调用
- DOM元素绑定事件调用函数
- 定时器中调用函数
- 立即执行函数
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 fun(){
console.log("普通函数");
}
fun();
// 2.对象中的函数调用
var o = {
say: function(){
alert("对象中的函数");
}
}
o.say();
// 3.构造函数的调用
function Star(){
}
new Star();
// 4.DOM元素绑定事件调用函数
btn.onclick = function(){
//按钮点击的时候调用
}
// 5.定时器中调用函数,自动一段时间调用
setInterval(function(){
alert("定时器函数调用");
},100);
// 6.立即执行函数
(function(){
alert("立即函数的调用");
})();this
函数内部的this指向,一般指向的是调用者
-普通函数 this 指向window- 对象的方法 this指向的是对象
- 构造函数 this和构造函数原型中的this 指向-> 构造函数new出来的实例对象
- 绑定事件函数 this 指向的是函数的调用者
- 定时器函数 this 指向的是window
- 立即执行函数 this指向的是window
改变函数内部的this指向
this一般指向的是调用对象call()
- 主要作用:
- 调用函数
- 改变this指向
- 实现构造函数中的继承
- 格式:
- fun.call(obj,args);
- args:传入的是obj函数中需要传入的参数
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20().apply().bind改变函数内部的指向
//call()主要作用:调用函数;改变this的指向;实现继承
var o = {
name: 'andy'
}
function fn(a,b){
console.log(this);
console.log(a+b);
}
fn.call(o,2,3);
function Father(name,age,gender){
this.name = name;
this.age = age;
this.gender = gender;
}
function Son(name,age,gender){
Father.call(this,name,age,gender);
}
var ldh = new Son('liu',34,'男');
console.log(ldh);
- 主要作用:
apply()
- 作用:
- 调用函数
- 改变函数中的this指向
- apply的主要应用时借助于函数的内置对象求最大值
1
2var math = Math.max.apply(Math,arr);
通过Math的内置对象返回arr的最大值
- 格式:fun.apply(thisArr,[argsArray])
- 注意:数组传入之后就编程了单个数值
- 这里与call不同的是传入的是一个参数的数组形式
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16var o = {
name: 'andy'
};
function fn(arr) {
console.log(this);
console.log(arr); // 'pink'传入的是数组,传入之后打印的是字符串
};
fn.apply(o,['pink']);
//apply的应用:利用函数的内置对象求最值
var arr = [6,4,3,6,7,8];
var max = Math.max.apply(Math,arr);
var min = Math.min.apply(Math,arr);
console.log(max);
console.log(min);
- 作用:
bind()
- 作用:
- 不会立即调用函数(与前两个最大的不同),
- 改变this的指向,返回的是一个新的函数[ 返回由指定的 this 值和初始化参数改造的原函数拷贝 ]
- 作用:
如果有的函数我们不需要立即调用,但是又想改变这个函数内部的this指向此时用bind
1
2
3
4
5
6
7
8
9
10
11<script>
//bind函数:改变this的指向;不会调用函数,改变之后需要重新尽心函数的嗲用
var o = {
name:'andy'
}
function fn(a,b){
console.log(this);
console.log(a+b);
}
var f = fn.bind(o,2,4);//不会调用函数,返回一个改变this之后的新函数
f();//调用改变后的新函数1
2
3
4
5
6
7
8
9
10
11
12
13//4. 我们有一个按钮,当我们点击之后就会禁用按钮,3s之后开启这个按钮
var btns = document.querySelectorAll('button');
//循环为每个button设置响应事件
for (var i = 0; i < btns.length; i++) {
for (var i = 0; i < btns.length; i++) {
btns[i].onclick = function () {
this.disabled = true;
setTimeout(function () {
this.disabled = false;//这里的this就是绑定的btn的this,通过bind将this指向btn
}.bind(this), 2000);
}
}
}call apply和bind的总结
概念:js中除了提供正常模式,还提供了严格模式,ES5的严格模式是采用具有限制性的,JavaScript 变体的一种方式,即在严格的条件下运行 JS 代码。
严格模式在 IE10 以上版本的浏览器中才会被支持,旧版本浏览器中会被忽略。
严格模式对正常的 JavaScript 语义做了一些更改:
严格模式可有应用到整个脚本或个别函数中,可以将严格模式分为
- 脚本开启严格模式[以可以将整个脚本文件放在一个立即执行的匿名函数之中。这样独立创建一个作用域而不影响其他 script 脚本文件。 ]或者直接在script开头直接定义严格模式
1
2
3
4(function (){
"use strict"; var num = 10;
function fn() {}
})(); - 为函数开启严格模式[ 要给某个函数开启严格模式,需要把“use strict”; (或 ‘use strict’; ) 声明放在函数体所有语句之前。 ]
1
2
3
4function fn(){
"use strict";
return "这是严格模式。";
}
- 脚本开启严格模式[以可以将整个脚本文件放在一个立即执行的匿名函数之中。这样独立创建一个作用域而不影响其他 script 脚本文件。 ]或者直接在script开头直接定义严格模式
严格模式中的变化
- 变量:
- 变量没有声明则不能使用
- 严禁删除已经声明的变量eg:delete x
- this的指向问题
- 在全局作用域中的this[由window->undefined]
- 构造函数的调用[不加new可以调用->不加new,不能调用,this指向undefined]
- new实例化的构造函数指向创建的对象实例
- 定时器还是指向window
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//脚本开启严格模式
'use strict';
//下面的js代码就按照严格模式执行代码
//1. 我们的变量名必须先声明在使用
// num = 10;报错
// console.log(num);
//2.我们不能随意删除已经声明好的变量
// delete num;报错
// 3.严格模式下全局作用域中函数的this是undefined
// function fn(){
// console.log(this);//undefined
// }
// fn();
// 4.严格模式下,如果构造函数不加new调用,this指向的是undefined,如果给他赋值则会报错
// function Star(){
// this.sex = '男';
// }
// Star();//构造函数调用this指向undefined
// var ldh = new Star();
// console.log(ldh.sex);
// 5. 定时器 this 还是指向 window
// setTimeout(function() {
// console.log(this);
// }, 2000);
// 6.严格模式下函数里面的参数是不允许有重名的
function fn(a,a){//报错
console(a+a);
}
fn(1,2); - 函数变化
- 函数不能有重名的参数
- 函数必须声明在顶层,新版本的JavaScript会引入’块级作用域’,不允许在费版本的代码块中申明函数(比如for,if的{}内)
[严格模式参考链接](https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Strict_mode
- 变量:
)
高阶函数
- 高阶函数是对其他函数进行操作的函数,可以接收函数作为参数或将参数作为返回值
1
2
3
4
5
6
7
8
9
10
11
12
13
14
151.函数作为参数
function fn(a,b,callback){
console.log(a+b);
callback && callback();
}
function test(){
alert('我是最后调用的函数');
}
//调用函数
fn(2,3,test);
2.函数作为返回值
<script>
function fn(){ return function() {}
} fn();
</script> - fn此时就是一个高阶函数也是一种数据类型,同样可以作为参数传递给另外一个参数使用,最典型的就是作为回函数
- 同样也可以作为返回值传递回来
闭包(closure)
- 函数的作用域:分为全局作用域和局部作用域
- 函数内部可以使用全局变量。
- 函数外部不可以使用局部变量。
- 3.当函数执行完毕,本作用域内的局部变量会销毁。
- 闭包:有权访问另一个函数作用域中变量的函数[,一个作用域可以访问另外一个函数内部的局部变量。 ],被访问的函数所在的函数就是闭包
- 闭包的作用:延伸变量的作用范围
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18//闭包:有权访问另一个函数作用域中变量的函数
// 使用fun函数作用域访问了另外一个函数fn里面的局部变量num
// 被访问的函数所在的函数就是闭包
function fn() {
var num = 10;
function fun() {
console.log(num);
}
fun();
}
fn();
//闭包的主要作用:延伸变量的作用范围
function fn(){
var num= 10;
return function(){console.log(num);}//返回值是一个函数
}
var f = fn();//接收返回的函数,通过返回的函数调用fn中的变量
f();//返回10 - 立即执行函数是一个小闭包(会出现内存泄漏的问题)[即立即执行函数里面的任何一个函数都可以使用它的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//1.闭包应用-点击li输出当前的索引号
//1. 利用动态添加属性的方式
var list = document.querySelectorAll('li');
// for(var i = 0;i<list.length;i++){
// list[i].index = i;//保存li的索引号
// //为每一个li添加单击相应函数
// list[i].onclick = function(){
// // console.log(i);//for函数执行块,点击事件执行时for函数已经执行完毕,此时的i一直是list.length,解决方案,为list[i]添加索引号
// console.log(this.index);
// }
// }
// 2.通过闭包的方式得到当前li的索引号
for(var i= 0;i<list.length;i++){
//利用for循环创建四个立即执行函数
//立即执行函数也成为一个小闭包[即立即执行函数里面的任何一个函数都可以使用它的i变量]
(function(i){
list[i].onclick = function(){
console.log(i);
}
})(i);
}
//2.通过小闭实现三秒钟之后打印出所有内容
var list = document.querySelectorAll('li');
for (var i=0;i<list.length;i++){
//通过小闭包立即执行函数解决异步执行过程i的值
(function(i){
setTimeout(function(){
console.log(list[i].innerHTML);
},3000);
})(i);
}
//3. 闭包应用-计算打车价格
// 打车起步价13(3公里内), 之后每多一公里增加 5块钱. 用户输入公里数就可以计算打车价格
// 如果有拥堵情况,总价格多收取10块钱拥堵费
// 定义一个立即执行函数直接开始调用
var car = (function(){
var start = 13;
var total = 0;
return {//返回一个函数数组
price: function(n){
if(n>5){
total = start;
}else{
total = start+(n-3)*5;
}
return total;
},
//拥堵之后的费用
yd: function(flag){
return flag?total+10:total;
}
}
})();
console.log(car.price(5));
console.log(car.yd(true));
console.log(car.price(10));
console.log(car.yd(false)); - 闭包的总结
- 递归:函数可以在其内部调用自身函数
- 递归函数和循环函数效果一样
- 递归函数很容易发生’栈溢出’(stack overflow),必须要添加退出条件return
- 递归函数应用
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// 1.利用递归函数求1-n的阶乘
function fn(n){
if(n==1){
return 1;
}
return n*fn(n-1);
}
console.log(fn(4));
//2./*
利用递归函数求斐波那契数列(兔子序列) 1、1、2、3、5、8、13、21...
// 用户输入一个数字 n 就可以求出 这个数字对应的兔子序列值
// 我们只需要知道用户输入的n 的前面两项(n-1 n-2)就可以计算出n 对应的序列值
*/
function fn(n){
if(n===1||n===2){
return 1;
}
return fn(n-1)+fn(n-2);
}
console.log((10));
//3.我们想要做输入id号,就可以返回的数据对象
var data = [{
id: 1,
name: '家电',
goods: [{
id: 11,
gname: '冰箱',
goods: [{
id: 111,
gname: '海尔'
}, {
id: 112,
gname: '美的'
},]
}, {
id: 12,
gname: '洗衣机'
}]
}, {
id: 2,
name: '服饰'
}];
//利用foreach去遍历每一个对象
function getID(json, id) {
var o = {};
json.forEach(function (item) {
if (item.id == id) {
o = item;
//如果我们想得到里层的数据11,12可以利用递归函数
//里面有goods这个数组并且数组的长度不为0
} else if (item.goods && item.goods.length > 0) {
o = getID(item.goods, id);
}
});
return o;
}
console.log(getID(data, 1));
console.log(getID(data, 2));
console.log(getID(data, 11));
console.log(getID(data, 12));
console.log(getID(data, 111));深拷贝和浅拷贝
- 浅拷贝:只拷贝最外层的数据,修改新拷贝的对象数据.原先数据也会被改变,对于引用数据类型这里值拷贝了地址,二者指向的是同一个内存空间
- 深拷贝:是拷贝了多层,每一级别的数据都会拷贝,拷贝以后重新开辟了新的内存空间,修改数据引用互不影响
- 浅拷贝实现(两种方式)
- 通过for in是吸纳
- 通过es6中Object.assign()方法实现,推荐使用
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24var obj = {
id: 1,
name: "angel",
msg: {
angel: 18
}
}
var o = {};
//浅拷贝
// 1.通过for in的方式
for(var k in obj){
//k是属性名,obj[k]是属性值
o[k] = obj[k];
}
console.log(o);
o.msg.angel = 30;//修改o连同obj的值也会改变
console.log(o);
console.log("---------------")
// 2.es6中提供了新的浅拷贝的方法Object.assign(newobj,oldobj)
Object.assign(o, obj);
console.log(o);
o.msg.angel = 30;//修改o连同obj的值也会改变
console.log(o);
- 通过递归函数实现深拷贝
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// 深拷贝是拷贝了多层,每一级别的数据都会拷贝,拷贝以后重新开辟了新的内存空间,修改数据引用互不影响
var obj = {
id: 1,
name: 'andy',
msg: {
age: 18
},
color: ['pink', 'red']
};
var o = {};
//封装函数
function deepCopy(newobj, oldobj) {
//获取属性值,通过for in
for (var i in oldobj) {
//判断拷贝的数据类型属于那种数据
// 1.获取属性值
var item = oldobj[i];
// 2.判断这个值是否是数组
if (item instanceof Array) {
newobj[i] = [];
deepCopy(newobj[i], item);
} else if (item instanceof Object) {
// 3.判断这个值是否是对象
newobj[i] = {};
deepCopy(newobj[i], item);
} else {
//4.普通数据类型直接复制
newobj[i] = item;
}
}
}
//调用函数
deepCopy(o, obj);
console.log(o);
var arr = [];
console.log(arr instanceof Object);
o.msg.age = 20;
console.log(obj);正则表达式
概述
- 正则表达式(Regular Expression)是用于匹配字符串中字符组合的模式;js中,正则表达式也是对象
- 正则表达式通常被用来检索,替换那些符合某些模式(规则的文本)eg:
- 表单验证:用户名表单只能输入英文字母、数字或者下划线, 昵称输入框中可以输入中文(匹配)
- 过滤掉页面内容中的一些敏感词(替换)
- 从字符串中获取我们想要的特定部分(提取)
- 正则表达式的特点
- 正则表达式的声明
- 通过构造函数的方式new RegExp()->var 变量名 = new RegExp(/表达式/);
- 通过字面量的方式-> var 变量名 = /表达式/;
1
2
3
4
5
6
7//正则表达式的两种声明方式'
// 1.通过new声明
var reg = new RegExp(/abc/);
// 2.通过字面生声明
var reg2 = /abc/;
//通过test()函数判断是否满足正则表达式的要求
console.log(reg.test("abfd"));//包含abc
- 测试表达式test()
1
regobject.test(待测字符串str)
- 正则表达式的组成
- 简单的正则表达式可以是简单的字符构成,也可以是简单和特殊的字符组成,特殊字符也称为元字符(^$+)
- 特殊字符查看mdn
- [正则表达式测试地址]{(https://tool.oschina.net/regex)}
- 正则表达式划分学习
- 边界符:^匹配行首的文本,$匹配行尾的文本,二者在一起必须是精确匹配
- 字符类:所有可供选择的字符都放在[]中,没有量词的情况下都是多选一;[^]表示取反,区分[]外面的^
- 量词:设定某个模式出现的次数
- *:0-n;
- ?:0-1;
- +:1-n
- {m,n}:m-n次
- {m}:只能是m次
- {m,}:m次或m次以上
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/*
正则表达式测试的方法:
test()
- 使用这个方法可以用来检查字符串是否否和正则表达式,符合返回true,反之false
*/
var result = reg.test(str);
console.log(result);
console.log("--------");
// 1.边界类
reg = /^bdc$/;
console.log(reg.test("bdc"));//唯一精确匹配
// 2.字符类
reg = /abc/ //包含字符abc
console.log(reg.test("sbcddfdsf"));
console.log(reg.test("aabc"));
console.log(reg.test("abcdss"));
console.log("----------");
reg = /[abc]/;//包含其中的字母返回他true,三选一
console.log(reg.test("sdkfjdskf"));//[-]表示范围
console.log(/^[a-z]$/.test('c'));
//[]中的^表示取反
reg = /[^1-9]$/;
console.log(reg.test('232'));//false
// 3.量词(用来设定某个模式出现的模式)?:0-1,+:1-n,*:0-n,{m,n}:m-n次[重复次数]
console.log("-----------------");
reg = /[1-9]{3,4}/;
console.log(reg.test('34fdgd'));
- 括号的总结:
- 大括号{}:量词符,里面表示重复的次数
- 中括号[]:字符集合,匹配方括号中的任意字符
- 小括号():表示优先级
- 预定义类[某些常见模式的简写形式]
- \d:匹配0-9之间的任意数字===[0-9];
- \D:匹配0-9之外的数字====[^0-9];
- \w:匹配任意的字母数字下划线====[A-Za-z0-9_]
- \W:除所有字母数字下划线以外的字符====[^A-Za-z0-9]
- \s:匹配空格(包括换行符,制表符,空格符)===[\t\n\v\f\r];
- \S:除非空格的字符[^\t\n\v\f\r]
- 正则表达式中的或”|”
- 查询可在正则表达式的测试网址查看
1
2
3var reg = /^\d{3,4}-\d{7,8}$/;
或者
var reg1 = /^\d{3}-\d{8}|\d{4}-\d{7}$/; - 正则表达式中的替换
-字符串和正则表达式相关的方法:- search(正则表达式)搜索字符串中是否含有指定内容,搜索到则返回第一个元素 的位置,搜索不到则返回-1;只会查找第一个
- match()可以将字符串中符合条件的内容提取出来,可以提取出所有的符合要求的内容,默认只会返回第一个,设置全局匹配模式,多个结果返回的是一个数组
- split(“*”)可以通过参数字符串拆分为字符数组,并返回所有的字符串数组
- replace(“被替换”,”替换内容”)可以将字符串中的内容替换为新的内容默认只会替换以一个,设置全局替换模式
- replace(reg/str,newstr);第一个参数可以是正则表达式也可以字符串
1
2
3
4
5
6
7
8// replace(被替换内容,"新的的内容");被替换内容可以是字符串也可以是正则表达式
var text = document.querySelector('textarea');
var button = document.querySelector('button');
var box = document.querySelector('div');
var reg = /黄色|激情/g;
button.onclick = function () {
box.innerHTML = text.value.replace(reg, "**");
}ES6
ES6解释
ES 的全称是 ECMAScript , 它是由 ECMA 国际标准化组织,制定的一项脚本语言的标准化规范。为什么使用ES6
每一次标准的诞生都意味着语言的完善,功能的加强。JavaScript语言本身也有一些令人不满意的地方。
- 变量提升特性增加了程序运行时的不可预测性
- 语法过于松散,实现相同的功能,不同的人可能会写出不同的代码
ES6新增的语法
1.let关键字
- 特点:
- let关键字是用来声明变量的
- 声明的变量具有块级作用域
- 在一个{}中let声明的变量才具有块级作用域,var关键字声明的不具有这个特点
- let关键字声明的变量防止循环变量变成全局变量
- 具有暂时性死区特性
- 没有变量的提升
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//1.let关键字是用来声明变量的,let关键字声明的变量具有块级作用域
let a = 0;
console.log(a);
// 2.let声明的变量具有块级作用域,只在{}内可以使用
if(true){
let b = 20;
console.log(b);
if(true){
let c = 30;
console.log(c);
}
console.log(c);
}
// console.log(b);//报错,开机作用域之外不能访问
//3.在一个{}中let声明的变量才具有块级作用域,var关键字声明的不具有这个特点
if(true){
let num = 10;
var abc = 200;
}
console.log(num);//报错没有声明
console.log(abc);
// 4,防止循环变量变成全局变量
for (var i = 0; i < 2; i++) { }
console.log(i);//2-----var声明的变量在循环体之外也可以访问,变量了全局变量----
// 5.let声明的变量没有变量的提升(必须先声明在使用)
console.log(a);//报错,a没有声明
let a = 10;
// 6.let关键字声明的变量具有暂时性死区特性
var num= 10;
var num = 5;
if(true){
console.log(num);//报错,变量没有初始化
let num = 20;
}
- 经典面试题
1
2
3
4
5
6
7
8var arr = [];
for (var i = 0; i < 2; i++) {
arr[i] = function () {
console.log(i);
}
}
arr[0]();//2
arr[1]();//2
经典面试题图解:此题的关键点在于变量i是全局的,函数执行时输出的都是全局作用域下的i值。
1 | let arr = []; |
经典面试题图解:此题的关键点在于每次循环都会产生一个块级作用域,每个块级作用域中的变量都是不同的,函数执行时输出的是自己上一级(循环产生的块级作用域)作用域下的i值.
const
- 特点:
- const声明的常量具有块级作用域
- 声明常量不许赋初始值
- 常量赋值后不能修改(引入数据类型地址不能修改,内部的值可以修改,普通数据类型值不能修改)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22// 1.const关键字声明的常量具有块级作用域,只在在即的{}有作用
if(true){
const a = 10;
if(true){
const a = 20;
console.log(a);//20
}
console.log(a);//10
}
console.log(a);//not defined
// 2.使用const关键字声明的常量必须赋初始值
// const PI;//没有初始值奥错
const PI = 3.29;
console.log(PI);
// 3.常量声明后不能更改
// PI = 3;//报错
const arr= [100,200];
arr[0] = 500;
arr[1]= 400;
console.log(arr);
arr = [2,4];//报错,常量声明后的地址不能更改
- 总结
- 使用var声明的变量,作用域为该语句所在的函数内且存在变量提升
- 使用let声明的变量,其作用域为该语句所在的代码块{}内
,不存在变量提升 - const声明的常量,在后面出现的代码块中不能再修改
解构赋值
- ES6中允许从数组中提取值,按照对应位置对变量赋值,对象也可以实现解构
数组解构
- 数组解构允许我们按照一一对应的关系从数组中提取值,然后赋值给变量
- 个数不匹配时,多出来的变量的值=undefined
1
2
3
4
5
6
7
8// 数组解构允许我们按照一一对相应的关系从数组找那个提取值,然后赋值给变量
let arr = [1,2,5,6,7];
let [a,b,c,d,e] = arr;//按照一一对应的关系获取arr中的数组,abcde都是变量
console.log(a);
console.log(b);
console.log(c);
console.log(d);
console.log(e);对象解构
- 对象解构允许我们使用变量的名字匹配对象的属性,匹配成功则将对象的属性值赋值给变量
1
2
3
4
5
6
7
8
9// 对象解构允许我们使用变量的名字匹配对象的属性 匹配成功 将对象属性的值赋值给变量
let person = {name:"lili",age:23,gender:"女"};
let {name,age,gender} = person;//将属性的值赋值给变量name,age,gender
console.log(name);
console.log(age);
console.log(gender);
let {name:myname} = person;//通过name属性去匹配person中的name属性,匹配成功将person中name属性的值赋值给myname
console.log(myname);小结
- 解构赋值就是把数据结构分解,然后给变量进行赋值
- 如果结构不成功,变量跟数值个数不匹配的时候,变量的值为undefined
- 数组解构用中括号包裹,多个变量用逗号隔开,对象解构用花括号包裹,多个变量用逗号隔开
- 利用解构赋值能够让我们方便的去取对象中的属性跟方法
箭头函数
- 箭头函数是用来简化语义定义语法
- 在箭头函数中如果函数体中只有一句代码,并且代码的执行结果就是函数的返回值{}可以省略,return关键字也要省略
- 在箭头函数中如果形参中只有一个()可以省略
- 箭头函数不绑定this,没有自己的this关键字,如果在箭头函数中使用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// 1.箭头函数是用来简化函数的语法
const fn = ()=>{
console.log(123);
}
fn();
// 2.在箭头函数中如果函数体中只有一句代码,并且代码的执行结果就是函数的返回值{}可以省略,return关键字也要省略
const sum = (a,b)=> a+b;
console.log(sum(3,4));
// 3.在箭头函数中如果形参中只有一个()可以省略
const fn1 = v=>{
alert(v);
}
fn1(4);
// 4.箭头函数不绑定this,没有自己的this关键字,如果在箭头函数中使用this关键字将指向箭头函数定义位置的this
function fn3(){
console.log(this);//这里的this->wiindow
return ()=>{
console.log(this);//指向箭头函数定义的位置的this->wiindow
}
}
var newfn = fn3();
newfn();
const obj = {name: "zhangsan"};
const resfn = fn3.call(obj);//this指向对象实例
resfn();//箭头函数也指向对象实例小结
- 箭头函数中不绑定this,箭头函数中的this指向是它所定义的位置,可以简单理解成,定义箭头函数中的作用域的this指向谁,它就指向谁
- 箭头函数的优点在于解决了this执行环境所造成的一些问题。比如:解决了匿名函数this指向的问题(匿名函数的执行环境具有全局性),包括setTimeout和setInterval中使用this所造成的问题
面试题
- 对象没有作用域
1
2
3
4
5
6
7
8var obj = {
age:32,
say: ()=>{
alert(this);//window
}
}
obj.say();
// 箭头函数指向的是定义位置的this,而对象没有作用域,所以箭头函数虽然在对象中被定义,但是this指向的是全局作用域剩余参数
- 剩余参数语法允许我们将一个不定数量的参数表示为一个数组,不定参数定义方式,这种方式很方便的去声明不知道参数情况下的函数
- 箭头函数中没有arguments对象,通过剩余参数来对参数进行操作
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17const sum = (...args)=>{
let total = 0;
// args.forEach(item=>{
// total += item;
// return total;
// })
// 简写
args.forEach(item=> total+=item);
return total;
}
console.log(sum(10,20,30));
console.log(sum(102,4,3,2));
let arr = ['张三','李四','王五'];
let [s1,...s2] = arr;
console.log(s1);//字符串
console.log(s2);//数组
ES6内置对象扩展
Array的扩展方法
扩展运算符
- 扩展运算符可以将数组拆分为以逗号分隔的参数序列
- 扩展运算符可以应用于数组合并[两种方式]
- 直接合并
- 通过push(可传输多个参数)方法
- 利用扩展运算符将伪数组转化为真正的数组[…array]
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// 扩展运算符可以将数组拆分成以逗号分隔的参数序列
let arr= ['a','b','c','d','f'];
console.log(...arr);//a b c d f参数序列
console.log('a','b','c','d','f');//a b c d f参数序列
console.log("----");
// 2.扩展运算符应用于数组合并
let arr1 = [1,2,4,5,6,7,8];
let arr2 = [2,3,4,5,6,6];
console.log(...arr1);
console.log(...arr2);
let arr3 = [...arr1,...arr2];//合并数组
console.log(arr3);
// 3.合并数组的第二种方法
let arr4 = [2,3,4];
let arr5 = [4,5,6];
arr5.push(...arr4);//push方法可以参数输入多个
console.log(arr5);
// 4.利用扩展运算符将伪数组转化为真正的数组
var oDivs = document.querySelectorAll('div');//得到的是伪数组,伪数组只有length属性,不能调用数组的方法,可以先将伪数组转化为真正的数组在使用数组的方法
console.log(oDivs);
console.log(oDivs.length);
var ary = [...oDivs];
ary.push('a');
console.log(ary);伪数组和真正数组的区别
- 拥有 length 属性,其它属性(索引)为非负整数(对象中的索引会被当做字符串来处理,这里你可以当做是个非负整数串来理解)
- 不具有数组所具有的方法
- 伪数组,就是像数组一样有 length 属性,也有 0、1、2、3 等属性的对象,看起来就像数组一样,但不是数组,比如:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16var fakeArray = {
"0": "first",
"1": "second",
"2": "third",
length: 3
};
- 伪数组存在的意义,是可以让普通的对象也能正常使用数组的很多方法
- 常见的伪数组
- 函数内部的 arguments
- DOM 对象列表(比如通过 document.getElementsByTags 得到的列表)
- jQuery 对象(比如 $("div") )
- 伪数组可以通过内置对象扩展中的扩展运算符转化为真正的数组,在调用数组的方法
- 总结:
- 对象没有数组 Array.prototype 的属性值,类型是 Object ,而数组类型是 Array
- 数组是基于索引的实现, length 会自动更新,而对象是键值对
- 使用对象可以创建伪数组,伪数组可以正常使用数组的大部分方法构造函数方法:Array.from
- 将伪数组或者可遍历对象转化为真正的数组
- Array.from(arrobj,callback)
- 第一个参数是伪数组
- 方法还可以接受第二个参数,作用类似于数组的map方法,用来对每个元素进行处理,将处理后的值放入返回的数组
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22// 伪数组->转化为真正的数组
//定义结构
let arrlike = {
'0': "张三",
'1': "李四",
'2': "王五",
"length": 3
}
//转成数组
let arr = Array.from(arrlike);
console.log(arr);
console.log(arr[2]);
// 2.Array.from(伪数组,callback),第二参数是一个函数可以对数组中的元素进行计算
let arrayLike = {
"0": "1",
"1": "2",
"length": 2
}
<!-- 注意:如果是对象,那么属性需要写对应的索引 -->
let ary = Array.from(arrayLike,(item)=> item*2);
console.log(ary);实例方法find()
- find()用于找出第一个符合条件的数组成员,如果没有找到返回undefined
- 找数组里面符合条件的值,当数组中元素id等于2的查找出来,注意,只会匹配第一个
1
2
3
4
5
6
7
8
9
10
11var ary = [{
id: 1,
name: '张三'
}, {
id: 2,
name: '李四'
}];
let target = ary.find((item)=>{
return item.id === 2;
})
console.log(target);实例方法:findIndex()
- 用于找出第一个符合条件的数组成员的位置(索引号),如果没有找到返回-1
1
2
3let arr = [1,2,3,4,5,6,9,77];
let index = arr.findIndex((value,index)=> value>9);
console.log(index);实例方法:includes()
- 判断某个数组是否包含给定的值,返回布尔值。
1
2
3let arr = ['a','b','c','d'];
let result = arr.includes('d');
console.log(result);String扩展方法
模板字符串
- ES6中新增的创建字符串的方法,tab键上的键
- 模板字符串可以解析变量${};
- 模板字符串可以换行
- 模板字符串可以调用函数
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22let name = `zhangsan `;
let sayHello = `我的名字叫做${name}`;
console.log(sayHello);
let result = {
name: '张三',
age: 29,
};
let html = `
<div>
<span>${result.name}</span>
<span>${result.age}</span>
</div>
`
console.log(html);//模板字符串中可以有换行,格式整齐
console.log("----");
const fn = ()=>{
return '我是fn字符串';
}
let h = `我是模板字符串${fn()}`;
console.log(h);实例方法:startsWith() 和 endsWith() repeat()
- startsWith():表示参数字符串是否在原字符串的头部,返回布尔值
- endsWith():表示参数字符串是否在原字符串的尾部,返回布尔值
- repeat()方法表示将原字符串重复n次,返回一个新字符串
1
2
3
4
5
6
7
8
9
10/*
startsWith():表示参数字符串是否在原字符串的头部,返回布尔值
endsWith():表示参数字符串是否在原字符串的尾部,返回布尔值
*/
let str = 'hello';
console.log(str.startsWith("h"));//true
console.log(str.endsWith('!'));//false
// repead()方法表示将原字符串重复n次,返回一个新字符串
console.log('x'.repeat(4));Set数据结构
- ES6 提供了新的数据结构 Set。它类似于数组,但是成员的值都是唯一的,没有重复的值。
- Set本身是一个构造函数,用来生成Set数据结构
- 利用set做数组去重
- 实例方法
- add(value):添加某个值,返回 Set 结构本身
- delete(value):删除某个值,返回一个布尔值,表示删除是否成功
- has(value):返回一个布尔值,表示该值是否为 Set 的成员
- clear():清除所有成员,没有返回值
- 遍历:Set 结构的实例与数组一样,也拥有forEach方法,用于对每个成员执行某种操作,没有返回值。
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- Set本身是一个构造函数,用来生成Set数据结构
*/
const s1 = new Set();
console.log(s1.size);
// 1.利用set做数组去重
const s2 = new Set(['a','b','a','d']);
console.log(s2.size);//4
const arr = [...s2];//返回数组,先将set中的数据拆分为序列,将序列放在数组中形成新的数组
console.log(arr);
// 2. 向set中添加值add()返添加后的set
const s3 = new Set();
s3.add('a').add('b');
console.log(s3.size);
console.log("----");
// 3.set中删除delete()//返回true或false
const r1 = s3.delete('c');//删除不存在的值会返回false
console.log(s3.size);
// console.log(s4);//
// 4.判断set结构中的成员是否含有has,返回true或false
const r2 = s3.has('a');
console.log(r2);
// 5.清空set结构 中的值clear()没有返回值
const s5 = new Set(['a','s','d']);
console.log(s5.size);
s5.clear();
console.log(s5.size);
// 6.遍历set数据结构中的值
const s6 = new Set(['3','5','6','a']);
s6.forEach(value=>console.log(value))线程机制和事件机制
线程机制
- 1.进程:程序的一次执行它占有一块独有的内存空间
- 2.线程:cpu的基本调度单位,是程序执行的一个完整的流程
- 3.关系
- 一个进程中至少包含一个线程:主线程
- 一个进程中可以运行多个线程:分线程
- 一个进程内的数据可以供多个线程共享
- 多个进程之间的数据是不能共享的
- 4.浏览器运行是单进程还是多进程
- 有的是单进程:firefox,老版本的IE
- 有的是多进程:chrome和最新的IE:
- 如何查看浏览器是多进程运行:任务管理器下的进程
1.什么是浏览器内核:支持浏览器运行的核心程序
2.不同的浏览器内核不同
- Chrome Safari:webkit
- firfox:Gecko
- IE:Trident
- 360等国内浏览器:Trident和webkit
3.内核由很多模块组成 - js引擎模块 : 负责js程序的编译与运行
- html,css文档解析模块
- dom/css:负责dom.css在内存中的相关处理
- 布局和渲染模块:负责页面的布局和效果的绘制
- 定时器模块:负责定时器的管理
- 网络请求模块:负责服务器的请求(常规/Ajax)
- 事件响应模块:负责事件的管理
js线程
- js是单线程执行(回调函数也是在主线程中)
- h5提出了实现多线程的方案:Web Workers
- 只能是主线程更新界面
- 如何证明js执行是单线程的?
- setTimeout()的回调函数是在主线程执行的
- 定时器回调函数只有在运行栈中的代码全部执行完后才有可能执行
- 为什么js要用单线程模式, 而不用多线程模式?
- JavaScript的单线程,与它的用途有关。
- 作为浏览器脚本语言,JavaScript的主要用途是与用户互动,以及操作DOM。
- 这决定了它只能是单线程,否则会带来很复杂的同步问题
- 代码的分类:
- 初始化代码
- 回调代码
- js引擎执行代码的基本流程
- 定时器真的定时执行吗?
- 定时器并不能保证真正的定时执行
- 一般会延迟一丁点,也有可能延迟很长时间
- 定时器回调函数在分线程执行的吗?
- 在主线程中执行,js是单线程
- 定时器是如何实现的
- 事件循环模型
- 代码划分:初始化代码和回调函数
- 代码分类
- 初始化执行代码: 包含绑定dom事件监听, 设置定时器, 发送ajax请求的代码
- 回调执行代码: 处理回调逻辑
- js引擎执行代码的基本流程:
- 初始化代码===>回调代码
- 模型的2个重要组成部分:
- 事件管理模块
- 回调队列
- 模型的运转流程
- 执行栈:所有代码都在此空间中执行
- 浏览器内核
- brower core
- js引擎模块(在主线程中处理)
- 其他模块(在主/分线程处理)
- callback queue
- 任务队列
- 消息队列
- 事件队列
- 事件轮询:even loop从回调队列中循环取出回调函数放在执行栈中处理(一个接以一个)
- 事件驱动模型(event-driven interaction model),整个模型原理图就是一个事件驱动交互模型
- 请求响应模型(request-response model)(浏览器和服务器端的交互)
Web Workers
1.h5中提供了分线程的实现,取名为Web Workers
- 我们可以将一些大计算量的代码交由web Worker运行而不冻结用户界面
- 但是子线程完全受主线程控制,且不得操作DOM。所以,这个新标准并没有改变JavaScript单线程的本质
- Web Workers 是 HTML5 提供的一个javascript多线程解决方案
2.相关API - Worker:构造函数,加载分线程执行的js文件
- Worker.prototype.onmessage:用于接收另一个线程的回调函数
- Workers.prototype.postMessage:向另一个线程发送消息
- 获取数据event.data
3.不足- worker内代码不能操作DOM(更新UI),此时的全局对象不是window所以不能直接操作
- 不能跨域加载JS
- 不是每个浏览器都支持这个特性
- H5 WebWorker多线程图解
- 使用
- 在主线程中创建js文件发送消息并设置回调
1
2
3
4
5
6
7
8//创建一个Worker对象并向它传递将在新线程中执行的脚本的URL
var worker = new Worker("worker.js");
//接收worker传过来的数据函数
worker.onmessage = function (event) {
console.log(event.data);
};
//向worker发送数据
worker.postMessage("hello world");
- 作用域作用:隔离变量,可以在不同作用域下定义相同的变量,作用域代码书写以后就确定了有(n+1)个作用域,n代表嵌套函数,
- 作用域链的作用:查找变量函数嵌套
- 执行上下文也是n+1,函数执行上下文调用开始,执行结束终止
- for(var i in obj):i是属性名,obj[i]是属性值
- 异步执行任务(都不是立即执行)
- 定时器
- 绑定事件
- ajax
- 正则表达式查询
- [正则表达式测试地址]{(https://tool.oschina.net/regex)}
- 内存溢出
- 一种程序运行出现的错误
- 当程序运行需要的内存超过了剩余的内存时,就出抛出内存溢出的错误
- 内存泄露
- 占用的内存没有及时释放
- 内存泄露积累多了就容易导致内存溢出
- 常见的内存泄露:
- 意外的全局变量
- 没有及时清理的计时器或回调函数
- 闭包
本文链接: https://sparkparis.github.io/2020/03/26/JavaScript-%E9%AB%98%E7%BA%A7/
版权声明: 本作品采用 知识共享署名-非商业性使用-相同方式共享 4.0 国际许可协议 进行许可。转载请注明出处!