javascript<——>进阶

一、作用域:变量可以被访问的范围

1.局部作用域

1.1函数作用域

在函数内部声明的变量,在函数内部被访问的,外部无法直接访问。

总结:1、函数内部声明的变量,在函数外部无法直接访问

2、函数的参数也是函数内部的局部变量

3、不同函数内部声明的变量无法互相访问

4、函数执行完毕后,函数内部的变量事件被清空

1.2块作用域

有“{}”的包裹的代码称为代码块,内部声明的变量外部将【有可能】无法被访问

###let /const声明的变量产生块作用域,var声明的不会产生块级作用域

2.全局作用域

写在<script>标签的或者写在js文件的,尽可能少声明全局作用域,防止全局变量被污染

3.作用域链

本质是底层的变量查找机制:(1)函数执行时,优先查找当前函数作用域中查找

                                               (2)查不到则依次逐级查找父级作用域直到全局作用域

###子作用域可以访问父作用域,但是父作用域无法访问子作用域

###相同作用域链按从小到大规则查找变量

4.js垃圾回收机制(GC)

4.1内存生命周期

(1)分配内存:声明变量、函数、对象时,系统自动分配内存

(2)内存使用:读写内存,使用函数,变量

(3)内存回收:使用完毕,垃圾回收机制自动回收不在使用的内存

###全局变量一般不会回收(关闭页面回收),

(4)内存泄漏:程序中分配的内存由于某种原因,程序无法释放或者未释放。

❤️算法说明:

1.栈(操作系统):由操作系统自动分配释放函数的参数值,局部变量等,基本数据类型放到栈里面

2.堆(操作系统):一般由程序员分配释放,不释放,则垃圾回收机制回收。复杂数据类型放到堆里面

4.2垃圾回收算法

(1)引用计数法

跟踪记录被引用的次数,引用一次则记录一次,多次引用会累加++,减少一次引用就--

次数为0,回收

缺点:嵌套使用。两个对象相互引用,尽管它们不再使用,GC不会进行回收,内存泄漏

(2)标记清除法

核心:1.将“不再使用的对象”定义为“无法达到的对象”。

           2.从根部出发定时扫描内存中的对象。凡是从根部到达的对象,都是还需要使用的。

           3.无法触及的对象被标记为不再使用,稍后进行回收。

5.闭包

一个函数对周围状态的引用捆绑在一起,内层函数中访问到其外层函数作用域,会产生内存泄漏

闭包=内层函数+外层函数的变量

<script>
function outer(){
    let a = 1
        function fn(){
        console.log(a)
    }
    return fn
}
const fun = outer()
fun()
</script>
//外层函数可以访问内层函数的变量
//const fun = outer() = fn = function fn(){}
//调用fun()

作用:闭包的应用:实现数据的私有,封闭数据。做个统计函数调用次数,调用一次就++

外部可以访问函数内部的变量

问题:内存泄漏

<script>
     function count(){
        let i = 0
            function fn(){
                i++
                console.log(`函数被调用了${i}次`)  
        }
        return fn
}
const fun = count()

</script>

6.变量提升

把var声明的变量提升到 当前作用域的最前面。

只提升声明,不提升赋值

总结:

  1. 变量在未声明即被访问时会报语法错误

  2. 变量在声明之前即被访问,变量的值为 undefined

  3. let 声明的变量不存在变量提升,推荐使用 let

  4. 变量提升出现在相同作用域当中

  5. 实际开发中推荐先声明再访问变量

<script>
function(){
    console.log(num)
    var num = 10
}
fun()

等价于
function(){
    var num
    console.log(num)
    num = 10
}
fun()
</script>

二、函数进阶

1.函数提升(只提升声明,不提升调用)

声明之前调用

函数表达式 必须先声明和赋值,后调用,否则报错。

总结:1、函数提升能够使函数的声明调用更灵活

2、函数表达式不存在提升的现象

3、函数提升出现在相同作用域当中

2.函数参数

###默认值

总结:1、声明函数时为形参赋值即为参数的默认值

2、如果参数未自定义默认值时,参数的默认值为undefined

3、调用函数时没有差引入对应实参时,参数的默认值被当做实参传入

2.1动态参数

arguments是一个伪数组 只存在于 函数里面

