js学习之路(六)——this,箭头函数,对象操作

1. this 指向

 /*
      this 指向
        + 定义:
          => this 是一个使用再作用域内部的关键字
          => 全局很少用, 大部分是在函数内部使用
        + 指向:
          => 全局使用: window
          => 函数使用: **不管函数怎么定义, 不管函数在哪定义, 只看函数的调用(箭头函数除外)**
            -> 普通调用(直接调用/全局调用)
              + 函数名(): this -> window
            -> 对象调用
              + xxx.函数名(): this -> 点前面是谁就是谁
            -> 定时器处理函数
              + setTimeout(function () {}, 0): this -> window
              + setInterval(function () {}, 0): this -> window
            -> 事件处理函数
              + xxx.onclick = function () {}: this: 事件源(绑定再谁身上的事件)
              + xxx.addEventListener('', function () {}): this: 事件源
            -> 自执行函数
              + (function () {})(): this -> window
    */

    // function fn() {
    //   console.log(this)
    // }

    // fn() // 普通调用 this -> window

    // var obj = {
    //   // 把 fn 存储的地址赋值给了 obj 的 f 成员
    //   // 从现在开始 obj.f 和 全局变量 fn 指向同一个函数空间
    //   f: fn,
    //   name: '我是 obj 对象'
    // }

    // obj.f() // 对象调用 this -> obj

    // // 把 fn 函数当作定时器处理函数使用
    // setTimeout(fn, 0) // 定时器处理函数 this -> window
    // setTimeout(obj.f, 0) // 定时器处理函数 this -> window


    // var div = document.querySelector('div')
    // // 当点击 div 的时候, 执行 obj.f 这个函数
    // // div.onclick = obj.f // 事件处理函数 this -> 事件源

    // div.addEventListener('click', obj.f) // 事件处理函数 this -> 事件源


    // function fn() {
    //   console.log(this)
    // }

    // fn() // window

    // setTimeout(function () {
    //   fn() // window
    // }, 0)

    // var div = document.querySelector('div')
    // div.onclick = function () {
    //   function f() {
    //     console.log(this)
    //   }

    //   f() // window
    // }



    // var obj = {
    //   name: '我是 obj 对象',
    //   fn: function () {
    //     console.log(this)
    //     function fun() {
    //       console.log(this)
    //     }

    //     fun() // window
    //   }
    // }

    // obj.fn() // this -> obj

    // 把 obj 里面 fn 成员存储的函数地址赋值给了全局变量 f
    // 全局变量 f 和 obj.fn 指向同一个函数空间
    // var f = obj.fn

    // f() // this -> window

2. 改变 this 指向

