JavaScript高级_01day


内容

js也是面向对象的语言。

这里的js高级就是es6+,包含了es6以上的语法。

801

作用域

作用域被分为全局作用域和局部作用域,局部作用域包含函数作用域和块级作用域。

局部作用域

函数作用域

802

1-函数的参数也是函数内部的一个局部变量

2-函数执行完毕,里面的变量实际被清空了。

3-var:函数作用域;存在变量提升;可重复定义;在最外层声明的变量作为window的属性。(在函数内声明的,捕获作为window的属性)

块级作用域

803

1-var声明的变量,不存在块级作用域,也就是说在块级{}比如for和if里面用var声明变量,外面也是可用访问到的。

全局作用域

804

1-不声明的变量,会被视为全局变量

2-写在最外层的是全局变量,当有两个script标签,其实也可以访问到,如下代码,是可用正常输出i的

<body>
    <script>
        let i =1
    </script>
    <script>
        console.log(i)
    </script>
</body>

作用域链

805

1-很简单就是一个就近原则,从使用的地方,逐步向外查找声明,先找到谁声明了就是什么作用域,上图a的作用域就是f()中的函数作用域。

2-作用域链就是一种变量查找机制。

js垃圾回收机制

垃圾回收机制是了解这种回收机制的执行过程,为闭包做铺垫。

什么是垃圾回收机制

806

内存泄漏不是说一些东西泄露出来了。而是内存垃圾没有及时得到释放。

内存的生命周期

807

分配—–使用——回收

for (let i = 1; i <= 3; i++) {

   }
   let num = 10
   function fn() {
     const str = 'andy'
     console.log(str)
   }
   fn()
   fn()
   fn()
 </script>

如上我调用三次fn(),不就声明了三次str吗?为什么不冲突?

其实第一次str用完后,这个局部变量就被垃圾回收器自动回收了,第二次调用的时候,第一个已经没了,

垃圾回收的算法说明

引用计数法

808

809

如图当有两个指向堆,那么就有两个应用,当person=1;p=null后,就没有指向了,那么引用为0,就会回收。

当嵌套引用时,会内存泄漏

810

如上,当这个函数执行完毕后,o1和o2因为是局部变量会被回收,但是内部堆里面的两个指向引用都为1,则不会被回收,导致内存泄漏。

标记清除法

811

812

先扫描内存中的对象,然后从根部global(就是全局对象一般为window)出发寻找,找到的的就有用,找不到的就说明无用了,清除掉。

还是那个对象的相互嵌套引用,但是当函数执行完毕后,o1和o2已经被回收掉了,虽然里面还有嵌套,但是已经无法从根部访问到它们,所以会被标记清除,就不会再内存泄漏了。

闭包

813

简单来说闭包就是,一个外函数包含了一个内函数,内函数还使用了外函数的变量。

814

我们可以看到,闭包被叫做closure闭合的,local是局部的,global是全局的。

误区:闭包并不是这个外函数outer,而是由内函数和外函数的变量组成。

闭包就是能够读取其他函数内部变量的私有变量

搞个闭包的概念是吃饱了撑着?它有什么作用?

闭包能让外部访问到函数内部的变量。

 // 简单的写法
// function outer() {
//   let a = 10
//   function fn() {
//     console.log(a)
//   }
//   fn()
// }
// outer()


// 常见的闭包的形式   外部可以访问使用 函数内部的变量
// function outer() {
//   let a = 100
//   function fn() {
//     console.log(a)
//   }
//   return fn
// }


// outer()   ===  fn   ===  function fn() {}
// const fun = function fn() { }

// // 常见的写法2
// function outer() {
//   let a = 100
//   return function () {
//     console.log(a)
//   }
// }
// const fun = outer()
// fun() // 调用函数
// 常见的写法3
// function outer() {
//   let a = 100
//   return function () {
//     return a
//   }
// }
// // console.log(outer())
// const fun1 = outer()
// // 调用函数
// console.log(fun1())

如上,其实就是重新调用了一次内函数,实现了使用外函数的变量。

问题

如果闭包就是能够读取其他函数内部变量的变量值,那么为什么不直接return变量?如下

function f1(){
    var n=123;
    return n;
}
x=f1();
alert(x);  ///123