作用是动态的获取函数的实参

可以通过for循环依次得到传递过来的参数

2.2剩余参数

(1)...是语法符号,置于最末函数形参之前,用于获取多余的实参

(2)借助...获取的剩余参数,是个真数组

  <script>
     function getSum(a,b,...arr){
      console.log(arr);
      
    }
      getSum(2,3,4,5,6,7)
      getSum(1,2,3,4,5,6,7,8,9)
  </script>

##使用场景:用于获取多余的实参

##和动态参数的区别是什么?

        动态参数是伪数组;

        剩余参数是真数组

❤️展开运算符【数组中使用】:将数组的数字全展开

      const arr = [1,2,3,4,5]
      console.log(...arr)
      console.log(Math.max(...arr));//求最大值
      console.log(Math.min(...arr));//求最小值

合并数组:

      const arr1 = [1,2,3]
      const arr2 = [4,5,6]
      const arr3 = [...arr1,...arr2]
      console.log(arr3);// [1,2,3,4,5,6]

3.箭头函数

(1)基本语法

const fn=()=>{

}

##只有一个形参时,可以省略小括号

const fn = x=>{


}

##只有一行代码时,可以省略大括号和return

const fn = (x,y) =>x+y

console.log(fn(1,2))

##箭头函数可以直接返回同一个对象

const fn1 = uname=>({uname:name})

console.log('刘德华')

总结:

1、箭头函数属于表达式函数,因此不存在函数提升

2、箭头函数只有一个参数时可以省略圆括号()

3、箭头函数函数体只有一行时代码可以省略花括号{},并自动作为返回值被返回

(2)箭头函数参数

只有剩余参数,没有动态参数arguments

(3)箭头函数this

箭头函数不会创建自己的this,从自己的作用域链的上一层沿用this

三、解构赋值

1.数组解构 :

将数组的单元值快速批量赋值给一系列变量的简介语法

<script>
    // 以下代码是否会正常执行,如果不会,如何改正
    const [min, avg, max] = [100,200,300];//必须加分号
    (function() {
      console.log(min);
    })();
  </script>
<script>

    const arr = [60,100,80]
    const [max,min avg] = arr
//将按照顺序赋值给变量
    console.log(max)
</script>

交换变量

<script>
let a = 1
let b = 2;
const [b,a]=[a,b]
console.log(a,b)
</script>
<script>
     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 [a, b, [c, d]] = [1, 2, [3, 4]]
    console.log(a) // 1
    console.log(b) // 2
    console.log(c) // 3
    console.log(d) // 4

</script>

2.对象解构

<script>
   // 对象解构
    const obj = {
      uname: 'pink老师',
      age: 18
    }
    obj.uname
    obj.age 
    //const uname = 'red老师'
    解构的语法
    const { uname, age } = {age: 18, uname: 'pink老师' }
   // 等价于 const uname =  obj.uname
    //要求属性名和变量名必须一致才可以
</script>

3.多级对象解构

<script>
     const pig = {
         name: '佩奇',
         family: {
         mother: '猪妈妈',
         father: '猪爸爸',
         sister: '乔治'
       },
      age: 6
     }
    // // 多级对象解构
     const { name, family: { mother, father, sister } } = pig
</script>

4.多级数组对象解构

 <script>
    //多级对象数组解构
    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)
  </script>

5.多级对象解构案例

 <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选出当做参数传递给 函数
    function render({data}){
      console.log(data)
    }
    render(msg)
 // 需求3, 为了防止msg里面的data名字混淆,要求渲染函数里面的数据名改为 myData
    function render({data:myData}){
      console.log(myData)
    }
</script>

###forEach()方法

用于调用数组的每个元素,并将元素传递给回调函数

注意:

1、forEach主要是遍历数组

2、参数当前数组元素使必须要写的,索引号可选。

  <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)
  </script>

###筛选数组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>