/*
      改变 this 指向
        + this 有他本身的指向性
        + 不管你本身指向哪里, 我让你指向谁, 你就指向谁
        + 三个方法
          1. call()
          2. apply()
          3. bind()

      1. call()
        + 使用方法, 就直接连接再函数名后面使用
        + 语法:
          -> fn.call()
          -> obj.fn.call()
        + 参数:
          -> 第一个参数, 就是函数内部的 this 指向
          -> 第二个参数开始, 依次给函数传递参数
        + 特点:
          -> 会立即执行函数(不适合用作定时器处理函数或者事件处理函数)
        + 作用:
          -> 伪数组借用数组方法

      2. apply()
        + 使用方法, 就直接连接再函数名后面使用
        + 语法:
          -> fn.apply()
          -> obj.fn.apply()
        + 参数:
          -> 第一个参数, 就是函数内部的 this 指向
          -> 第二个参数: 是一个数组或者伪数组都行, 里面的每一项依次给函数传递参数
        + 特点:
          -> 会立即执行函数
        + 作用: 可以以数组的形式给某些功能函数传参
          -> Math.max()

      3. bind()
        + 使用方法, 就直接连接再函数名后面使用
        + 语法:
          -> fn.apply()
          -> obj.fn.apply()
        + 参数:
          -> 第一个参数. 就是函数内部的 this 指向
          -> 从第二个参数开始, 依次给函数传递参数
        + 特点:
          -> 不会立即调用函数
          -> 会返回一个新的函数, 一个已经被改变好 this 指向的函数
        + 作用:
          -> 改变事件处理函数或者定时器处理函数的 this 指向
    */

    function fn(a, b) {
      console.group('fn 函数')
      console.log(this)
      console.log(a)
      console.log(b)
      console.groupEnd()
    }

    var obj = {
      name: '我是 obj 对象'
    }

    // fn(10, 20) // this -> window

    // 1. call()
    // 使用 call 方法去调用 fn 函数, 把函数内部的 this 指向改变成 obj
    // fn.call(obj, 100, 200)


    // 2. apply()
    // 使用 apply 方法调用 fn 函数, 把函数内部的 this 指向改变成 obj
    // fn.apply(obj, [1000, 'world'])

    // 3. bind()
    // 使用 bind 方法改变 fn 函数的 this 指向
    // res 是一个 fn 函数的克隆版, 只不过里面的 this 被锁死了, 指向 obj
    // var res = fn.bind(obj, 'hello', 'world')
    // res()








    /*
      小例子
    */

    // var arr = [100, 200, 33, 56, -200, -300, 72]

    // // var res = Math.max(100, 200, 33, 56, -200, -300, 72)
    // var res = Math.max.apply(null, arr)
    // console.log(res)


    /*
      小例子
    */

    // function f() {
      // console.log(arguments)

      // 伪数组用不了数组常用方法
      // 但是数组可以使用

      // every() 方法的调用, 需要接收一个函数作为实参
      // arguments.every() 因为 arguments 没有 every 方法, 所以报错
      // var res = arguments.every(function (t) { return t >= 20 })

      // 数组再调用 every 方法
      // every 里面的 this 指向前面的数组
      // var res = [].every()

      // 利用 call 方法来执行 数组的 every() 函数
      // var res = [].every.call()

      // 第一个参数是 every 里面的 this 指向
      // 原先 every 的 this 指向 数组的时候, 遍历查看数组
      // 现在 evert 的 this 指向 arguments, 遍历查看 arguments
      // var res = [].every.call(arguments)

      // call 的第二个参数是给函数传递参数
      // 函数 a 就是传递给 every 方法里面的参数函数
      // var res = [].every.call(arguments, function a(item) { return item >= 8 })
      // console.log(res)
    // }

    // f(10, 20, 30, 40, 50)


    /*
      小例子
    */

    // var div = document.querySelector('div')
    // function handler(a, b) {
    //   console.log(this)
    //   console.log(a, b)
    // }
    // var obj = { name: '我是 obj 对象' }
    // call 不合适, 因为还没等到点击呢, 函数就执行了
    // div.onclick = handler.call(obj)
    // div.onclick = handler.apply(obj)
    // 不会把 handler 函数执行, 而是返回一个新的函数, 一个已经锁死 this 指向的函数
    // var res = handler.bind(obj)
    // 调用的是一个被 bind 锁死 this 指向的函数
    // div.onclick = handler.bind(obj, 100, 200)

3. ES6 定义变量

ES6:
      + 官方名称叫做 ES2015
      + 语法层面的更新
        => 原先: var a
        => ES6: let a
      + 我们书写代码:
        => 不需要考虑语法层面的兼容
        => ES6 转换 ES5 的工具叫做 babel
        => https://www.babeljs.cn/ -> 试一试 -> 你在左边框书写 ES6 语法
		
ES6 定义变量
        + ES6 确认了两个定义变量的关键字
          1. let   : 变量
          2. const : 常量

      let/const 一起和 var 的区别
        1. var 会进行预解析
          => let/const 不会进行预解析, 必须先定义后使用
        2. var 可以声明重复变量名
          => let/const 不能声明重复的变量名
        3. var 没有块级作用域
          => let/const 有块级作用域

      let 和 const 的区别
        1. let 叫做变量
          => const 叫做常量
        2. let 可以再声明的时候不进行赋值
          => const 再声明的时候必须进行赋值
        3. let 生命的变量可以被修改
          => const 声明的常量不能被修改, 一旦修改就报错

      变量的定义规范
        1. 尽量使用 let 和 const 定义
        2. 书写代码的时候尽可能优先使用 const



      块级作用域
        + 被代码块限制变量的使用方法
        + var: 只有函数私有作用域才能限制使用范围
        + let/const: 只要是能书写代码段的 {} 都能限制使用范围
    */

    // let 和 const 的区别
    // 1. 声明不赋值
    // let num
    // console.log(num)

    // const 声明不赋值会报错
    // const num2
    // console.log(num2)

    // 2. 不允许被修改
    // let num = 100
    // console.log(num)
    // num = 200
    // console.log(num)

    // const 不允许被修改
    // const num2 = 100
    // console.log(num2)
    // num2 = 200


    // const obj = {
    //   name: 'Jack'
    // }
    // console.log(obj.name)
    // obj.name = 'Rose'
    // console.log(obj.name)

    // let/const 和 var 的区别
    // 1. 预解析
    // console.log(a)
    // var a = 100
    // console.log(a)

    // 使用再定义之前会报错
    // console.log(a)
    // let a = 100
    // console.log(a)
    // console.log(a)
    // const a = 100
    // console.log(a)

    // 2. 重复变量名
    // var num = 100
    // var num = 200
    // console.log(num)

    // 不能重名
    // let num = 100
    // console.log(num)
    // let num = 200
    // const num = 200
    // console.log(num)
    // const num = 300

    // 3. 块级作用域
    // if (true) {
    //   // 全局变量
    //   var num = 100
    // }
    // console.log(num)

    // let 定义的 num 离开这个 if 的 {} 就用不了了
    // if (true) {
    //   let num = 200
    //   console.log(num)
    // }

    // let num = 300
    // console.log(num)