因为这里拿到的是一个值,而闭包还有很多功能

  1. 直接 return 返回的是变量值,闭包返回的是执行环境;
  2. 闭包不是为了让函数外部拿到内部变量。而是为了得到的私有变量不被随意修改
  3. return 出来的是一个值,不是变量本身,此处的 return 是取得私有变量值的一种方法,跟闭包没有严格关系;
  4. 闭包作为一个可以访问函数内部变量的函数,更多的是可以在其中添加其他的条件,过滤无效的值。如果直接return 变量,然后赋值的话,还要在后续去判断这个值的有效性。

参考链接https://segmentfault.com/q/1010000006178363

所以如果直接返回拿到的只是一个值,不是私有变量;而闭包是通过重新访问内函数,达到访问外函数私有变量的效果,然后添加其他条件,过滤无效的值,如果直接返回变量值,那么无法判断这个值的合法性。

闭包的应用,统计函数调用的次数

815

如上,如果直接这样写,这个count是全局变量,就容易会被篡改

就像上面提到的直接返回return变量,它得到的只是一个值赋予一个变量,如果我们是去利用这个变量去做一个统计,那么它是可以被随意修改的,但是使用闭包的方法,就不会被随意篡改。

闭包可能引起的问题

闭包可能会引起内存泄漏,因为当那个私有变量不需要再被使用,但是如上result—-fn—fun—count,这个count是能被根部找到的,所以标记清除法和引用计数法的回收机制,都无法回收这个count,则导致了内存泄漏。

变量提升

1-变量提示只有var定义的变量才会有,let和const是没有的,它的机制是在代码执行前,先去查找当前作用域下所有的var声明的变量,然后提升到当前作用域的最前面。

2-只提升声明,不提升赋值

console.log(num + '件')
var num = 10

上面这段代码打印的是undefined件,而不是10件

因为它等同于下面代码,只提升了声明,但不提升赋值。

var num
console.log(num + '件')
num = 10

误区:

我以前的理解是var会直接提升到所有代码的最上面,自然以为它变成了全局作用域,其实它只能提升到当前作用域,在一个函数中使用var,也只是把声明提升到函数所有代码的最上面,不会提升到函数外面。

<script>
    let num = 1
    function fn() {
         console.log(num)
         var num = 10
    }
</script>

等于

<script>
    let num = 1
    function fn() {
         var num
         console.log(num)
         num = 10
    }
</script>

函数进阶

函数提升

函数提升即,函数其实在声明之前就可以被调用。如下

fn()
function fn(){
    console.log('函数提升')
}

这段代码是可以正常打印的,但是我在声明前就使用了它

执行机制

只提示函数声明,不提升函数调用,把函数声明提升到当前作用域的最前面。

函数表达式必须先声明后调用如下这一段代码会报错

816

因为这种函数表达式的声明方法,就没有函数提升,它是一种变量赋值,有变量提升,var fun会被提升到当前作用域最前面,但是后面的赋值(function的内容),是不提升的,自然无法运行。

函数参数

函数参数有三种,默认参数,动态参数,剩余参数。

箭头函数

动态参数

动态参数,简而言之就是当不知道用户要传递多少个参数进来,如需求:不管用户传递多少个值,都要累加,这种情况下我们就无法写参数来接收,只能通过动态参数,通过动态参数arguments这个函数内部伪数组变量来实现。

<script>
  function getSum() {
    // arguments 动态参数 只存在于 函数里面
    // 是伪数组 里面存储的是传递过来的实参
    // console.log(arguments)  [2,3,4]
    let sum = 0
    for (let i = 0; i < arguments.length; i++) {
      sum += arguments[i]
    }
    console.log(sum)
  }
  getSum(2, 3, 4)
  getSum(1, 2, 3, 4, 2, 2, 3, 4)
</script>

注意这个argument只在当前函数内有效,在外面使用打印是打印不了的

剩余参数

剩余参数可以把多余的参数写入一个数组,如下代码

<script>
  function getSum(a, b, ...arr) {
    console.log(arr)  // 使用的时候不需要写 ...
  }
  getSum(2, 3)
  getSum(1, 2, 3, 4, 5)
</script>

其中a,b是变量,当用户传递过来的参数超过两个,则把多余的写入到arr数组中(arr这个名字自己随便设置,这个数组只能在当前函数使用)

注意:arr数组在使用中,不需要加…

剩余参数和动态参数的区别

1-它们都能实现多个不确定参数的传入使用

