12.8(JS第六周)

一、深入对象

1.创建对象三种方式

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

(2)利用 new Object 创建对象

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

// const obj = new Object()
// console.log(obj)
// obj.uname = '灰太狼'
const obj = new Object({uname: '灰太狼'})
console.log(obj)

2.构造函数:一种特殊的函数,主要用来初始化对象(通过构造函数来快速创建多个类似的对象

注意:

<body>
  <script>
    // 创建一个猪 构造函数
    function Pig(uname, age) {
      this.uname = uname
      // this.age = age
    }
    // console.log(new Pig('皮皮', 1))
    // console.log(new Pig('taylor', 1))

    const peppa = new Pig('皮皮')
    console.log(peppa)  // {name: '皮皮'}
    
    // const pepa = {uname: '灰太狼', age: 1}


    function Goods(name, price, count) {
      this.name = name
      this.price = price
      this.count = count
    }
    const mi = new Goods('小米', 1999, 20)
    console.log(mi)
    const hw = new Goods('华为', 3999, 59)
    console.log(hw)
    
  </script>
</body>

实例化执行过程:

3.实例成员&静态成员

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

// 实例成员和静态成员
// 1 实例成员, 实例对象上的属性和方法属于实例成员
function Pig(name) {
   this.name = name
}
const peiqi = new Pig('皮皮')
const qiaozhi = new Pig('乔治')
peiqi.name = '小猪皮皮'  // 实例属性
peiqi.asyHi = () => {   //实例方法
  console.log('hi~~')   
}
console.log(peiqi)
 console.log(qiaozhi)
console.log(qiaozhi === peiqi)

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

// 2 静态成员 : 构造函数上的属性和方法 
function Pig(name) {
  this.name = name
}
Pig.eyes = 2  // 静态属性
Pig.sayHi = function () {  // 静态方法
  console.log(this)
}
Pig.sayHi()
console.log(Pig.eyes)  // 2

二、内置构造函数

基本数据类型: 字符串、数值、布尔、undefined、null

引用类型:Object,Array,RegExp,Date

包装类型:String,Number,Boolean

1.Object(用于创建普通对象)【推荐使用字面量方式声明对象,而不是 Object 构造函数】

三个常用静态方法(静态方法就是只有构造函数Object可以调用的)

(1)Object.keys 静态方法获取对象中所有属性(键)

语法:

注意: 返回的是一个数组

(2)Object.values 静态方法获取对象中所有属性值

语法:

注意: 返回的是一个数组

(3)Object. assign 静态方法常用于对象拷贝

语法:

使用:经常使用的场景给对象添加属性

const o = {uname: '灰太狼', age: 18 }
// 1 获得所有的属性名
console.log(Object.keys(o))  // 返回数组['uname', 'age']
// 2 获得所有的属性值
console.log(Object.values(o))  // ['灰太狼', 18]
// 3 拷贝对象(给对象添加属性)
// const oo = {}
// Object.assign(oo. o)
// console.log(oo)
Object.assign(o, {gender: '女'})
console.log(o)

2.Array(用于创建数组 )【创建数组建议使用字面量创建,不用 Array构造函数创建】

(1)数组常见实例方法-核心方法

作用:reduce 返回函数累计处理的结果,经常用于求和等

基本语法:

参数:起始值可以省略,如果写就作为第一次累计的起始值

语法:

// 数组reduce方法
// arr.reduce(function(上一次值, 当前值){},初始值)
const arr = [1, 5, 8]
// 1 没有初始值
const total = arr.reduce(function(prev, current) {
  return prev + current
})
console.log(total)
    
// 2 有初始值
const total = arr.reduce(function(prev, current) {
  return prev + current
}, 10)
console.log(total)
    
// 3 箭头函数的写法
const total = arr.reduce((prev,current) => prev + current, 10)
console.log(total)

(2)数组常见方法-其他方法

<body>
  <script>
    // const arr = ['red', 'blue', 'green']
    // const re = arr.find(function (item) {
    //   return item === 'blue'
    // })
    // console.log(re)

    const arr = [
      {
        name: '小米',
        price: 1999
      },
      {
        name: '华为',
        price: 3999
      },
    ]

    // 1 find 查找
    // 找小米 这个对象,并且返回这个对象
    const mi = arr.find(function (item) {
    // console.log(item) // 
    // console.log(item.name) // 
    return item.name === '小米'
    })
    console.log(mi)
    const mi = arr.find(item => item.name === '小米')
    console.log(mi)
    
    // 2 every 每个是否都符合条件, 如果都符合返回 true, 否则返回 false 
    const arr1 = [10, 20, 30]
    // const flag = arr1.every(item => item >= 10)  //true
    const flag = arr1.some(item => item >= 20)  //只要有一个符合条件就可以
    console.log(flag)
    
  </script>
</body>

(3)数组常见方法- 伪数组转换为真数组

        静态方法 Array.from()

<body>
  <ul>
    <li>1</li>
    <li>2</li>
    <li>3</li>
  </ul>
  <script>
    // Array.from(lis) 把伪数组钻换为真数组
    const lis = document.querySelector('ul li')
    // console.log(lis)
    // lis.pop()  报错
    const liss = Array.from(lis)
    liss.pop()
    console.log(liss)
    
  </script>
</body>

3.String

(1)常见实例方法

<body>
  <script>
    // 1 split 把字符串 转换为 数组  和 join() 相反
    const str = '灰太狼,红太狼' 
    const arr = str.split(',')
    console.log(arr)
    
    const str1 = '2025-7-19'
    const arr1 = str1.split('-')
    console.log(str1)
    
    // 2 字符串的截取   substring(开始的索引号[, 结束的索引号])
    // 2.1 如果省略 结束的索引号 默认取到到结束
    // 2.2 结束的索引号不包含想要截取的部分
    const str = '我一定会回来的'
    console.log(str.substring(4,8))
    
    // 3 startsWith 判断是不是以某个字符开头
    const str = '灰太狼在学习种'
    console.log(str.startsWith('灰太狼'))
    
    // 4 inclues 判断某个字符是不是包含在一个字符串里面
    const str = '我是灰太狼'
    console.log(str.includes('灰太狼'))  // true
    
  </script>

4.Number(用于创建数值 )

常用方法: toFixed() 设置保留小数位的长度

三、编程思想

1.面向过程介绍:分析出解决问题所需要的步骤,然后用函数把这些步骤一步一步实现,使用的时候再一个一个的依次调用

面向过程,就是按照我们分析好了的步骤,按照步骤解决问题

2.面向对象介绍:把事务分解成为一个个对象,然后由对象之间分工与合作

面向对象是以对象功能来划分问题,而不是步骤

特性:封装性;继承性;多态性

四、构造函数

总结:构造函数体现了面向对象的封装特性;构造函数实例创建的对象彼此独立、互不影响

构造函数存在浪费内存的问题

五、原型

1.原型:一个对象,我们也称为 prototype 原型对象

作用:共享方法;  可以把那些不变的方法,直接定义在 prototype 对象上

构造函数和原型里面的this指向实例化的对象

(1)原型- this指向实例化的对象

<body>
  <script>
    let that 
    function Star (uname) {
      // that = this
      // console.log(this)
      this.uname = uname
    }
    // 原型对象里面的 this 也是 实例对象 taylor
    Star.prototype.sing = function() {
      that = this
      console.log('唱歌')
    }
    // 实例对象 taylor
    // 构造函数里面的 this 就是 实例对象 taylor
    const taylor = new Star('taylor')
    taylor.sing()
    console.log(that === taylor)
    
  </script>
</body>

(2)数组扩展方法

<body>
  <script>
    // 自己定义 数组扩展方法 求和 和 最大值
    // 1 我们定义的这个方法,任何一个数组实例对象都可以使用
    // 2 自定义的方法写到 数组 .prototype 身上
    // 1 最大值
    const arr = [1, 2, 3]
    Array.prototype.max = function() {
      // 展开运算符
      return Math.max(...this)
      // 原型函数里面的 this 指向 实例对象 arr
    }
    // 2 最小值
    Array.prototype.min = function() {
      // 展开运算符
      return Math.min(...this)
      // 原型函数里面的 this 指向 实例对象 arr
    }
    console.log(arr.max())
    console.log([2, 5, 9].max())
    console.log(arr.min())
    
    // const arr = new Array(1, 2)
    // console.log(arr)
    // 3 求和 方法
    Array.prototype.sum = function () {
     return this.reduce( (prev , item) => prev + item, 0)
    }
    console.log([1, 2, 3].sum())
    console.log([11, 21, 31].sum())
    
  </script>
</body>

2.constructor 属性:每个原型对象里面都有个constructor 属性(constructor 构造函数)

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

<body>
  <script>
    // constructor 单词 构造函数
    function Star () {
    }
    // Star.prototype.sing = function() {
    //   console.log('唱歌')
    // }
    // Star.prototype.dance = function() {
    //   console.log('跳舞')
    // }
    console.log(Star.prototype)
    Star.prototype = {
      // 重新指回创造这个原型对象的 构造函数
      constructor: Star,
      sing: function () {
        console.log('唱歌')
      },
      dance: function () {
        console.log('跳舞')
      },
    }
    // console.log(Star.prototype.constructor)






    // const taylor = new Star()
    // console.log(Star.prototype)
    // console.log(Star.prototype.constructor === Star)
    
  </script>
</body>

3.对象原型:对象都会有一个属性 __proto__ 指向构造函数的 prototype 原型对象

<body>
  <script>
    function Star () { }
    const taylor = new Star()
    // 对象原型__proto__ 指向 该构造函数的原型对象
    console.log(taylor.__proto__)
    console.log(taylor.__proto__ === Star.prototype)
    // 对象原型里面有constructor 指向 构造函数 Star
    console.log(taylor.__proto__.constructor === Star)
    
  </script>
</body>

4.原型继承:

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

(1)封装-抽取公共部分

(2)继承-让男人和女人都能继承人类的一些属性和方法

(3)问题解决

需求:男人和女人不要使用同一个对象,但是不同对象里面包含相同的属性和方法

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

    // 女人 构造函数  继承 想要 继承 Person
    function Woman () {
      
    }
    // Woman 通过原型来继承 Person
    // 父构造函数(父类)   子构造函数(子类)
    // 子类的原型 = new 父类
    Woman.prototype = new Person()
    // 指回原来的构造函数
    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 blue = new Man()
    console.log(blue)
  </script>

5.原型链

查找原则

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

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

六、深浅拷贝

1.浅拷贝:拷贝的是地址

<body>
  <script>
    const obj = {
      uname: '灰太狼',
      age: 18,
      family: {
        baby: '小灰灰'
      }
    }
    // 浅拷贝
    // const o = {...obj}
    // console.log(o)
    // o.age = 20
    // console.log(o)
    // console.log(obj)
    
    const o = {}
    Object.assign(o, obj)
    o.age = 20
    o.family.baby = '懒洋洋'
    console.log(o)
    console.log(obj)
    
    
  </script>
</body>

2.深拷贝:拷贝的是对象,不是地址

(1)通过递归实现

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

<body>
  <script>
    const obj = {
      uname: '灰太狼',
      age: 18,
      hobby: ['写信','听歌'],
      family: {
        baby: '小灰灰'
      }
    }
    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  (k是变量要用变量)  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 = '懒洋洋'
    console.log(obj)
  </script>
</body>

(2) js库lodash里面cloneDeep内部实现了深拷贝

<body>
  <!-- 先引用 -->
   <script src="./lodash.min_1765119256219.js"></script>
  <script>
    const obj = {
        uname: '灰太狼',
        age: 18,
        hobby: ['写信', '听歌'],
        family: {
          baby: '小灰灰'
        }
      }
    const o = _.cloneDeep(obj)
    console.log(o);
    o.family.baby = '懒洋洋'
    console.log(obj)
    
  </script>
</body>

(3)通过JSON.stringify()实现

<body>
  <script>
    const obj = {
      uname: '灰太狼',
      age: 18,
      hobby: ['写信', '听歌'],
      family: {
        baby: '小灰灰'
      }
    }
    // 把对象转换为 JSON 字符串
    // console.log(JSON.stringify(obj))
    const o = JSON.parse(JSON.stringify(obj))
    console.log(o)
    o.family.baby = '懒洋洋'
    console.log(obj)
  </script>
</body>

七、异常处理

1.throw 抛异常

<body>
  <script>
    function fn(x, y) {
      if (!x || !y) {
        console.log(11)
        // throw '没有参数传递进来'
        throw new Error('没有参数传递进来')
      }

      return x + y
    }
    console.log(fn())
    
  </script>
</body>

2.try /catch 捕获异常

<body>
  <p>123</p>
  <script>
    function fn() {
       try {
        // 可能发送错误的代码 要写到 try
        const p = document.querySelector('.p')
        p.style.color = 'red'
       } catch(err) {
        // 拦截错误, 提示浏览器提供的错误信息, 但是不中断程序的执行
        console.log(err.message)
        throw new Error('错误了吧')
        // 需要加return 中断程序
        return
       }
       finally {
        // 不管你的程序对不对, 一定会执行的代码
        alert('弹出对话窗')
       }
       console.log(11)
       
    }
    fn()
  </script>
</body>

3.debugger

我们可以通过try / catch 捕获错误信息(浏览器提供的错误信息)

八、处理this

1.this指向

(1)普通函数:普通函数的调用方式决定了 this 的值,即【谁调用 this 的值指向谁】

普通函数没有明确调用者时 this 值为 window,严格模式下没有调用者时 this 的值为 undefined

<body>
  <button>点击</button>
  <script>
    // 普通函数, 谁调用我,我指向谁
    console.log(this) // window
    function fn() {
      console.log(this) // window
    }
    Window.fn()
    window.setTimeout(function() {
      console.log(this) // window
    }, 1000)
    document.querySelector('button').addEventListener('click', function () {
      console.log(this) // button
    })
    const obj = {
      sayHi: function () {
        console.log(this) // obj
      }
    }
    obj.sayHi()
  </script>
</body>

(2)箭头函数

箭头函数中的this与普通函数完全不同,也不受调用方式的影响,事实上箭头函数中并不存在this

2.改变this

(1)call()

使用 call 方法调用函数,同时指定被调用函数中 this 的值

语法:

<body>
  <script>
    const obj = {
      uname: '灰太狼'
    }
    function fn(x, y) {
      console.log(this) // window
      console.log(x + y)
    }
    // 1 调用函数
    // 2 改变this指向
    // fn.call(obj)  // this 指向 obj
    fn.call(obj, 1,2)
  </script>
</body>

(2)apply()

使用 apply 方法调用函数,同时指定被调用函数中 this 的值

语法:

<body>
  <script>
    const obj = {
      age: 18
    }
    function fn(x, y) {
      console.log(this)  // {age: 18}
      console.log(x + y)
      
    }
    // 1 调用函数
    // 2 改变this指向
    // fn.apply(this指向谁, 数组参数)
    // fn.apply(obj)
    fn.apply(obj, [1, 2])
    // 3 返回值  本身就是在调用函数, 所以返回值就是函数的返回值

    // 使用场景: 求数组最大值
    // const max = Math.max(1, 2, 3)
    // console.log(max)
    const arr = [100, 45, 63]
    const max = Math.max.apply(Math, arr)
    const min = Math.min.apply(Math, arr)
    console.log(max, min)
    
    // 使用场景: 求数组最大值
    console.log(Math.max(...arr))
    
  </script>
</body>

(3)bind()

bind() 方法不会调用函数。但是能改变函数内部this 指向

语法:

<body>
  <button>点击</button>
  <script>
    const obj = {
      age: 18
    }
    function fn() {
      console.log(this)
    }
    // 1 bind 不会调用函数
    // 2 能改变this指向
    // 3 返回值是函数, 但是这个函数里面的this 是更改过的obj
    const fun = fn.bind(obj)
    // console.log(fun)
    fun()

    // 需求: 有一个按钮,点击,里面就禁用,2秒后开启
    const btn = document.querySelector('button')
    btn.addEventListener('click', function () {
      // 禁用按钮
      this.disabled = true
      window.setTimeout(function () {
        // 在这个普通函数里面,我们要this由原来的window 改变为 btn
        this.disabled = false
      }.bind(this), 2000)
    })
  </script>
</body>

九、性能优化

1.防抖:

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

函数实现:

<body>
  <div class="box"></div>
  <script>
    // 利用防抖实现性能优化
    // 需求: 鼠标在盒子上移动, 里面的数组变化 + 1
    const box = document.querySelector('.box')
    let i = 1
    function mouseMove() {
      box.innerHTML = i++
      // 如果里面存在大量消耗性能的代码, 比如dom操作, 比如数据处理, 可能造成卡顿
    }
    // box.addEventListener('mousemve', mouseMove)

    // 手写防抖函数
    // 核心是利用setTimeout定时器来实现
    // 1.声明定时器变量
    // 2,每次鼠标移动(事件触发)的时候都要先判断是否有定时器,如果有先清除以前的定时器
    // 3.如果没有定时器,则开启定时器,存入到定时器变量里面
    // 4.定时器里面写函数调用
    function debounce(fn, t) {
      let timer
      // return 返回一个匿名函数
      return function() {
        // 2.3.4
        if(timer) clearTimeout(timer)
        setTimeout(function(){
          fn()  // 加小括号调用fn函数
        }, t)
      }
    }
    box.addEventListener('mousemove', debounce(mouseMove, 500))
    // debounce(mouseMove, 500)  // 调用函数
    // debounce(mouseMove, 500)  = function() {2.3.4}
  </script>
</body>

2.节流:就是指连续触发事件但是在 n 秒中只执行一次函数

函数实现:

<body>
  <script>
    // 利用防抖实现性能优化
    // 需求: 鼠标在盒子上移动, 里面的数组变化 + 1
    const box = document.querySelector('.box')
    let i = 1
    function mouseMove() {
      box.innerHTML = i++
      // 如果里面存在大量消耗性能的代码, 比如dom操作, 比如数据处理, 可能造成卡顿
    }
    // box.addEventListener('mousemve', mouseMove)

    // 利用lodash库实现防抖 - 500毫秒后采用+1   (没有下载lodash)
    // 语法: _.throttle(fun, 时间)
    // box.addEventListener('mousemove', _.throttle(mouseMove, 500))

    //手写一个节流函数-每隔500ms+1
    //节流的核心就是利用定时器(setTimeout)来实现
    // 1.声明一个定时器变量
    // 2.当鼠标每次滑动都先判断是否有定时器了,如果有定时器则不开启新定时器
    // 3.如果没有定时器则开启定时器,记得存到变量里面
    // 3.1定时器里面调用执行的函数
    // 3.2定时器里面要把定时器清空
    function throttle(fn, t) {
      let timer = null
      return function() {
        if(!timer) {
          timer = setTimeout(function() {
            fn()
            // 清空定时器
            timer = null
          }, t)
        }
      }
    }
    box.addEventListener('mousemove', throttle(mouseMove, 500))
  </script>
</body>

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值