4. 04_变量的块级作用域

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <meta http-equiv="X-UA-Compatible" content="ie=edge">
  <title>Document</title>
  <style>
    * {
      margin: 0;
      padding: 0;
      list-style: none;
    }

    .box {
      width: 600px;
      height: 300px;
      border: 10px solid #333;
      margin: 30px auto;
    }

    .box > ul {
      width: 100%;
      height: 60px;
      overflow: hidden;
    }

    .box > ul > li {
      width: 200px;
      height: 100%;
      background-color: orange;
      text-align: center;
      line-height: 60px;
      color: #fff;
      float: left;
    }

    .box > ul > li.active {
      background-color: skyblue;
    }

    .box > ol {
      width: 100%;
      height: 240px;
      position: relative;
    }

    .box > ol > li {
      width: 100%;
      height: 100%;
      background-color: purple;
      color: #fff;
      position: absolute;
      left: 0;
      top: 0;
      line-height: 240px;
      text-align: center;
      display: none;
    }

    .box > ol > li.active {
      display: block;
    }






  </style>
</head>
<body>

  <div class="box">
    <ul>
      <li class="active">1</li>
      <li>2</li>
      <li>3</li>
    </ul>
    <ol>
      <li class="active">1</li>
      <li>2</li>
      <li>3</li>
    </ol>
  </div>


  <script>
    // const btns = document.querySelectorAll('ul > li')
    // const tabs = document.querySelectorAll('ol > li')

    // for (var i = 0; i < btns.length; i++) {
    //   btns[i].setAttribute('index', i)

    //   btns[i].onclick = function () {
    //     for (var i = 0; i < btns.length; i++) {
    //       btns[i].className = tabs[i].className = ''
    //     }

    //     this.className = 'active'
    //     tabs[this.getAttribute('index') - 0].className = 'active'
    //   }
    // }

    /*
      var i = 3
      {
        i = 0
        btns[0].onclick = function () { console.log(i) }
      }
      {
        i = 1
        btns[1].onclick = function () { console.log(i) }
      }
      {
        i = 2
        btns[2].onclick = function () { console.log(i) }
      }
      {
        i = 3 循环结束
      }
    */

    // for (let i = 0; i < btns.length; i++) {
    //   btns[i].onclick = function () {
    //     for (let j = 0; j < btns.length; j++) {
    //       btns[j].className = tabs[j].className = ''
    //     }
    //     tabs[i].className = btns[i].className = 'active'
    //   }
    // }

    /*
      {
        let i = 0
        btns[0].onclick = function () { console.log(i) }
      }
      {
        let i = 1
        btns[0].onclick = function () { console.log(i) }
      }
      {
        let i = 2
        btns[0].onclick = function () { console.log(i) }
      }
      {
        let i = 3 循环结束
      }
    */


  </script>

  <script>
    /*
      变量的块级作用域
        + let 和 const 定义的变量每一个可以书写代码段的 {} 都会限制使用范围
        + 特点: 我们可以把 循环过程中 每一次的变量限制在每一次的 {} 里面
    */

    // for (var i = 0; i < 3; i++) {
    //   console.log(i)
    // }

    // console.log(i) // 3

    /*
      var i = 3
      {
        i = 0
        console.log(i)
      }
      {
        i = 1
        console.log(i)
      }
      {
        i = 2
        console.log(i)
      }
      {
        i = 3 循环结束
      }
    */


    // for (let i = 0; i < 3; i++) {
    //   console.log(i)
    // }
    // console.log(i)
    /*
      {
        let i = 0
        console.log(i)
      }
      {
        let i = 1
        console.log(i)
      }
      {
        let i = 2
        console.log(i)
      }
      {
        let i = 3 结束循环
      }
    */
  </script>