2-动态参数使用的是argument伪数组,剩余参数是自己设置的数组名称,它是真数组

3-开发中建议使用剩余参数,因为箭头函数中没有argument。

展开运算符

在函数参数里面的…是剩余参数,在数组中使用的叫展开运算符。

817

如上,它可以把数组展开,那有什么用呢?

1-平时我们判断数组里的值的最大值和最小值,是写一个for循环去慢慢判断,当有了展开运算符,我们就可以把这个数组展开当做多个数字去使用Math对象提供的方法如max()和min()去判断。

2-我们还可以合并数组,把两个数组展开,然后按照正常赋值给一个数组。

<script>
    const arr1 = [1, 2, 3]
    // 展开运算符 可以展开数组
    // console.log(...arr)

    // console.log(Math.max(1, 2, 3))
    // ...arr1  === 1,2,3
    // 1 求数组最大值
    console.log(Math.max(...arr1)) // 3
    console.log(Math.min(...arr1)) // 1
    // 2. 合并数组
    const arr2 = [3, 4, 5]
    const arr = [...arr1, ...arr2]
    console.log(arr)

  </script>

箭头函数

基本语法

箭头函数属于表达式函数,没有函数提升。

818

<script>
    //  1-原函数表达式
    const fn = function(){
        console.log('原函数表达式')
    }
    fn()


    // 2-箭头函数
    const fn1 = () => {
        console.log('箭头函数')
    }
    fn1()


    // 3-箭头函数当只有一个参数的时候,小括号可以省略,注意没有参数的话要写小括号
    const fn2 = x => {
        console.log(x)
    }


    fn2(2)
    //4-只有一行代码可以省略大括号
    const fn3 = x => console.log(x)
    fn3('只有一行代码可以省略大括号')

    //5-当只有一行return代码可以写成
    const fn4= (x, y) => x + y
    console.log(fn4(1,2))
    </script>

819

以前的去除实际默认事件就有箭头函数的写法,比普通写法简洁很多。

6-箭头函数还可以直接返回对象

820

因为对象是大括号,函数体也是大括号,所以把函数体的大括号换成了小括号

箭头函数参数

821

箭头函数this

822

箭头函数的this不是以前的谁调用就指向 谁,箭头函数没有自己的this,会沿用作用域链上一层的this

<script>
    // 以前this的指向:  谁调用的这个函数,this 就指向谁
    // console.log(this)  // window
    // // 普通函数
    // function fn() {
    //   console.log(this)  // window
    // }
    // window.fn()
    // // 对象方法里面的this
    // const obj = {
    //   name: 'andy',
    //   sayHi: function () {
    //     console.log(this)  // obj
    //   }
    // }
    // obj.sayHi()

    // 2. 箭头函数的this  是上一层作用域的this 指向
    // const fn = () => {
    //   console.log(this)  // window
    // }
    // fn()
    // 对象方法箭头函数 this
    // const obj = {
    //   uname: 'pink老师',
    //   sayHi: () => {
    //     console.log(this)  // this 指向谁? window,它沿用了上一层的this
    //   }
    // }
    // obj.sayHi()

    const obj = {
      uname: 'pink老师',
      sayHi: function () {
        console.log(this)  // obj
        let i = 10
        const count = () => {
          console.log(this)  // obj  沿用上一层的this
        }
        count()
      }
    }
    obj.sayHi()

  </script>

823

在事件中的匿名函数,还是不建议使用箭头函数。

解构赋值

数组解构

824

它的作用就是批量声明赋值,把数组里面的值赋予给声明的不同的变量

<script>
   // const arr = [100, 60, 80]
   // 数组解构 赋值
   // // const [max, min, avg] = arr
   const [max, min, avg] = [100, 60, 80]
   // // const max = arr[0]
   // // const min = arr[1]
   // // const avg = arr[2]
   console.log(max) // 100
   console.log(avg) // 80
   // 交换2个变量的值
   let a = 1
   let b = 2;  // 这里必须加上分号
   [b, a] = [a, b]
   console.log(a, b)
 </script>
<script>
  // const pc =['小米','海尔','联想','方正']
  // const [xm,hr,lx,fz] = ['小米','海尔','联想','方正']
  // console.log(xm,hr,lx,fz)
  function getValue(){
      return[100,60]
  }
  let[max,min] = getValue()
  console.log(max,min)
  </script>

注意解构的时候也要加上let或者const,否则变量就会变成未声明的,就会变成全局变量。