###渲染商品案例

 <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: '99.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',
      },
    ]
    function render(arr) {
      let str = ''
      arr.forEach(item => {
        const { name, price, picture } = item
        str += `
        <div class="item">
          <img src="${picture}" alt="">
         <p class="name">${name}</p>
         <p class="price">${price}</p>
        </div>`
      });
      document.querySelector('.list').innerHTML = str
    }
    render(goodsList)
    document.querySelector('.filter').addEventListener('click', e => {
      const { tagName, dataset } = e.target
      if (tagName == 'A') {
        //console.log(11);
        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>

四、构造函数与数据常用函数

1.深入对象

1.1创建对象三种方式

(1)利用对象字面量创建对象

const 0 = {

        name : 'abc'

}

(2)利用new Object创建对象

const 0 = new Object({name:'abc'})

(3)利用构造函数创建对象

1.2构造函数

一种特殊的函数,主要用来初始化对象。

##使用new关键字的调用函数行为被称为实例化

##实例化构造函数时没有参数可以省略

##构造函数内部无需写return,返回值即新创建的对象

##构造函数内部的return返回值无效,所以不要写return

##new Object()new Date()也是实例化构造函数

两个约定:

<1>命名大写字母开头

<2>它们只能由“new”操作符来执行

<script>
 function Pig(uname,age){
    this.uname = uname
    this.age = age
}

    const p = new Pig('peiqi',6)
    const q = new Pig('qiaozhi',3)
    console.log(p,q)
</script>

❤️实例化执行过程

(1)创建新对象

(2)构造函数this指向新对象

(3)执行函数构造代码,修改this,添加新属性

(4)返回新的对象

1.3实例成员和静态成员

1.3.1实例成员

通过构造函数创建的对象称为实例对象,实例对象中的属性和方法称为实例成员(实例属性和实例方法)

1.3.2静态成员

构造函数的属性和方法被称为静态成员

<script>
    function Pig(name){
        this.name = name
}
Pig.eyes = 2//静态属性
Pig.sayHi = function(){//静态方法
    console.log(this)
}
Pig.sayHi()
console.log(Pig.eyes)//2
</script>

说明:

1.静态成员只能构造函数来访问

2.静态方法中的this指向构造函数

2.内置构造函数

1.Object

.keys(obj):返回所有属性名

.values(obj):获得所有的属性值【返回的是数组】

 const o = { uname: 'pink', age: 18 }
    // 1.获得所有的属性名
    console.log(Object.keys(o))  //返回数组['uname', 'age']
    // 2. 获得所有的属性值
    console.log(Object.values(o))  //  ['pink', 18]

.assign(new,old):对象拷贝

 // 3. 对象的拷贝
    const o = { uname: 'pink', age: 18 }
    // const oo = {}
    // Object.assign(oo, o)
    // console.log(oo)
   //对象中添加属性
    Object.assign(o, { gender: '女' })
    console.log(o)

2.Array

方法作用说明
filter过滤数组返回新数组,返回的是筛选满足条件的数组与数组元素
forEach遍历数组不返回数组,经常用于查找遍历数组元素
map迭代数组返回新数组,返回的是处理后的数组元素,想要使用返回的新数组
reduce累计器返回累计处理的结果,经常用于求和等

join

拼接数组元素拼接成字符串,返回字符串
find查找元素返回符合测试条件的第一个数组,如果没有符合条件的则返回undefined
every检测数组所有元素是否都符合指定条件如果都符合,返回true,否则返回false
sort排序对原数组单元值进行排序
from伪数组转换为真数组返回真数组

<script>
 const arr =  [1,3,5,7,9]
    const result = arr.reduce((a,b)=>(a+b),10)
    console.log(result)//35
</script>

(1)reduce(function(prev,curret){return prev+curret},start)

prev:代表初始值

curret:代表累加的值

start:初始值具体数字

###

1.如果没有初始值,则上一次的值以数组的第一个数组元素的值。

2.每一次循环,把返回值给作为 下一次循环的上一次值。

3.如果有初始值,则起始值作为上一次值。

  <script>
    const arr = [{
      name: '张三',
      salary: 10000
    }, {
      name: '李四',
      salary: 10000
    }, {
      name: '王五',
      salary: 20000
    },
    ]
    const money = arr.reduce((prev, item) => prev + item.salary * 1.3, 0)
    console.log(money)
  </script>

包装类型:

字符串、数值、布尔类型数据是js底层使用Object构造函数“包装”来的,即为包装类型

3.String

.split【‘分隔符’】:把字符串转换成数组(和join相反)

.substring【第一个索引,最后一个索引】截取字符串

.startsWith(检测字符串,【检测位置的索引号】),检测是否以某字符开头

.includes(搜索的字符串,检测的字符串位置索号),判断一的字符串是否包含在另一个字符串中,根据情况返回true或false

.replace(代替字符串)

4.Number

.toFixed:设置保留小数位的长度,四舍五入。

3.综合案例

五、深入面向对象

1.编程思想

(1)面向过程

分析解决问题的步骤,然后用函数一步一步地实现,使用的时候调用

优点:性能高

缺点:没有面向易维护,复用,扩展

(2)面向对象(oop)

优点:易维护、复用、扩展,使系统更加易于维护,更加灵活

缺点:性能低

分解为一个个对象,然后对象之间分工和合作。

###特性:

【1】封装性:封装代码,js面向对象可以通过构造函数实现的封装,同时将变量和函数组合到一起并能通过this实现数据的共享,所不同的是借助构造函数创建出来的实例对象之间互相不影响。

总结:

1、构造函数体现了面向对象的封装特性

2、构造函数实例创建的对象彼此独立,互不影响。

【2】继承性:继承接口

【3】多态性

2.构造函数

存在浪费内存的问题

3.原型对象

构造函数通过原型分配的函数是所有对象所共享的

###js规定,每一个构造函数都有一个prototype属性,指向另一个对象,所以我们也称为原型对象

###这个对象可以挂载函数,对象实例化不会多次创建原型上函数,节约内存

###我们可以把那些不变的方法,直接定义为prototype对象上,这样所有对象的实例就可以共享这些方法

###构造函数和原型对象中的this都指向实例化的对象

原型作用:
(1)共享方法

(2)可以把那些不变的方法,直接定义在prototype对象上

构造函数和原型里面的this指向谁?

指向实例化对象

constructor 属性

在哪里? 每个原型对象里面都有个constructor 属性(constructor 构造函数)

作用:该属性指向该原型对象的构造函数, 简单理解,就是指向我的爸爸,我是有爸爸的孩子

使用场景:

如果有多个对象的方法,我们可以给原型对象采取对象形式赋值.

但是这样就会覆盖构造函数原型对象原来的内容,这样修改后的原型对象 constructor 就不再指向当前构造函数了

此时,我们可以在修改后的原型对象中,添加一个 constructor 指向原来的构造函数。

对象原型

构造函数创建实例对象和含有原型对象(prototype),实例对象的__proto__指向原型对象,实例对象的__protype__和原型对象的constructor指向构造函数。

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

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

注意:

  • __proto__是JS非标准属性

  • [[prototype]]和__proto__意义相同

  • 用来表明当前实例对象指向哪个原型对象prototype

  • __proto__对象原型里面也有一个 constructor属性,指向创建该实例对象的构造函数

原型继承

继承是面向对象编程的另一个特征,通过继承进一步提升代码封装的程度,JavaScript 中大多是借助原型对象实现继承

的特性。

龙生龙、凤生凤、老鼠的儿子会打洞描述的正是继承的含义。

<body>
  <script>
    // 继续抽取   公共的部分放到原型上
    // const Person1 = {
    //   eyes: 2,
    //   head: 1
    // }
    // const Person2 = {
    //   eyes: 2,
    //   head: 1
    // }
    // 构造函数  new 出来的对象 结构一样,但是对象不一样
    function Person() {
      this.eyes = 2
      this.head = 1
    }
    // console.log(new Person)
    // 女人  构造函数   继承  想要 继承 Person
    function Woman() {

    }
    // Woman 通过原型来继承 Person
    // 父构造函数(父类)   子构造函数(子类)
    // 子类的原型 =  new 父类  
    Woman.prototype = new Person()   // {eyes: 2, head: 1} 
    // 指回原来的构造函数
    Woman.prototype.constructor = Woman

    // 给女人添加一个方法  生孩子
    Woman.prototype.baby = function () {
      console.log('宝贝')
    }
    const red = new Woman()
    console.log(red)
    // console.log(Woman.prototype)
    // 男人 构造函数  继承  想要 继承 Person
    function Man() {

    }
    // 通过 原型继承 Person
    Man.prototype = new Person()
    Man.prototype.constructor = Man
    const pink = new Man()
    console.log(pink)
  </script>
</body>

原型链(查找规则)

基于原型对象的继承使得不同构造函数的原型对象关联在一起,并且这种关联的关系是一种链状的结构,我们将原型对象的链状结构关系称为原型链

<body>
  <script>
    // function Objetc() {}
    console.log(Object.prototype)
    console.log(Object.prototype.__proto__)

    function Person() {

    }
    const ldh = new Person()
    // console.log(ldh.__proto__ === Person.prototype)
    // console.log(Person.prototype.__proto__ === Object.prototype)
    console.log(ldh instanceof Person)
    console.log(ldh instanceof Object)
    console.log(ldh instanceof Array)
    console.log([1, 2, 3] instanceof Array)
    console.log(Array instanceof Object)
  </script>
</body>

① 当访问一个对象的属性(包括方法)时,首先查找这个对象自身有没有该属性。

② 如果没有就查找它的原型(也就是__ proto__指向的 prototype 原型对象)

③ 如果还没有就查找原型对象的原型(Object的原型对象)

④ 依此类推一直找到 Object 为止(null)

⑤ __proto__对象原型的意义就在于为对象成员查找机制提供一个方向,或者说一条路线

⑥ 可以使用 instanceof 运算符用于检测构造函数的 prototype 属性是否出现在某个实例对象的原型链上

六、深浅拷贝

只针对引用数据类型

1.浅拷贝

拷贝的是地址

常见方法:

(1)拷贝对象:Object.assgin()/展开运算符{…obj}拷贝对象

(2)拷贝数组:Array/ptotype.concat()或者{…arr}

2.深拷贝

拷贝的是对象,不是地址

2.1递归实现深拷贝

1.深拷贝实现拷贝出来的新对象不会影响旧对象 ,通过函数递归实现

2.普通拷贝的话直接赋值就可以,遇到数组再次调用这个递归函数

3.如果遇到对象,就调用递归函数解决对象,【先array,后对象】  

<body>
  <script>
    const obj = {
      uname: 'pink',
      age: 18,
      hobby: ['乒乓球', '足球'],
      family: {
        baby: '小pink'
      }
    }
    const o = {}
    // 拷贝函数
    function deepCopy(newObj, oldObj) {
      debugger
      for (let k in oldObj) {
        // 处理数组的问题  一定先写数组 在写 对象 不能颠倒
        if (oldObj[k] instanceof Array) {
          newObj[k] = []
          //  newObj[k] 接收 []  hobby
          //  oldObj[k]   ['乒乓球', '足球']
          deepCopy(newObj[k], oldObj[k])
        } else if (oldObj[k] instanceof Object) {
          newObj[k] = {}
          deepCopy(newObj[k], oldObj[k])
        }
        else {
          //  k  属性名 uname age    oldObj[k]  属性值  18
          // newObj[k]  === o.uname  给新对象添加属性
          newObj[k] = oldObj[k]
        }
      }
    }
    deepCopy(o, obj) // 函数调用  两个参数 o 新对象  obj 旧对象
    console.log(o)
    o.age = 20
    o.hobby[0] = '篮球'
    o.family.baby = '老pink'
    console.log(obj)
    console.log([1, 23] instanceof Object)
    // 复习
    // const obj = {
    //   uname: 'pink',
    //   age: 18,
    //   hobby: ['乒乓球', '足球']
    // }
    // function deepCopy({ }, oldObj) {
    //   // k 属性名  oldObj[k] 属性值
    //   for (let k in oldObj) {
    //     // 处理数组的问题   k 变量
    //     newObj[k] = oldObj[k]
    //     // o.uname = 'pink'
    //     // newObj.k  = 'pink'
    //   }
    // }
  </script>
</body>

2.2js库lodash里面cloneDeep

<body>
  <!-- 先引用 -->
  <script src="./lodash.min.js"></script>
  <script>
    const obj = {
      uname: 'pink',
      age: 18,
      hobby: ['乒乓球', '足球'],
      family: {
        baby: '小pink'
      }
    }
    const o = _.cloneDeep(obj)
    console.log(o)
    o.family.baby = '老pink'
    console.log(obj)
  </script>
</body>

2.3JSON序列化

<body>
  <script>
    const obj = {
      uname: 'pink',
      age: 18,
      hobby: ['乒乓球', '足球'],
      family: {
        baby: '小pink'
      }
    }
    // 把对象转换为 JSON 字符串
    // console.log(JSON.stringify(obj))
    const o = JSON.parse(JSON.stringify(obj))
    console.log(o)
    o.family.baby = '123'
    console.log(obj)
  </script>
</body>

七、异常处理

异常处理指预估代码执行过程中可能发生的错误,然后最大程度的避免错误的发生导致整个程序无法继续运行

throw抛异常

1、throw抛出异常信息后,程序也会终止执行

2、throw后面跟的是错误提示信息

3、Error对象配合throw使用,能够设置更详细的错误信息

<script>
  function counter(x, y) {

    if(!x || !y) {
      // throw '参数不能为空!';
      throw new Error('参数不能为空!')
    }

    return x + y
  }

  counter()
</script>

try/catch捕获错误信息

  1. try...catch 用于捕获错误信息

  2. 将预估可能发生错误的代码写在 try 代码段中

  3. 如果 try 代码段中出现错误后,会执行 catch 代码段,并截获到错误信息

<script>
   function foo() {
      try {
        // 查找 DOM 节点
        const p = document.querySelector('.p')
        p.style.color = 'red'
      } catch (error) {
        // try 代码段中执行有错误时,会执行 catch 代码段
        // 查看错误信息
        console.log(error.message)
        // 终止代码继续执行
        return

      }
      finally {//不管代码报不报错误,仍然执行该行代码
          alert('执行')
      }
      console.log('如果出现错误,我的语句不会执行')
    }
    foo()
</script>

debugger

八、处理this

1.普通函数this指向

谁调用就指向谁

严格模式下指向undefined

2.箭头函数this指向

箭头函数中的 this 与普通函数完全不同,也不受调用方式的影响,事实上箭头函数中并不存在 this !箭头函数中访问的 this 不过是箭头函数所在作用域的 this 变量

总结:1.函数内部不存在this,沿用上一级的this

2.不适用:构造函数、原型函数、dom事件函数等

3.适用:需要使用上层的this的地方

4.如果正确的话,它会在很多地方带来方便。

3.改变this指向

(1)call

<script>
  // 普通函数
  function sayHi() {
    console.log(this);
  }

  let user = {
    name: '小明',
    age: 18
  }

  let student = {
    name: '小红',
    age: 16
  }

  // 调用函数并指定 this 的值
  sayHi.call(user); // this 值为 user
  sayHi.call(student); // this 值为 student

  // 求和函数
  function counter(x, y) {
    return x + y;
  }

  // 调用 counter 函数,并传入参数
  let result = counter.call(null, 5, 10);
  console.log(result);
</script>

总结:

  1. call方法能够在调用函数的同时指定 this 的值

  2. 使用 call 方法调用函数时,第1个参数为 this 指定的值

  3. call 方法的其余参数会依次自动传入函数做为函数的参数

(2)apply

总结:

  1. apply 方法能够在调用函数的同时指定 this 的值

  2. 使用 apply 方法调用函数时,第1个参数为 this 指定的值

  3. apply 方法第2个参数为数组,数组的单元值依次自动传入函数做为函数的参数

call和apply的区别是?

都是调用函数,都能够改变this指向。但它们的参数不一样,apply传递的必须是数组

(3)bind

不会调用函数,但是能改变this的指向

返回值是个函数,但是这个函数里面的this是更改过的obj

主要应用场景:

call调用函数并且可以传递参数

apply经常跟数组有关系,比如借助数学对象实现求最大值和最小值

bind不调用函数,但是还想改变this指向,比如改变定时器内部的this指向

九、防抖节流

防抖(debounce):

单位时间内,频繁触发事件,只执行最后一次

所谓防抖,就是指触发事件后在 n 秒内函数只能执行一次,如果在 n 秒内又触发了事件,则会重新计算函数执行时间

使用场景:输入手机号,邮箱验证输入检测

节流(throttle):

单位时间内,频繁出发事件,只执行一次

所谓节流,就是指连续触发事件但是在 n 秒中只执行一次函数

使用场景:鼠标移动mousemove,页面尺寸缩放resize、滚动条滚动scroll

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值