</body>
</html>

5. ES6 的箭头函数

/*
      ES6 的箭头函数
        + 一种新的函数定义方式
        + 对于函数表达式的简写方式(匿名函数)
        + 匿名函数
          => var fn = function () {}
          => var obj = { fn: function () {} }
          => setTimeout(function () {}, 0)
          => setinterval(function () {}, 0)
          => [].forEach(function () {})
          => div.onclick = function () {}
          => div.addEventListener('click', function () {})
          => ...
        + 语法: () => {}
          => (): 形参的位置
          => =>: 箭头函数的标志
          => {}: 代码段
    */

    var fn = function () { console.log('我是一个函数') }
    fn()

    var fun = (a, b) => {
      console.log('我是一个 fun 函数')
      console.log(a)
      console.log(b)
    }
    fun(100, 200)

6. 箭头函数的特性

/*
      箭头函数的特性
        1. 箭头函数如果只有一个形参
          => 那么可以省略小括号不写
          => (a) => {}
            -> a => {}

        2. 箭头函数代码段里面只有一句话, 可以省略大括号不写
          => 并且会自动 return 这一句话的结果
          => () => { return 123 }
          => () => 123

        3. 箭头函数里面没有 arguments 这个东西
          => 压根没有, 你用不了

        4. 箭头函数里面没有 this 关键字
          => 官方: 箭头函数里面的 this 是 上下文(context), 外部作用域的 this 就是箭头函数内的 this
          => 私人: 箭头函数的 this 是, 你的箭头函数写在哪一行, 上一行的 this 就是箭头函数里面的 this

        5. 箭头函数里面的 this 任何方法改变不了
          => 因为箭头函数没有 this
          => call / apply / bind 不能改变 箭头函数的 this 指向
    */

    // 1. 一个形参可以省略小括号不写
    // let fn = (a) => { console.log(a) }
    // let fun = a => { console.log(a) }
    // fn(100)
    // fun(200)

    // 2. 一句话可以省略大括号不写, 并且自动 return
    // let fn = (a, b) => { return a + b }
    // console.log(fn(10, 20))
    // // 把 a + b 的结果当作 fun 的返回值
    // var fun = (a, b) => a + b
    // console.log(fun(100, 200))

    // let arr = [10, 20, 30, 40, 50]
    // let res = arr.every(function (item) { return item >= 10 })
    // let res2 = arr.every(item => { return item >= 10 })
    // let res3 = arr.every(item => item >= 10)
    // console.log(res, res2, res3)

    // 3. 箭头函数里面没有 arguments
    // let fn = () => { console.log(arguments) }
    // let fun = function () { console.log(arguments) }
    // fun(100, 200, 300)
    // fn(10, 20, 30)


    // 4. 箭头函数里面没有 this

    // let div = document.querySelector('div')
    // div.onclick = function () {
    //   console.log(this)
    // }

    // div.onclick = () => {
    //   console.log(this) // window
    // }

    // div.onclick = function () {
    //   let fn = function () {
    //     console.log(this)
    //   }
    //   fn() // window

    //   let fun = () => { // 就是 84 行的 this
    //     console.log(this)
    //   }
    //   fun() // div
    // }

    // let obj = {
    //   name: '我是 obj 对象',
    //   fn: function () { console.log(this) },
    //   fun: () => { console.log(this) }
    // }

    // obj.fn() // obj
    // obj.fun() // window

    // let div = document.querySelector('div')
    // div.onclick = function () {
    //   // this
    //   let obj = {
    //     name: '我是 obj 对象',
    //     fn: function () { console.log(this) },
    //     fun: () => { console.log(this) }
    //   }

    //   obj.fn() // obj
    //   obj.fun() // div
    // }


    // 5. 箭头函数改变不了 this 指向
    // let fn = () => { console.log(this) }
    // fn() // window
    // let obj = { name: 'Jack' }
    // fn.call(obj)