js必须加分号的两种情况

一种是立即执行函数,一种是数组的直接使用。

const arr = [1, 2, 3]
  const str = 'pink';
  [1, 2, 3].map(function (item) {
    console.log(item)
  })

如上,如果不加分号,编译器会人为这个[1,2,3]不应该出现在这,会把这一行语句上移,就变成了

const arr = [1, 2, 3]
  const str = 'pink' [1, 2, 3].map(function (item) {
    console.log(item)
  })

所以就会报错。

数组解构细节和问题

// 1. 变量多, 单元值少 , undefined
  // const [a, b, c, d] = [1, 2, 3]
  // console.log(a) // 1
  // console.log(b) // 2
  // console.log(c) // 3
  // console.log(d) // undefined
  // 2. 变量少, 单元值多
  // const [a, b] = [1, 2, 3]
  // console.log(a) // 1
  // console.log(b) // 2

我们也可以用剩余参数来解决数组解构多余单元值的问题。

剩余参数不止是能用于函数,也能用于数组结构

// 3.  剩余参数 变量少, 单元值多
 // const [a, b, ...c] = [1, 2, 3, 4]
 // console.log(a) // 1
 // console.log(b) // 2
 // console.log(c) // [3, 4]  真数组
 // 4.  防止 undefined 传递
 // const [a = 0, b = 0] = [1, 2]
 // const [a = 0, b = 0] = []
 // console.log(a) // 1
 // console.log(b) // 2
 // 5.  按需导入赋值
 // const [a, b, , d] = [1, 2, 3, 4]
 // console.log(a) // 1
 // console.log(b) // 2
 // console.log(d) // 4

 // const arr = [1, 2, [3, 4]]
 // console.log(arr[0])  // 1
 // console.log(arr[1])  // 2
 // console.log(arr[2])  // [3,4]
 // console.log(arr[2][0])  // 3

 // 多维数组解构
 // const arr = [1, 2, [3, 4]]
 // const [a, b, c] = [1, 2, [3, 4]]
 // console.log(a) // 1
 // console.log(b) // 2
 // console.log(c) // [3,4]


 const [a, b, [c, d]] = [1, 2, [3, 4]]
 console.log(a) // 1
 console.log(b) // 2
 console.log(c) // 3
 console.log(d) // 4

对象解构

826

注意对象解构的变量必须和属性名相同,它们提供名字来对应上。

为什么数组解构不用呢?因为数组是有序的,对象是无序的,无序就得用名字来对应

// 对象解构
    const obj = {
    uname: 'pink老师',
      age: 18
    }
   // obj.uname
   // obj.age 
   // const uname = 'red老师'
   // 解构的语法
    const { uname, age } = {age: 18, uname: 'pink老师' }

当然这个变量名字也是可以改的,不然如果原先已经声明过了,岂不是这个对象就不能解构了?但是有一定的规则。(旧变量名字:新变量名字)

const { uname: username, age } = { uname: 'pink老师', age: 18 }

这样如果原先就声明了uname变量,在对象解构的时候就能用username代替

数组对象的解构

const pig = [
     {
       uname: '佩奇',
       age: 6
     }
   ]
   const [{ uname, age }] = pig
   // const [{ uname }] = pig 你不一定要获取完,你就获取uname也行,那么age就没值 
   console.log(uname)
   console.log(age)

当数组里面有两个对象,变量名放在不同大括号中,也用逗号隔开,如下图

<script>
const goods =[
    {
        goodsName:'xiaomia',
        price:9999
    },
    {
        userName:'zdq',  
        age:21
    }
]
const[{goodsName,price},{userName,age}] = goods
console.log(goodsName,price,userName,age)

</script>

多级对象解构

对象包对象

827

数组包对象,对象又包对象

const person = [
    {
      name: '佩奇',
      family: {
        mother: '猪妈妈',
        father: '猪爸爸',
        sister: '乔治'
      },
      age: 6
    }
  ]
  const [{ name, family: { mother, father, sister } }] = person
  console.log(name)
  console.log(mother)
  console.log(father)
  console.log(sister)

真实案例JSON数据的使用

