Fork me on GitHub
晴宝

吃饱好减肥


  • 首页

  • 分类

  • 归档

  • 标签

  • 关于

JS

发表于 2021-06-27 | 分类于 Front
字数统计 16.8k 字 | 阅读时长 78 分钟

JS学习笔记

对象和变量

语法:var变量名 = 值;

数据类型

  • 基本(值)类型

    String, Number, boolean, undefined,null

  • 对象(引用)类型

    Object, Function, Array

1
2
3
4
5
var obj ={x:1, y:2, z:3};
var obj1= Object.assign({}, obj,{z:4, a:5});
var obj2 = {...obj, z:6, a:5};

console.log(obj1, obj2); // { x: 1, y: 2, z: 4, a: 5 } { x: 1, y: 2, z: 6, a: 5 }>

判断(=== 恒等:判断值+类型相同,==:判断值相同)

typeof:可以判断undefined/数值/字符串/boolean,不能判断null和object

1
2
3
4
5
6
7
8
9
10
11
12
var a;
console.log(typeof a === 'undefined', a === undefined); // true true
a = 4;
console.log(typeof a === 'number'); // true
a = 'zhangsan';
console.log(typeof a === 'string'); // true
a = true;
console.log(typeof a === 'boolean'); // true
a = null;
console.log(typeof a); // object
a = () => {};
console.log(typeof a); // function

instanceof: 判断对象的具体类型

1
2
3
4
5
6
7
8
var b = {
data:function(){
return function(){
return 'zhangsan';
}
}
}
console.log(b.data() instanceof Function); // true

重点

  • undefined 和null的区别:undefined 代表定义未赋值;null 代表定义并赋值为null
  • 什么时候给变量赋值为null:初始赋值,表明将要赋值;结束前,让对象成为垃圾对象,以便被垃圾回收器回收

变量提升

变量提升:当栈内存(作用域)形成,JS代码自上而下执行之前,浏览器首先会把所有带var/function关键字进行提前声明或者定义

var 声明/function 声明+定义

​ 变量提升只发生在当前作用域(开始加载页面的时候只对全局作用域下进行变量提升,函数中存储的都是字符串而已);函数作用域下的变量在函数执行前也会发生变量提升,注意变量提升阶段window中已经存在该属性

  • 带 var 和不带 var 的区别

    1、在全局作用域下声明一个变量,相当于给window对象设置了一个属性,变量的值就是属性值

    2、在变量提升阶段,全局作用域中声明一个变量a,此时会把a作为属性赋值给window

1
2
3
4
5
6
7
8
9
10
// 首先运行这两句,会出现错误,因为a没有定义
console.log(a);
console.log('a' in window)

// 因变量提升,下面运行就不会出现错误
console.log(a); // undeifine
console.log('a' in window) // true
var a = 12;
console.log(a); // 12
console.log(window.a); // 12

等号左边变量提升

var fn;只对等号左边进行变量提升,所以表达式定义的函数一定是在其生命赋值之后执行的

1
2
3
4
5
6
fn();  // 此时fn是undefined
var fn = function(){
console.log('execution');
}

fn(); // execution

es6以后的变化

ES6 中基于let/const方式创建变量或者函数,不存在变量提升机制,同时阻断了全局变量和window属性的映射机制

1
2
3
4
5
6
7
let n = 10;
if(!('n' in window)){
let n = n + 30; // let会产生块级私有作用域,let n = 13;赋值操作是先准备值,然后再声明变量,再给变量赋值。当前操作中,先处理n+30,然后声明n,再赋值,在计算n+30的时候,块级作用域中的n还没有声明,所以直接报错。
console.log(n);
}

console.log(n);
1
2
3
4
5
6
let a = {n : 4}
let b = a;
b.x = a = {n : 4}

console.log(a.x) // undefined
console.log(b.x) // { n: 4 }

内置对象 —- 不懂

Array其实是一个Function类型的对象,它的prototype指向了Function.prototype

new Array()这是一个Object对象,它的prototype是指向了Object.prototype

1
2
3
4
5
6
7
8
9
10
11
12
(function(){
window.onload = function(){
var Person = function(){
this.name = 'zhang';
this.age = 11;
}

var person = new Person();
console.log(Object.prototype.toString.call(Person));
console.log(Object.prototype.toString.call(person))
}
})()

This对象

改变this的是Object,Function不会改变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
<script>
var name = "Smith";
console.log(this) // window
console.log(this.name) // Smith

// Object 对象
const person = {
name:"小仙女",
age:3,
printName : function(){
console.log(this); // {name: "小仙女", age: 3, printName: ƒ}
console.log(this.name, this.age); // 小仙女 3
}
}
person.printName();

console.log(this) // window

// function 函数
function a()
{
console.log(this); // window
}
a();

</script>

1、js中的this在运行期进行绑定,这是js中this关键字具备多重含义的本质原因

2、js中this可以是全局对象,当前对象或者任意对象,这取决于函数的调用方式

3、自执行函数中的this是Window

Js中函数的调用有以下几种方式:作为对象方法调用,作为函数调用,作为构造函数调用和使用apply和call调用

1、给当前元素的某个事件绑定方法,当事件触发方法执行的时候,方法中的this是当前操作的元素对象

2、普通函数执行,函数中的this取决于执行的主体,方法前面是否有点,有点前面的是谁this就是谁,严格模式下,没有点this是undefined

3、构造函数执行,方法中的this一般都是当前类的实例

4、箭头函数中没有自己的THIS,THIS是上下文的THIS

  • call

    call方法的作用:调用这个函数,修改函数运行时this的指向

    [fn].call([this],[param]…) 当前实例(函数fn)通过原型链的查找机制,找到Function.prototype上的call方法

    ​ console.dir(Function);

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
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>学习call函数</title>
</head>
<body>
<h1>JavaScript --- call函数</h1>

<p>此例调用 person 的 fullName 方法,在 person1 上使用它:</p>

<p id="demo"></p>

<script>
var person = {
fullName: function() {
return this.firstName + " " + this.lastName;
}
}
var person1 = {
firstName:"Bill",
lastName: "Gates"
}
var person2 = {
firstName:"Steve",
lastName: "Jobs"
}
var x = person.fullName.call(person1);
document.getElementById("demo").innerHTML = x;
</script>
</body>
</html>


<script>
function fn(param = {}) {
console.log(this);
console.log(param);
}
var o = {
name: 'obj'
}
fn.call(o,{'key1': 'value1'});
</script>
  • bind:bind()方法会创建一个新的函数,在bind()被调用时,这个新函数的this被指定为bind的第一个参数,而其余参数将被作为新函数的参数,供调用时使用

​ 注意有两个特征

​ 1、多次bind,仅第一次的bind传入的绑定的this生效

​ 2、使用new操作bind返回的构造函数,曾经绑定的this会失效

1
2
3
4
5
6
7
8
9
10
<button>点我</button>
<script>
var button = document.querySelector('button');
button.onclick = function () {
this.disabled = true;
setTimeout(function(){
this.disabled = false;
}.bind(button), 3000);
}
</script>
  • apply:和call基本上一模一样,唯一区别在于传参方式.fn.apply(thisArg,[argsArray])

    ​ thisArg:在fun函数运行时指定的this值

    ​ argsArray:传递的值,必须包含在数组里面

1
2
3
4
5
6
7
8
9
<script>
function fn(p1,p2){
console.log(p1,p2);
}

fn.apply(null,['a','b']);
fn.call(null,'a','b');

</script>

重点:

  • apply:

    1、参数一:改变this的指向(this指向参数一)

    2、类数组形式传参:[name,age]

    3、立即调用函数

  • call

    1、参数一:改变this的指向(this指向参数一)

    2、之后的参数:表示传参 需要枚举出来

    3、立即调用函数

  • bind

    1、参数一:改变this的指向(this指向参数一)

    2、之后的参数:表示传参 需要枚举出来

    3、z需要手动调用

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
function Person(name, age){    
this.name = name;
this.age = age;
}
function Student(name,age){
this.height = 170;
// this.name = name;
this.study = function(){
console.log(this.name + '在学习' + this.age)
}
// call 和apply是一个立即执行函数
// // Person.call(this,name,age) // 小仙女在学习
// Person.apply(this,["apply"]) // apply在学习
// Person.apply(this,[name,age]) // 小仙女在学习
// Person.apply(this,arguments) // 小仙女在学习
// bind 手动执行
Person.bind(this,name,age)();}
var s1 = new Student("小仙女",18 )
s1.study()
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
// 简单应用<
script>
// var wc = {
// type:'dag',
// name:'旺财',
// age:1,
// color:red,
// owner:'晴大宝',
// wang:function(){
// console.log(this.name + ' wang wang')
// },
// info(){
// console.log('我叫'+ this.name + ",我是一只" + this.color + '的' + this.type + ',我今年' + this.age + "岁,我的主人是" + this.owner)
// }
// }
function Animal(type, name, age, color, owner){
this.type = type;
this.name = name;
this.age = age;
this.color = color;
this.owner = owner;
this.info = function(){
console.log(`我叫${this.name},我是一只${this.color}的${this.type },我今年${this.age}岁,我的主人是${this.owner}`)
}
}
function dog(type, name, age, color, owner){
this.wang=function(){
console.log(this.name + " wang wang")
};
Animal.apply(this,arguments)
}
var wc = new dog("Dog","旺财","1",'red',"晴大宝")
wc.info()
wc.wang()
</script>

对象的创建的几种模式

  • Object构造函数模式

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    var p = new Object();
    p.name = 'Tom';
    p.age = 12;
    console.log(p) // {name: "Tom", age: 12}
    p.setName = function(name){
    this.name = name;
    }
    console.log(p) // {name: "Tom", age: 12, setName: ƒ} p.setName('zhangsan');
    console.log(p); // {name: "zhangsan", age: 12, setName: ƒ}
    console.log(p.setName) // ƒ (name){ this.name = name; }
  • 对象字面量模式

    1
    2
    3
    4
    5
    6
    7
    8
    var p = {   
    name: 'tom',
    age: 12,
    setname: function(name) {
    this.name = name;
    }
    }
    console.log(p) // {name: "tom", age: 12, setname: ƒ}
  • 工厂模式:通过工厂函数动态创建对象并返回

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    function createPerson(name,age){    
    var obj = {
    name: name,
    age: age,
    setName:function(name){
    this.name = name;
    }
    }
    return obj;
    }
    var p1 = createPerson('zhangsan', 23)
    var p2 = createPerson('lisi', 28)
    console.log(p1) // {name: "zhangsan", age: 23, setName: ƒ}
    console.log(p2) // {name: "lisi", age: 28, setName: ƒ}
  • 构造函数+原型

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    function Person(name, age){    
    this.name = name;
    this.age = age;
    }
    var p1 = new Person('Tom', 23);
    var p2 = new Person('Jack',24);
    console.log(p1); // Person {name: "Tom", age: 23}
    console.log(p2); // Person {name: "Jack", age: 24}
    // 类的显式原型 == 对象的隐士原型
    Person.prototype.setName = function(name){
    this.name = name;
    }
    var p1 = new Person('Tom', 23);
    var p2 = new Person('Jack',24);
    console.log(p1); // Person {name: "Tom", age: 23}
    console.log(p2); // Person {name: "Jack", age: 24}

深拷贝与浅拷贝

浅拷贝

大家用的同一块地址,没有开辟新的地址,改动原值就会改变

1
2
3
4
5
6
7
var a = {    
name: 'zhangsan',
age: 13
};
var b = a;
b.name = 'lisi'
console.log(a) // {name: "lisi", age: 13}console.log(b) // {name: "lisi", age: 13}

深拷贝

开辟了新的地址,改动原值也不会改变

1
2
3
4
5
6
7
8
9
10
11
var a = {    
name: 'zhangsan',
age : 13
};
var b = {};
for(var attr in a){
b[attr] = a[attr];
}
b.name = 'lisi';
console.log(a) // {name: "zhangsan", age: 13}
console.log(b) // {name: "lisi", age: 13}

关于引用变量赋值问题

​ 1、n个引用变量指向同一个对象,通过一个变量修改对象内部数据,其他所有变量看到的都是修改后的数据

​ 2、2个引用变量指向同一个对象,让其中一个引用变量指向另一个对象,另一个引用变量依然指向前一个对象

函数

所有的函数都是Function的实例

定义函数的三种方式

1、函数声明:

1
function fn1(){ // 函数申明    console.log("fn1");}

2、表达式

1
var fn2 = function(){    console.log('fn2')}

3、

1
var fn = new Function('参数1','参数2',...,'函数体')

箭头函数和普通函数的区别:

​ 1、箭头函数没有arguments,但是可以基于…arg获取实参集合

​ 2、没有自己的this,箭头函数中的this是上下文的this

函数的三种角色

1、普通函数:堆栈内存释放,作用域链

2、类

3、普通对象,和普通的OBJ没有区别,主要是对键值对进行增删改查

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
function Fn(){    
var n = 10;
this.m = 100;
}
Fn.prototype.aa = function(){
console.log('aa');
}
// Fn视为普通对象
Fn.bb = function(){
console.log('bb');
}
// 普通函数Fn();
//this=>window,有一个私有变量n,和原型以及私有属性bb没有关系
// 构造函数var f = new Fn();
// this => f
console.log(f.n); // undefined
console.log(f.m); // 100f.aa();
// aa
// 普通对象Fn.bb();
// bb

回调函数

常见的回调函数:

  • dom事件的回调函数

    1
    2
    3
    document.getElementById('btn').onclick = function(){    
    alert(this, innnerHTML);
    }
  • 定时器回调函数

  • ajax回调函数

  • 声明周期回调函数

IIFE

Immediately-Invoked Function Expression 立即执行函数表达式

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
// 立即执行函数
(function(){
console.log('fn2'); // fn2
})()
// 也可写成~
function(){
console.log('fn2') // fn2
}();
// 但是两个的默认返回值不同,第一个没有默认返回值,第二个默认返回值是-1
(function(){
console.log('fn2'); //ƒ ()
{
console.log('fn2');
}
})
// 这种写法错误,Function statements require a function name
function(){
console.log('fn2');
}()
  • 作用1:不会污染全局命名空间

    1
    2
    3
    4
    5
    6
    (function(){    
    var a = 10;
    console.log(a + 3); // 13
    })()
    var a = 20;
    console.log(a); // 20
  • 作用2:隐藏实现

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    (function(){    
    var a = 1;
    function f1(){
    console.log(++a);
    } // 2
    function f2(){}
    window.$ = function(){
    // 向外暴露一个全局函数
    return{
    func:f1
    }
    }})()$().func(); // $是一个函数,执行后返回的是一个对象

原型(重点、难点)

原型是一个对象,原型的作用是存储一个公共的属性和方法供它的实例调取使用

预备知识

[函数]

​ 普通函数,类

[对象]

​ 普通对象,数组,正则,arguments…,prototype的值也是对象类型的,函数也是对象类型的

1、所有的函数数据类型都带有prototype属性,这个属性是一个对象,浏览器会默认给它开辟一个堆内存

prototype的作用就是存储一些公共的属性和方法,供他的实例调取使用

2、默认的prototype带有constructor属性,这个属性存储的值是函数本身 (Date.prototype.constructor === Date)

3、每一个对象都有一个__proto__的属性,这个属性指向当前实例所属类的prototype原型对象其实就是普通对象,但Function.prototype是一个匿名函数也是一个空函数,执行没有任何输出*所有的函数都是Function的实例**

原型链:他是一个基于proto\向上查找的机制,这个属性指向当前实例所属类的prototype

​ 1、找到查找结果,使用自己私有的即可

​ 2、找不到,则基于__proto__找所属类的prototype,如果找到了就使用;如果没有找到就沿着原型上的__proto__继续向上查找,一直找到Object.prototype的原型为止;如果仍然没有找到,操作的属性或方法不存在

原型链

JavaScript的成员查找机制

​ 先在自身属性中查找,找到返回

​ 如果没有,在沿着__proto__这条链上查找,找到返回

​ 如果最终没有找到,返回undefined

原型链的属性问题:

​ 1、读取对象的属性值时:会自动到原型链中查找

​ 2、设置对象的属性时,不会查到原型链,如果当前对象中没有此属性,直接添加此属性并设置其值

​ 3、方法一般定义子原型中,属性一般通过构造函数定义在对象本身中

instanceof的判断原理

​ 表达式:A instanceof B

​ 如果B的显式原型对象在A对象的原型链上,返回true,否则返回false

闭包 — 不懂

一种理解:闭包指有权访问另一个函数作用域中变量;简单理解:一个作用域可以访问另一个函数内部的局部变量,局部变量所在的函数产生闭包

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

简单的说:函数形成一个私有的作用域,保护里面的私有变量不受外界的干扰,这种保护机制成为闭包

1
2
3
4
function fn(){    
return function(){
}}
var f = fn();
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
    <ul>        
<li>苹果</li>
<li>香蕉</li>
<li>葡萄</li>
</ul>
<script>
var list = document.querySelectorAll('li');
for(let index = 0; index < list.length; index++){
(function(index){
// 函数访问了立即执行函数中的变量,立即执行函数形成闭包
list[index].onclick = function(){
console.log(index);
}
})(index)
}
</script>

闭包的作用

1、使用函数内部的变量在函数执行完后,仍然存活在内存中(延长了局部变量的生命周期)

2、让函数外部可以操作(读写)函数内部的数据(变量/函数)

循环遍历加监听
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
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>prototype学习</title>
</head>
<body>
<button type="button">Click Me!</button>
<button type="button">Pink Me!</button>
<script>
var btns = document.getElementsByTagName('button');
for(var i = 0, length = btns.length; i < length; i++){
// 这种方法会有问题
var btn = btns[i];
btn.onclick = function(){
alert('第' + (i + 1) + '个');
}
// 解决办法1:
(function(i){
var btn = btns[i];
btn.onclick = function(){
alert('第' + (i + 1) + '个');
}
})(i)
// 解决办法2:利用es6解决,将var改成let
}
</script>
</body>
</html>
内存溢出与内存泄漏

内存溢出:当程

序需要的内存超过了剩余的内存时,就抛出了内存溢出的错误

内存泄漏:占用的内存内有及时释放

常见的内存泄漏:没有及时清理的计时器或回调函数,闭包

面向对象

>多态:父类定义一个方法不去实现,让继承它的子类去实现,每一个子类都有不同的表现

单例模式

高级单例模式形成私有作用域a,在a中创建一个堆内存,把对内存地址赋值给命名空间

1
2
3
4
5
var namespace = (function(){    
function fn(){}
return {
fn:fn
}})()

基于构造函数创建自定义类:new xxx(),这样普通函数就变成了构造函数,接收的返回结果是当前类的一个实例

创建对象的方式

字面量表达式

1
var obj = {};

构造函数模式

1
var obj = new Object();

基于数据类型基于两种模式创建出来的值是不一样的,基于字面量方式创建出来的值是基本类型值;基于构造函数创建出来的值是引用类型

通过构造函数创建对象

1
2
3
4
5
6
7
8
9
10
<script>    
function Star(name){
this.name = name
this.sing = function(){
console.log('我会唱歌');
}
}
var star = new Star('zhangsan');
star.sing();
</script>

构造函数是一种特殊的函数,主要用来初始化对象,即为对象成员变量赋初始值,它总是与new一起使用,通常把对象中公共的属性和放大抽取出来,然后封装到这个函数里面

构造函数的实例成员和静态成员

JavaScript中的构造函数中可以添加一些成员,可以在构造函数本身上添加(静态成员),也可以在构造函数内部的this上添加(实例成员)

静态成员:在构造函数本身上添加的成员称为静态成员,只能由构造函数本身访问

实例成员:在构造函数内部创建的对象成员称为实例成员,只能由实例化的对象来访问

​

1
2
3
4
5
6
7
8
9
10
11
12
<script>        
function Star(name){
// 成员变量
this.name = name;
}
// 静态变量
Star.age = 18;
var star = new Star('zhangsan');
console.log(star.age); // undefined
console.log(star.name) // zhangsan
console.log(Star.age); // 18
</script>

构造函数通过原型分配的函数所有对象是共享的,JavaScript规定,在每一个构造函数都有一个prototype属性,可以把方法定义在prototype对象上,这样所有对象实例就可以共享这些方法

对象原型__proto__:对象都会有一个属性__proto__指向构造函数的prototype,之所以我们对象可以使用构造函数prototype原型对象的属性和方法,就是因为对象有__proto__原型的存在

1
2
3
4
5
6
7
8
9
10
11
12
<script>        
function Star(name){
// 成员变量
this.name = name;
}
Star.prototype.sing = function(){
console.log('唱歌...')
}
var star = new Star('zhangsan');
star.sing(); // 唱歌...
console.log(Star.prototype === star.__proto__); // true
</script>

constructor: __proto__和prototype里面都有一个constructor属性,它指构造函数本身

继承

JS的继承并不是把父类的属性方法克隆一份给子类,而是让子类和父类建立原型链接的机制,子类的实例调取父类原型上的方法是基于原型链的查找机制完成的

通过构造函数+原型对象模拟实现继承

构造函数继承父类类型的属性;原型对象继承父类型的方法

​

属性的继承

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
<script>        
function Father(name, age){
//this指向父构造函数的对象实例
this.name = name;
this.age = age;
}
function Son(name, age, num){
//this指向子构造函数的对象实例
// 把父类Father作为普通函数执行,让Father中的this变为Son实例,
// 即把Father中的私有属性变为子类Son实例的私有属性
Father.call(this, name, age)
this.num = num;
}
var son = new Son('zhangsan', 12, 10);
console.log(son.age); // 12
</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
 <script>        
function Father(name, age){
this.name = name;
this.age = age;
}
Father.prototype.money = function(){
console.log('make money...');
}
function Son(name, age, num){
Father.call(this, name, age);
this.num = num;
}
// Father实例本身具备Father的私有属性和公共方法,子类son的原型指向它,
// 子类Son的实例就可以找到这些属性和方法了
Son.prototype = new Father();
Son.prototype.constructor = Son;
var son = new Son('zhangsan', 12, 10);
console.log(son.money); //
ƒ (){
console.log('make money...');
}
console.log(son.money()) // make money...
console.log(son.num) // 10
</script>

es6中的面向对象

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
class User{    
// 添加到实例上的类属性
z = 0;
// 构造函数
constructor(name, age = 10){
// this指向实例对象
this.name = name;
this.age = age;
}
// 成员方法,此方法实际创建在User的原型对象上
sayHello(){
return "hello, " + this.name;
}
// 静态方法
static isAdult(age){
if(age >= 18){
return "成年人";
}
return "未成年人";
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>面向对象学习</title>
<script src="../js/mian_object.js">
</script>
</head>
<body>
<script>
// 实例化对象
let user = new User('zhangsan', 30);
console.log(user.sayHello()); // hello, zhangsan
// console.log(user.isAdult(20)); // user.isAdult is not a function console.log(User.isAdult(20)); // 成年人
console.log(user.hasOwnProperty('sayHello')); // false console.log(user.__proto__.hasOwnProperty('sayHello')); // true
</script>
</body>
</html>

继承

​ super关键字用于访问和调用对象父类上的函数,可以调用父类的构造函数,也可以调用父类的普通函数

​ 继承中的属性或者方法查找原则:就近原则

​ 1、继承中,如果实例化子类输出一个方法,先看子类有没有这个方法,如果有先执行子类的方法

​ 2、继承中,如果子类里面没有,就去查找父类有没有这个方法,如果有就执行这个方法

注意:

​ 1、super必须放在子类this之前

​ 2、在es6中类没有变量提升,所以必须先定义类再实例化对象

​ 3、类里面的公有属性和方法一定要加this使用

​ 4、constructor里面的this指向实例对象,方法里面的this指向这个方法的调用者

​ 5、子类会将父类的静态方法继承下来

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
class Parent{    
constructor(name){
this.name = name;
}
getName(){
console.log(this)
return this.name;
}
static getNativeName(){
console.log(this);
return this.name;
}}
class Child extends Parent{
constructor(name, age){
super(name);
this.age = age;
}
}
1
2
3
4
5
6
7
    <script>        
const c = new Child('zhangsan', 18);
c.getName(); // Child {name: "zhangsan", age: 18}
c.name(); // 18
Child.getNativeName() // "Child"
Parent.getNativeName() // "Parent"
</script>

super作为对象:普通方法中表示父类的原型对象;在静态方法中表示父类

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
class Parent{    
constructor(name){
this.name = name;
}
getName(){
return this.name;
}}
Parent.msg = 'test';class Child extends Parent{
constructor(name, age){
super(name);
this.age = age;
}
getParentName(){
console.log(super.getName()); // 普通方法中super表示父类的原型对象
}
static getParentMsg(){
console.log(super.msg) // 静态方法中super表示父类
}
}
1
2
3
4
5
6
    <script>       
const c = new Child('zhangsan', 18);
c.getParentName(); // zhangsan
console.log(Child.msg); // test
console.log(Child.getParentMsg()); // test
</script>

子类的__proto__指向父类本身,即Child.__proto__ === Parent

子类的prototype属性的__proto__指向父类的prototype属性 // Child.prototype.__proto__ === Parent.prototype

实例的__proto__属性的__proto__指向父类实例的__proto__

es5中原生构造函数无法继承,es6中可以继承,包括:Boolean,Number,String,Array,Date,Function,RegExp,Error,Object

json(对象)解析

访问对象的内部数据的两种方式

属性名:编码简单,有时不能用

​ 属性名包含特殊字符p[‘content-type’] = ‘text/json’;

​ 变量名不确定

1
2
3
4
5
6
var p = {};
var propname = 'myAge';
var value = 18;
// p.propName = value; // 有问题
p[propname] = value;
console.log(p) // {myAge: 18}p.myAge // 18

[‘属性名’]:通用

​ JSON转字符串:JSON:stringify(data)

​ 字符串转JSON:JSON.parse(str)

定义

1
var json = {'name':'zhang'};json['name'] = 'yang';console.log(json.name)	// yang

遍历

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
var json = {    id: 1,    name: 'zhang',   
fun: function()
{
alert("function");
}
};
for(var i in json){
console.log(i) // id name fun // i 表示key值
if(typeof json[i] == "function"){
json[i](); // 调用该方法
}else{
alert(json[i]);
}
}
var jsonStr = "{name:'zhang',age:18}";
// var jsonObj = eval(jsonStr);
// 错误var jsonObj = eval("(" + jsonStr + ")");
console.log(jsonObj) // {name: "zhang", age: 18}
console.log(jsonStr) // {name:'zhang',age:18}

原因:json是以“{}”的方式来开始和结束的,在js中,它会被当作一个语句块来处理,所以必须强制性的把它转成表达式。加上圆括号的目的是迫使eval函数在处理javascript代码的时候强制将括号内的表达式转成对象,而不是作为语句来执行。

数组

push : 在数组的末尾添加元素,返回值为数组的长度

unshift:在数组的头部添加元素,返回值为数组的长度

pop:弹出尾部的元素

shift:弹出头部的元素

splice:

​ 删除arr.splice(0,2) ,从数组的第一个元素开始,删除2个数据

​ 替换arr.splice(0, 1, ‘haha’),将数组的第一个元素替换为‘haha’

字符串转数组:arr.split(‘’)

字符串反转:

​ var str = ‘zhangchuang’;

​ var arr = str.split(‘’).reverse().join(‘’);

随机数:

​ Math.random() : 返回0-1之间的小数

​ Math.round() : 四舍五入

​ 返回x-y之间的整数:Math.round(Math.random() * (y - x)) + x

1
var jsonArray = "[{name:'zhang', age:22}, {name:'heng', age:32}]";var jsonObj = eval("(" + jsonArray + ")");for(var i = 0; i < jsonObj.length; i++){    alert(jsonObj[i].name);}

正则表达式

正则匹配:验证字符串是否符合某个规则

正则捕获:把一个字符串中符合规则的字符提取出来

创建正则的两种方式

字面量的方式:/pattern/flag

​ RegExp对象创建:new RegExp(‘a’, ‘i’) // 忽略大小写

​ 正则中的元字符(特殊字符):( [ { \ ^ $ | ) ? * + . ] }

​ 注意斜杠 / 需要转义

​ 修饰符(flag):

​ i:执行对大小写不敏感的匹配

​ g:执行全局匹配,查找所有匹配而非在找到第一个匹配后停止

​ s:(ES9)dotAll模式,.可以匹配换行符

类

类使用 [] 来表达,用于查找某个范围内的字符

预定义类

预定义类 等价 描述
\s [\t\n\x0B\f\r] 空格
\S [^\t\n\x0B\f\r] 非空格
\d [0-9] 数字
\D [^0-9] 非数字
\w [a-zA-Z_0-9] 单词字符(字符、数字、下划线)
\W [^a-zA-Z_0-9] 非单词字符
. [^\r\n] 任意字符,除了回车与换行符外所有字符
\f \x0c \cL 匹配一个换页符
\n \x0a \cJ 匹配一个换行符
\r \x0d \cM 匹配一个回车符
\t \x09 \cI 匹配一个制表符
\v \x0b \cK 匹配一个垂直制表符
\xxx 查找以八进制数 xxx 规定的字符
\xdd 查找以十六进制数 dd 规定的字符
\uxxxx 查找以十六进制数 xxxx 规定的Unicode字符

注意事项

中括号出现的元字符一般代表本身的含义

1
2
3
let reg = /^[.]+$/;console.log(reg.test('n'));     // false
console.log(reg.test('...')); // truelet reg = /^[12-65]$/; //匹配1或2-6或5console.log(reg.test('13')); // false
console.log(reg.test('5')); // true

量词

量词表示匹配多少个目标对象,精确匹配长度使用 {}

量词 等价 描述
n * {0,} 匹配零个或多个n
n + {1,} 匹配至少一个n的字符串
n ? {0,1} 匹配零个或一个n
{n} 匹配n次
{n,m} 匹配n到m次
{n,} 至少匹配n次

边界

边界 描述
^ 以xx开始,在类[]中表示非
$ 以xx结束
\b 单词边界
\B 非单词边界

1、^匹配字符串开始位置,也就是位置0,如果设置了RegExp对象的Multiline属性m,^也匹配’\n’或’\r’之后的位置

2、$一般匹配字符串结束位置,如果设置了RegExp对象的Multiline属性m,$也匹配’\n’或’\r’之前的位置

3、\b匹配一个单词边界,也就是指定单词和空格间的位置,如er\b可以匹配”never”中的‘er’。但不能匹配“verb”中的’er‘

​ \B匹配非单词边界,如er\B能匹配“verb”中的‘er’,但不能匹配“never”中的’er‘

分组

分组使用()

​ 分组中使用 | 可以达到或的效果

改变默认优先级

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
let reg = /^18|19$/;
console.log(reg.test('18')); // true
console.log(reg.test('19')); // true
console.log(reg.test('1819')); // true
console.log(reg.test('189')); // true
console.log(reg.test('181')); // true
console.log(reg.test('819')); // true
console.log(reg.test('119')); // true
let reg = /^(18|19)$/; //只匹配18和19
console.log(reg.test('18')); // true
console.log(reg.test('19')); // true
console.log(reg.test('1819')); // false
console.log(reg.test('189')); // false
console.log(reg.test('181')); // false
console.log(reg.test('819')); // false
console.log(reg.test('119')); // false

分组引用(后向引用)

\n 表示后向引用, \1 是指在正则表达式中,从左往右第1个()中的内容;以此类推,\2 表示第2个(),\0表示整个表达式

1
2
3
let reg = /^([a-z])([a-z])\2\1$/; // \1表示和第一个分组中一模一样的内容console.log(reg.test('oppo'));  // true//匹配日期格式,表达式中的\1代表重复(-|\/|.)var rgx = /\d{4}(-|\/|.)\d{1,2}\1\d{1,2}/
console.log(rgx.test("2016-03-26")) // true
console.log(rgx.test("2016-03.26")) // false

反向引用

使用()后可以使用 $1-$9等来匹配

1
2
let str = '2018-02-11';let ret = str.replace(/(\d{4})\-(\d{2})\-(\d{2})/g, '$2/$3/$1');
console.log(ret); // 02/11/2018

​ 后向引用和反向引用的区别是:\n只能用在表达式中,而 $n只能用在表达式之外的地方。

分组捕获

被正则表达式匹配(捕获)到的字符串会被暂存起来. 其中由分组捕获的串会从1开始编号

1
2
3
4
let reg = /(\d{4})-(\d{2})-(\d{2})/;let date = '2010-04-12';reg.test(date);console.log(RegExp.$1); // 2010console.log(RegExp.$2); // 04
console.log(RegExp.$3); // 12//非捕获型, (?:\d{4})分组不会捕获任何串,所以$1为(\d{2})捕获的串let reg = /(?:\d{4})-(\d{2})-(\d{2})/;let date = '2010-04-12';
reg.test(date);console.log(RegExp.$1); // 04
console.log(RegExp.$2); // 12console.log(RegExp.$3); //

​ 分组捕获的命名(ES9)

1
2
3
4
5
const pattern = /(\d{4})-(\d{2})-(\d{2})/u;const result = pattern.exec('2018-10-25');console.log(result[0]);  //2018-10-25console.log(result[1]);  // 2018
console.log(result[2]); // 10
console.log(result[3]); // 25// 现在可以通过指定分组的名称const pattern = /(?<year>\d{4})-(?<month>\d{2})-(?<day>\d{2})/u;const result = pattern.exec('2018-10-25');
console.log(result.groups.year);// 2018console.log(result.groups.month);// 10
console.log(result.groups.day);// 25
正则捕获的懒惰性

exec - > 正则的捕获

每一次捕获的时候都是先进行默认的匹配,如果没有匹配成功的,捕获的结果是null;只有有匹配的内容我们才能捕获到;

捕获的内容格式

1、捕获到的内容是一个数组,数组中的第一项是当前正则捕获的内容

  index:捕获内容在字符串中开始的索引位置

​ input:捕获的原始字符串

1
2
3
var  reg = /\d+/;var str = 'woshi2016ni2017';
var res = reg.exec(str);console.log(res) // ["2016", index: 5, input: "woshi2016ni2017", groups: undefined]//第二次通过exec捕获的内容还是第一个"2016"var res = reg.exec(str);
console.log(res) // ["2016", index: 5, input: "woshi2016ni2017", groups: undefined]
正则捕获的贪婪性

正则表达式在匹配的时候默认会尽可能多的匹配,叫贪婪模式。通过在限定符后加 ?可以进行非贪婪匹配

比如 \d{3,6}默认会匹配6个数字而不是3个,在量词 {}后加一个 ?就可以修改成非贪婪模式,匹配3次

1
2
3
console.log("12345678".replace(/\d{3,6}/, '-'))// -78
console.log("12345678".replace(/\d{3,6}?/, '-'))// -45678
console.log("abbbb".replace(/ab+?/, '-'))// -bbb

?在正则中有很多的作用:

   放在一个普通的元字符后面代表出现0-1次 /\d?/ ->数字可能出现也可能不出现

​ 放在一个量词的元字符后面是取消捕获时候的贪婪性

表格操作相关

> 动态创建表格
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
<!DOCTYPE html><html lang="en"><head>    <meta charset="UTF-8">    <meta name="viewport" content="width=device-width, initial-scale=1.0">    
<title>Table学习</title>
<style> </style>
</head><body>
<script>
var table = document.createElement("table");
var cap = table.createCaption();
var th = table.createTHead();
var tb = table.createTBody();
var tf = table.createTFoot();
var tr = table.insertRow();
let newCell = tr.insertCell();
let newText = document.createTextNode('jiayou');
newCell.appendChild(newText);
let newCell2 = tr.insertCell();
let newText2 = document.createTextNode('cainiao');
newCell2.appendChild(newText2);
let newCell3 = tr.insertCell();
let newText3 = document.createTextNode('dalao');
newCell3.appendChild(newText3);
var tr2 = table.insertRow();
tr2.insertCell();
tr2.insertCell();
tr2.insertCell();
document.body.appendChild(table);
</script>
</body>
</html>

常用方法

参考文档:

https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/filter#Examples

form()

The Array.from()static method creates a new, shallow-copiedArray instance from an array-like or iterable object.

1
2
>  Array.from(arrayLike [, mapFn [, thisArg]])
>
1
2
console.log(Array.from('foo'));// expected output: Array ["f", "o", "o"]
console.log(Array.from([1, 2, 3], x => x + x));// expected output: Array [2, 4, 6]

isArray()

The Array.isArray() method determines whether the passed value is an [Array]

1
2
>  Array.isArray(value)
>
1
2
Array.isArray([1, 2, 3]);  // trueArray.isArray({foo: 123}); // false
Array.isArray('foobar'); // falseArray.isArray(undefined); // false

of()

The Array.of() method creates a new Array instance from a variable number of arguments, regardless of number or type of the arguments.

The difference betweenArray.of()and the Arrayconstructor is in the handling of integer arguments: Array.of(7)creates an array with a single element, 7, whereas Array(7)creates an empty array with a length property of 7 (Note: this implies an array of 7 empty slots, not slots with actual undefined values).

1
2
> Array.of(element0[, element1[, ...[, elementN]]])
>
1
Array.of(7);       // [7] Array.of(1, 2, 3); // [1, 2, 3]Array(7);          // array of 7 empty slotsArray(1, 2, 3);    // [1, 2, 3]

concat()

The concat()method is used to merge two or more arrays. This method does not change the existing arrays, but instead returns a new array.

1
2
>const new_array = old_array.concat([value1[, value2[, ...[, valueN]]]])
>
1
const array1 = ['a', 'b', 'c'];const array2 = ['d', 'e', 'f'];const array3 = array1.concat(array2);console.log(array3);// expected output: Array ["a", "b", "c", "d", "e", "f"]

copyWithin()

The copyWithin() method shallow copies part of an array to another location in the same array and returns it without modifying its length.

1
2
>  arr.copyWithin(target[, start[, end]])
>
1
2
3
const array1 = ['a', 'b', 'c', 'd', 'e'];// copy to index 0 the element at index 3
console.log(array1.copyWithin(0, 3, 4));// expected output: Array ["d", "b", "c", "d", "e"]// copy to index 1 all elements from index 3 to the end
console.log(array1.copyWithin(1, 3));// expected output: Array ["d", "d", "e", "d", "e"]

entries()

The entries()method returns a newArray Iterator` object that contains the key/value pairs for each index in the array.

1
2
>  array.entries()
>
1
2
const a = ['a', 'b', 'c'];for (const [index, element] of a.entries())  console.log(index, element);// 0 'a' // 1 'b' // 2 'c'
var a = ['a', 'b', 'c'];var iterator = a.entries();for (let e of iterator) { console.log(e);}// [0, 'a']// [1, 'b']// [2, 'c']

every()

The every() method tests whether all elements in the array pass the test implemented by the provided function. It returns a Boolean value.

1
2
>  arr.every(callback(element[, index[, array]])[, thisArg])
>
1
2
3
4
5
6
7
8
function isBigEnough(element, index, array) {  
return element >= 10;}[12, 5, 8, 130, 44].every(isBigEnough); // false
[12, 54, 18, 130, 44].every(isBigEnough); // true
[12, 5, 8, 130, 44].every(x => x >= 10); // false
[12, 54, 18, 130, 44].every(x => x >= 10); // true
const isBelowThreshold = (currentValue) => currentValue < 40;
const array1 = [1, 30, 39, 29, 10, 13];
console.log(array1.every(isBelowThreshold));// expected output: true

fill()

The fill()method changes all elements in an array to a static value, from a start index (default 0) to an end index (default array.length). It returns the modified array.

1
2
>  arr.fill(value[, start[, end]])
>
1
2
3
4
const array1 = [1, 2, 3, 4];// fill with 0 from position 2 until position 4
console.log(array1.fill(0, 2, 4));// expected output: [1, 2, 0, 0]// fill with 5 from position 1
console.log(array1.fill(5, 1));// expected output: [1, 5, 5, 5]
console.log(array1.fill(6));// expected output: [6, 6, 6, 6]
#### 8.9 filter()

The filter() method creates a new array with all elements that pass the test implemented by the provided function.

1
2
>  let newArray = arr.filter(callback(element[, index, [array]])[, thisArg])
>
1
const words = ['spray', 'limit', 'elite', 'exuberant', 'destruction', 'present'];const result = words.filter(word => word.length > 6);console.log(result); // (3) ["exuberant", "destruction", "present"]

1597198087241

1
2
3
4
5
6
7
8
9
10
function isBigEnough(value) {  return value >= 10}let filtered = [12, 5, 8, 130, 44].filter(isBigEnough)// filtered is [12, 130, 44]const array = [-3, -2, -1, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13]; 
function isPrime(num) {
for (let i = 2; num > i; i++) {
if (num % i == 0) {
return false;
}
}
return num > 1;
}
console.log(array.filter(isPrime)); // [2, 3, 5, 7, 11, 13]

find()

The find()method returns the value of the first element in the provided array that satisfies the provided testing function.

1
2
>  arr.find(callback(element[, index[, array]])[, thisArg])
>
1
2
3
const array1 = [5, 12, 8, 130, 44];
const found = array1.find(element => element > 10);
console.log(found);// expected output: 12

findIndex()

The findIndex()method returns the index of the first element in the array that satisfies the provided testing function. Otherwise, it returns -1, indicating that no element passed the test.

1
2
>  arr.findIndex(callback( element[, index[, array]] )[, thisArg])
>
1
2
3
const array1 = [5, 12, 8, 130, 44];
const isLargeNumber = (element) => element > 13;
console.log(array1.findIndex(isLargeNumber));// expected output: 3

flat()

The flat() method creates a new array with all sub-array elements concatenated into it recursively up to the specified depth.

1
2
>  var newArray = arr.flat([depth]);
>
1
2
3
4
5
6
7
const arr1 = [0, 1, 2, [3, 4]];
console.log(arr1.flat()); // Array [0, 1, 2, 3, 4]// expected output: [0, 1, 2, 3, 4]
const arr2 = [0, 1, 2, [[[3, 4]]]];
console.log(arr2.flat(2)); // Array [0, 1, 2, Array [3, 4]]// expected output: [0, 1, 2, [3, 4]]
const arr3 = [0, 1, 2, [[[3, 4]]],[[[[[5, 6]]]]]];
console.log(arr3.flat(3)); // Array [0, 1, 2, 3, 4, Array [Array [5, 6]]]
console.log(arr3.flat()); // Array [0, 1, 2, Array [Array [3, 4]], Array [Array [Array [Array [5, 6]]]]]

flatMap()

The flatMap()method first maps each element using a mapping function, then flattens the result into a new array. It is identical to a map() followed by a flat() of depth 1, but flatMap() is often quite useful, as merging both into one method is slightly more efficient.

1
2
>  var new_array = arr.flatMap(function callback(currentValue[, index[, array]]) { // return element for new_array}[, thisArg])
>
1
2
3
let arr1 = [1, 2, 3, 4];arr1.map(x => [x * 2]); // [[2], [4], [6], [8]]
arr1.flatMap(x => [x * 2]);// [2, 4, 6, 8]// only one level is flattened
arr1.flatMap(x => [[x * 2]]);// [[2], [4], [6], [8]]
1
2
3
4
let arr1 = ["it's Sunny in", "", "California"];
arr1.map(x => x.split(" "));
// [["it's","Sunny","in"],[""],["California"]]arr1.flatMap(x => x.split(" "));
// ["it's","Sunny","in", "", "California"]

forEach()

The forEach()method executes a provided function once for each array element.

1
2
>  arr.forEach(callback(currentValue [, index [, array]])[, thisArg])
>
1
const array1 = ['a', 'b', 'c'];array1.forEach(element => console.log(element));

includes()

The includes()method determines whether an array includes a certain value among its entries, returning true or false as appropriate.

1
2
>  arr.includes(valueToFind[, fromIndex])
>
1
2
3
4
5
const array1 = [1, 2, 3];
console.log(array1.includes(2));// expected output: true
const pets = ['cat', 'dog', 'bat'];
console.log(pets.includes('cat'));// expected output: true
console.log(pets.includes('at'));// expected output: false

indexOf()

The indexOf()method returns the first index at which a given element can be found in the array, or -1 if it is not present.

1
2
>  arr.indexOf(searchElement[, fromIndex])
>
1
2
3
4
const beasts = ['ant', 'bison', 'camel', 'duck', 'bison'];
console.log(beasts.indexOf('bison'));// expected output: 1// start from index 2
console.log(beasts.indexOf('bison', 2));// expected output: 4
console.log(beasts.indexOf('giraffe'));// expected output: -1

jion()

The join() method creates and returns a new string by concatenating all of the elements in an array (or an array-like object), separated by commas or a specified separator string. If the array has only one item, then that item will be returned without using the separator.

1
2
> arr.join([separator])
>
1
2
3
4
const elements = ['Fire', 'Air', 'Water'];
console.log(elements.join());// expected output: "Fire,Air,Water"
console.log(elements.join(''));// expected output: "FireAirWater"
console.log(elements.join('-'));// expected output: "Fire-Air-Water"

keys()

The keys() method returns a new Array Iterator object that contains the keys for each index in the array.

1
2
>  arr.keys()
>
1
2
3
4
5
const array1 = ['a', 'b', 'c'];
const iterator = array1.keys();
for (const key of iterator) {
console.log(key);
}// expected output: 0// expected output: 1// expected output: 2

lastIndexOf()

The lastIndexOf() method returns the last index at which a given element can be found in the array, or -1 if it is not present. The array is searched backwards, starting at fromIndex.

1
2
>arr.lastIndexOf(searchElement[, fromIndex])
>
1
2
3
const animals = ['Dodo', 'Tiger', 'Penguin', 'Dodo'];
console.log(animals.lastIndexOf('Dodo'));// expected output: 3
console.log(animals.lastIndexOf('Tiger'));// expected output: 1

map()

The map() method creates a new array populated with the results of calling a provided function on every element in the calling array.

1
2
>  let new_array = arr.map(function callback( currentValue[, index[, array]]) { // return element for new_array}[, thisArg])
>
1
2
const array1 = [1, 4, 9, 16];// pass a function to mapconst map1 = array1.map(x => x * 2);
console.log(map1);// expected output: Array [2, 8, 18, 32]

pop()

The pop() method removes the last element from an array and returns that element. This method changes the length of the array.

1
2
>  arrName.pop()
>
1
2
3
4
const plants = ['broccoli', 'cauliflower', 'cabbage', 'kale', 'tomato'];
console.log(plants.pop());// expected output: "tomato"
console.log(plants);// expected output: Array ["broccoli", "cauliflower", "cabbage", "kale"]plants.pop();
console.log(plants);// expected output: Array ["broccoli", "cauliflower", "cabbage"]

push()

The push() method adds one or more elements to the end of an array and returns the new length of the array.

1
2
>  arr.push([element1[, ...[, elementN]]])
>
1
2
3
const animals = ['pigs', 'goats', 'sheep'];const count = animals.push('cows');console.log(count);// expected output: 4
console.log(animals);// expected output: Array ["pigs", "goats", "sheep", "cows"]animals.push('chickens', 'cats', 'dogs');
console.log(animals);// expected output: Array ["pigs", "goats", "sheep", "cows", "chickens", "cats", "dogs"]

reduce()

The reduce() method executes a reducer function (that you provide) on each element of the array, resulting in single output value.

1
2
>  arr.reduce(callback( accumulator, currentValue[, index[, array]] )[, initialValue])
>
1
2
3
4
const array1 = [1, 2, 3, 4];
const reducer = (accumulator, currentValue) => accumulator + currentValue;// 1 + 2 + 3 + 4
console.log(array1.reduce(reducer));// expected output: 10// 5 + 1 + 2 + 3 + 4
console.log(array1.reduce(reducer, 5));// expected output: 15

reduceRight()

The reduceRight() method applies a function against an accumulator and each value of the array (from right-to-left) to reduce it to a single value.

1
2
>  arr.reduceRight(callback(accumulator, currentValue[, index[, array]])[, initialValue])
>
1
2
const array1 = [[0, 1], [2, 3], [4, 5]].reduceRight(  (accumulator, currentValue) => accumulator.concat(currentValue));
console.log(array1);// expected output: Array [4, 5, 2, 3, 0, 1]

reverse()

The reverse() method reverses an array in place. The first array element becomes the last, and the last array element becomes the first.

1
2
>  a.reverse()
>
1
2
3
4
5
const array1 = ['one', 'two', 'three'];
console.log('array1:', array1);// expected output: "array1:" Array ["one", "two", "three"]
const reversed = array1.reverse();
console.log('reversed:', reversed);// expected output: "reversed:" Array ["three", "two", "one"]// Careful: reverse is destructive -- it changes the original array.
console.log('array1:', array1);// expected output: "array1:" Array ["three", "two", "one"]

shift()

The shift() method removes the first element from an array and returns that removed element. This method changes the length of the array.

1
2
>  arr.shift()
>
1
2
const array1 = [1, 2, 3];const firstElement = array1.shift();console.log(array1);// expected output: Array [2, 3]
console.log(firstElement);// expected output: 1

slice()

The slice() method returns a shallow copy of a portion of an array into a new array object selected from start to end (end not included) where start and end represent the index of items in that array. The original array will not be modified.

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

some()

The some() method tests whether at least one element in the array passes the test implemented by the provided function. It returns a Boolean value.

1
2
> arr.some(callback(element[, index[, array]])[, thisArg])
>
1
2
3
const array = [1, 2, 3, 4, 5];// checks whether an element is even
const even = (element) => element % 2 === 0;
console.log(array.some(even));// expected output: true

sort()

The sort() method sorts the elements of an array in place and returns the sorted array. The default sort order is ascending, built upon converting the elements into strings, then comparing their sequences of UTF-16 code units values.

The time and space complexity of the sort cannot be guaranteed as it depends on the implementation.

1
2
> arr.sort([compareFunction])
>
1
2
3
4
5
const months = ['March', 'Jan', 'Feb', 'Dec'];
months.sort();console.log(months);// expected output: Array ["Dec", "Feb", "Jan", "March"]
const array1 = [1, 30, 4, 21, 100000];
array1.sort();
console.log(array1);// expected output: Array [1, 100000, 21, 30, 4]

splice()

The splice() method changes the contents of an array by removing or replacing existing elements and/or adding new elements in place.

1
2
>  let arrDeletedItems = array.splice(start[, deleteCount[, item1[, item2[, ...]]]])
>
1
2
3
4
const months = ['Jan', 'March', 'April', 'June'];
months.splice(1, 0, 'Feb');// inserts at index 1
console.log(months);// expected output: Array ["Jan", "Feb", "March", "April", "June"]months.splice(4, 1, 'May');// replaces 1 element at index 4
console.log(months);// expected output: Array ["Jan", "Feb", "March", "April", "May"]

toLocaleString()

The toLocaleString() method returns a string representing the elements of the array. The elements are converted to Strings using their toLocaleString methods and these Strings are separated by a locale-specific String (such as a comma “,”).

1
2
>  arr.toLocaleString([locales[, options]]);
>
1
2
const array1 = [1, 'a', new Date('21 Dec 1997 14:12:00 UTC')];const localeString = array1.toLocaleString('en', { timeZone: 'UTC' });
console.log(localeString);// expected output: "1,a,12/21/1997, 2:12:00 PM",// This assumes "en" locale and UTC timezone - your results may vary

toSource() ???

The toSource() method returns a string representing the source code of the array.

1
2
>  arr.toSource()
>
1
2


toString()

ThetoString()method returns a string representing the specified array and its elements.

1
2
>  arr.toString()
>
1
2
const array1 = [1, 2, 'a', '1a'];
console.log(array1.toString());// expected output: "1,2,a,1a"

unshift()

The unshift() method adds one or more elements to the beginning of an array and returns the new length of the array.

1
2
> arr.unshift(element1[, ...[, elementN]])
>
1
2
3
const array1 = [1, 2, 3];
console.log(array1.unshift(4, 5));// expected output: 5
console.log(array1);// expected output: Array [4, 5, 1, 2, 3]

values()

The values() method returns a new Array Iterator object that contains the values for each index in the array.

1
2
>  arr.values()
>
1
2
3
4
5
const array1 = ['a', 'b', 'c'];
const iterator = array1.values();
for (const value of iterator) {
console.log(value);
}// expected output: "a"// expected output: "b"// expected output: "c"

@@iterator

The @@iterator method is part of The iterable protocol, that defines how to synchronously iterate over a sequence of values.

The initial value of the @@iterator property is the same function object as the initial value of the values() property.

1
2
> arr[Symbol.iterator]()
>

WebApi

参考API: https://developer.mozilla.org/zh-CN/docs/WEB/API

Dom对象

定义

1、DOM是Document Object Model文档对象模型的缩写。根据W3C DOM规范,DOM是一种与浏览器,平台,语言无关的接口,使得你可以访问页面其他的标准组件

2、D:文档 – html 文档 或 xml 文档

3、O:对象 – 把document里的所有节点都看成对象

4、M:模型(用于建立从文档到对象的模型)

5、DOM 是针对xml(html)的基于树的API。

6、DOM树:节点(node)的层次。

7、DOM 把一个文档表示一个树模型

8、DOM定义了Node的接口以及许多种节点类型来表示XML节点的多个方面

DOM的映射机制

  1. 页面中的HTML元素, 和JS通过相关方法获取到的元素集合或者元素对象存在映射关系(一个改变另外一个跟着自动修改)

  2. xxx.style.color = ‘red’; 把xxx元素对象对应堆内存中的style属性下的color属性值改为red, 由于DOM映射关系, 页面中的标签和xxx元素对象绑定在一起, 修改元素对象空间的值, 页面中的元素会按照最新的值进行渲染.

  3. queryElementsBytagName获取到的空元素集合, 元素数据绑定后, 不需要重新获取; 而querySelectorAll不存在DOM映射机制, 数据绑定之后要重新获取.

BOM

browser object model: 浏览器对象模型

  1. 弹出新的浏览器窗口

  2. 移动、关闭浏览器窗口以及调整窗口大小

  3. 提供用户屏幕分辨率详细信息的屏幕对象

    页面加载完毕触发的事件:window.onload

    定时器: window.setInterval, window.setTimeout;

    清理定时器: clearInterval, clearTimeout

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Dom学习</title>
</head>
<body>
<button>Pink Me!</button>
<script>
var time = 5;
var btn = document.querySelector('button');
var timeid = setInterval(function(){
btn.innerText = 'please read('+ time-- + ')' ;
if(time <= 0){
clearInterval(timeid);
btn.disabled = false;
btn.innerText = 'OK';
}
},1000)
</script>
</body>
</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
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Dom学习</title>
</head>
<body>
<div>点击下面的按钮,你就见不到我喽!</div>
<button>Pink Me!</button>
<script>
window.onload = function(){
var dv = document.querySelector('div'); document.querySelector('button').onclick = function(){
var opacity = 10;
var timeid = setInterval(function(){
opacity--;
if(opacity <= 0){
clearTimeout(timeid);
}
dv.style.opacity = opacity / 10;
}, 100)
};
}
</script>
</body>
</html>
document

document.body //获取body

document.title //获取title

document.documentElement //获取html

xnavigator

对象主要包含了有关客户端浏览器的信息

cookieEnabled:cookie的支持情况

userAgent:获取浏览器版本与版本号

platform: 判断浏览器所在的系统平台的类型

###### 10.1.1.5 Screen
1
2
3
4
5
6
7
8
    <!-- 滚动条自动滚动 -->    
<script>
var i = 0;
setInterval(function(){
document.body.scrollTop = i++;
console.log(i);
}, 1000);
</script>
History

History对象包含客户端在浏览器中访问的URL

1
length,back(),forward(),go()
Location

包含有关当前URL的信息

hash: 设置或返回URL锚点

href: 设置或返回URL,可以设置跳转

hostname: 设置或返回当前的主机名

pathname: 设置或返回URL路径部分

protocol: 设置或返回当前传输协议

search: 设置或返回从?开始的URL查询部分

assign(): 加载新的文档

reload(): 重新加载

replace(): 新文档替换当前文档

获取元素

getElementById()

​ 1、 查询给定ID属性值的元素,返回该元素的元素节点。也称为元素对象。

​ 2、 因为在一个html页面中id的值是唯一的,所以返回值也是唯一的。所以方法的名称为getElementById()而不是getElementsById()

​ 3、 该方法只能用于document对象,只有document这个实例的原型上才能找到这个方法

​ 4、 如果获取不到,返回null

getElementsByName()

​ 1、查找给定name属性的所有元素,这个方法将返回一个节点集合,也可以称为对象集合。

​ 2、这个集合可以作为数组来对待,length属性的值表示集合的个数。

​ 3、因为在html页面中,name不能唯一确定一个元素,所以方法的名称为getElementsByName而不是getElementByName

​ 4、不推荐使用,有兼容性问题

getElementsByTagName()

    1、查询给定标签名的所有元素

​     2、因为在html页面中,标签名不是唯一的,所以返回值为节点的集合。

​     3、这个集合可以当做数组来处理,length属性为集合里所有元素的个数

​     4、可以有两种形式来执行这个方法:

​             1、var elements =document.getElementsByTagName(tagName); 

​             2、var elements = element.getElementsByTagName(tagName);

​     5、从这两种方法可以看出持有这个方法的对象并不一定是整个文档对象           

​                (document).也可以是某一个元素节点。

    6、如果获取不到,返回一个空集合

getElementsByClassName()

根据class属性名获取元素,ie9以后支持.

querySelector()

https://developer.mozilla.org/en-US/docs/Web/API/Document/querySelector

返回文档中与指定选择器或选择器组匹配的第一个 html元素

querySelectAll()

https://developer.mozilla.org/zh-CN/docs/Web/API/Document/querySelectorAll

返回与指定的选择器组匹配的文档中的元素列表

document.documentElement

document.body

document.head.

addEventListener()

为元素绑定(同时多个)事件: 对象.addEventListener() 谷歌和火狐支持,IE8不支持

第三个参数控制事件是捕获事件(true,从外向里传递)还是冒泡事件(false,从里向外传递)

1
2
3
4
5
6
7
    <button>Pick Me!</button>   
<script>
var btn = document.querySelector('button');
btn.addEventListener('click',function(){
console.log('click');
},false);
</script>

removeEventListener()

为元素解除绑定的事件

事件、节点、属性

事件

事件名称: click

事件源: 谁触发的事件

鼠标事件

​ click: 点击(PC端是点击, 移动端代表单击(300ms延迟))

​ dbclick: 双击

​ mouseover: 鼠标经过

​ mouseout: 鼠标移出

​ mouseenter: 鼠标进入

​ mouseleave: 鼠标离开

​ mousemove: 鼠标移动

​ mousedown: 鼠标按下(鼠标左右键都起作用)

​ mouseup: 鼠标抬起

​ mousewheel: 鼠标滚轮移动

​ mouseenter和mouseover的区别:

​ 1. over属于滑过事件, 从父元素进入到子元素, 属于离开了父元素, 会触发父元素的out子元素over; enter属于进入, 从父元素进入子元素, 并不算离开父元素, 不会触发父元素的leave, 而会触发子元素的enter

​ 2. mouseenter和mouseleave阻止了事件的冒泡传播, 而mouseover和mouseout存在冒泡传播

​ 3. 因此鼠标的进入和离开多使用mouseenter和mouseleave

键盘事件

​ keydown: 键盘按下

​ keyup: 键盘抬起

​ input: 移动端使用input统一代替

表单元素常用的事件

​ focus:获取焦点

​ blur:失去焦点

​ change:内容改变

其它常用的事件

​ load:加载完成

​ unload

​ beforeunload

​ scroll:滚动条滚动事件

​ resize:大小改变事件 window.onresize=function(){} 当浏览器窗口大小发生改变,会触发这个事件,执行对应的事情

事件的绑定

[DOM0级事件绑定] //事件的冒泡阶段执行

onclick是事件的一个私有属性, DOM0绑定的原理就是给元素某一个事件私有属性赋值.

[element].onxxx = function(){ }

[DOM2级事件绑定] //如果第三个参数设置为true, 会在事件的捕获阶段执行; 设置为false则会在事件的冒泡阶段执行.

​ DOM2事件绑定可以给当前元素的某一个事件行为绑定多个不同的方法

​ document.getElementById(‘box’).addEventListener(“click”,function(e){

​ console.log(e);

​ });

DOM0和DOM2的区别

  1. 机制不一样

    DOM0采用的是给私有属性赋值, 所以只能绑定一个方法

    DOM2采用的是事件池机制, 所以能绑定多个不同的方法

    2.移除

    box.onclick = null;

    DOM2在移除的时候, 必须清楚要移除哪一个方法

  2. DOM2事件增加了DOMContentLoaded事件(当页面中的HTML结构加载完成就会执行)

    box.addEventListener(‘DOMContentLoaded’, fn)

    window.onload是当页面中的资源都加载完成(html结构,css和js资源加载完成才会触发执行)

定义一个形参EV接收方法执行的时候,浏览器传递的信息值称为事件对象, MouseEvent鼠标事件对象、KeyboardEvent键盘事件对象 等

  • MouseEvent

ev.target:事件源(操作的是哪个元素)

ev.clientX / ev.clientY :当前鼠标触发点距离当前窗口左上角的X/Y轴坐标

ev.pageX / ev.pageY:当前鼠标触发点距离BODY(第一屏幕)左上角的X/Y轴坐标

ev.preventDefault():阻止默认行为

ev.stopPropagation():阻止事件的冒泡传播

ev.type:当前事件类型

  • KeyboardEvent

ev.code:当前按键’keyE’

ev.key:当前按键’e’

ev.which / ev.keyCode:当前按键的键盘码 69

let code = ev.which || ev.keyCode;

事件的传播机制

捕获阶段: 点击inner的时候, 首先从最外层开始向内查找(找到操作的数据源), 查找的目的是构建冒泡传播时传播的路线. 可以从ev.path看到这个层次结构

冒泡传播: 按照捕获阶段的路线, 自内向外把当前事件源的祖先元素的相关元素事件依次触发

事件的冒泡

多个元素嵌套,有层次关系,这些元素注册了相同的事件,如果里面的元素的事件触发了,外面元素该事件自动触发了

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
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title id="title">Title</title>
<style>
*{
box-sizing: border-box;
}
.wai{
width: 200px;
height: 200px;
background-color: red;
position: relative;
}
.zhong{
width: 100px;
height: 100px;
background-color: blue;
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%,-50%);
}
.li{
width: 50px;
height: 50px;
background-color: yellow;
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%,-50%);
}
</style>
</head>
<body>
<div class="wai">
<div class="zhong">
<div class="li"></div>
</div>
</div>
<button>点我</button>
<script>
var dvs = document.querySelectorAll('div');
for(var i = 0; i < dvs.length; i++){
(function(i){
dvs.item(i).onclick = function(){
console.log('div ' + i);
}
})(i)
}
</script>
</body>
</html>

阻止事件的冒泡

1
document.getElementById("box").onmousedown = function(e){		e = e || window.event;		e.stopPropagation ? (e.stopPropagation()) : (e.cancelBubble = true);}
为同一个元素注册多个不同的事件
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
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title id="title">Title</title>
<style>
*{
box-sizing: border-box;
}
span{
display: inline-block;
width:100px;
height: 40px;
background-color: #eee;
border-radius: 4px;
text-align: center;
line-height: 40px;
cursor: pointer;
}
.red{
background-color: red;
}
</style>
</head>
<body>
<span>button</span>
<script>
var span = document.querySelector('span');
span.onclick = f1;
span.onmouseover = f1;
span.onmouseout = f1;
function f1(e){
switch(e.type){
case 'click':
alert('click');
break;
case 'mouseover':
this.className = 'red';
break;
case 'mouseout':
this.removeAttribute('class');
break;
}
}
</script>
</body>
</html>

鼠标的跟随

当鼠标经过目标区域时,根据鼠标的位置,目标区域的位置,和一个固定的偏移量,即可算出跟随的区域的位置

1
2
3
4
lis[i].onmousemove = function(ev){            
mark.style.left = ev.pageX - container.offsetLeft + 20 + 'px';
mark.style.top = ev.pageY - container.offsetTop + 20 + 'px';
}
事件的委托机制

事件委托就是利用事件冒泡,只指定一个事件处理程序,就可以管理某一类型的所有事件.

属性的操作

dom的属性一般对应元素的属性

非表单元素的属性

href, title, id, src, className等

案例:控制div的显示与隐藏和按钮文字的切换

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
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title id="title">Title</title>
<style>
div{
background-color: red;
width: 200px;
height:200px;
}
.hidden{
display: none;
}
.show{
display: block;
}
</style>
</head>
<body>
<button>显示</button>
<div class="show"></div>
<script>
var btn = document.querySelector('button');
var div = document.querySelector('div');
var isShow = true;
btn.onclick = function () {
if(isShow){
isShow = false;
btn.innerHTML = "隐藏";
div.className = 'hidden';
}else{
isShow = true;
btn.innerHTML = "展示";
div.className = 'show';
}
}
</script>
</body>
</html>
innerHTML 和 innerText

innerHTML和innerText获取或设置开始标签与结束标签之间的内容

innerHTML: 获取内容的时候,如果内容中有标签,会把标签页获取到;设置内容时,如果内容中带标签,会以HTML的方式解析

innerText/textContent: 获取标签的内容,如果内容中有标签,会把标签过滤掉; 设置内容时,如果内容中带标签,会以字符串形式显示出来

div中插入一张图片

1
2
3
4
5
    <div>1111111111111</div>    
<script>
var div = document.querySelector('div');
div.innerHTML = ' <img src="../img/1.jpg">';
</script>
判断某个标签是否含有某个属性
1
typeof element.innerText === 'string' //如果不存在,值为undefined.
表单元素的属性

value: 用于大部分表单元素的内容获取

type: 可以获取input标签的类型(输入框或复选框等)

disabled: 禁用属性

checked: 复选框选中属性,当标签的属性只有一个值得时候,dom对应的属性的值是bool类型

selected: 下拉菜单选中属性

案例1:

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
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title id="title">Title</title>
</head>
<body>
<button>button</button>
<select>
<option value="1">北京</option>
<option value="2">上海</option>
<option value="3">天津</option>
<option value="4">重庆</option>
</select>
<script>
var btn = document.querySelector('button');
btn.onclick = function () {
var select = document.querySelector('select');
var options = select.querySelectorAll('option');
var randomIndex = parseInt(Math.random() * options.length);
var option = options.item(randomIndex);
option.selected = true;
}
</script>
</body>
</html>

案例2:简易表格

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
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title id="title">Title</title>
<style>
table{
width: 300px;
border: 1px solid #eee;
border-collapse: collapse
}
table thead{
background-color: #008ac3;
}
table tr{
height: 40px;
line-height: 40px;
border-bottom: 1px solid #eee;
}
table td, table th{
text-align: center;
border-right: 1px solid #eee;
}
</style>
</head>
<body>
<table>
<thead>
<tr>
<th><input type="checkbox"></th>
<th>First name</th>
<th>Last name</th>
</tr>
</thead>
<tbody>
<tr>
<td><input type="checkbox"></td>
<td>First name</td>
<td>Last name</td>
</tr>
<tr>
<td><input type="checkbox"></td>
<td>First name</td>
<td>Last name</td>
</tr>
<tr>
<td><input type="checkbox"></td>
<td>First name</td>
<td>Last name</td>
</tr>
</tbody>
</table>
<script>
//获取父checkbox,注册点击事件
var fcheck = document.querySelector('thead input');
var subcheck = document.querySelectorAll('tbody input[type="checkbox"]'); fcheck.onclick = function(){
var that = this;
for(var i = 0; i < subcheck.length; i++){
(function(i){
var check = subcheck.item(i);
check.checked = that.checked;
})(i)
}
}
//子checkbox点击与父checkbox的联动
for(var i = 0; i < subcheck.length; i++){
(function(){
var check = subcheck.item(i);
check.onclick = function(){
var allChecked = true;
for(var j = 0; j < subcheck.length; j++){ if(!subcheck.item(j).checked){
allChecked = false;
break;
}
}
if(allChecked){
fcheck.checked = true;
}else {
fcheck.checked = false;
}
} })(i)
}
</script>
</body>
</html>
操作属性的值

获取自定义属性: getAttribute

将给定元素添加一个新的属性或改变它现有属性的值: setAttribute

删除属性: removeAttribute

排他性的一种写法:

1
span[i].onclick = function(){for(var j = 0; j < spans.length; j++){    span[j].removeAttribute("class");}this.className = "current";}

案例:js动画

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
<!DOCTYPE html><html lang="en">
<head>
<meta charset="UTF-8">
<title id="title">Title</title>
<style>
*{
margin: 0;
padding: 0;
list-style: none;
box-sizing: border-box;
}
div{
width:200px;
height: 100px;
background-color: green;
position: absolute;
}
</style>
</head>
<body>
<button>移动400px</button>
<div></div>
<script>
window.onload = function(){
document.querySelector('button').onclick = function(){
var dv = document.querySelector('div');
clearInterval(dv.timeid);
const step = 10;
dv.timeid = setInterval(function(){
var current = dv.offsetLeft;
var now = current + step;
if(now > 400){
clearInterval(dv.timeid);
return;
}
dv.style.left = now + 'px';
},100)
}
}
</script>
</body>
</html>

节点

1、从结构图可以看出,整个html称为dom树。而dom的引用为document,也称为一个节点

2、每一个HTML标签都为一个元素节点

3、dom中元素、文本、属性都是节点

nodeName

1、文档中的每一个节点都有这个属性

2、为给定节点的名称

3、如果节点是元素节点,nodeName返回元素的名称

​ 如果给定节点为属性节点,nodeName返回属性的名称

​ 如果给定节点为文本节点,nodeName返回为#text的字符串

nodeType

1、该节点表明节点类型,返回值为一个整数

2、常用的节点类型有三种:

​ 1、元素节点类型 值为1

​ 2、属性节点类型 值为2

​ 3、文本节点类型 值为3

nodeValue

1、返回给定节点的当前值(字符串)

2、如果给定节点是属性节点,返回值是这个属性的值

​ 如果给定节点是文本节点,返回值是这个文本节点的内容

​ 如果给定节点是元素节点,返回值是null

3、nodeValue是一个读写属性

parentNode 和 parentElement

1、parentNode返回给定元素节点的父节点

2、document没有父节点

3、parentElement返回给定元素节点的父元素

childNodes, children, 属性

childNodes: 获取节点:元素1(通常是html标签),文本3(包括空文本),注释8

children: 只能获取元素节点

hasChildNode()

1、该方法用来判断一个元素是否有子节点

2、返回值为true或者false

3、文本节点和属性节点不可能再包含子节点,所以对于这两类节点使用 ChildNodes()方法 返回值永远为false.

4、如果hasChildNodes()返回值为false,则childNodes,firstChild,lastChild将为空数组或者空字符串。

获取属性

元素. attributes //获取元素身上所有属性构成的集合

得到里面的值 // attributes[i].value

firstChild

1、该属性返回给定节点的第一个子节点

2、var reference = node.firstChild

3、文本节点和属性节点不包括任何子节点,所以返回值为null

4、node.firstChild=node.childNodes[0]

firstElementChild:返回给定节点的第一个子元素(标签)

​ lastChild:该属性返回给定节点的最后一个子节点

​ lastElementChild:返回给定节点的最后一个子元素(标签)

​ nextSibling:返回给定节点的下一个兄弟节点

​ nextElementSibling:返回给定节点的下一个兄弟元素(标签)

​ previousSibling:返回给定节点的上一个兄弟节点

​ previousElementSibling:返回给定节点的上一个兄弟元素(标签)

节点的增删改查

createElement

1、按照给定的标签名创建一个新的元素节点,方法的参数为被创建的元素的名称

2、var reference = document.createElement(elementName);

3、方法的返回值指向新建节点的引用,返回值是一个元素节点,所以nodeType 为1

4、新建的节点不会自动添加到文档里,只是存在于document里一个游离的对象

createTextNode()

1、创建一个包含给定文本的新文本节点

2、这个方法的返回值指向这个新建的文本节点的引用

3、该方法有一个参数:新建文本节点的文本内容

4、它是一个文本节点,所以nodeType值为3

5、新建的文本对象不会自动添加到文档里,属于游离态的对象。

removeChild()

1、从给定的元素里删除一个子节点

2、var reference = element.removeChild(node)

3、返回值指向已经被删除的子节点的引用

4、当某个子节点被删除时,这个子节点所包含的子节点也被删除掉

replaceChild()

1、把一个给定父元素里的一个子节点替换为另外一个子节点

2、var reference = element.replaceChild(newChild,oldChild)

3、返回值指向已经被替换掉的那个子节点的引用

appendCild()

1、为给定元素增加一个子节点

​ var newreference = element.appendChild(newChild);

2、给定子节点newChild将成为element的最后一个节点

3、方法的返回值指向新增节点的引用

4、该方法通常与createElement()与createTextNode()一起使用

5、新节点可以追加给文档中的任何一个元素(不是属性和文本)

insertBefore()

1、把一个给定节点插入到一个给定元素子节点的前面

2、var reference = element.insertBefore(newNode,targetNode)

3、newNode节点将作为element的子节点出现,并在targetNode节点的前面

4、节点targetNode必须是element的一个子节点

5、该方法通常与createElement和createTextNode结合使用

JS盒子模型属性

Js获取盒子模型属性属性值的特点:

  1. 获取的都是数字不带单位.

  2. 获取的都是整数,不会出现小数(一般都会四舍五入, 尤其获取的偏移量)

  3. 获取的结果都是复合符合样式值(几个元素的样式组合在一起的值)

client系列

top/left/width/height

  1. clientWidth & clientHeight: 获取当前元素可视区域的宽高(内容的宽高+左右/上下padding). 和内容是否溢出无关(和是否设置了OVERFLOW:HIDDEN也无关), 就是设置的宽高+padding

    css中, 默认的height指内容的宽高, 不含padding+border; 设置了box-sizing: border-box, 代表了整个盒子的宽高, 内容+ padding+border.

  2. clientTop & clientLeft: 获取(上/左)边框的宽度

  3. 获取当前页面一屏幕(可视区域)的宽度和高度

    document.documentElement.clientWidth || document.body.clientWidth

    document.documentElement.clientHeight || document.body.clientHeight

offset系列

top/left/width/height/parent

  1. offsetWidth & offsetHeight: 在client的基础上加上border(和内容是否溢出也没有关系).

  2. offsetTop / offsetLeft : 获取当前盒子距离其父参照物的偏移量(上偏移/左偏移)

  3. offsetParent: 获取当前盒子的参照物.

在没有脱离文档流时: offsetLeft = 父级元素margin + 父级元素padding + 父级元素border + 自己的margin

脱离文档流时: offsetLefts = 自己的left + 自己的margin

scroll系列

scrollWidth & scrollHeight:真实内容的宽高(不一定是自己设定的值,因为可能会存在内容溢出,有内容溢出的情况下,需要把溢出的内容也算上)+ 左/上PADDING,而且是一个约等于的值 (没有内容溢出和CLIENT一样)在不同浏览器中,或者是否设置了OVERFLOW:HIDDEN都会对最后的结果产生影响,所以这个值仅仅做参考,属于约等于的值

element.scrollTop //向上卷出去的距离

element.scrollLeft //向左卷出去的距离

最小卷去值: 0

最大卷去值: 真实的页面高度 - 一屏幕的高度 document.documentElement.scrollHeight - document.documentElement.clientHeight

获取当前页面的真实宽高(包含溢出的部分)

​ document.documentElement.scrollWidth || document.body.scrollWidth

​ document.documentElement.scrollHeight || document.body.scrollHeight

滚动事件: element.onscroll = function(){}

封装浏览器的滚动事件

1
2
3
4
5
6
function getScroll(){       
return {
left: window.pageXOffset || document.documentElement.scrollLeft || document.body.scrollLeft || 0,
top: window.pageYOffset || document.documentElement.scrollTop || document.body.scrollTop || 0
}
}
获取元素具体的某个样式值
  1. [元素].style.xxx操作获取, 但是这种方法只能获取行内上的样式

  2. 获取当前元素经过浏览器计算过的样式.(无论样式写在哪)

    window.getComputedStyle([元素], [伪类, 一般都写null]) , 使用与IE9+

    [元素].currentStyle, 获取经过计算的样式

常用方法的封装
匀速与变速函数的封装

今日心情

已被气死 不知道为啥就乱序了 格式就乱了。改呀改 改呀改 就气死了。凑凑合合看吧

## 帮助文档

​ https://developer.mozilla.org/en-US/docs/Web/JavaScript

HTMLandCSS

发表于 2021-06-27 | 分类于 Front
字数统计 4.2k 字 | 阅读时长 21 分钟

HTML&CSS学习笔记

任务-2020.07.30

块级元素,内联(行内)元素,行内块,浮动,怎样清除浮动

Block块级元素

特点

1、块级元素独占一行(默认情况下占父元素的)

2、块级元素可以使用CSS属性中的width、height

3、块级元素可以使用CSS属性中的margin,padding

常用的块级元素

p 段落元素

h1 - h6 正文标题元素

hr 水平分割(水平分割线)

div 划分分割

table 表格标签

ol 有序列表

ul 无序列表

dl 定义列表

举例

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
<!DOCTYPE html>
<html lang="en">

<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>学习块级元素</title>
<style>
/* .classname 表示类选择器 */

.block-elements {
width: 480px;
height: 320px;
/* 能够居中 */
margin: auto;
/* 内边距 */
padding: 10px 5px 8px 12px;
background-color: darksalmon;
border: 1px solid black;
}
</style>
</head>

<body>
<div class="block-elements"> </div>
<div class="block-elements"> </div>
</body>

</html>

inline内联(行内)元素

特点

只会更改水平方向,使用宽高没有用

常用的内联元素

input输入标签

strong语气强调标签

em 感情强调标签

span 文字容器+标签

sup,sub 上标,下标

a 锚点标签

img 图像元素

举例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>inline内联标签</title>
<style>
.inline-anchor {
width: 320pd;
height: 64px;
padding-top: 100px;
padding-bottom: 100px;
}
</style>
</head>

<body>
<!-- href超链接引用,url统一资源定位器 -->
<!-- 发现使用宽高并没有任何变化 -->
<a href="#">这里是一个锚点标签</a>
<a href="#">这里是一个锚点标签</a>
</body>

</html>

inline-block行内块

特点

属性

block

inline

inline-block

float浮动

特点

块级元素 -> 内联元素:将不同行的元素放置在一行

浮动:将不同行的元素放置在一行

浮动(压缩):压缩的是自己的空间

浮动的元素:不在标准文档流之中(网页无法识别原有的空间)

float: none| left | right

clear: left | right | both

举例

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
<!DOCTYPE html>
<html lang="en">

<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>学习使用浮动和清除浮动</title>
<style>
* {
margin: 0;
padding: 0;
}

.shangpin ul li {
list-style: none;
}

.shangpin {
width: 1000px;
background-color: pink;
margin: 10px auto;
}

.shangpin h1 {
font-size: 25px;
}

.shangpin h1 span {
float: right;
margin-right: 20px;
font-size: 15px;
}

.shangpin ul li {
margin-top: 20px;
float: left;
}

.shangpin ul li p {
font-size: 15px;
color: purple;
}
</style>
</head>

<body>
<div class="shangpin">
<h1>商品列表<span>更多</span></h1>
<ul>
<li>
<img src="./img/1.jpg" />
<p>这是第一张图片</p>
</li>
<li>
<img src="./img/2.jpg" />
<p>这是第二张图片</p>
</li>
<li>
<img src="./img/7.jpg" />
<p>这是第三张图片</p>
</li>
<li>
<img src="./img/7.jpg" />
<p>这是第四张图片</p>
</li>
</ul>
</div>

</body>

</html>

重点总结


1、浮动会使元素提升一个层级. 例如:两个块级元素排列,上边的元素设置了浮动,它自身会提升一个层级,把他占用的位置空出来. 下边的元素如果没有设置浮动,就会占用上边元素原来的位置;如果设置了浮动,它的层级和上边的是平级的,现象是左右排列

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
<!DOCTYPE html>
<html lang="en">

<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
<style>
.o1 {
/* width: 1000px; */
background-color: salmon;
}

.i1 {
width: 100px;
height: 400px;
background-color: skyblue;
}

.i2 {
width: 100px;
height: 200px;
background-color: red;
}
</style>
</head>

<body>
<div class="o1 ">
<div class="i1"></div>
<div class="i2"></div>
</div>
</body>

</html>

div1使用浮动:

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
<!DOCTYPE html>
<html lang="en">

<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
<style>
.o1 {
width: 1000px;
background-color: rgb(114, 250, 148);
}

.i1 {
width: 100px;
height: 400px;
background-color: skyblue;
float: left;
}

.i2 {
width: 100px;
height: 200px;
background-color: red;
}
</style>
</head>

<body>
<div class="o1 ">
<div class="i1"></div>
<div class="i2"></div>
</div>
</body>

</html>

div2使用浮动:

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
<!DOCTYPE html>
<html lang="en">

<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
<style>
.o1 {
width: 1000px;
background-color: rgb(114, 250, 148);
}

.i1 {
width: 100px;
height: 400px;
background-color: skyblue;
/* float: left; */
}

.i2 {
width: 100px;
height: 200px;
background-color: red;
float: left;
}
</style>
</head>

<body>
<div class="o1 ">
<div class="i1"></div>
<div class="i2"></div>
</div>
</body>

</html>
  1. 浮动会影响父级的高(前提是父级元素没有设置高度,他的高度又子元素撑起来)

  2. 清除浮动(影响了谁的高度,在谁上清浮动)

    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
    <!DOCTYPE html>
    <html lang="en">

    <head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
    <style>
    .o1 {
    width: 1000px;
    background-color: rgb(114, 250, 148);
    }

    .i1 {
    width: 100px;
    height: 400px;
    background-color: skyblue;
    float: left;
    }

    .i2 {
    width: 100px;
    height: 200px;
    background-color: red;
    float: left;
    }

    /*清除浮动代码*/
    .clearfloat:after {
    display: block;
    clear: both;
    content: "";
    visibility: hidden;
    height: 0
    }

    .clearfloat {
    zoom: 1
    }
    /*兼容低版本IE*/
    </style>
    </head>

    <body>
    <div class="o1 clearfloat">
    <div class="i1"></div>
    <div class="i2"></div>
    </div>
    </body>

    </html>

任务-2020.07.31

定位:相对定位,绝对定位,固定定位,写个例子;

各种选择器讲讲:比如元素的,关系的,属性的,伪类的

position定位

position: static(默认值,没有定位) | relative(相对位置)| absolute(绝对位置)| fixed(固定位置)

relative相对位置

相对于自身给的位置,left是指往右边,top指望下边(其实就是位置相反)

代码

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
<!DOCTYPE html>
<html lang="en">

<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>relative相对定位学习</title>
<style>
div {
border: 1px solid red;
}

#father {
width: 302px;
height: 302px;
margin: auto;
margin-top: 200px;
}

#father div {
height: 100px;
width: 100px;
background-color: violet;
}

#father .d2 {
position: relative;
left: 200px;
top: -102px;
}

.d4 {
position: relative;
left: 200px;
top: -102px
}

.d5 {
position: relative;
left: 102px;
top: -304px;
}
</style>
</head>

<body>
<div id="father">
<div class="d1">第一个div</div>
<div class="d2">第二个div</div>
<div class="d3">第三个div</div>
<div class="d4">第四个div</div>
<div class="d5">第五个div</div>

</div>
</body>

</html>

absolute 绝对定位

是否存在已经定位的祖先元素

不存在:参照浏览器定位

存在:参照已经存在的祖先元素进行定位

参照浏览器定位
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
<!DOCTYPE html>
<html lang="en">

<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>absolute绝对定位</title>
<style>
.d1 {
width: 100px;
height: 100px;
border: 1px solid red;
background-color: teal;
position: absolute;
right: 100px;
bottom: 300px;
}
</style>
</head>

<body>
<div class="d1">absolute绝对定位</div>
</body>

</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
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
<!DOCTYPE html>
<html lang="en">

<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>absolute绝对定位学习2</title>

<style>
#father {
height: 600px;
width: 600px;
border: 1px solid red;
position: relative;
left: 500px;
top: 30px;
}

.d1 {
height: 100px;
width: 200px;
background-color: rgb(255, 0, 179);
}

.d2 {
height: 100px;
width: 200px;
background-color: rosybrown;
position: absolute;
right: 30px;
}

.d3 {
height: 100px;
width: 200px;
background-color: tan;
}
</style>
</head>

<body>
<div id="father">
<div class="d1">div1</div>
<div class="d2">div2</div>
<div class="d3">div3</div>
</div>
</body>

</html>

fixed固定位置

永远参照浏览器定位

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
<!DOCTYPE html>
<html lang="en">

<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>fixed固定定位学习</title>
<style>
.d1 {
height: 200px;
width: 100%;
background-color: violet;
position: fixed;
bottom: 0px;
}

.d2 {
height: 200px;
width: 200px;
background-color: yellowgreen;
position: fixed;
top: 300px;
left: 1700px;
}
</style>
</head>

<body>
<div class="d1">div1</div>
<div class="d2">div2</div>
</body>

</html>

重点总结


坐标系是向右和向下为正(left,top相当于坐标原点)

相对定位:

  1. 如果没有父级元素,可以认为参照系是浏览器; 如果有可以认为是父级元素

  2. 不会释放位置(即使设置了偏移)

    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
    <!DOCTYPE html>
    <html lang="en">

    <head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
    <style>
    .d1 {
    width: 100px;
    height: 100px;
    background-color: red;
    position: relative;
    }

    .d2 {
    width: 100px;
    height: 100px;
    background-color: violet;
    }
    </style>
    </head>

    <body>
    <div class="d1"></div>
    <div class="d2"></div>
    </body>

    </html>
1
2
3
4
5
6
7
8
.d1 {
width: 100px;
height: 100px;
background-color: red;
position: relative;
top: 10px;
left: 10px;
}

绝对定位

  1. 最常见的是子绝父相(父元素占位置, 子元素偏移)

固定定位

​ 参考水平垂直居中案例 —— 就是不论页面怎么改变,都在中心展示

第一种方法(这种方法用的比较多)

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
<!DOCTYPE html>
<html lang="en">

<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
<style>
.d1 {
width: 400px;
height: 200px;
background-color: yellowgreen;
position: fixed;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
}
</style>
</head>

<body>
<div class="d1">

</div>
</body>

</html>

第二种方法—-忘了

选择器

id选择器 > 标签选择器

元素选择器

标签选择器:在CSS中以标签的形式书写

选择器通常将是某个 HTML 元素,比如 p、h1、em、a,甚至可以是 html 本身:

书写方法:

1
2
3
4
html {color: salmon; background-color: skyblue;}
div {width: 200px;height: 200px;background-color: pink;border: 1px;}

<div>这是元素选择器</div>

关系选择器

包含选择器(E F)

选择所有被E元素包含的F元素,中间用空格隔开

书写方法:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
ul li{ color: darkolivegreen;}

<ul>
<li>葡萄</li>
<li>香蕉</li>
<li>苹果</li>
<li>橙子</li>
</ul>
<ol>
<li>葡萄汁</li>
<li>香蕉汁</li>
<li>苹果汁</li>
<li>橙汁</li>
</ol>

子选择器(E>F)

选择所有作为E元素的直接子元素F,对更深一层的元素不起作用,用>表示

书写方法:

1
2
3
4
5
6
7
8
9
 div > a{ color: lawngreen;}

<div>
<a href="#">子元素1</a>
<p>
<a href="#">孙元素</a>
</p>
<a href="#">子元素2</a>
</div>

相邻选择器(E+F)

选择紧跟E元素后的F元素,用+表示,选择相邻的第一个兄弟元素。

1
2
3
4
5
 h1 + p{ color: mediumpurple; }

<h1>h1元素</h1>
<p>第一个元素</p>
<p>第二个元素</p>

兄弟选择器(E~F)

选择E元素之后的所有兄弟元素F,作用于多个元素,用~隔开

书写方法:

1
2
3
4
5
6
7
.div1 h1 ~ p{ color:orange;}

<div class="div1">
<h1>兄弟选择器</h1>
<p>兄弟选择器第一个元素</p>
<p>兄弟选择器第二个元素</p>
</div>

属性选择器

可以为拥有指定属性的 HTML 元素设置样式,而不仅限于 class 和 id 属性。

注释:只有在规定了 !DOCTYPE 时,IE7 和 IE8 才支持属性选择器。在 IE6 及更低的版本中,不支持属性选择。

书写方法(以title为例):

1
span[title="ti_2"] {color: darkblue; }<span title="ti_1">这里是第一个span</span><span title="ti_2">这里是第二个span</span>

伪类选择器

锚伪类:

a:link {color: #FF0000} / 未访问的链接 /
a:visited {color: #00FF00} / 已访问的链接 /
a:hover {color: #FF00FF} / 鼠标移动到链接上 /
a:active {color: #0000FF} / 选定的链接 /

提示:**在 CSS 定义中,a:hover 必须被置于 a:link 和 a:visited 之后,才是有效的。

提示:在 CSS 定义中,a:active 必须被置于 a:hover 之后,才是有效的。

提示:伪类名称对大小写不敏感。

超链接 - :focus 的使用

语法:

1
2
3
4
> selector : pseudo-class {property: value}
> /* CSS类也可与伪类搭配使用 */
> selector.class : pseudo-class {property: value}
>

书写方法:

1
2
3
 a.a2:hover{ color: black; }

<a class="a2" href="#">CSS Syntax</a>

1596444900155

1596444924302

id选择器

ID 选择器前面有一个 # 号 - 也称为棋盘号或井号。

书写方法:

1
2
3
#p1 { width: 200px; height: 100px; color: yellow;}

<p id="p1">这里是id选择器</p>

多元素选择器

即是多个选择器使用统一样式,中间用逗号分隔开

书写方法(使用id选择器举例):

1
2
3
4
#p1,#p2 {width: 400px;height: 10px; color: brown; }

<p id="p1">这里是id选择器</p>
<p id="p2">这是尝试使用多元素选择器,使id为p1,p2的样式相同</p>

class类选择器

class类选择器前面有一个.号

书写方法:

1
2
3
.a1{ color: crimson;}

<a class="a1" href="#">这里是一个类锚点</a>

transform变形函数

  • translate(xpx,ypx) :平移 translateX(px) or translateY(px)

  • scale(x,y) :放缩 scaleX() or scaleY()

  • rotate(度数deg):旋转 默认顺时针

    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
    <!DOCTYPE html>
    <html lang="en">

    <head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>动画学习</title>

    <style>
    .box {
    width: 960px;
    height: 500px;
    border: 1px solid red;
    position: fixed;
    top: 50%;
    left: 50%;
    transform: translate(-50%, -50%);
    }
    /* nth-child 选择器,even代表偶数,odd代表基数*/

    div img:nth-child(even) {
    width: 200px;
    height: 150px;
    /* border: 2px solid red; */
    }

    div img:nth-child(odd) {
    width: 150px;
    height: 150px;
    /* border: 2px solid blue; */
    }

    div img {
    border: 4px solid #ddd;
    /* padding 内间距 */
    padding: 10px;
    background-color: #fff;
    }
    /* 选择器 hover 照片悬浮 */
    /* id选择器 */

    div img:hover {
    /* box-shadow:水平X偏移,Y偏移,偏移面积 颜色*/
    box-shadow: 5px 5px 5px rgb(221, 221, 221);
    transform: scale(1.5) rotate(40deg);
    }
    /* 标签选择器 */
    /* 单独对第一张图片进行操作 */

    div img:nth-child(1) {
    position: absolute;
    left: 250px;
    top: 250px;
    transform: rotate(-15deg);
    }

    div img:nth-child(6) {
    position: absolute;
    left: 0px;
    top: 300px;
    transform: scale(1.5px) rotate(-30deg);
    }
    </style>


    </head>

    <body>
    <div class="box" id="box">
    <img src="./img/1.jpg" />
    <img src="./img/2.jpg" />
    <img src="./img/3.jpg" />
    <img src="./img/4.jpg" />
    <img src="./img/5.jpg" />
    <img src="./img/6.jpg" />

    </div>
    </body>

    </html>

transition过渡

transition: XX过度啥(all全部,width宽,color颜色) XX多长时间 XX速度 XX延迟时间

速度:ease:由快到慢

​ ease-in:越来越快

​ ease-out:越来越慢

​ linear:匀速

​ ease-in-out:先加速后减速

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
<!DOCTYPE html>
<html lang="en">

<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>动画学习2---过渡</title>
<style>
#father img {
background-color: pink;
width: 200px;
height: 200px;
transition: all 10s ease-out;
}

#father img:hover {
background-color: red;
width: 200px;
height: 200px;
/* 过度啥(all全部,width宽),多长时间,速度,延迟时间
速度:ease:由快到慢
ease-in:越来越快
ease-out:越来越慢
linear:匀速
ease-in-out:先加速后减速 */
/* transition: width 5s ease-out -3s; */
transform: rotate(180deg);
}
</style>
</head>

<body>
<div id="father">
<img src="./img/1.jpg" />
</div>
</body>

</html>

animation动画

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
<!DOCTYPE html>
<html lang="en">

<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>animation学习</title>

<style>
.father img {
width: 100px;
height: 200px;
animation: x 2s infinite;
}

@keyframes x {
0% {
width: 0px;
transform: translateX(100px);
}
25% {
width: 20px;
transform: translateX(200px);
}
50% {
width: 50px;
transform: translateX(300px);
}
75% {
width: 100px;
transform: translateX(400px);
}
100% {
width: 150px;
transform: translateX(500px);
}
}
</style>
</head>

<body>
<div class="father">
<img src="./img/1.jpg" />
</div>
</body>

</html>

git

发表于 2021-06-27 | 分类于 git
字数统计 579 字 | 阅读时长 2 分钟

Git学习笔记

创建仓库

使用Git前,需要先建立一个仓库(repository)。您可以使用一个已经存在的目录作为Git仓库或创建一个空目录。使用您当前目录作为Git仓库,我们只需使它初始化。

1
git init

使用我们指定目录作为Git仓库。

1
git init newrepo

创建文件夹

1
2
3
mkdir w3cschoolcc
cd w3cschoolcc
ls -a

远程仓库操作

1
git remote // 远程仓库操作

从远程获取代码库

1
git fetch  // 从远程获取代码库

查看分支命令

1
git branch (branchname)   // 查看分支命令      (-a 所有、  )

切换分支命令

1
git checkout (branchname) // 切换分支命令

下载远程代码并合并

1
git pull     // 下载远程代码并合并

提交

1
git add .    // .是提交全部的代码,如果要提交只改的文件,就在后面加入文件名

提交暂存区到本地仓库

1
git commit -m "注释" --no-verify   // 提交暂存区到本地仓库

修改的文件已被git commit,但想再次修改不再产生新的Commit

1
# 修改最后一次提交 $ git add sample.txt$ git commit --amend -m"说明"

上传远程代码并合并

1
git push     // 上传远程代码并合并

撤销

1
git reset HEAD 名

回滚

1
git log --all  // 打印日志git reset --hard 名  // 回滚

改变分支拉代码

1
git initgit remote add origin http://10.124.163.76:8888/support/tgportal/mqfrontgit fetchgit branch -agit checkout XXXgit pull origin 分支:分支

强制拉代码

1
git reset --hard origin/master

迁移

1
cd 拉下来的代码文件删除隐藏的.gitgit initgit remote add origin 地址栏链接git add .git commit -m "Initial commit"git pull --rebase origin mastergit push origin master

删除最后一次远程提交

方式一:使用revert

1
git revert HEADgit push origin master

方式二:使用reset

1
git reset --hard HEAD^git push origin master -f

二者区别:

  • revert是放弃指定提交的修改,但是会生成一次新的提交,需要填写提交注释,以前的历史记录都在;
  • reset是指将HEAD指针指到指定提交,历史记录中不会出现放弃的提交记录。

情况三:回滚某次提交

1
# 找到要回滚的commitIDgit loggit revert commitID

删除某次提交

1
git log --oneline -n5git rebase -i "commit id"^ //注意:需要注意最后的^号,意思是commit id的前一次提交git rebase -i "5b3ba7a"^

go_fiveDay

发表于 2021-06-27 | 分类于 GO
字数统计 10k 字 | 阅读时长 48 分钟

Go语言-五天学习

image-20210531163821254

介绍、安装

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
go//package demo_fiveday

// 导入的包 不使用 会报错
// 变量定义了 不使用 也会报错

// 这个包表示是main包 也就是主包 整个程序入口是该文件
package main
// fmt 是内置的包
import "fmt"

// func 关键字 表示定义一个函数 函数名必须叫main
func main () {
fmt.Println("Hello World") // 必须是双引号
}

// 编译 go build 名.go => 名.exe
// 执行 名.exe

// 编译并执行 go run 名.go

image-20210531153934314

go语言命名

25个关键字

image-20210531153949947

关键字用途

image-20210531154003509

数据类型

image-20210531154020127

image-20210531154035168

  • bool 中 true false小写
  • 字符串类型可相加
  • 不同类型不能相加 需转换比如转成int int(a)

语言命名规范

image-20210531154052130

go语言内置类型和函数

内置类型

image-20210531154113843

image-20210531154133478

内置函数

image-20210531154146175

image-20210531154156680

内置接口error

image-20210531154210977

标准输入输出

1
2
3
4
5
6
7
var s1,s2 string
fmt.Scan(&s1, &s2)
fmt.Println("--s1--", s1, "--s2--", s2)
fmt.Scanln(&s1, &s2) // 只能空格 不能回车
fmt.Println("--s1--", s1, "--s2--", s2)
fmt.Scanf("%s", &s1)
fmt.Println("--s1--", s1, "--s2--", s2)

变量

变量是什么

image-20210531154223605

1、变量必须先定义在使用

2、变量不能被重复定义

3、首字母大写 共有 首字母小写 私有

声明单个变量

  1. 基本定义:var a int = 10;
  2. 类型推导: var a = 10
  3. 简便写法: a:= 10
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
// 变量声明
// 第一种方式
var a int = 10; // var 关键字,表示声明一个变量,a是变量名 ,int 变量类型 = 变量值
fmt.Println(a);

// 第二种方式 类型推导 不用指定变量类型 自动推导变量类型
var b = 20;
fmt.Println(b);

var c = "lvqing"
fmt.Println(c)

// 第三种方式
d:= 30
e:= "lvqing007"
fmt.Println(d)
fmt.Println(e)

声明多个变量

1
2
3
4
5
6
7
8
9
10
11
12
// 声明多个变量
var f, g int = 40,50
fmt.Println(f,g)

var h,i,j = 60, "lvqing008", "lvqing009"
fmt.Println(h)
fmt.Println(i,j)

m:= "lvqing110"
n:= 007
// m, n := "lvqing110", 007
fmt.Println(m,n)

常量

关键字: const

定义后 不能改变其值

1
2
3
4
5
//常量定义
const x = "lvq"
const y = 12;

fmt.Println(x, y)

函数

函数定义

func 函数名(函数参数名 类型 , 参数名 类型) 返回值类型{

}

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
/*
函数:
1、没有位置和关键字一说,都是按位置传参
*/

func test () {
fmt.Println("test")
}

// 传参
func test01(a int, b int){
fmt.Println(a + b)
}

// 返回值
func test02(a int, b int) int {
fmt.Println(a + b)

return a+ b
}

//多个返回值,用括号括起来
func test03(a int, b string) (int, string) {
return a, b
}

// 可变长参数
func test04(a ... int){
fmt.Println(a)
}

// 命名返回值
func test05(a int, b int)(c,d int){ // 或者 c int,d int
c = a
d = b

return // 可写return c,d
}

//空白符 舍弃一个返回值
func test06(a int, b int)(c,d int){
c = a
d = b

return
}

匿名函数

没有名字的函数

1
2
3
4
5
6
7
8
9
10
11
// 匿名函数,没有名字的函数,定义在函数内部

a:= func(a, b, c int){
fmt.Println(a,b,c)
}
a(2,3,4)

// 匿名函数其实真正的用 只用一次 以后不会执行了
func(){
fmt.Println("-------------------")
}()

闭包函数

闭包:定义在函数内部 对外层作用域有引用

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
/*
1、闭包函数使用外部作用域的变量,其实是个引用
*/
x:=10
fmt.Println("10的地址----",&x)
f:= closure(x)
fmt.Println(f(1))



func closure(x int) func(int) int {
fmt.Printf("%p\n", &x)
return func(y int) int {
fmt.Printf("%p\n",&x)
return x+y
}
}

可变长参数

1
2
3
4
5
6
7
8
9
10
11
12
13
14
// 可变长参数	
func_test(3, 1,2,5,3,0,6,9,8,22,55,33)
fmt.Println("*********传切片**********")
// 传切片不可以 打散了传进去 为 n...
n := []int{88, 66, 7, 3, 99}
func_test(99, n...)
func func_test(num int, nums ...int){
for k,v := range nums{
fmt.Println("key:",k, " value:",v)
if num == v{
fmt.Println("已找到num, key为", k, "num为", num)
}
}
}

if-else

1
2
3
4
5
6
7
8
9
 // if-else  注意: i1>i2 后面不能换行	 
i1,i2 := 2,3
if i1>i2{
fmt.Println("i1小于i2")
}else if i1 == i2 {
fmt.Println("i1等于i2")
}else {
fmt.Println("i1大于i2")
}

循环

没有while循环,只有for循环

image-20210531163857269

1
2
3
4
5
6
7
8
9
	 //循环	 
for i := 0; i < 10; i++{
fmt.Println(i)
}
var f1 = 0
for ; f1 < 3; f1++{
fmt.Println("--------------")
fmt.Println(f1)
}
1
2
3
4
5
//用for模拟while  会死循环
var t1= truefor t1{
fmt.Println("t1")
t1 = false
}

switch-case

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
var s1 = 4
switch s1 {
case 1, 2:
fmt.Println("1")
case 3:
fmt.Println("3")
case 4:
fmt.Println("4")
case 5:
fmt.Println("5")
default:
fmt.Println("6")
}
//无条件
var s2 = 4
switch { // 表达式被忽略
case s2 >= 0 && s2 <= 3:
fmt.Println("s2---1")
case s2 >= 4:
fmt.Println("s2---2")
default:
fmt.Println("s2---3")
}

fallthrough

穿透下面那层 不论符合不符合 无条件执行

1
2
3
4
5
6
7
8
9
10
11
12
13
14
   var s1 = 4
switch s1 {
case 1, 2:
fmt.Println("1")
case 3:
fmt.Println("3")
case 4:
fmt.Println("4")
fallthrough
case 5:
fmt.Println("5")
default:
fmt.Println("6")
}

数组

image-20210531163912425

数组声明

1、数组有长度 有类型

2、int类型空值 0 字符串空值 “”

3、不能直接扩容 只能重新定义一个新的把数据copy过来

4、类型相同 长度不同的数组 不能直接赋值 比如: b = a

5、长度 len

6、循环两种方式: 一种for循环 另一种 range

7、给某个值赋初始值 var b7[7] int = [7]int{3:88, 2:77}

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
var b1[3] int	
fmt.Println(b1)
var b2[3] string
fmt.Println(b2)
var b3[3]
string b3[0] = "lv" b3[1] = "qing"
fmt.Println(b3)
// 定义并赋初值
var b4[3] string = [3]string{"ll","qq","ii"}
fmt.Println(b4)
b5 := [...]int{1,2,3,4,5,6} // 虽然写的... 但是长度还是后面固定的 (小样 ...还想骗人 我是不会上当的)
fmt.Println(b5)
//数组是值类型
b6:= [3]int{4,5,6}
test07(b6)
fmt.Println(b6)
fmt.Println(len(b6))
// 遍历
for key,val := range b6{
// 用一个是索引 两个是索引 、值
fmt.Println(key)
fmt.Println(val)
}
// 给数组某个值赋初始值
var b7[7] int = [7]int{3:88, 2:77}
fmt.Println(b7)
// 数组函数
func test07(a [3]int){
a[1] = 66
fmt.Println(a)
}

range

1
2
3
4
5
6
7
8
9
10
//数组是值类型	
b6:= [3]int{4,5,6}
test07(b6)
fmt.Println(b6)
fmt.Println(len(b6))
// 遍历
for key,val := range b6{ // 用一个是索引 两个是索引 、值
fmt.Println(key)
fmt.Println(val)
}

多维数组

1
2
3
4
// 多维数组	
var b8[2][3]
int = [2][3]int { {1,2,3}, {4,5,6}, }
fmt.Println(b8)

切片

可以动态往里面添加值

可以自动扩容

1、[]int 切片类型

2、切片底层依附于数组,改值 数组也会变化

3、切片动态的追加值 超过数组的len 数组不会变化 不会在依附于此时的数组 会重新定义一个长度为原来数组的两倍的新数组
切片会依赖于新数组 在改值 原来旧数组不会变 变化的是新数组

定义

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
/*
1、[]int 切片类型
2、切片底层依附于数组,改值 数组也会变化
3、切片动态的追加值 超过数组的len
数组不会变化 不会在依附于此时的数组 会重新定义一个长度为原来数组的两倍的新数组
切片会依赖于新数组 在改值 原来旧数组不会变 变化的是新数组
*/
var b[] int // 切片类型
var c[3] int = [3]int {} // 数组
fmt.Println(b)
fmt.Println(c)
//定义并初始化
var c2[3] int = [3]int {1,2,3} // 数组
fmt.Println(c2)
// 第一种方式: make初始化
var b2[] int = make([]int, 3) // 切片
fmt.Println(b2)
// 第二种方式: 通过数组切片
b3 := c2[0:len(c2)] // 前闭后开
b3[0] = 100 // 切片改值 数组也会变
b3 = append(b3, 800) // 切片追加值
b3[1] = 999 // 超过长度 不会在依附于数组了

fmt.Println(b3)
fmt.Println(c2)
fmt.Println(len(b3), cap(b3)) // 查看切片长度和底层数组长度

copy

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
// copy
fmt.Println("-------copy------")
var b4 = make([]int,3)
b4[0] = 8
b4[1] = 6
b4[2] = 4
fmt.Println(b4)

b5:= make([]int, 6)
fmt.Println(b5)

copy(b5, b4)
fmt.Println(b4, len(b4), cap(b4))
fmt.Println(b5, len(b5), cap(b5))
b5[0] = 111
b5[1] = 222
b5[2] = 333
fmt.Println("------------")
fmt.Println(b4, len(b4), cap(b4))
fmt.Println(b5, len(b5), cap(b5))

copy(b4, b5)
fmt.Println("------------")
fmt.Println(b4, len(b4), cap(b4))
fmt.Println(b5, len(b5), cap(b5))

切片当做参数传给函数

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
//切片当做参数传给函数  函数修改 切片会被修改
fmt.Println("-----func-------")
b6:= make([]int, 3)
b6[0] = 1
b6[1] = 2
b6[2] = 3
fmt.Println(b6)
test_slice(b6)
fmt.Println(b6)

func test_slice(b[] int){
b[0] = 999

fmt.Println(b)
}

map

1、key-value 对

2、Map是引用类型

3、判断map相等不能用== ==只能用来判断map是否为nil

4、map是无序的

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
/*		
1、key-value 对
2、Map是引用类型
3、判断map相等不能用== ==只能用来判断map是否为nil
4、map是无序的
*/
fmt.Println("-----map-------")
// 定义
var m1 map[int]string
// 空值是nil格式
//m1[1] = "QQQQ" 为空的不能直接赋值 有问题
fmt.Println(m1)
// 定义并初始化 - int key类型,string value类型
// 第一种
// var m2 map[int]string = map[int]string{1:"ll", 2:"vv"}
// 第二种
var m2 map[int]string = make (map[int]string)
m2[1] = "ll"
m2[2] = "vv"
m2[3] = "qq"
fmt.Println(m2)
// 取值
fmt.Println(m2[3])
// 取值不存在返回空
fmt.Println(m2[2])
// 删除某个元素
delete(m2, 1)
// 传的是key
fmt.Println(m2)
fmt.Println(len(m2))
// map当参数传递
test_map(m2)
fmt.Println(m2)
// 相等性
m3:= map[int]string{1: "XXXX", 2: "YYYYY"}
fmt.Println(m3)
// 复杂map
var m4 map[int]map[int]string = make(map[int]map[int]string)
if m4[1] == nil{
m4[1] = make(map[int]string)
}
m4[1][1] = "LLLLLLLLLLLL"
m4[1][2] = "VVVVVVVVVVVV"
m4[1][3] = "QQQQQQQQQQQQ"
fmt.Println(m4) // 这只是m4[1] m4[2] m4[3]都没有 要循环
for mk,mv := range m4{
fmt.Println(mk)
fmt.Println(mv)
}
func test_map(m map[int]string){
m[1] = "XXXXXX"
m[2] = "YYYYYY"
}

指针

1、符号 & 表示取地址

2、符号 * 表示通过地址取值

3、指针不支持运算 不支持增加、减少

4、数组类型指针可以直接当做数组来修改,赋值

5、切片类型指针 如要修改切片,需要反解回来 再修改

6、指向数组的指针

7、指针数组

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、符号 & 表示取地址
2、符号 * 表示通过地址取值
3、指针不支持运算 不支持增加、减少
4、数组类型指针可以直接当做数组来修改,赋值
5、切片类型指针 如要修改切片,需要反解回来 再修改
6、指向数组的指针
7、指针数组
*/
// 定义
fmt.Println("-----port-------")
p1:= 123
var p2 *int = &p1
fmt.Println(p2)
fmt.Println(*p2)
// 传递指针类型数组
p3:= [3]int{1,2,3}
fmt.Println(p3)
test_port(&p3)
fmt.Println(p3)

p4 := 123456
p5 := &p4
p6 := &p5
p7 := &p6
fmt.Println(*p7) // p6的地址
fmt.Println(*(*(*p7))) // 值

// 切片类型指针
p8:= []int{2,3,4,5}
fmt.Println(p8)
test_SP(&p8)
fmt.Println(p8)

// 指向数组的指针
var p9 = [...]int{99:1}
fmt.Println(p9)

var p10 *[100]int = &p9
fmt.Println(p10)

// 指针数组
p11 := 11
p12 := 12
var p13 [2]*int = [...]*int{&p11, &p12}
fmt.Println(p13)
fmt.Printf("%T", p13) // 查看类型

func test_port(p *[3]int){
(*p)[0]= 999
p[1] = 888 // 数组类型指针可以直接当做数组来修改,赋值
fmt.Println(p)
fmt.Println(*p)
}

func test_SP(sp *[]int){
fmt.Println(*sp)
(*sp)[0] = 666 // 切片类型指针不能直接赋值 需要反解
fmt.Println(sp)
}

Defer

image-20210531163925622

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
func defer_Test(){
defer fmt.Println("我最后打印11111")
defer fmt.Println("我最后打印22222")
fmt.Println("我先打印111111")
fmt.Println("我先打印222222")

for i:=0;i<3;i++{
defer fmt.Println("i=",i)
}
fmt.Println("-------------------------")

for j:=0;j<3;j++{
defer func(){ // 匿名函数-引用
fmt.Println("j=",j)
}()
}
}

Panic

主动抛出异常

Recover

恢复程序

一般写在defer里面 写外面不执行

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
// panic recover
func p1(){
fmt.Println("-------p1--------")
}
func p2(){
defer func() {
// 恢复程序 recover
//recover()
// 出现异常之后不执行 从p2之后执行
//err:= recover()
// err 为空时 表示没有异常
//if err!=nil{
// fmt.Println("ERR",err)
//}
if err2:=recover();
err2!=nil{
fmt.Println("err2",err2)
} }()
fmt.Println("-------p2--------")
panic("出现异常")
fmt.Println("--------p4---------")}
func p3(){
fmt.Println("-------p3--------")
}

小练习

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
// 测试练习	
fmt.Println("----------测试练习--------------")
var test01 = [4]func(){}
for i:= 0; i< 4; i++{
defer fmt.Println("defer看i= ", i)
defer func() {
fmt.Println("匿名函数看i= ", i)
}()
test01[i] = func() {
fmt.Println("test01看i= ", i)
}
}
for _,v := range test01{
fmt.Println("----v----", v)
v()
}

文件

读文件

1、普通读文件 ioutil 一次性读出来 不需要手动关闭 内部自己关闭了

2、分片读文件 每次读自定义的个数 os

3、按行读文件 os

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、普通读文件 ioutil 一次性读出来 不需要手动关闭 内部自己关闭了
2、分片读文件 每次读自定义的个数
3、按行读文件

*/
// 普通读文件
f_ioutil, err01 := ioutil.ReadFile("file01.txt")
if err01 != nil{
fmt.Println("打开文件失败")
return
}
fmt.Println("----f_ioutil----", f_ioutil)
fmt.Println("----string(f_ioutil)---------", string(f_ioutil))

// 分片、按行读文件
f_os,err02 := os.Open("file01.txt")
if err02 != nil{
fmt.Println("打开文件失败")
return
}

defer func() {
f_os.Close()
fmt.Println("---文件关闭了----")
}()
// 分片读文件 用NewReader
//fmt.Println("-----分片读文件 用NewReader------")
//r:= bufio.NewReader(f_os) // 前面 Open 打开不能读
//b:= make([]byte, 8) // 定义一个切片 读8个
//for{
// _,err := r.Read(b)
// if err!=nil{
// break
// }
// fmt.Println("----string(b)----", string(b))
//}

// 按行读文件 用NewScanner
fmt.Println("-----按行读文件 用NewScanner-----")
s:= bufio.NewScanner(f_os)
for s.Scan(){
fmt.Println( s.Text())
}

写文件

1、按字节写入文件

2、按字符串写入文件

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
//写入文件
fmt.Println("---------写入文件------------")
f_c,err03 := os.Create("file02.txt")
if err03!=nil{
fmt.Println("创建文件失败")
return
}

defer f_c.Close()

//按字符串按字符串写入文件
fmt.Println("---------按字符串写入文件------------")
l,err04 := f_c.WriteString("我增加了一行字符串")
if err04!=nil{
fmt.Println("写文件失败")
f_c.Close()
}
fmt.Println(l, "success")

// 按字节写文件
fmt.Println("--------按字节写入文件--------")
d2:=[]byte{104, 101, 108, 109}
n2,_:= f_c.Write(d2)

fmt.Println(n2, "success")

// 追加文件
fmt.Println("---------追加文件------------")
f_ofile,_:= os.OpenFile("file02.txt", os.O_APPEND,0644)
newLine := "File handing is easy"
_,_ = fmt.Fprintln(f_ofile,newLine)

fmt.Println( "success")

结构体

image-20210531163938962

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
// 结构体定义 : type 名 struct{}
type Person struct {
name string
age int
}

// 结构体的嵌套
type man struct {
sex string
person Person
}


/*
1、结构体定义 : type 名 struct{} 定义在外部
2、匿名结构体 定义在内部
3、结构体的嵌套
4、
*/
var p1 Person = Person{
name: "lv qing",
age: 18,
}
p2:= Person{

}
p2.age=17
fmt.Println(p1.name)
fmt.Println(p2.age)
fmt.Println(p1)

// 匿名结构体
ss:= struct {
name string
age int
}{name:"lvq"}
ss.age=16
fmt.Println(ss)

// 结构体的嵌套
p:=man{}
p.sex = "man"
p.person.name = "liu"
p.person.age = 17

p4:=man{"man", Person{"heng", 16}}

fmt.Println(p)
fmt.Println(p4)
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
package main

import (
//"demo_fiveday/demo0510"
"fmt"
)

type Dog struct {
string
pro string
}

type Cat struct {
string
pro string
}

type Animal struct {
string
int
pro string
Dog
}

func main(){

fmt.Println("--------开始导入包-----没有弄出来 不会--")
//demo0510.Import_test()

//匿名字段
fmt.Println("-----------匿名字段-------------")
//a := Animal{"dog", 2}
//fmt.Println(a.string)
//fmt.Println(a.int)

// 匿名字段一般用于嵌套结构
//a := Animal{"dog", 2, Dog{"man", "wangwangwang"}}
//fmt.Println(a.Dog)
//fmt.Println(a.pro) // 字段提升 直接可以用到嵌套里面的属性

// 嵌套结构中有重复的字段
a:= Animal{"Animal", 2, "都会叫", Dog{"小狗", "wangwangwang"}}
fmt.Println(a)
fmt.Println(a.pro)
fmt.Println(a.Dog.pro)

// 结构体的比较
d := Dog{"Dog", "wangwangwang"}
d1 := d
//c := Cat{"Cat", "miaomiaomiao"}

fmt.Println(d1 == d)
//fmt.Println(d == c) // 不能比较
}

方法

方法其实就是函数,在 func 这个关键字和方法名中间加了一个特殊的接收器类型。接收器可以是结构体类型或者是非结构体类型。接收器可以在放大的内部访问的。

1、基本使用

2、指针接收器和值接受器

3、函数使用指针接收器

4、自定义类型加方法

​ 函数传参数时,有自动类型转换

​ 不同类型不能直接相比较

1
2
3
4
5
6
7
8
9
10
// 定义 -- 非结构体
func (d myInt) add(b myInt) myInt{
return d + b
}
// 定义 -- 结构体 -- 值接受器
func (b Dog) getName() string{
b.string = "BigDog"
fmt.Println("-方法-", b)
return b.string
}

指针接收器、值接收器

值接收器是值的copy 拷贝的结构体代价过于昂贵

指针接收器 耗费的空间很小 以后做的都用指针接收器

image-20210531154242032

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
// 定义  -- 结构体  -- 值接受器
func (b Dog) getName() string{
b.string = "BigDog"
fmt.Println("-方法-", b)
return b.string
}
// 指针接收器
func (c *Dog) getPro(){
fmt.Println("---指针接收器-*c--", (*c).pro)
fmt.Println("---指针接收器-c--", c.pro)
}
// 这个叫函数
func getName(b Dog) string{
b.string = "Litter Litter Dog"
fmt.Println("-函数-", b)
return b.string
}
func getPro(b *Dog) {
fmt.Println("-函数指针-", b.pro)
}

接口

1、定义

2、空接口(匿名接口)interface{}

3、类型断言

4、接口的嵌入结构

5、空接口值为nil

image-20210531154301370

协成(goroutin)

并发:立即处理多个任务的能力,单位时间内可以干多少的事 串串执行

并行:同时处理多个任务

image-20210531154344359

1
2
3
4
5
6
7
8
9
10
fmt.Println("---------协成-----------")

go goroutin()
fmt.Println("---------come on--------------")
time.Sleep(2*time.Second) // 睡两秒等等


func goroutin() {
fmt.Println("------go go go-----------")
}

通信(channel)

image-20210531184339430

image-20210531190239742

image-20210531190259174

select

mutex

反射

image-20210601163618707

image-20210601163939665

image-20210601164026862

image-20210601164253085

image-20210601164528565

总结

代码-0428

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
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
//package demo_fiveday
/**
导入的包 不使用 会报错
变量定义了 不使用 也会报错
编译 go build 名.go => 名.exe
执行 名.exe
**/


// 这个包表示是main包 也就是主包 整个程序入口是该文件
package main
// fmt 是内置的包
import "fmt"

// func 关键字 表示定义一个函数 函数名必须叫main
func main () {
//fmt.Println("Hello World") // 必须是双引号

// 变量声明
/*
1、变量必须先定义在使用
2、变量不能被重复定义
*/
// 第一种方式
var a int = 10 // var 关键字,表示声明一个变量,a是变量名 ,int 变量类型 = 变量值
fmt.Println(a)

// 第二种方式 类型推导 不用指定变量类型 自动推导变量类型
var b = 20
fmt.Println(b)

var c = "lvqing"
fmt.Println(c)

// 第三种方式
d := 30
e := "lvqing007"
fmt.Println(d)
fmt.Println(e)

// 声明多个变量
var f, g int = 40, 50
fmt.Println(f, g)

var h, i, j = 60, "lvqing008", "lvqing009"
fmt.Println(h)
fmt.Println(i, j)

m := "lvqing110"
n := 007
fmt.Println(m, n)

//常量定义
const x = "lvq"
const y = 12;

fmt.Println(x, y)

// 函数测试
test()
test01(10, 20) // 传参
test02(10, 20) // 返回值
var l, q = test03(10, "多个返回值") //多个返回值,用括号括起来
fmt.Println(l, q)

test04(1, 2, 3, 4, 5, 6) // 可变长参数 这是一个数组类型
w1, w2 := test05(10, 20)
fmt.Println(w1, w2)

w3, _ := test05(10, 20)
fmt.Println(w3) // 但是_不能打印

// if-else 注意: i1>i2 后面不能换行
i1, i2 := 2, 3
if i1 > i2 {
fmt.Println("i1大于i2")
} else if i1 == i2 {
fmt.Println("i1等于i2")
} else {
fmt.Println("i1小于i2")
}

//循环
for i := 0; i < 3; i++ {
fmt.Println(i)
}

var f1 = 0
for ; f1 < 3; f1++ {
fmt.Println("--------------")
fmt.Println(f1)
}

//用for模拟while 会死循环
var t1 = true
for t1 {
fmt.Println("t1")
t1 = false
}

//switch-case
var s1 = 4
switch s1 {
case 1, 2:
fmt.Println("1")
case 3:
fmt.Println("3")
case 4:
fmt.Println("4")
fallthrough
case 5:
fmt.Println("5")
default:
fmt.Println("6")
}
//无条件
var s2 = 4
switch { // 表达式被忽略
case s2 >= 0 && s2 <= 3:
fmt.Println("s2---1")
case s2 >= 4:
fmt.Println("s2---2")
default:
fmt.Println("s2---3")
}

// 数组
/*
1、数组有长度 有类型
2、int类型空值 0 字符串空值 ""
3、不能直接扩容 只能重新定义一个新的把数据copy过来
4、类型相同 长度不同的数组 不能直接赋值 比如: b = a
5、长度 len
6、循环两种方式: 一种for循环 另一种 range
*/
var b1[3] int
fmt.Println(b1)

var b2[3] string
fmt.Println(b2)

var b3[3] string
b3[0] = "lv"
b3[1] = "qing"
fmt.Println(b3)

// 定义并赋初值
var b4[3] string = [3]string{"ll","qq","ii"}
fmt.Println(b4)
b5 := [...]int{1,2,3,4,5,6} // 虽然写的... 但是长度还是后面固定的
fmt.Println(b5)

//数组是值类型
b6:= [3]int{4,5,6}
test07(b6)
fmt.Println(b6)
fmt.Println(len(b6))

// 遍历
for key,val := range b6{ // 用一个是索引 两个是索引 、值
fmt.Println(key)
fmt.Println(val)
}

// 给数组某个值赋初始值
var b7[7] int = [7]int {3:88, 2:77}
fmt.Println(b7)

// 多维数组
var b8[2][3] int = [2][3]int {
{1,2,3},
{4,5,6},
}
fmt.Println(b8)

}

/*
函数:
1、没有位置和关键字一说,都是按位置传参
*/

func test () {
fmt.Println("test")
}

// 传参
func test01(a int, b int){
fmt.Println(a + b)
}

// 返回值
func test02(a int, b int) int {
fmt.Println(a + b)

return a+ b
}

//多个返回值,用括号括起来
func test03(a int, b string) (int, string) {
return a, b
}

// 可变长参数
func test04(a ... int){
fmt.Println(a)
}

// 命名返回值
func test05(a int, b int)(c,d int){ // 或者 c int,d int
c = a
d = b

return // 可写return c,d
}

//空白符 舍弃一个返回值
func test06(a int, b int)(c,d int){
c = a
d = b

return
}

// 数组函数
func test07(a [3]int){
a[1] = 66
fmt.Println(a)

}

运行结果-0428

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
10
20
lvqing
30
lvqing007
40 50
60
lvqing008 lvqing009
lvqing110 7
lvq 12
test
30
30
10 多个返回值
[1 2 3 4 5 6]
10 20
10
i1小于i2
0
1
2
--------------
0
--------------
1
--------------
2
t1
4
5
s2---2
[0 0 0]
[ ]
[lv qing ]
[ll qq ii]
[1 2 3 4 5 6]
[4 66 6]
[4 5 6]
3
0
4
1
5
2
6
[0 0 77 88 0 0 0]
[[1 2 3] [4 5 6]]

代码-0429

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
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
package main

import (
"fmt"
)

func main(){

// 冒泡排序
var a = [10]int {4,5,6,7,23,90,39,456,56,432}
fmt.Println("排序前",a)
for i:= 0; i<len(a); i++{
for j := i+1; j < len(a); j++{
if a[i] < a[j]{
tmp:= a[i]
a[i]= a[j]
a[j] = tmp
}
}
}
fmt.Println("排序后",a)

// 切片
/*
1、[]int 切片类型
2、切片底层依附于数组,改值 数组也会变化
3、切片动态的追加值 超过数组的len
数组不会变化 不会在依附于此时的数组 会重新定义一个长度为原来数组的两倍的新数组
切片会依赖于新数组 在改值 原来旧数组不会变 变化的是新数组
*/
var b[] int // 切片类型
var c[3] int = [3]int {} // 数组
fmt.Println(b)
fmt.Println(c)
//定义并初始化
var c2[3] int = [3]int {1,2,3} // 数组
fmt.Println(c2)
// 第一种方式: make初始化
var b2[] int = make([]int, 3) // 切片
fmt.Println(b2)
// 第二种方式: 通过数组切片
b3 := c2[0:len(c2)] // 前闭后开
b3[0] = 100 // 切片改值 数组也会变
b3 = append(b3, 800) // 切片追加值
b3[1] = 999 // 超过长度 不会在依附于数组了

fmt.Println(b3)
fmt.Println(c2)
fmt.Println(len(b3), cap(b3)) // 查看切片长度和底层数组长度

// copy
fmt.Println("-------copy------")
var b4 = make([]int,3)
b4[0] = 8
b4[1] = 6
b4[2] = 4
fmt.Println(b4)

b5:= make([]int, 6)
fmt.Println(b5)

copy(b5, b4)
fmt.Println(b4, len(b4), cap(b4))
fmt.Println(b5, len(b5), cap(b5))
b5[0] = 111
b5[1] = 222
b5[2] = 333
fmt.Println("------------")
fmt.Println(b4, len(b4), cap(b4))
fmt.Println(b5, len(b5), cap(b5))

copy(b4, b5)
fmt.Println("------------")
fmt.Println(b4, len(b4), cap(b4))
fmt.Println(b5, len(b5), cap(b5))

//切片当做参数传给函数 函数修改 切片会被修改
fmt.Println("-----func-------")
b6:= make([]int, 3)
b6[0] = 1
b6[1] = 2
b6[2] = 3
fmt.Println(b6)
test_slice(b6)
fmt.Println(b6)

// map
/*
1、key-value 对
2、Map是引用类型
3、判断map相等不能用== ==只能用来判断map是否为nil
4、map是无序的
*/
fmt.Println("-----map-------")
// 定义
var m1 map[int]string // 空值是nil格式
//m1[1] = "QQQQ" 为空的不能直接赋值 有问题
fmt.Println(m1)
// 定义并初始化 - int key类型,string value类型
var m2 map[int]string = make (map[int]string)
m2[1] = "ll"
m2[2] = "vv"
m2[3] = "qq"
fmt.Println(m2)
// 取值
fmt.Println(m2[3]) // 取值不存在返回空
fmt.Println(m2[2])
// 删除某个元素
delete(m2, 1) // 传的是key
fmt.Println(m2)
fmt.Println(len(m2))
// map当参数传递
test_map(m2)
fmt.Println(m2)
// 相等性
m3:= map[int]string{1: "XXXX", 2: "YYYYY"}
fmt.Println(m3)

// 复杂map
var m4 map[int]map[int]string = make(map[int]map[int]string)
if m4[1] == nil{
m4[1] = make(map[int]string)
}
m4[1][1] = "LLLLLLLLLLLL"
m4[1][2] = "VVVVVVVVVVVV"
m4[1][3] = "QQQQQQQQQQQQ"
fmt.Println(m4) // 这只是m4[1] m4[2] m4[3]都没有 要循环
for mk,mv := range m4{
fmt.Println(mk)
fmt.Println(mv)
}

// 指针
/*
1、符号 & 表示取地址
2、符号 * 表示通过地址取值
3、指针不支持运算 不支持增加、减少
4、数组类型指针可以直接当做数组来修改,赋值
5、切片类型指针 如要修改切片,需要反解回来 再修改
6、指向数组的指针
7、指针数组
*/
// 定义
fmt.Println("-----port-------")
p1:= 123
var p2 *int = &p1
fmt.Println(p2)
fmt.Println(*p2)
// 传递指针类型数组
p3:= [3]int{1,2,3}
fmt.Println(p3)
test_port(&p3)
fmt.Println(p3)

p4 := 123456
p5 := &p4
p6 := &p5
p7 := &p6
fmt.Println(*p7) // p6的地址
fmt.Println(*(*(*p7))) // 值

// 切片类型指针
p8:= []int{2,3,4,5}
fmt.Println(p8)
test_SP(&p8)
fmt.Println(p8)

// 指向数组的指针
var p9 = [...]int{99:1}
fmt.Println(p9)

var p10 *[100]int = &p9
fmt.Println(p10)

// 指针数组
p11 := 11
p12 := 12
var p13 [2]*int = [...]*int{&p11, &p12}
fmt.Println(p13)
fmt.Printf("%T", p13) // 查看类型
}

func test_slice(b[] int){
b[0] = 999

fmt.Println(b)
}

func test_map(m map[int]string){
m[1] = "XXXXXX"
m[2] = "YYYYYY"
}

func test_port(p *[3]int){
(*p)[0]= 999
p[1] = 888 // 数组类型指针可以直接当做数组来修改,赋值
fmt.Println(p)
fmt.Println(*p)
}

func test_SP(sp *[]int){
fmt.Println(*sp)
(*sp)[0] = 666 // 切片类型指针不能直接赋值 需要反解
fmt.Println(sp)
}

运行结果-0429

1
2
3
4
5
6
7
排序前 [4 5 6 7 23 90 39 456 56 432]
排序后 [456 432 90 56 39 23 7 6 5 4]
[][0 0 0][1 2 3][0 0 0][100 999 3 800][100 2 3]4 6
-------copy------[8 6 4][0 0 0 0 0 0][8 6 4] 3 3[8 6 4 0 0 0] 6 6------------[8 6 4] 3 3[111 222 333 0 0 0] 6 6------------[111 222 333] 3 3[111 222 333 0 0 0] 6 6-----func-------[1 2 3][999 2 3][999 2 3]-----map-------map[]map[1:ll 2:vv 3:qq]qqvvmap[2:vv 3:qq]2map[1:XXXXXX 2:YYYYYY 3:qq]map[1:XXXX 2:YYYYY]map[1:map[1:LLLLLLLLLLLL 2:VVVVVVVVVVVV 3:QQQQQQQQQQQQ]]1map[1:LLLLLLLLLLLL 2:VVVVVVVVVVVV 3:QQQQQQQQQQQQ]-----port-------0xc00000a2e8123[1 2 3]&[999 888 3][999 888 3][999 888 3]0xc000006030123456[2 3 4 5][2 3 4 5]&[666 3 4 5][666 3 4 5][0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1]&[0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1][0xc00000a6b8 0xc00000a6c0][2]*int


----ao hu 乱序了 不想改了 凑凑合合看吧 你是最棒的----

代码-0507

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
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
package main

import (
"bufio"
"fmt"
"io/ioutil"
"os"
)

// 结构体定义 : type 名 struct{}
type Person struct {
name string
age int
}

// 结构体的嵌套
type man struct {
sex string
person Person
}

func main(){
// 匿名函数和闭包
// 匿名函数,没有名字的函数,定义在函数内部

a:= func(a, b, c int){
fmt.Println(a,b,c)
}
a(2,3,4)

// 匿名函数其实真正的用 只用一次 以后不会执行了
func(){
fmt.Println("--------匿名函数-----------")
}()

/*
1、闭包函数使用外部作用域的变量,其实是个引用
*/
x:=10
fmt.Println("----10的地址----",&x)
f:= closure(x)
fmt.Println(f(1))

// defer
defer_Test()
// panic 和 recover
fmt.Println("---------panic 和 recover------------")
p1()
p2()
p3()

// 可变长参数
func_test(3, 1,2,5,3,0,6,9,8,22,55,33)
fmt.Println("*********传切片**********")
// 传切片不可以 打散了传进去 为 n...
n := []int{88, 66, 7, 3, 99}
func_test(99, n...)

//// 测试练习
//fmt.Println("----------测试练习--------------")
//var test01 = [4]func(){}
//for i:= 0; i< 4; i++{
// defer fmt.Println("defer看i= ", i)
//
// defer func() {
// fmt.Println("匿名函数看i= ", i)
// }()
//
// test01[i] = func() {
// fmt.Println("test01看i= ", i)
// }
//}
//
//for _,v := range test01{
// fmt.Println("----v----", v)
// v()
//}

// 文件
fmt.Println("-------------文件练习--------------")
/*
1、普通读文件 ioutil 一次性读出来 不需要手动关闭 内部自己关闭了
2、分片读文件 每次读自定义的个数
3、按行读文件

1、按字节写入文件
2、按字符串写入文件
3、追加文件

*/
// 普通读文件
f_ioutil, err01 := ioutil.ReadFile("file01.txt")
if err01 != nil{
fmt.Println("打开文件失败")
return
}
fmt.Println("----f_ioutil----", f_ioutil)
fmt.Println("----string(f_ioutil)---------", string(f_ioutil))

// 分片、按行读文件
f_os,err02 := os.Open("file01.txt")
if err02 != nil{
fmt.Println("打开文件失败")
return
}

defer func() {
f_os.Close()
//fmt.Println("---文件关闭了----")
}()
// 分片读文件 用NewReader
//fmt.Println("-----分片读文件 用NewReader------")
//r:= bufio.NewReader(f_os) // 前面 Open 打开不能读
//b:= make([]byte, 8) // 定义一个切片 读8个
//for{
// _,err := r.Read(b)
// if err!=nil{
// break
// }
// fmt.Println("----string(b)----", string(b))
//}

// 按行读文件 用NewScanner
fmt.Println("-----按行读文件 用NewScanner-----")
s:= bufio.NewScanner(f_os)
for s.Scan(){
fmt.Println( s.Text())
}


//写入文件
fmt.Println("---------写入文件------------")
f_c,err03 := os.Create("file02.txt")
if err03!=nil{
fmt.Println("创建文件失败")
return
}

defer f_c.Close()

//按字符串按字符串写入文件
fmt.Println("---------按字符串写入文件------------")
l,err04 := f_c.WriteString("我增加了一行字符串")
if err04!=nil{
fmt.Println("写文件失败")
f_c.Close()
}
fmt.Println(l, "success")

// 按字节写文件
fmt.Println("--------按字节写入文件--------")
d2:=[]byte{104, 101, 108, 109}
n2,_:= f_c.Write(d2)

fmt.Println(n2, "success")

// 追加文件
fmt.Println("---------追加文件------------")
f_ofile,_:= os.OpenFile("file02.txt", os.O_APPEND,0644)
newLine := "File handing is easy"
_,_ = fmt.Fprintln(f_ofile,newLine)

fmt.Println( "success")

//// 标准输入输出
//var s1,s2 string
//fmt.Scan(&s1, &s2)
//fmt.Println("--s1--", s1, "--s2--", s2)
//fmt.Scanln(&s1, &s2) // 只能空格 不能回车
//fmt.Println("--s1--", s1, "--s2--", s2)
//fmt.Scanf("%s", &s1)
//fmt.Println("--s1--", s1, "--s2--", s2)

// 结构体
fmt.Println("--------结构体-----------")
/*
1、结构体定义 : type 名 struct{} 定义在外部
2、匿名结构体 定义在内部
3、结构体的嵌套
4、
*/
var p1 Person = Person{
name: "lv qing",
age: 18,
}
p2:= Person{

}
p2.age=17
fmt.Println(p1.name)
fmt.Println(p2.age)
fmt.Println(p1)

// 匿名结构体
ss:= struct {
name string
age int
}{name:"lvq"}
ss.age=16
fmt.Println(ss)

// 结构体的嵌套
p:=man{}
p.sex = "man"
p.person.name = "liu"
p.person.age = 17

p4:=man{"man", Person{"heng", 16}}

fmt.Println(p)
fmt.Println(p4)
}

func closure(x int) func(int) int {
fmt.Printf("%p\n", &x)
return func(y int) int {
fmt.Printf("%p\n",&x)
return x+y
}
}

func defer_Test(){
defer fmt.Println("我最后打印11111")
defer fmt.Println("我最后打印22222")
fmt.Println("我先打印111111")
fmt.Println("我先打印222222")

for i:=0;i<3;i++{
defer fmt.Println("i=",i)
}
fmt.Println("------------defer测试-------------")

for j:=0;j<3;j++{
defer func(){ // 匿名函数-引用
fmt.Println("j=",j)
}()
}
}

// panic recover
func p1(){
fmt.Println("-------p1--------")
}
func p2(){

defer func() {
// 恢复程序 recover
//recover() // 出现异常之后不执行 从p2之后执行

//err:= recover() // err 为空时 表示没有异常
//if err!=nil{
// fmt.Println("ERR",err)
//}
if err2:=recover();err2!=nil{
fmt.Println("err2",err2)
}

}()

fmt.Println("-------p2--------")
panic("出现异常")
fmt.Println("--------p4---------")
}
func p3(){
fmt.Println("-------p3--------")
}

func func_test(num int, nums ...int){
for k,v := range nums{
fmt.Println("key:",k, " value:",v)
if num == v{
fmt.Println("已找到num, key为", k, "num为", num)
}
}
}

运行结果-0507

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
2 3 4
--------匿名函数-----------
----10的地址---- 0xc00000a0a8
0xc00000a0c0
0xc00000a0c0
11
我先打印111111
我先打印222222
------------defer测试-------------
j= 3
j= 3
j= 3
i= 2
i= 1
i= 0
我最后打印22222
我最后打印11111
---------panic 和 recover------------
-------p1--------
-------p2--------
err2 出现异常
-------p3--------
key: 0 value: 1
key: 1 value: 2
key: 2 value: 5
key: 3 value: 3
已找到num, key为 3 num为 3
key: 4 value: 0
key: 5 value: 6
key: 6 value: 9
key: 7 value: 8
key: 8 value: 22
key: 9 value: 55
key: 10 value: 33
*********传切片**********
key: 0 value: 88
key: 1 value: 66
key: 2 value: 7
key: 3 value: 3
key: 4 value: 99
已找到num, key为 4 num为 99
-------------文件练习--------------
----f_ioutil---- [230 130 168 229 165 189 32 228 184 150 231 149 140 13 10 229 184 166 231 157 128 228 184 150 231 149 140 229 135 186 229 142 187 231 156 139 231 156 139 13 10 45 45 45 45 45 45 45 45 45 45 45 45 45 13 10 42 42 42 42 42 42 42 42 42 42 42 42 42 13 10 48 48 48 48 48 48 48 48 48 48 48 48 48 13 10 108 108 108 108 108 108 108 108 108 108 108 108 108 13 10 118 118 118 118 118 118 118 118 118 118 118 118 118]
----string(f_ioutil)--------- 您好 世界
带着世界出去看看
-------------
*************
0000000000000
lllllllllllll
vvvvvvvvvvvvv
-----按行读文件 用NewScanner-----
您好 世界
带着世界出去看看
-------------
*************
0000000000000
lllllllllllll
vvvvvvvvvvvvv
---------写入文件------------
---------按字符串写入文件------------
27 success
--------按字节写入文件--------
4 success
---------追加文件------------
success
--------结构体-----------
lv qing
17
{lv qing 18}
{lvq 16}
{man {liu 17}}
{man {heng 16}}

代码-0510

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
package main

import (
//"demo_fiveday/demo0510"
"fmt"
)

type Dog struct {
string
pro string
}

type Cat struct {
string
pro string
}

type Animal struct {
string
int
pro string
Dog
}

// 非结构体
type myInt int

func main(){

fmt.Println("--------开始导入包-----没有弄出来 不会--")
//demo0510.Import_test()

//匿名字段
fmt.Println("-----------匿名字段-------------")
//a := Animal{"dog", 2}
//fmt.Println(a.string)
//fmt.Println(a.int)

// 匿名字段一般用于嵌套结构
//a := Animal{"dog", 2, Dog{"man", "wangwangwang"}}
//fmt.Println(a.Dog)
//fmt.Println(a.pro) // 字段提升 直接可以用到嵌套里面的属性

// 嵌套结构中有重复的字段
a:= Animal{"Animal", 2, "都会叫", Dog{"小狗", "wangwangwang"}}
fmt.Println(a)
fmt.Println(a.pro)
fmt.Println(a.Dog.pro)

// 结构体的比较
d := Dog{"Dog", "wangwangwang"}
d1 := d
//c := Cat{"Cat", "miaomiaomiao"}

fmt.Println(d1 == d)
//fmt.Println(d == c) // 不能比较


// 方法的使用
fmt.Println("------------方法的使用------------")
b:= Dog{"LitterDog", "wangwangwang"}
c:= b.getName()
c1:= getName(b)
fmt.Println("--方法--", c)
fmt.Println("--函数--",c1)

(&b).getPro()
b.getPro()
// 函数上使用指针类型
getPro(&b)

// 非结构的方法
var a1 myInt = 10
num :=a1.add(6)
fmt.Println(num)
}


// 方法
/*****
1、基本使用
2、指针接收器和值接受器
3、函数使用指针接收器
4、自定义类型加方法
函数传参数时,有自动类型转换
不同类型不能直接相比较
*****/
// 定义 -- 结构体 -- 值接受器
func (b Dog) getName() string{
b.string = "BigDog"
fmt.Println("-方法-", b)

return b.string
}

// 这个叫函数
func getName(b Dog) string{
b.string = "Litter Litter Dog"
fmt.Println("-函数-", b)
return b.string
}

func getPro(b *Dog) {
fmt.Println("-函数指针-", b.pro)
}

// 指针接收器
func (c *Dog) getPro(){
fmt.Println("---指针接收器-*c--", (*c).pro)
fmt.Println("---指针接收器-c--", c.pro)
}

// 定义 -- 非结构体
func (d myInt) add(b myInt) myInt{
return d + b
}

运行结果-0510

1
-------开始导入包-----没有弄出来 不会-------------匿名字段-------------{Animal 2 都会叫 {小狗 wangwangwang}}都会叫wangwangwangtrue------------方法的使用-------------方法- {BigDog wangwangwang}-函数- {Litter Litter Dog wangwangwang}--方法-- BigDog--函数-- Litter Litter Dog---指针接收器-*c-- wangwangwang---指针接收器-c-- wangwangwang---指针接收器-*c-- wangwangwang---指针接收器-c-- wangwangwang-函数指针- wangwangwang16

代码-0519

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
package main

import "fmt"


// 接口
/***
1、定义
2、空接口(匿名接口)interface{}
3、类型断言
4、接口的嵌入结构
***/
// 1、定义接口
type Usb interface {
Connect()
Close()
}

type Phone struct {
name string
}

type Computer struct {
name string
}

func (a Phone) Connect(){
fmt.Println("-----连接了-----", a.name)
}

func (a Phone) Close(){
fmt.Println("----关闭了-----", a.name)
}

func (a Computer) Connect(){
fmt.Println("-----连接了-----", a.name)
}

func (a Computer) Close(){
fmt.Println("----关闭了-----", a.name)
}



func main() {
// 接口
fmt.Println("-------接口--------")
phone := Phone{name:"iphonexmax"}
phone.Connect()
phone.Close()

computer := Computer{"联想电脑"}
computer.Connect()
computer.Close()


testc(computer)
testp(phone)
fmt.Println("-------test---------")
testu(computer)
testu(phone)

//匿名空接口
fmt.Println("-------匿名空接口---------")
test2(1)
test2("hhahah")

// 类型断言
fmt.Println("-------类型断言---------")
testType(phone)
testType(computer)
}

// 现在写一个连接电脑处理事情的 关闭电脑的方法
func testc(c Computer){
c.Connect()
fmt.Println("-----电脑处理一下事情-----")
c.Close()
}

// 现在写一个连接手机处理事情的 关闭手机的方法
func testp(p Phone){
p.Connect()
fmt.Println("-----手机处理一下事情-----")
p.Close()
}

//这样太麻烦了 写一个usb类型 都可以传
func testu(u Usb){
u.Connect()
fmt.Println("-----通用方法处理事情--------")
u.Close()
}

// 2、空接口 interface{} -- 所有的类型都可以传
func test2(a interface{}){
fmt.Println("---匿名空接口---", a)
}

//类型断言
func testType(u Usb){
// 第一种书写方式
phone,ok := u.(Phone) // 类型判断 变量u是不是Phone类型
if ok{
fmt.Println(phone.name)
}
// 第二种书写方式
if computer,ok := u.(Computer); ok{
fmt.Println(computer.name)
}

// 用switch进行类型判断
switch v:=u.(type) {
case Computer:
fmt.Println("-我是电脑--",v.name)
case Phone:
fmt.Println("-我是手机--",v.name)
default:
fmt.Println("-不知道啥类型")
}
}

运行结果-0519

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
-------接口--------
-----连接了----- iphonexmax
----关闭了----- iphonexmax
-----连接了----- 联想电脑
----关闭了----- 联想电脑
-----连接了----- 联想电脑
-----电脑处理一下事情-----
----关闭了----- 联想电脑
-----连接了----- iphonexmax
-----手机处理一下事情-----
----关闭了----- iphonexmax
-------test---------
-----连接了----- 联想电脑
-----通用方法处理事情--------
----关闭了----- 联想电脑
-----连接了----- iphonexmax
-----通用方法处理事情--------
----关闭了----- iphonexmax
-------匿名空接口---------
---匿名空接口--- 1
---匿名空接口--- hhahah
-------类型断言---------
iphonexmax
-我是手机-- iphonexmax
联想电脑
-我是电脑-- 联想电脑

代码-0531

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
package main

import (
"fmt"
"sync"
"time"
)

type Usb interface {
Connect()
// 接口嵌套使用
Use2
}

type Use2 interface {
Close()
GetName() string
}

type Phone struct {
name string
}


func (a Phone) Connect(){
fmt.Println("-----连接了-----", a.name)
}

func (a Phone) Close(){
fmt.Println("----关闭了-----", a.name)
}

func (a Phone) GetName() string{
fmt.Println("----名字----")

return a.name
}

func main(){
phone:= Phone{"iphoneX"}
// 把phone转成了Usb接口类型 注意: phone没有实现usb的全部接口 是不能转成usb的
// phone 能转成usb usb 不能转成phone 只能向下转
var usb = Usb(phone)

fmt.Println("--usb--",usb.GetName())

// 空接口
var nty interface { }

fmt.Println("---------调用空接口-----------")
fmt.Println(nty == nil) // true

var n *int = nil
nty = n
fmt.Println(nty == nil) // false n不为nil a存的是指向nil的指针,不为空,所以不是nil
fmt.Println(n == nil)

// 协成
fmt.Println("---------协成-----------")
//go goroutin()
fmt.Println("---------come on--------------")
time.Sleep(2*time.Second) // 睡两秒等等

// 通道
fmt.Println("---------通道-----------")
/***
1、 定义 make创建 close关闭
2、 引用类型
3、 等着线程执行完 在执行 不需要sleep
4、 通过for range不断的取值
*/
var c = make(chan bool)
// 往信道中放值
//c<-3
go goroutin(c)
c2:=<-c
fmt.Println("-----", c2)

var c3= make(chan bool)
go func() {
fmt.Println("------c3-----")
c3<-true

close(c3)
}()
// 用for循环的时候一定要用close关闭通道
for v :=range c3{
fmt.Println("---v---",v)
}

// 有缓冲通道 无缓冲通道
fmt.Println("------有缓冲通道 无缓冲通道-----")
var c4 = make(chan int, 10) // 通道里面可以放三个值 有缓冲(放满了才回取运行) 不写默认是0 无缓冲(放一个就开始运行)

for i:= 0; i< 10; i++{
go goroutin_c(c4, i) // 开了10个线程计算0加到10000
}

for i := 0; i< 10; i++{
//<- c4 // 取出来
count := 0
num := <-c4
count = count + num

fmt.Println("---count---", count)
}

// WaitGroup实现同步,有三个方法: Add,Done,Wait
fmt.Println("---WaitGroup实现同步---")
wg:= sync.WaitGroup{} // 定义一个WaitGroup
// 增加10个任务
wg.Add(10)
for i := 0; i < 10; i++{
// 传入wg的引用
go goroutin_wg(&wg, i)
}

wg.Wait() // 等待所有的任务完成


}

func goroutin(g chan bool) {
fmt.Println("------go go go-----------")
g<-true
}

func goroutin_c (g chan int, index int) {
a := 1
for i := 0; i < 10000; i++{
a++
}
fmt.Println(index, a)
g <- a
}

func goroutin_wg(wg *sync.WaitGroup, index int){
a:= 1
for i:= 0; i< 10000; i++{
a++
}
fmt.Println("----", index, a)
wg.Done()
}

运行结果-0531

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
----名字----
--usb-- iphoneX
---------调用空接口-----------
true
false
true
---------协成-----------
---------come on--------------
---------通道-----------
------go go go-----------
----- true
------c3-----
---v--- true
------有缓冲通道 无缓冲通道-----
0 10001
---count--- 10001
4 10001
9 10001
5 10001
---count--- 10001
---count--- 10001
---count--- 10001
6 10001
8 10001
---count--- 10001
2 10001
---count--- 10001
3 10001
---count--- 10001
---count--- 10001
7 10001
1 10001
---count--- 10001
---count--- 10001
---WaitGroup实现同步---
---- 0 10001
---- 2 10001
---- 1 10001
---- 4 10001
---- 3 10001
---- 6 10001
---- 9 10001
---- 5 10001
---- 7 10001
---- 8 10001

designer

发表于 2021-06-22 | 分类于 Exam
字数统计 12.2k 字 | 阅读时长 41 分钟

计算机网络

二级 IP 地址

IP地址的格式是 (32bit) = net-id + host-id

IP地址一般分为三类:

  • A类: IP(32bit)= net-id(8bit) + host-id(24bit)
  • B类: IP(32bit)= net-id(16bit) + host-id(16bit)
  • C类: IP(32bit)= net-id(24bit) + host-id(8bit)

子网划分

子网划分是为了解决网络IP不够用的情况,它的实质其实就是,在A,B,或者C类中把原先分配给它的主机号位数拿出若干个位来作网络号.这样就可以缓解网络IP不够用的情况了.

比如我们拿一个B类IP来划分:X.X.0.0 里面host-id位数有16位,这时可以根据具体需要(具体需要几位后面会讲)拿出若干位来作net-id,剩下的作host-id. (这时你可能会问,把 主机号位数拿去分了,那可以连的主机数不是少了?确实是这样,划分子网就是以牺牲主机数来增加网络数。事实也如此,很多企业单位本来没有那么多主机,但他就是要了个大的网络ID,IP地址不够用也是这种原因引起的)

好了,知道划分子网的实质就是把host-id分出若干位数来作net-id,这时外界是怎样和划分好了的子网内的主机联系的呢?

在没有子网掩码的情况下,外界要和子网内的主机联系必须通过先前没划分的总的网络路由器,然后由路由器查找网内的各主机,这样效率就很低下。可不可以让各个子网独自通过自己的路由和外界通信呢?掩码正是为了解决这个问题。

各个子网要和外界独自通信,必须让外界知道你是划分了的子网,你的具体网络ID。但路由表并没有划分子网的具体信息,所以外界也无法通过你的路由器和你联系。掩码就是在你划分了的子网IP地址中,net-id相对应的地方标上1, host-id相对应的地方标上0.再在路由表中添加掩码这一项,这样外界就很容易知道你的具体网络ID了。这就是掩码的作用。

接下来我们来看例题。200.200.200.0是一个C类地址。要求划分一个子网100主机,另外四个子网20主机,我们可以先把该网络划分成两个子网。一个给100主机的子网,一个给另外20主机的四子网。

C类地址有8bit的主机号,划分子网就是把主机号拿出若干位来作网络ID。

具体要拿出多少位这里有一个公式:子网内主机数=2的x次方-2(x是主机号的位数)

现在主机数是100,我们取2的x次方-2略大于100。即x=7。

也就是说主机号位数是7位,这个子网才能够连100台主机。本来有8位的,剩下的一位拿去当网络号。(也实在是巧,这一位刚好可以标识两个子网(0或者1)下面的红色部分!)

image-20210531164408192

image-20210531164431904

image-20210531164445216

三级IP地址

image-20210531164455537

202.17.192.0/20 /20的意思是,对于这个ip,前20位表示网络号

应用: 考题1

IP地址与子网掩码相与,得出的就是网络地址;

image-20210531164508158

应用: 考题2

image-20210531164517572

应用: 考题3

image-20210531164527543

应用: 考题4

image-20210531164542120

image-20210531164608677

协议

image-20210531164621852

TCP/IP协议

image-20210531164636515

image-20210531164646741

ARP

地址解析协议,即ARP(Address Resolution Protocol),是根据IP地址获取物理地址的一个TCP/IP协议。

URL

image-20210531164701683

DNS

image-20210531164726608

安全防范体系

  • 物理环境的安全性: 通信线路、物理设备、机房安全等;

    • 物理层的安全,主要体现在通信路线的可靠性(线路备份、网管软件、传输介质)、软硬件设备的安全性(替换设备、拆卸设备、增加设备)、设备的备份、防灾害能力、防干扰能力、设备的运行环境(温度、湿度、烟尘)、不间断电源保障等;
  • 操作系统的安全性:

    • 操作系统本身的权限带来的不安全因素,包括身份认证、访问控制、系统漏洞等;

    • 对操作系统的安全配置问题;

    • 病毒对操作系统的威胁;

  • 网络的安全性:主要体现在计算机网络方面的安全性,包括网络层身份认证、网络资源的访问控制、数据传输的保密和完整性、远程接入的安全、域名系统的安全、路由系统的安全、入侵检测的手段和网络设施防病毒等;
  • 应用的安全性: 提供服务所采用的的应用软件和数据的安全性产生,包括Web服务、电子邮件系统、DNS等,此外还包括病毒对系统的威胁;
  • 管理的安全性: 包括安全技术和设备的管理、安全管理制度、部门与人员的组织规划等。管理的制度化极大程度地影响着整个计算机网络的安全,严格的安全管理制度、明确的部门安全职责划分与合理的人员角色配置,都可以在很大程度上降低其他层次的安全漏洞;

安全控制技术

image-20210531164748566

常用攻击方法

image-20210531164758660

MITM攻击 (中间人攻击)

中间人攻击(Man-in-the-MiddleAttack,简称“MITM攻击”)是一种“间接”的入侵攻击,这种攻击模式是通过各种技术手段将受入侵者控制的一台计算机虚拟放置在网络连接中的两台通信计算机之间,这台计算机就称为“中间人”,如SMB会话劫持、DNS欺骗等攻击都是典型的MITM攻击。简而言之,所谓的MITM攻击就是通过拦截正常的网络通信数据,并进行数据篡改和嗅探,而通信的双方却毫不知情。

DDoS攻击(分布式拒绝服务攻击)

分布式拒绝服务攻击(英文意思是Distributed Denial of Service,简称DDoS)是指处于不同位置的多个攻击者同时向一个或数个目标发动攻击,或者一个攻击者控制了位于不同位置的多台机器并利用这些机器对受害者同时实施攻击。由于攻击的发出点是分布在不同地方的,这类攻击称为分布式拒绝服务攻击,其中的攻击者可以有多个

MAC攻击

MAC(Message Authentication Code)是一种确认完整性并进行认证的技术,取三个单词的首字母,简称MAC。它是一种与密钥相关联的函数。

Dos攻击

image-20210531164808228

海明码

海明码具有检错和纠错双功能,它基于奇偶校验原理,只能检查出某一位错码的位置。当有多位错码时,它就不适用了。

image-20210531164821863

I/O设备与主机间交换数据

I/O设备与主机间进行数据输入输出主要有直接程序控制方式、中断方式、DMA方式和通道控制方式;

image-20210531164830239

直接程序控制方式

CPU直接通过I/O指令对I/O接口进行访问操作,主机与外设之间交换信息的每个步骤均在程序中表示出来,整个输入输出过程由CPU执行程序来完成的;

中断方式

当I/O接A准备好接收数据或向CPU传送数据时,就发出中断信号通知CPU,对中断信号进行确认后,CPU保存正在执行的程序的现场,转而执行提前设置好的I/O中断服务程序,完成一次数据传送的处理。这样,CPU就不需要主动查询外设的状态,在等待数据期间可以执行其他程序,从而提高了CPU利用率,采用中断方式管理I/O设备,CPU和外设可以并行工作。虽然中断方式可以提高CPU利用率,能处理随机事件和实时任务,但一次中断处理过程需要经历保存现场、中断处理和恢复现场等阶段,需要执行若干条指令才能处理一次中断事件,因此这种方式无法满足高速的批量数据传送要求

直接内存存储(DMA)

通过硬件控制和实现主存与I/O设备间的直接数据传送,数据的传送过程由DMA控制器(DMAC)进行控制,不需要CPU的干预,在DMA方式下,需要CPU启动传送过程,即向设备发出“传送一块数据”的命令,在传送过程结束时,DMAC通过中断方式通知CPU进行一些后续处理工作
DMA方式简化了CPU对数据传送的控制提高了主机与外设并行工作的程度,实现了快速外设与主存之间成批的数据传送,使系统的效率明显提高;

DMA(Direct Memory Access,直接存储器访问) 是所有现代电脑的重要特色,它允许不同速度的硬件装置来沟通,而不需要依赖于 CPU 的大量中断负载。否则,CPU 需要从来源把每一片段的资料复制到暂存器,然后把它们再次写回到新的地方。在这个时间中,CPU 对于其他的工作来说就无法使用。

image-20210531164838560

通道

通道是一种专用控制器,它通过执行通道程序进行I/O操作的管理,为主机与I/O设备提供一种数据传输通道,用通道指令编制的程序存放在存储器中,当需要进行I/O操作时,CPU只要按照约定格式准备好命令和数据,然后启动通道即可,通道则执行相应的通道程序,完成所要去的操作,用通道程序也可完成较复杂的I/O管理和预处理,从而很大程度上将主机从繁重的I/O管理工作中解脱出来,提高了系统的效率;

数字签名

image-20210531164848790

image-20210531164915042

image-20210531164931337

防火墙

image-20210531164941257

病毒

病毒具有隐秘性、传染性、潜伏性、触发性、破坏性

image-20210531164950917

引导区病毒

引导型病毒,指寄生在磁盘引导区或主引导区的计算机病毒。

此种病毒利用系统引导时,不对主引导区的内容正确与否进行判别的缺点,在引导系统的过程中侵入系统,驻留内存,监视系统运行,待机传染和破坏。按照引导型病毒在硬盘上的寄生位置又可细分为主引导记录病毒和分区引导记录病毒。

病毒感染硬盘的主引导区,如大麻病毒、2708病毒、火炬病毒等;

分区引导记录病毒感染硬盘的活动分区引导记录,如小球病毒、Girl病毒等。

宏病毒

宏病毒是一种寄存在文档或模板的宏中的计算机病毒。一旦打开这样的文档,其中的宏就会被执行,于是宏病毒就会被激活,转移到计算机上,并驻留在Normal模板上。从此以后,所有自动保存的文档都会“感染”上这种宏病毒,而且如果其他用户打开了感染病毒的文档,宏病毒又会转移到他的计算机上。

木马病毒

木马病毒是指隐藏在正常程序中的一段具有特殊功能的恶意代码,是具备破坏和删除文件、发送密码、记录键盘和攻击Dos等特殊功能的后门程序。

木马病毒其实是计算机黑客用于远程控制计算机的程序,将控制程序寄生于被控制的计算机系统中,里应外合,对被感染木马病毒的计算机实施操作

。一般的木马病毒程序主要是寻找计算机后门,伺机窃取被控计算机中的密码和重要文件等。可以对被控计算机实施监控、资料修改等非法操作。

木马病毒具有很强的隐蔽性,可以根据黑客意图突然发起攻击。

蠕虫病毒

病毒是一种常见的计算机病毒,是无须计算机使用者干预即可运行的独立程序,它通过不停的获得网络中存在漏洞的计算机上的部分或全部控制权来进行传播。

计算机病毒是指编制或者在计算机程序中插入的破坏计算机功能或者破坏数据和恶意篡改系统.影响计算机使用并且能够自我复制的一组计算机指令或者程序代码。

网络规划与设计

image-20210531165000734

常用命令

Linux chmod(英文全拼:change mode)命令是控制用户对文件的权限的命令

Linux chgrp(英文全拼:change group)命令用于变更文件或目录的所属群组。

Ctrl+Alt+Tab 使用箭头键在打开的项目之间切换

Alt+Tab 在打开的项目之间切换

Alt+Esc 以项目打开的顺序循环切换项目

Alt+Delete 显示当前窗口的系统菜单

image-20210531165017150

image-20210531165026302


帧中继

image-20210531165035973

计算机组成与系统结构

CPU

image-20210531165044123

软件特点

软件可靠性

系统在给定时间间隔内和条件下无失效运行的概率;

image-20210531165107754

软件可用性

在特定使用环境下为特定用户用于特定用途时所具备的有效性;

image-20210531165117453

软件可维护性

与软件维护的难易程度相关的一组软件属性;

软件可伸缩性

是否可以通过运行更多的实例或者采用分布式处理支持更多的用户;

健壮性(鲁棒性)

鲁棒是Robust的音译,也就是健壮和强壮的意思。它也是在异常和危险情况下系统生存的能力。比如说,计算机软件在输入错误、磁盘故障、网络过载或有意攻击情况下,能否不死机、不崩溃,就是该软件的鲁棒性。

软件过程改进(SPI)

image-20210531165126864

SEI能力成熟度模型(SEICMM)

image-20210531165211864

软件维护

image-20210531165227216

软件质量保证

image-20210531165245461

image-20210531165256582

层次化存储体系

虚拟存储器(Virtual Memory)

在具有层次结构存储器的计算机系统中,自动实现部分装入和部分替换功能,能从逻辑上为用户提供一个比物理贮存容量大得多,可寻址的“主存储器”。虚拟存储区的容量与物理主存大小无关,而受限于计算机的地址结构和可用磁盘容量。

高速缓冲存储器(Cache)

其原始意义是指存取速度比一般随机存取记忆体(RAM)来得快的一种RAM,一般而言它不像系统主记忆体那样使用DRAM技术,而使用昂贵但较快速的SRAM技术,也有快取记忆体的名称。高速缓冲存储器是存在于主存与CPU之间的一级存储器, 由静态存储芯片(SRAM)组成,容量比较小但速度比主存高得多, 接近于CPU的速度。在计算机存储系统的层次结构中,是介于中央处理器和主存储器之间的高速小容量存储器。它和主存储器一起构成一级的存储器。高速缓冲存储器和主存储器之间信息的调度和传送是由硬件自动进行的。

image-20210531165309976

存储

image-20210531165319832

直接相联映射方式

主存中每一块只能映射到Cache的一个特定的块

全相联映射方式

主存中任一块都可以映射到Cache中任一块的方式

组相联映射方式

主存编址计算

image-20210531165336674

总线系统

image-20210531165347899

硬件基础知识

image-20210531165404659

程序计数器

用来存放下一条指令所在的单元的地址的地方

地址寄存器

一般用来保存当前CPU所访问的内存单元的地址,以方便对内存的读写操作

数据寄存器

主要用来保存操作数和运算结果等信息的,其目的是为了节省读取操作数所需占用总线和访问存储器的时间

指令寄存器

一般用来保存当前正在执行的一条指令

CRC计算冗余码

image-20210531165526810


操作系统

信号量-PV操作

互斥信号量的初值为1;

PV操作与信号量的处理相关,P表示通过的意思,V表示释放的意思。

image-20210531165540350

磁盘

image-20210531165618500

文件

image-20210531165638583

页式存储

image-20210531165650029

浮点计算

image-20210531165714831

寻址

image-20210531165749929

系统可靠性

image-20210531165814916

进程

image-20210531165830005

嵌入式操作系统

image-20210531165839499

设备管理

image-20210531165853256


软件工程

内聚性

image-20210531165906890

UP(统一过程)

image-20210531165925058

UML(统一建模语言)

类图

展现了一组对象、接口、协作和它们之间的关系。在面对对象系统的建模中所建立的最常见的图就是类图。类图给出系统的静态设计视图,包含主动类的类图给出了系统的静态进程视图;

从弱到强 依赖 关联 聚合 组合 继承

泛化(类与 继承关系) = 实现(类与接口关系) > 组合(整体与部分的关系) > 聚合(整体与部分的关系) > 关联(拥有的关系) > 依赖(使用的关系)

依赖

是一种使用的关系,即一个类的实现需要另一个类的协助

【箭头及指向】:带箭头的虚线,指向被使用者

image-20210531165934443

关联

是一种拥有的关系,它使一个类知道另一个类的属性和方法;如:老师与学生

【箭头及指向】:带普通箭头的实心线,指向被拥有者

image-20210531165951436

image-20210531165943594

聚合

是整体与部分的关系,且部分可以离开整体而单独存在

“部分”对象可以在不同的“整体”对象之间共享,并且“部分”对象的生命周期可以与“整体”对象不同,甚至“部分”对象可以脱离“整体”对象而单独存在;

【箭头及指向】:带空心菱形的实心线,菱形指向整体

image-20210531165959547

组合

是整体与部分的关系,但部分不能离开整体而单独存在,组合关系中,整体与部分的生命周期一致。

是一种很强的“拥有”关系,“部分”和“整体”的生命周期通常一样,整体对象完全支配其组成部分,包括它们的创建和销毁等;

【箭头及指向】:带实心菱形的实线,菱形指向整体

image-20210531170007402

继承

是一种继承关系,表示一般与特殊的关系,它指定了子类如何特化父类的所有特征和行为

箭头指向】:带三角箭头的实线,箭头指向父类

image-20210531170016420

实现

是一种类与接口的关系,表示类是接口所有特征和行为的实现.

【箭头指向】:带三角箭头的虚线,箭头指向接口

image-20210531170028470

对象图

展现了一组对象以及它们之间的关系。对象图描述了在类图中所建立的事务实例的静态快照;

用例图

展现了一组用例、参与者以及它们之间的关系。给出系统的静态用例对象,对系统的行为进行组织和建模非常重要;

image-20210531170046945

image-20210531170059793

序列图

是场景的图形化表示,描述了时间顺序组织的对象之间的交互活动;

协作图

强调收发消息的对象的结构组织;

序列图和协作图都是交互图: 交互图展现了一种交互,它由一组对象和它们之间的关系组成,包括它们之间可能发送的消息。交互图关注系统的动态图,序列图和协助图是同构的,它们之间可以相互转换;

状态图

展示了一个状态机,它由状态、转换、事件和活动组成。

活动图

展示了系统内一个活动到另一个活动的流程,对系统的功能建模特别重要,并强调对象间的控制流程;

构(组)件图

展示了一组构建之间的组织和依赖,它与类图相关,通常把构件映射为一个或多个类、接口或协作;

部署图

展示了运行处理节点以及其中的构件的配置。它与构件图相关,通常一个节点包含一个或多个构件;

通信图

描述的是对象和对象之间的关系,即一个类操作的实现。简而言之,就是对象和对象之间的调用关系,体现的是一种组织关系。其中一个框中的名称中带有:,说明这表示的是一个对象,:前的部分是对象名,:后面的部分是类名,而对象之间连线上面的箭头所标识的是对象之间通信的消息

image-20210531170133283

image-20210531170146994

image-20210531170221059

image-20210531170233465

image-20210531170249298

image-20210531170302717

image-20210531170348684

image-20210531170404802

image-20210531170421445

image-20210531170451621

设计模式

观察模式(发布-订阅Subscribe模式、模型-视图View)

观察者模式(发布-订阅Subscribe模式、模型-视图View): 一个目标物件管理所有相依鱼她的观察者物件,并且在它的本身的状态改变时主动发出通知

image-20210531170520703

适配器模式

适配器模式(包装): 将一个类的接口适配成用户所期待的。

状态模式

状态模式: 当一个对象的内在状态改变是允许改变其行为,这个对象看起来像是改变了其类。

Bridge(桥接)模式

Bridge(桥接)模式: 接口与其实现分离,使得接口和实现的变化不产生相互的影像;

Singleton (单件、单例) 模式

Singleton (单件、单例) 模式:确保一个类只有一个实例,并提供一个全局访问点。

Composition (组合) 模式

Composition (组合) 模式:将对象组合成树形结构以表示“部分-整体”的层次结构,使得用户对单个对象和组合对象的使用具有一致性。

image-20210531170538522

Facade (外观) 模式

Facade (外观) 模式:定义一个高层接口,它包含了对各个子系统的引用,客户端可以通过它访问各个子系统的功能。包含以下主要角色。

  • 外观(Facade)角色:为多个子系统对外提供一个共同的接口。

  • 子系统(Sub System)角色:实现系统的部分功能,客户可以通过外观角色访问它。

  • 客户(Client)角色:通过一个外观角色访问各个子系统的功能。

image-20210531170547309

抽象工厂模式

抽象工厂模式: 提供一个接口,可以创建一系列相关或相互以来的对象,而无需指定它们具体的类;

image-20210531170601239

亨元模式

亨元模式: 提供支持大量细粒度对象共享的有效方法;

装饰模式

装饰模式:动态地给一个对象添加一些额外的职责,它提供了用子类扩展功能的一个灵活的替代,比派生一个子类更加灵活;

代理(Proxy)模式

为其他对象提供一种代理以控制这个对象的访问

生成器(Builder)模式

将一个复杂类的表示与其构造相分离,使得相同的构建过程能够得出不同的表示

image-20210531170623673

image-20210531170640396

image-20210531170650872

image-20210531170701144

image-20210531170716098

image-20210531170726117

开发模型

image-20210531170735756

image-20210531170743568

McCabe度量法

McCabe度量法:在程序控制流程图中,节点是程序中代码的最小单元,边代表节点间的程序流。一个有e条边和n个节点的流程图F,可以用下述3种方法中的任何一种来计算环形复杂度。

  1. 流图中的区域数等于环形复杂度。
  2. 流图G的环形复杂度V(G)=E-N+2,其中,E是流图中边的条数,N是结点数。
  3. 流图G的环形复杂度V(G)=P+1,其中,P是流图中判定结点的数目。

image-20210531170753739

需求分析

image-20210531170802751

数据流图(DFD)

数据流图主要由实体、数据存储、处理过程和数据流四部分组成

建模遵循:自顶向下、抽象到具体

顶层数据流图只含有一个加工表示整个系统;输出数据流和输入数据流为系统的输入数据和输出数据,表明系统的范围,以及与外部环境的数据交换关系。

中层数据流图是对父层数据流图中某个加工进行细化,而它的某个加工也可以再次细化,形成子图;中间层次的多少,一般视系统的复杂程度而定。

底层数据流图是指其加工不能再分解的数据流图,其加工称为“原子加工”。

image-20210531170827417

image-20210531170845196

image-20210531170855220

数据字典

image-20210531170907055

软件开发方法

image-20210531170917619

软件测试

image-20210531170930388

image-20210531170950890

image-20210531171002348

image-20210531171012844


数据结构

排序

image-20210531171025872

  • 计数排序: 计数排序是一个非基于比较的排序算法,该算法于1954年由 Harold H. Seward 提出。它的优势在于在对一定范围内的整数排序时,它的复杂度为Ο(n+k)(其中k是整数的范围),快于任何比较排序算法。 [1] 当然这是一种牺牲空间换取时间的做法,而且当O(k)>O(nlog(n))的时候其效率反而不如基于比较的排序(基于比较的排序的时间复杂度在理论上的下限是O(nlog(n)), 如归并排序,堆排序)
  • 桶排序:桶排序 (Bucket sort)或所谓的箱排序,是一个排序算法,工作的原理是将数组分到有限数量的桶子里。每个桶子再个别排序(有可能再使用别的排序算法或是以递归方式继续使用桶排序进行排序)。桶排序是鸽巢排序的一种归纳结果。当要被排序的数组内的数值是均匀分配的时候,桶排序使用线性时间(Θ(n))。但桶排序并不是 比较排序,他不受到 O(n log n) 下限的影响。
  • image-20210531171046378

B树B+树

B-tree树即B树,B即Balanced,平衡的意思。因为B树的原英文名称为B-tree,而国内很多人喜欢把B-tree译作B-树,其实,这是个非常不好的直译,很容易让人产生误解。如人们可能会以为B-树是一种树,而B树又是另一种树。而事实上是,B-tree就是指的B树。特此说明。

二叉树

二叉链表存储结构每个节点有2个指针。每个结点有0个、1个或者2个空指针对应有2个、1个、0个非空指针。

二叉树中边的个数等于非空指针的个数

假设二叉树中 节点的总个数为:N 边的个数为: M 度为0、1、2 的结点的个数为 n0、n1、 n2

n0 + n1 + n2 = N (1)

除了根节点,都有一条进入到此节点的边,所以 M = N -1 (2)

又 M = n1 + 2*n2 (3)

因此(1)(2)(3)得: n0 = n2 + 1 (4)

设空节点的个数为K,则 K = 2*n0 + n1 (5)

因此(1)(4)(5)得: K = N + 1

因此(2)得: M = N -1

因此(4)得: 度为0的结点的个数(叶子结点的个数)= 度为2的结点的个数 + 1(n0 = n2 + 1)

image-20210531171108258

image-20210531171129205

哈夫曼树

image-20210531171138657

关键路径

  • 关键路径: 从起点到终点长度最长的那条路径;
  • 活动的松弛时间: 要求出活动的最早开始时间和最晚开始时间,其最晚开始时间减去最早开始时间就是活动松弛时间;

图

无向图

image-20210531171158391

image-20210531171207737

沟通途径相当于无向图, 为 n *(n-1)/ 2

编码

image-20210531171227386

Huffman编码

  • 定长编码: 2x
  • Huffman编码:是一种最优的不定长编码,可以有效地压缩数据。要是用Huffman编码,除了知道文件中出现的字符之外,还需要知道每个字符出现的频率。每一步取两个最小权值作为左右子树构造一个新树

算法

image-20210531171245161

image-20210531171254342

0-1 背包问题

image-20210531171326224

image-20210531171348037

动态规划

image-20210531171400280

动态规划是一种自底向上的求解方式,将子问题的解存储在表中,当出现重复子问题时,进行查表操作,避免重复的计算,极大提高算法的效率。

是一种以空间换时间的方式,最典型个人感觉为斐波那契数列求解。

在求解问题中,对于每一步决策,列出各种可能的局部解,再依据某种判定条件,舍弃那些肯定不能得到的最优解的局部解,再每一步都经过筛选,以每一步都是最优解来保证全局都是最优解。

贪心算法

image-20210531171413023

在对问题求解时,总是做出在当前看来是最好的选择。也就是说,不从整体最优上加以考虑,它所做出的仅仅是在某种意义上的局部最优解。

贪心算法没有固定的算法框架,算法设计的关键是贪心策略的选择。必须注意的是,贪心算法不是对所有问题都能得到整体最优解,选择的贪心策略必须具备无后效性(即某个状态以后的过程不会影响以前的状态,只与当前状态有关。)

贪心算法是一种自顶向下的解决方案,通过当前阶段的评价准则,得到局部最优解,不断减小问题规模,得到最终解。是一种局部最优解组合替代整体最优解的方案。但有些时候计算得到的最终解并非整体最优解

所以,对所采用的贪心策略一定要仔细分析其是否满足无后效性。

回溯

是一种选优搜索法,按优选条件向前搜索,以达到目标。但当搜索到某一步时,发现原先选择并不优或达不到目标,就退回一步重新选择。这种走不通就退回再走的技术就是回溯法。

分治

特征: 对于一个规模为n的问题,若该问题可以容易的解决(比如说规模n比较小)则直接解决;否则将其分解为k个规模较小的子问题,这些子问题互相独立且与原问题形式相同,递归地解这些子问题,然后将各个子问题的解合并得到原问题的解;

image-20210531171422242

广义表

image-20210531171432844


时间复杂度、空间复杂度

image-20210531171441391

数据库

代数关系

image-20210531171507911

image-20210531171521332

image-20210531171543639

image-20210531171625042

image-20210531171634904

\一、关系代数的9种操作:**

关系代数中包括了:并、交、差、乘、选择、投影、联接、除、自然联接等操作。

五个基本操作:

并(∪)、差(-)、笛卡尔积(×)、投影(π)、选择(σ)

各种机制

image-20210531171646676

数据库系统的安全措施: 权限机制、视图机制、数据加密

权限机制

限定用户对数据的操作权限,把数据的操作限定在具体指定权限的用户范围内,使用GRANT语句实现权限管理

视图机制

保密数据对无权存取的用户隐藏,对数据提供安全保护

数据加密

数据加密是防止数据库中的数据在存储和传输过程中被窃取的有效手段

安全性控制

是指系统防止非法用户对系统进行操作所采取的机制。

视图可以将表中视图之外的数据屏蔽从而保证其安全,密码验证和用户授予权都是对用户合法性的管理,而完整性是对合法用户非法输入的限制,不属于安全控制

范式

image-20210531171657931

image-20210531171705429

第一范式(1NF)

符合1NF的关系中的每个属性都不可再分

字段是最小的的单元不可再分

第二范式(2NF)

2NF在1NF的基础之上,消除了非主属性对于码的部分函数依赖。

满足1NF,表中的字段必须完全依赖于全部主键而非部分主键

第三范式 (3NF)

3NF在2NF的基础之上,消除了非主属性对于码的传递函数依赖。也就是说, 如果存在非主属性对于码的传递函数依赖,则不符合3NF的要求。

满足2NF,非主键外的所有字段必须互不依赖

第四范式(4NF)

满足3NF,消除表中的多值依赖


规范化理论

image-20210531171717932

关系模式

image-20210531171731583

image-20210531171744611

程序设计语言

面向对象

针对接口的编程

基本概念

image-20210531171800218

面向对象分析

为了获取对应用问题的理解,其主要任务是抽取和整理用户需求并建立问题域精确模型

面向对象设计

采用协作的对象、对象的属性和方法说明软件解决方案的一种方式,强调的是定义软件对象和这些软件对象如何协作来满足需求,延续了面向对象分析

面向对象实现

主要强调采用面向对象程序设计语言系统实现

面向对象测试

根据规范说明来验证系统设计的正确性

ADSL 采用频分多路技术在普通电话线上划分上行、下行和话音等不同的信道,从而实现上网和通话同时传输

基本原则

单一职责原则

对于单一职责原则,其核心思想为:一个类,最好只做一件事,只有一个引起它的变化。单一职责原则可以看做是低耦合、高内聚在面向对象原则上的引申,将职责定义为引起变化的原因,以提高内聚性来减少引起变化的原因。职责过多,可能引起它变化的原因就越多,这将导致职责依赖,相互之间就产生影响,从而大大损伤其内聚性和耦合度。通常意义下的单一职责,就是指只有一种单一功能,不要为类实现过多的功能点,以保证实体只有一个引起它变化的原因。

专注,是一个人优良的品质;同样的,单一也是一个类的优良设计。交杂不清的职责将使得代码看起来特别别扭牵一发而动全身,有失美感和必然导致丑陋的系统错误风险。

开放封闭原则

对于开放封闭原则,它是面向对象所有原则的核心,软件设计说到底追求的目标就是封装变化、降低耦合,而开放封闭原则就是这一目标的最直接体现。

开放封闭原则,其核心思想是:软件实体应该是可扩展的,而不可修改的。也就是,对扩展开放,对修改封闭的。

因此,开放封闭原则主要体现在两个方面:1、对扩展开放,意味着有新的需求或变化时,可以对现有代码进行扩展,以适应新的情况。2、对修改封闭,意味着类一旦设计完成,就可以独立完成其工作,而不要对其进行任何尝试的修改。

实现开开放封闭原则的核心思想就是对抽象编程,而不对具体编程,因为抽象相对稳定。让类依赖于固定的抽象,所以修改就是封闭的;而通过面向对象的继承和多态机制,又可以实现对抽象类的继承,通过覆写其方法来改变固有行为,实现新的拓展方法,所以就是开放的。

“需求总是变化”没有不变的软件,所以就需要用封闭开放原则来封闭变化满足需求,同时还能保持软件内部的封装体系稳定,不被需求的变化影响。

依赖倒置原则

对于依赖倒置原则,其核心思想是:依赖于抽象。具体而言就是高层模块不依赖于底层模块,二者都同依赖于抽象;抽象不依赖于具体,具体依赖于抽象。

我们知道,依赖一定会存在于类与类、模块与模块之间。当两个模块之间存在紧密的耦合关系时,最好的方法就是分离接口和实现:在依赖之间定义一个抽象的接口使得高层模块调用接口,而底层模块实现接口的定义,以此来有效控制耦合关系,达到依赖于抽象的设计目标。

抽象的稳定性决定了系统的稳定性,因为抽象是不变的,依赖于抽象是面向对象设计的精髓,也是依赖倒置原则的核心。

依赖于抽象是一个通用的原则,而某些时候依赖于细节则是在所难免的,必须权衡在抽象和具体之间的取舍,方法不是一层不变的。依赖于抽象,就是对接口编程,不要对实现编程。

接口隔离原则

对于接口隔离原则,其核心思想是:使用多个小的专门的接口,而不要使用一个大的总接口。

具体而言,接口隔离原则体现在:接口应该是内聚的,应该避免“胖”接口。一个类对另外一个类的依赖应该建立在最小的接口上,不要强迫依赖不用的方法,这是一种接口污染。

接口有效地将细节和抽象隔离,体现了对抽象编程的一切好处,接口隔离强调接口的单一性。而胖接口存在明显的弊端,会导致实现的类型必须完全实现接口的所有方法、属性等;而某些时候,实现类型并非需要所有的接口定义,在设计上这是“浪费”,而且在实施上这会带来潜在的问题,对胖接口的修改将导致一连串的客户端程序需要修改,有时候这是一种灾难。在这种情况下,将胖接口分解为多个特点的定制化方法,使得客户端仅仅依赖于它们的实际调用的方法,从而解除了客户端不会依赖于它们不用的方法。

分离的手段主要有以下两种:1、委托分离,通过增加一个新的类型来委托客户的请求,隔离客户和接口的直接依赖,但是会增加系统的开销。2、多重继承分离,通过接口多继承来实现客户的需求,这种方式是较好的。

Liskov替换原则

对于Liskov替换原则,其核心思想是:子类必须能够替换其基类。这一思想体现为对继承机制的约束规范,只有子类能够替换基类时,才能保证系统在运行期内识别子类,这是保证继承复用的基础。在父类和子类的具体行为中,必须严格把握继承层次中的关系和特征,将基类替换为子类,程序的行为不会发生任何变化。同时,这一约束反过来则是不成立的,子类可以替换基类,但是基类不一定能替换子类。

Liskov替换原则,主要着眼于对抽象和多态建立在继承的基础上,因此只有遵循了Liskov替换原则,才能保证继承复用是可靠地。实现的方法是面向接口编程:将公共部分抽象为基类接口或抽象类,通过Extract Abstract Class,在子类中通过覆写父类的方法实现新的方式支持同样的职责。

Liskov替换原则是关于继承机制的设计原则,违反了Liskov替换原则就必然导致违反开放封闭原则。

Liskov替换原则能够保证系统具有良好的拓展性,同时实现基于多态的抽象机制,能够减少代码冗余,避免运行期的类型判别。

image-20210531171815854

各种对象的职责

image-20210531171828065

边界对象

边界对象: 系统与参与者之间的接口,该对象从参与者处收集信息,并将之转化为一种被实体对象和控制对象使用的形式;

image-20210531171837303

可视化对象
抽象对象
实体对象
基本概念
继承
多态

多态分为两种: 通用的多态、特定的多态;

​ 动态绑定是实现多态的基础

  • 两者的区别: 前者对工作的类型不加限制,允许对不同类型的值执行相同的代码;
    后者只对有限数量的类型有效,而且对不同类型的值可能要执行不同的代码;
  • 通用的多态:参数多态、包含多态;

    • 参数多态:采用参数化模板,通过不同的类型参数,是的一个结构有多种类型;

    • 包含多态:同样的操作可用于一个类型以及其值类型(注意不是子类),一般需要进行运行时的类型检查;

  • 特定的多态: 过载多态、强制多态;

    • 强制多态: 编译程序通过语义操作,把操作对象的类型强行加以变换,以符合函数或者操作符的要求;
    • 过载多态:同一个名(操作符、函数名)在不同的上下文中有不同的类型;

image-20210531171847433

image-20210531171902810

image-20210531171914019

类

image-20210531171932535

XP(极限编程)

  • 简单设计: 只处理当前的需求,使设计保持简单;
  • 测试先行: 先写测试代码,在编写程序;
  • 持续集成: 可以按照甚至按照小时为客户提供可运行的版本;
  • 现场客户: 系统最终用户代表应该全程配合XP团队;
  • 其他:
    • 计划游戏: 快速制定计划,随着细节不断变化而完善;
    • 小型发布: 系统的设计要能够尽可能早的交付;
    • 隐喻:找到合适的比喻传达信息;
    • 重构: 重新审视需求和设计,重新明确地描述他们以符合新的和现有的需求;
    • 结对编程;
    • 集体代码所有制;
    • 每周工作40小时;
    • 编码标准;

image-20210531171943148

RISC、CISC

CISC计算机:复杂指令集计算机

  • 趋于多用途、强功能化,指令系统的复杂化使得设计周期变长,正确性难以保证,不易维护,需要大量硬件支持的复杂指令利用率却很低

RISC计算机:精简指令计算机

  • 只包含使用频率较高但不复杂的指令

  • 指令长度固定,格式少,寻址方式少

  • 只能存取数指令访问主存,其他指令都在寄存器之间运算

  • 大部分指令在一个机器周期内完成,采用流水技术

  • cpu中增加了通用寄存器的数量

  • 硬联逻辑控制,不用微程序控制技术

  • 采用优化的编译,以有效地支持高技语言

引用、值传递

image-20210531172013708

image-20210531172026770

image-20210531172036129

文法

image-20210531172050723

image-20210531172105905

编译与解释

image-20210531172127905

编译器分析过程

image-20210531172141022

词法分析

从左到右逐个扫描源程序中的字符, 识别其中如关键字(或称保留字)、识别符、常数、运算符以及分隔符(标点符号和括号)等

语法分析

根据语法规则将单词符号分解成各类语法单位,并分析程序是否存在语法上的错误,包括语言结构出错、if…end if不匹配, 缺少分号、括号不匹配、表达式缺少操作数等

语义分析

进行类型分析和检查,主要检测源程序是否存在静态语义错误,包括:运算符和运算类型不符合,如取余时用浮点数

正规式

image-20210531172149583


其他

法律法规与标准化

image-20210531172204040

知识产权

image-20210531172240691

项目管理

配置管理

版本控制、变更管理、配置状态报告、配置审计

image-20210531172254770

风险识别

风险识别,是指风险管理的第一步,也是风险管理的基础。只有在正确识别出自身所面临的风险的基础上,人们才能够主动选择适当有效的方法进行的处理。

风险识别是指在风险事故发生之前,人们运用各种方法系统的、连续的认识所面临的各种风险以及分析风险事故发生的潜在原因。风险识别过程包含感知风险和分析风险两个环节。

感知风险:即了解客观存在的各种风险,是风险识别的基础,只有通过感知风险,才能进一步在此基础上进行分析,寻找导致风险事故发生的条件因素,为拟定风险处理方案,进行风险管理决策服务。

分析风险:即分析引起风险事故的各种因素,它是风险识别的关键。

风险预测

风险预测是指在工作之前对工作过程中以及工作结果可能出现的事物异常进行预测制订对策从而预防事故发生的一种措施.

风险预测是风险管理的重要组成部分,它是风险规避即控制的基础。任何风险事件的发生,都是在外界各种因素的综合作用下进行的。因此,需要在对风险事件进行预测中,需要综合考虑考虑到这些不确定的、随机的因素可能造成的破坏性影响。

风险评估

风险评估(Risk Assessment) 是指,在风险事件发生之前或之后(但还没有结束),该事件给人们的生活、生命、财产等各个方面造成的影响和损失的可能性进行量化评估的工作。即,风险评估就是量化测评某一事件或事物带来的影响或损失的可能程度。

从信息安全的角度来讲,风险评估是对信息资产(即某事件或事物所具有的信息集)所面临的威胁、存在的弱点、造成的影响,以及三者综合作用所带来风险的可能性的评估。作为风险管理的基础,风险评估是组织确定信息安全需求的一个重要途径,属于组织信息安全管理体系策划的过程。

风险控制

风险控制是指风险管理者采取各种措施和方法,消灭或减少风险事件发生的各种可能性,或风险控制者减少风险事件发生时造成的损失。

总会有些事情是不能控制的,风险总是存在的。作为管理者会采取各种措施减小风险事件发生的可能性,或者把可能的损失控制在一定的范围内,以避免在风险事件发生时带来的难以承担的损失。风险控制的四种基本方法是:风险回避、损失控制、风险转移和风险保留。

Gant图与Pert图

image-20210531172308399

image-20210531172318142

人机界面设计

黄金三原则: 用户操纵控制、减少用户的记忆负担、保持界面的一致性

有限自动机

image-20210531172328536

image-20210531172338332

image-20210531172347432

正规式中: * 表示 重复若干次(包括0次) | 表示 或

确定有限自动机 :对每一个可能的输入只有一个状态的转移

多媒体

image-20210531172404281

1B = 8b 1KB = 1024B 1MB = 1024KB 1GB = 12024MB

表示媒体

为了传输感觉媒体而人为研究出来的媒体,借助于此种媒体,能够有效地存储感觉媒体或将感觉媒体从一个地方传送到另一个地方,如 语言编码、电报码、条形码等

表现媒体

用于通信中使电信号和感觉媒体之间产生转换用的媒体,如输入、输出设备,包括键盘、鼠标、显示器、打印机等;

信号数字化

image-20210531172423841

其他

image-20210531172434955

About Animals

发表于 2020-01-27 | 分类于 English
字数统计 1.2k 字 | 阅读时长 6 分钟

Informed that you have a vacancy for a student to serve as the spokesman for animals, I cannot resist my inner excitement,hoping to seize the opportunity to do something for animals .

得知你们有一个空缺给学生充当动物发言人,我抑制不住内心的激动,想要抓住这次机会为动物做些什么。

Unfortunately,such a rare species is now faced with the danger of being extinct

不幸的是,这种稀有物种现在正频临灭绝的危险。

What I am eager to do is to raise people’s awareness of animal protection and appeal to more people to care for our earth companies.

我想要做的是提高人们保护环境的意识,呼吁更多的人关心我们地球的伴侣。

Though the world is advocating protecting the environment all the time and a lot of effort has been made, still some animals are being hunted by the greedy humans. As the saying that no trade, no kill, for the components of some animals’ skin make up of the luxury products, so the ivory and skin of crocodile are always the hottest stuff in the market. Some businessmen go on their illegal trade for the purpose of making more profit.

尽管全世界一直都在提倡保护环境,也做出了很多努力,但是仍然有些贪婪的人去猎杀一些动物。就像那句话说的,没有买卖,就没有杀害。因为一些动物的皮肤成分是奢侈品的材料,所以象牙和鳄鱼皮在市场上一直很热门。一些商人进行非法贸易的目的就是获取更多的利润。

The profit is the everlast motivation for people to kill animals. Though some laws have carried out to protect the animals, it can’t stop the greedy people. The most efficient way is to advocate people refuse to buy relative products. The combination of the law and people’s act can protect the animals thoroughly.

利润是人们杀害动物永恒的动力。尽管颁布了一些保护动物的法律,但是也无法阻止那些贪婪的人。最有效的方法是提倡人们拒绝购买与之有关的产品。法律和人们的行动可以彻底保护动物。

We condemn such behavior that killing the animals. Protecting the animals is everybody’s job.

我们谴责杀动物的行为。保护动物,人人有责。

It is my belief that all human beings can make a difference together.

我相信,所有人团结起来就会有很大的不同。

We should Try our best to Protect Wild Animals

Recently, our effect to protect wild animals was questioned by the public, because the news is reporting that more and more wild animals was killed by human beings, especially the very perious one. Innocent wild animals was dead for their meat, fur and tooth, it was just used to fill up human beings` desire. In my point of view, the wild animals were protected effective still has a long way to go.

Firstly, people should try harder to make a better place for animals. The land where the wild animals live was deprived by human being. For hundreds years, people cut the trees to make land for their living and farming, wild animals’ homeland was took, they have no choice but run away. What is more, human beings break the food link in nature; wild animals can not find enough food to keep alive. There is no doubt that the destruction of nature has become a big problem to kill wild animal, for instance, a polar bear was starved to death because of the greenhouse effect, the ice was melt away, the seadog and fish that polar bear like to eat are far away from them. For those reasons, people should protect the environment first if they want to protect wild animals effectively.

Second, no buying, no killing. Some rich people like to wear fur coat, they treat it as a status symbol, especially for rich woman, they think animal fur is beautiful and warm, they will never consider that the fur they wear for years is the skin that animal wear for their whole life, and they knew how bloody when people took the fur from animal but they choose to ignore it. If people do not buy the fur and tooth, lots of wild animals will be survived.

In sum, wild animals were our friends, we should give back and protect their homeland, and stop buying the their body part, or our further generations will see them in zoo and museum.

An Appeal for Animal Protection

Since we go to school, we receive the education that animals are our friends that we should protect them. If they die out from the earth, then human won`t live long, either.

Animals are part of nature, so is human being. In the long history, human conquers the nature and creates the greatest civilization. It seems that we are in the dominant situation of nature. while the fact is not. When we receive the punishment from nature, we start to realize our fault. For example, nature punishes us with all kinds of disasters, the earthquake, the disease and so on. We are just a small part in front of the great nature, so the only way out is to get along well with it.

Human killed many animals for chasing profits, but now the price is obvious. The air we breath is no longer clean and have damaged our health greatly. In fact, the way we treat decide our further fate. If they disappear, then we won`t live long.

The government should make some laws and regulations. The one who kills animals should be punished, and there needs more laws to punish these bad people. Besides, the ordinary people also have the duty to make contribution to protecting the animals and the conservation area. For instance, we should not throw rubbish away and call on people around us to pay attention to protect environment. The small things we do will make a big difference in helping maintain the habitat.

Animals have their lives and should be respected. They and human beings are part of nature. We live and die together, so it is our duty to protect animals. We need to create a harmonious society.

About Disease

发表于 2020-01-27 | 分类于 English
字数统计 387 字 | 阅读时长 2 分钟

Informed that you have a vacancy for a student to serve as the spokesman for animals, I cannot resist my inner excitement,hoping to seize the opportunity to do something for animals .

得知你们有一个空缺给学生充当动物发言人,我抑制不住内心的激动,想要抓住这次机会为动物做些什么。

Unfortunately,such a rare species is now faced with the danger of being extinct

不幸的是,这种稀有物种现在正频临灭绝的危险。

How to keep fit?

Keeping fit is the dream of everyone. Nowadays, with the improvement of living standard and the stress in their life and work, more and more people are in the state of unhealthy. So, it arouses the problem. How to keep fit? I believe different people have different methods. For me, I think there are two aspects that we need to pay attention to.

Firstly, exercise is the most important. Anyone wants to have a healthy body should do exercise often. Through exercise, people can strengthen their body, keeping illness from them. Having a healthy body, they can have enough power to work, to face the difficulties in their life. Otherwise, if they are in bad health, they always have no power to do useful things. So, doing exercise frequently is helpful.

Secondly, keeping a balanced diet is also necessary. As people’s life is getting better and better, they have more choices for food. They begin to dietary bias, which makes the nutrition in their body in out-off-balance. So they start to have a variety of illness, such as, fat, diabetes mellitus, angiocardiopathy and so on. Thus, it is necessary for people to eat healthy food to keep nutrition balance, such as, milk, fruit, vegetables and suitable meat.

Keeping fit is not a dream. If people can follow the above suggestions, I am sure that everyone will have a different life, which is a better life. Let’s join hands to work for our health together.

An Appeal for Animal Protection

Since we go to school, we receive the education that animals are our friends that we should protect them. If

About English

发表于 2020-01-27 | 分类于 English
字数统计 985 字 | 阅读时长 5 分钟

议论文之利弊型

​ Nowadays, there is a widespread concern over (the issue that)作文题目. In fact, there are both advantages and disadvantages in 题目议题_. Generally speaking, it is widely believed there are several positive aspects as follows. Firstly, _优点一__. And secondly _优点二___.

  Just As a popular saying goes, “every coin has two sides”, 讨论议题____ is no exception, and in another word, it still has negative aspects. To begin with, _缺点一____. In addition, 缺点二__.

  To sum up, we should try to bring the advantages of 讨论议题__ into full play, and reduce the disadvantages to the minimum at the same time. In that case, we will definitely make a better use of the __讨论议题_.

警示性句型

​ As the issue of census plays such an important role both in our society and life, due attention should be paid from the general public as well as the government.

  Our society is no longer prepared to tolerate unnecessary cruelty to animals for science and entertainment. If we continue our crimes against these creatures, we will be remembered as cruel and inhuman by the generations of the future.

  Not surprisingly … demands/ requires/ deserves immediate/ serious attention/ consideration.

观点性句型

​ As far as I am concerned, I hate the terrible dirt and noise in the city. So, given the chance, I would prefer to live peacefully in the country.

  In my opinion, the real implication of the author is that …

  Personally, I prefer to …

  In my point of view, I think …

  From my personal point of view, I agree with the opinion that …

号召性句型

​ It is high time that parents, educators and the government made combined efforts to put an end to this situation.

  It is high time that broadcasters provided public messages on TV screens that would warn viewers about the potentially harmful effects of viewing televised violence.

  From now on, let us take actions to heal our globe, and clean up our environment! Some day, peoples in all nations will be enveloped with the blue sky and green lands.

  It is prime time that we put considerable / great/ special emphasis on …

话题型模版

Nowadays, there are more and more [某种现象] in [某种场合]. It is estimated that [相关数据]. Why have there been so many [某种现象]? Maybe the reasons can be listed as follows. The first one is [原因一]. Besides, [原因二]. The third one is [原因三]. To sum up, the main cause of [某种现象] is due to [最主要原因]. It is high time that something were done upon it. For one thing, [解决办法一]. On the other hand, [解决办法二]. All these measures will certainly reduce the number of [某种现象].

说明原因型

Nowadays ,there are more and more XX in some big cities . It is estimated that ( 1 ). Why have there been so many XX ? Maybe the reasons can be listed as follows.The first one is that ( 2 ) .Besides,( 3 ) . The third reason is ( 4 ).To sum up ,the main cause of XX is due to ( 5 ) .

  It is high time that something were done upon it. For one thing ,( 6 ).On the other hand ,( 7 ). All these measures will certainly reduce the number of XX .

  注释:

  (1)用具体数据说明XX现象(2)原因一(3)原因二(4)原因三(5)指出主要原因

  (6)解决建议一 (7)解决建议二

  Generation gap between parents and children

  Nowadays , there are more and more misunderstanding between parents and children which is so- called generation gap . It is estimated that (75 percentages of parents often complain their children’s unreasonable behavior while children usually think their parents too old fashioned).

  Why have there been so much misunderstanding between parents and children?Maybe the reasons can be listed as follows . The first one is that ( the two generations,having grown up at different times, have different likes and dislikes ,thus the disagreement often rises between them) . Besides(due to having little in common to talk about , they are not willing to sit face to face ) . The third reason is (with the pace of modern life becoming faster and faster , both of them are so busy with their work or study that they don’t spare enough time to exchange ideas ).To sum up ,the main cause of XX is due to ( lake of communication and understanding each other) .

  It is high time that something were done upon it. For one thing (children should respect their parents ).On the other hand ,( parents also should show solicitue for their children). All these measures will certainly bridge the generation gap .

展望未来型

​ With the rapid advances of _ in recent years, _has__________(引出现象). However, ___ has________________, as____________(提出问题). As a result, _has ____(指出影响).  The effects ___ has produced on____________ can be boiled down to two major ones.

  First , __(影响一). More importantly, ____(影响二). Hence, I believe that we will see a ____(提出展望)/ Nevertheless, I do not think we will see a __(或反面展望).

  There are numerous reasons why , and I would like to explore a few of the most important ones here. The first is that the more(比较级)_, the more (比较级). In addition, we all agree that________________________(第二个原因).

leetcode之258. 各位相加

发表于 2019-04-27 | 分类于 leetcode
字数统计 143 字 | 阅读时长 1 分钟

题目描述:

给定一个非负整数 num,反复将各个位上的数字相加,直到结果为一位数。

示例:

1
2
3
4
> 输入: 38
> 输出: 2
> 解释: 各位相加的过程为:3 + 8 = 11, 1 + 1 = 2。 由于 2 是一位数,所以返回 2。
>

进阶:
你可以不使用循环或者递归,且在 O(1) 时间复杂度内解决这个问题吗?

阅读全文 »

剑指Offer之正则表达式匹配

发表于 2019-04-18 | 分类于 剑指Offer
字数统计 834 字 | 阅读时长 3 分钟

题目描述:

请实现一个函数用来匹配包括’.’和’‘的正则表达式。模式中的字符’.’表示任意一个字符,而’‘表示它前面的字符可以出现任意次(包含0次)。 在本题中,匹配是指字符串的所有字符匹配整个模式。例如,字符串”aaa”与模式”a.a”和”abaca”匹配,但是与”aa.a”和”ab*a”均不匹配

阅读全文 »
12…16
晴宝宝

晴宝宝

151 日志
10 分类
18 标签
GitHub
© 2017 - 2021 晴宝宝
由 Hexo 强力驱动
主题 - NexT.Muse