7. 函数的参数默认值

 /*
      函数的参数默认值
        + 给函数的形参设置一个默认值
          => 如果你传递了实参, 就使用你传递
          => 如果你没有传递实参, 那么就使用默认值
        + 直接再形参后面使用 等于号(=) 进行赋值
    */

    // 1. 普通函数的参数默认值
    function fn(a = 100, b = 200) {
      // 形参 a 的默认值是 100
      console.log(a)
      console.log(b)
    }
    fn()
    fn(10)
    fn(10, 20)

    // 箭头函数也可以书写参数默认值
    let fun = (a = 1000, b = 2000) => {
      console.log(a, b)
    }

    fun()
    fun(100)
    fun(100, 200)

    // 箭头函数只要你设置参数默认值, 不管多少个形参, 都得写小括号
    // let f = a = 100 => {} // 报错的
    let f = (a = 100) => {}

8.模板字符串

/*
      模板字符串
        + ES6 定义了一种声明字符串的方式
        + 使用 反引号(``)
        + 特点:
          1. 可以换行书写
          2. 可以直接进行变量的拼接
          3. 模板字符串可以调用函数
            => 字符串里面的内容是函数的参数
            => ${} 把字符串切开, 组合成一个数组当作第一个参数
            => 从左到右开始依次是每一个 ${} 里面的内容作为函数后面的参数
    */

    // 1. 换行书写
    // let str = `
    //   123
    //   456
    // `

    // 2. 拼接变量
    // let age = 18
    // let str2 = `
    //   我今年 ${ age } 岁了
    // `
    // console.log(str2)


    // function fn(a, b, c) {
    //   console.log(a)
    //   console.log(b)
    //   console.log(c)
    // }

    // var num = 100
    // var num2 = 200

    // fn`hello ${ num }  world ${ num2 } 你好`
    // 1. ${} 切开字符串   ['hello ', ' world ', ' 你好']
    // 2. ${ num } 里面得 num 就是函数的第二个参数
    // 3. ${ num2 } 里面的 num2 就是函数的第三个参数

9.点点点(…) 运算符

/*
      点点点(...) 运算符
        + 展开运算符
          => 当你再函数的实参位置或者数组或者对象里面使用的时候是 展开
          => 就是把包裹的内容全部打开
        + 合并运算符
          => 当你再函数的形参位置使用的时候是 合并
          => 作用: 箭头函数没有 arguments, 我们使用 合并运算符整一个
    */

    // 1. 展开
    // let arr1 = [1, 2, 3, 4]
    // console.log(...arr1)
    // console.log(Math.max(...arr1))

    // let arr2 = [...arr1, 5, 6, 7, 8]
    // console.log(arr2)

    // let obj = {
    //   name: 'Jack',
    //   age: 18
    // }

    // let obj2 = {
    //   ...obj,
    //   gender: '男'
    // }

    // console.log(obj, obj2)


    // 2. 合并
    // function fn(...a) {
    //   // 定义一个 a 变量, 从第一个实参开始到最后一个实参全部获取, 放在一个数组里面
    //   console.log(a)
    // }
    // fn(100, 200, 300, 400, 500, 600)

    // function fn(a, ...b) {
    //   // 定义一个 a 变量, 接收第一个实参
    //   // 定义一个变量 b, 从第二个开始到最后一个实参放在一个数组里面
    //   console.log(a)
    //   console.log(b)
    // }
    // fn(100, 200, 300, 400, 500, 600)

    // function fn(a, b, ...c) {
    //   // 定义一个 a 变量, 接收第一个实参
    //   // 定义一个 b 变量, 接收第二个实参
    //   // 定义一个变量 c, 从第三个开始到最后一个实参放在一个数组里面
    //   console.log(a)
    //   console.log(b)
    //   console.log(c)
    // }
    // fn(100, 200, 300, 400, 500, 600)


    let fn = (...arg) => {
      console.log(arg)
    }

    fn(10, 20, 30, 40, 50)

10.解构赋值