<script>
    // 1. 这是后台传递过来的数据
    const msg = {
      "code": 200,
      "msg": "获取新闻列表成功",
      "data": [
        {
          "id": 1,
          "title": "5G商用自己,三大运用商收入下降",
          "count": 58
        },
        {
          "id": 2,
          "title": "国际媒体头条速览",
          "count": 56
        },
        {
          "id": 3,
          "title": "乌克兰和俄罗斯持续冲突",
          "count": 1669
        },

      ]
    }

    // 需求1: 请将以上msg对象  采用对象解构的方式 只选出  data 方面后面使用渲染页面
    // const { data } = msg
    // console.log(data)
    // 需求2: 上面msg是后台传递过来的数据,我们需要把data选出当做参数传递给 函数
    // const { data } = msg
    // msg 虽然很多属性,但是我们利用解构只要 data值
    function render({ data }) {
      // const { data } = arr
      // 我们只要 data 数据
      // 内部处理
      console.log(data)

    }
    render(msg)

    // 需求3, 为了防止msg里面的data名字混淆,要求渲染函数里面的数据名改为 myData
    function render({ data: myData }) {
      // 要求将 获取过来的 data数据 更名为 myData
      // 内部处理
      console.log(myData)

    }
    render(msg)

  </script>

数组的forEach方法和map方法遍历数组

<script>
    // forEach 就是遍历  加强版的for循环  适合于遍历数组对象
    const arr = ['red', 'green', 'pink']
    const result = arr.forEach(function (item, index) {
      console.log(item)  // 数组元素 red  green pink
      console.log(index) // 索引号
    })
    // console.log(result) 这里是undefined 因为forEach根本没有返回值
  </script>

一般匿名函数会用箭头函数代替,如(item,index)=>{}

828

forEach原理https://www.bilibili.com/video/BV154411k7yW?spm_id_from=333.337.search-card.all.click&vd_source=a1105d9022e153cf264a26ab9fa134c6

1-map是返回一个新数组,但是forEach不返回东西。

2-参数有三个,分别是数组当前项值item和数组值的索引index,还有原始数组input,item必须写,其他两个可以不写。

3-forEach通过语句可以改变原数组,map通过语句也不改变原数组,因为它生成了一个新数组,操作也在新数组中操作。

filter方法

filter过滤,筛选

<script>
   const arr = [10, 20, 30]
   // const newArr = arr.filter(function (item, index) {
   //   // console.log(item)
   //   // console.log(index)
   //   return item >= 20
   // })
   // 返回的符合条件的新数组

   const newArr = arr.filter(item => item >= 20)
   console.log(newArr)
 </script>

你可以自己设置函数体内容,返回相应条件的新数组。

833

其中currentvalue必须传入,索引index可以不传入参数

综合案例

832

834

<!DOCTYPE html>
<html lang="en">

<head>
  <meta charset="UTF-8">
  <meta http-equiv="X-UA-Compatible" content="IE=edge">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>商品渲染</title>
  <style>
    * {
      margin: 0;
      padding: 0;
      box-sizing: border-box;
    }

    .list {
      width: 990px;
      margin: 0 auto;
      display: flex;
      flex-wrap: wrap;
    }

    .item {
      width: 240px;
      margin-left: 10px;
      padding: 20px 30px;
      transition: all .5s;
      margin-bottom: 20px;
    }

    .item:nth-child(4n) {
      margin-left: 0;
    }

    .item:hover {
      box-shadow: 0px 0px 5px rgba(0, 0, 0, 0.2);
      transform: translate3d(0, -4px, 0);
      cursor: pointer;
    }

    .item img {
      width: 100%;
    }

    .item .name {
      font-size: 18px;
      margin-bottom: 10px;
      color: #666;
    }

    .item .price {
      font-size: 22px;
      color: firebrick;
    }

    .item .price::before {
      content: "¥";
      font-size: 14px;
    }

    .filter {
      display: flex;
      width: 990px;
      margin: 0 auto;
      padding: 50px 30px;
    }

    .filter a {
      padding: 10px 20px;
      background: #f5f5f5;
      color: #666;
      text-decoration: none;
      margin-right: 20px;
    }

    .filter a:active,
    .filter a:focus {
      background: #05943c;
      color: #fff;
    }
  </style>
</head>