/*
      解构赋值
        + 定义: 快速从 对象 或者 数组 里面获取一些数据
        + 分成两种
          1. 解构数组
            => 语法: let [变量1, 变量2, ...] = [数据1, 数据2, ...]
            => 也可以解构多维数组
          2. 解构对象
            => 语法: let { key1, key2, ... } = { 键值对1, 键值对2, ... }
            => 解构的时候可以给解构的变量起一个别名
              -> { name: 别名 } = {}
            => 也可以解构多维对象
    */

    // 2. 解构对象
    // let obj = { name: 'Jack', age: 18, gender: '男' }
    // let name = obj.name
    // let age = obj.age
    // let gender = obj.gender
    // console.log(name, age, gender)
    // 解构赋值
    // let { name, age, gender } = obj
    // console.log(name, age, gender)

    // 解构起一个别名
    // let n = obj.name
    // let { name: n, age: a, gender } = obj // => let n = obj.name
    // console.log(n, a, gender)

    // 解构多维对象
    let o1 = {
      a: 100,
      b: 200,
      o2: {
        c: 300,
        o3: {
          d: 400
        }
      }
    }

    // console.log(o1)
    // let d = o1.o2.o3.d
    // let c = o1.o2.c
    // let b = o1.b
    // let a = o1.a
    // console.log(a, b, c, d)

    // 解构赋值
    // let { a, b, o2: { c, o3: { d } } } = o1
    // console.log(a, b, c, d)
    // let { c, o3: { d } } = o1.o2
    // let c = o1.o2.c
    // let { d } = o1.o2.o3
    // let d = o1.o2.o3.d



    // 1. 解构数组
    // let arr = [10, 20, 30, 40]
    // 获取数组中的索引 [0]
    // let a = arr[0]
    // let b = arr[1]
    // let c = arr[2]
    // let d = arr[3]
    // console.log(a, b, c, d)
    // 解构赋值获取数组里面的成员
    // let [a, b, c, d] = arr
    // console.log(a, b, c, d)

    // 解构多维数组
    // let arr = [10, 20, [30, 40, [50]]]
    // let a = arr[0]
    // let b = arr[1]
    // let c = arr[2][0]
    // let d = arr[2][1]
    // let e = arr[2][2][0]
    // console.log(a, b, c, d, e)

    // 解构赋值的形式
    // let [a, b, [c, d, [e]]] = arr
    // console.log(a, b, c, d, e)

    // 交换变量
    // var a = 5
    // var b = 6
    // console.log(a, b)
    // var [b, a] = [a, b]
    // console.log(a, b)

11.对象的简写

/*
      对象的简写:
        + 再 ES6 标准下, 有一个对象的简写方式
          1. 当 key 和 value 一模一样的时候, 可以只写一个
          2. 当 某一个 key 的值是一个函数的时候, 并且不是箭头函数
            => 可以直接省略 function 关键字和 冒号 不写
    */

    // 1. key 个 value 一样, 可以省略一个不写
    // let age = 18
    // let obj = {
    //   name: 'Jakc',
    //   age, // 等价于 age : age
    //   gender: '男' // 不能省略
    // }
    // console.log(obj)

    // 2. 函数省略
    // f1 和 f3 是一回事
    let obj = {
      name: '我是 obj 对象',
      f1: function () { console.log(this) },
      f2: () => { console.log(this) },
      f3 () { console.log(this) }
    }

    obj.f1() // obj
    obj.f2() // window
    obj.f3() // obj

12.了解面向对象开发

/*
      了解面向对象开发
        + 是一个我们的开发思想(你写代码的方式)
        + 面向过程
          => 再开发的过程中, 关注每一个 步骤 细节 顺序, 实现效果
        + 面向对象
          => 再开发的过程中, 只关注有没有一个对象能帮我完成

      例子: 我今天要吃面条
        + 面向过程
          1. 和面 - 多少面粉 多少水
          2. 切面 - 多宽 多细
          3. 煮面 - 多长时间
          4. 拌面 - 多少酱 多少面
          5. 吃面 - 一口吃多少
        + 面向对象
          1. 找一个面馆
          2. 点一碗面
          3. 等着吃
        + 面向对象: 对面向过程的高度封装(高内聚低耦合)

      在开发过程中
        + 面向过程
          => 按照顺序一步一步来
        + 面向对象(轮播图)
          => 找到一个对象, 能帮我完成轮播图
          => JS 本身没有, 我们需要第三方
          => swiper: 生成一个完成轮播图的对象
        + 我们:
          => 当你需要完成一个 功能A 的时候
          => 我们找到 JS 有没有这个完成 功能A 的对象
          => 如果没有, 我们 制造一个 "机器"
          => 这个 "机器" 可以制造完成 功能A 的对象

      "机器" 是什么
        + 能力: 能创造一个 有属性 有方法 合理的 对象
        + 构造函数就是这个 "机器"


      模拟: 选项卡
        + 面向过程
          1. btns: [按钮1, 按钮2, 按钮3]
          2. tabs: [盒子1, 盒子2, 盒子3]
          3. 事件: 让 btns 里面的成员添加点击事件, 操作 btns 和 tabs 里面的每一个
        + 抽象成对象
          o = {
            btns: [按钮1, 按钮2, 按钮3],
            tabs: [盒子1, 盒子2, 盒子3],
            方法: function () {
              给 o.btns 里面的每一个绑定事件
              操作 o.btns 和 o.tabs 里面的每一个操作类名
             }
          }
        + 面向对象
          => 书写一个构造函数
          => 能创建一个对象包含三个成员
            1. btns
            2. tabs
            3. 方法, 能操作自己的btns 和tabs 的方法
          => 使用这个构造函数创建一个对象, 根据你传递参数来实现选项卡效果

    */