<body>
  <div class="filter">
    <a data-index="1" href="javascript:;">0-100元</a>
    <a data-index="2" href="javascript:;">100-300元</a>
    <a data-index="3" href="javascript:;">300元以上</a>
    <a href="javascript:;">全部区间</a>
  </div>
  <div class="list">
    <!-- <div class="item">
      <img src="" alt="">
      <p class="name"></p>
      <p class="price"></p>
    </div> -->
  </div>
  <script>
    // 2. 初始化数据
    const goodsList = [
      {
        id: '4001172',
        name: '称心如意手摇咖啡磨豆机咖啡豆研磨机',
        price: '289.00',
        picture: 'https://yanxuan-item.nosdn.127.net/84a59ff9c58a77032564e61f716846d6.jpg',
      },
      {
        id: '4001594',
        name: '日式黑陶功夫茶组双侧把茶具礼盒装',
        price: '288.00',
        picture: 'https://yanxuan-item.nosdn.127.net/3346b7b92f9563c7a7e24c7ead883f18.jpg',
      },
      {
        id: '4001009',
        name: '竹制干泡茶盘正方形沥水茶台品茶盘',
        price: '109.00',
        picture: 'https://yanxuan-item.nosdn.127.net/2d942d6bc94f1e230763e1a5a3b379e1.png',
      },
      {
        id: '4001874',
        name: '古法温酒汝瓷酒具套装白酒杯莲花温酒器',
        price: '488.00',
        picture: 'https://yanxuan-item.nosdn.127.net/44e51622800e4fceb6bee8e616da85fd.png',
      },
      {
        id: '4001649',
        name: '大师监制龙泉青瓷茶叶罐',
        price: '139.00',
        picture: 'https://yanxuan-item.nosdn.127.net/4356c9fc150753775fe56b465314f1eb.png',
      },
      {
        id: '3997185',
        name: '与众不同的口感汝瓷白酒杯套组1壶4杯',
        price: '108.00',
        picture: 'https://yanxuan-item.nosdn.127.net/8e21c794dfd3a4e8573273ddae50bce2.jpg',
      },
      {
        id: '3997403',
        name: '手工吹制更厚实白酒杯壶套装6壶6杯',
        price: '100.00',
        picture: 'https://yanxuan-item.nosdn.127.net/af2371a65f60bce152a61fc22745ff3f.jpg',
      },
      {
        id: '3998274',
        name: '德国百年工艺高端水晶玻璃红酒杯2支装',
        price: '139.00',
        picture: 'https://yanxuan-item.nosdn.127.net/8896b897b3ec6639bbd1134d66b9715c.jpg',
      },
    ]

    // 1. 渲染函数  封装
    function render(arr) {
      // 声明空字符串
      let str = ''
      // 遍历数组 
      arr.forEach(item => {
        // 解构
        const { name, picture, price } = item
        str += `
         <div class="item">
          <img src=${picture} alt="">
          <p class="name">${name}</p>
          <p class="price">${price}</p>
        </div> 
        `
      })
      // 追加给list 
      document.querySelector('.list').innerHTML = str
    }
    render(goodsList)  // 页面一打开就需要渲染

    // 2. 过滤筛选  
    document.querySelector('.filter').addEventListener('click', e => {
      // e.target.dataset.index   e.target.tagName
      const { tagName, dataset } = e.target
      // 判断 
      if (tagName === 'A') {
        // console.log(11) 
        // arr 返回的新数组
        let arr = goodsList
        if (dataset.index === '1') {
          arr = goodsList.filter(item => item.price > 0 && item.price <= 100)
        } else if (dataset.index === '2') {
          arr = goodsList.filter(item => item.price >= 100 && item.price <= 300)
        } else if (dataset.index === '3') {
          arr = goodsList.filter(item => item.price >= 300)
        }
        // 渲染函数
        render(arr)
      }
    })
  </script>
</body>

</html>

1-这里面的data-index就是一个自定义属性

2-e.taget.dataset就是自定义的属性的对象

3-e.target.tagName就是记录你点的是什么标签,这里就是A

835


文章作者: 瑾年
版权声明: 本博客所有文章除特別声明外,均采用 CC BY 4.0 许可协议。转载请注明来源 周东奇 !
免责声明: 本站所发布的一切内容,包括但不限于IT技术资源,网络攻防教程及相应程序等文章仅限用于学习和研究目的:不得将上述内容用于商业或者非法用途,否则一切后果请用户自负。本站部分信息与工具来自网络,版权争议与本站无关。您必须在下载后的24个小时之内,从您的电脑中彻底删除上述内容。如有侵权请邮件(jinnian770@gmail.com)与我们联系处理。
  目录