13.创建对象的四种方式

/*
      创建对象的四种方式
    */

    /*
      字面量创建
        => var obj = {}
    */

    // let o1 = {
    //   name: 'Jack',
    //   age: 18,
    //   gender: '男'
    // }

    // 当我需要第二个对象的时候
    // let o2 = {
    //   name: 'Rose',
    //   age: 20,
    //   gender: '女'
    // }

    /*
      内置构造函数创建
        => var obj = new Object()
    */

    // let o1 = new Object()
    // o1.name = 'Jack'
    // o1.age = 18

    // 当我想创建第二个对象的时候
    // let o2 = new Object()
    // o2.name = 'Rose'
    // o2.age = 20


    /*
      工厂函数创建对象
        1. 先自己做一个工厂函数
        2. 使用自己做的工厂函数来创建对象
    */

    // 1. 创建一个工厂函数
    // function createObj(name, age, gender) {
    //   // 1-1. 手动创建一个对象
    //   let obj = {}

    //   // 1-2. 手动添加成员
    //   obj.name = name
    //   obj.age = age
    //   obj.gender = gender

    //   // 1-3. 手动返回这个对象
    //   return obj
    // }

    // 2. 使用工厂函数创建对象
    // 创建第一个对象
    // let o1 = createObj('Jack', 18, '男')
    // console.log(o1)

    // 创建第二个对象
    // let o2 = createObj('Rose', 20, '女')
    // console.log(o2)


    /*
      自定义构造函数创建
        1. 自己书写一个构造函数
        2. 使用构造函数创建对象

      构造函数
        + 就是普通函数, 没有任何区别
        + 只有在你调用的时候和 new 关键字连用, 才有构造函数的能力
          => 只要你和 new 关键字连用 this => 当前对象(new 前面的那个变量名)
    */

    // 1. 创建一个构造函数
    function createObj(name, age, gender) {
      // 1-1. 自动创建一个对象

      // 1-2. 手动向对象上添加内容
      this.name = name
      this.age = age
      this.gender = gender

      // 1-3. 自动返回这个对象
    }

    // 2. 创建对象
    let o1 = new createObj('Jack', 18, '男') // 本次调用的时候, 函数内部的 this 就指向 o1
    console.log(o1)

    let o2 = new createObj('Rose', 20, '女')
    console.log(o2)

14.构造函数的书写和使用

/*
      构造函数的书写和使用
        + 明确: 构造函数也是函数, 只不过是在调用的时候和 new 关键字连用了
        + 目的: 就是为了创建一个 有属性 有方法 合理的 对象

      1. 调用必须有 new 关键字
        => 如果没有, 那么没有创建对象的能力
        => 只要有, 就会自动创建一个对象
      2. 在构造函数内部不要写 return
        => 如果 return 基本数据类型, 写了白写
        => 如果 return 复杂数据类型, 构造函数白写
      3. 构造函数在调用的时候, 如果不需要传递参数, 最后的小括号可以不写
        => 但是推荐我们都写上
      4. 构造函数推荐首字母大写
        => 是为了直观看出和普通函数的区别
        => 看到首字母大写的函数, 基本上就要和 new 连用
      5. 当函数和 new 关键字连用
        => 会创造对象, 我们关创造出来的对象叫做 实例对象
        => 我们关创造的过程叫做 实例化 的过程
        => 构造函数体内的 this 指向当前实例对象
        => 也就是本次 new 的时候创建的那个对象
    */

    // 1. 必须和 new 关键字连用
    // function fn() {}
    // let o1 = new fn()
    // console.log(o1)
    // let o2 = fn()
    // console.log(o2)

    // 2. 不要写 return
    // function fn() {
      // this.a = 100
      // 基本数据类型写了白写
      // return 123
      // 复杂数据类型, 构造函数白写
      // return new Date()
    // }
    // let o1 = new fn()
    // console.log(o1)

    // 3. 不传递参数, () 可以不写
    // function fn() {
    //   this.a = 100
    //   this.b = 200
    // }
    // let o1 = new fn()
    // console.log(o1)
    // let o2 = new fn
    // console.log(o2)

    // 4. 首字母大写
    // function Fn() {
    //   this.a = 100
    //   this.b = 200
    //   this.c = 300
    // }
    // function fun() {
    //   var a = 100
    //   var b = 200
    //   return a + b
    // }

    function Person(name) {
      this.name = name
      this.sayHi = function () { console.log('hello world') }
    }

    // 本次 new 的时候, Person 会创造一个对象
    // 把创造出来的对象赋值给了 变量 p1
    // 我们管 p1 叫做 Person 的实例化对象
    // 本次调用的时候, Perosn 函数内部的 this 就指向 p1
    let p1 = new Person('张三')

    // 本次 new 的时候, Perosn 创造第二个对象
    // 赋值给了 p2 变量
    // p2 也是 Perosn 的实例化对象
    // 本次调用的时候, Person 函数内部的 this 就指向 p2
    let p2 = new Person('李四')

    console.log(p1, p2)

    p1.sayHi()
    p2.sayHi()

15.构造函数是否合理

 /*
      构造函数是否合理
        + 一旦在构造函数体内书写方法的时候
        + 你创建多少个实例化对象, 那么就有多少个方法要占用内存空间
        + 不合理: 因为会有对于的函数内存空间被占用
    */

    // function Person(name) {
    //   this.name = name
    //   this.sayHi = function a() { console.log('你好 世界') }
    // }

    // let p1 = new Person('张三') // 本次创建的时候, 会开辟一段内存空间存储 a 函数
    // let p2 = new Person('李四') // 本次创建的时候, 会开辟一段内存空间存储 a 函数


    // 函数单独提取出来没有问题
    // 但是每一个函数占用一个变量
    // function a() { console.log('你好 世界') }
    // function b() { console.log('你好 世界') }

    // function Person(name) {
    //   this.name = name
    //   this.sayHi = a
    //   this.sayHi2 = b
    // }

    // let p1 = new Person('张三')
    // let p2 = new Person('李四')




    // 把所有的方法放在一个对象里面
    // 我要是有 一百个 构造函数, 就要有 一百个 对象
    let PerosnObj = {
      a: function a() { console.log('你好 世界') },
      b: function b() { console.log('你好 世界') }
    }
    function Person(name) {
      this.name = name
      this.sayHi = PersonObj.a
      this.sayHi2 = PersonObj.b
    }

    let p1 = new Person('张三')
    let p2 = new Person('李四')

17.prototype

/*
      prototype (原型 / 原型对象)
        + 定义: **每一个函数天生自带一个属性叫做 prototype, 他是一个对象**
        + 只要函数定义好以后, 这个 prototype 就出生了
        + 构造函数也是函数, 构造函数也有 prototype, 我们可以像里面添加一些内容
        + 这个天生自带的 prototype 里面有一个属性叫做 constructor
          => 表示我是哪一个构造函数伴生的原型对象
    */

    function Person() {}

    // Person.prototype 已经出生了
    // 像 prototype 上添加一些属性和方法
    Person.prototype.sayHi = function () { console.log('hello world') }

    console.log(Person.prototype)

18.proto

 /*
      __proto__
        + 定义: **每一个对象天生自带一个属性, 叫做 __proto__, 指向所属构造函数的 prototype**
        + 实例化对象也是一个对象
        + 实例化对象也有 __proto__ 属性
    */

    function Person() {}

    // Person 出生的时候就带着 prototype
    // 像里面添加 一些内容
    Person.prototype.sayHi = function () { console.log('hello world') }

    // 因为 P1 是 Person 实例化出来的对象
    // 所以 p1 所属的构造函数就是 Person
    let p1 = new Person()
    console.log(p1)

    // 当我访问 p1.__proto__ 的时候就应该由 sayHi
    console.log('Person.prototype', Person.prototype)
    console.log('p1.__proto__', p1.__proto__)
    console.log(Person.prototype === p1.__proto__) // true
  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

EOPG

你的鼓励是我创造的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值