JavaScript高级_03day


今天的课程主要是理解什么是面向对象的编程思想,还有写一个案例。

wpi考试错点

1-当css中写了!important,那么它的优先级最高,在如下js中也无法改变它,因为这个下面的js的本质是修改style即css。

853

2-捕获类型事件,只能通过addEventListener注册,传统注册方法只能是事件冒泡。

3-事件对象e.preventDefault()可以阻止默认事件,e.stopPropagation()可以阻止冒泡和捕获。

propagation传播; 扩展; 宣传; 培养;

问题:我阻止冒泡和捕获干啥?我阻止了还咋执行事件?

<body>
    <div>
        <a href="#">qqq</a>
    </div>
    <script>
        let box = document.querySelector('a')
        let div = document.querySelector('div')
        div.addEventListener('click', function (e) {
            alert('2')
        })
        box.addEventListener('click', function (e) {
            alert('1')
        })
    </script>
</body>

如上,如果阻止了冒泡和捕获,本身的事件会执行,但是div的事件不会执行,因为阻止了冒泡。

4-document.querySelectorAll就算一个元素对象没取到,得到的是一个空数组而不是null;

document.querySelector如果元素不存在,会得到null

5-在事件委托绑定后,事件程序中e.target和this是不同的,e.target是指向鼠标点击的那个元素对象,this是谁调用指向谁。

编程思想

https://blog.csdn.net/youif/article/details/107322845

面向对象有封装性,继承性,多态性。

构造函数

js中面向对象需要构造函数来实现封装

<script>
      function Awesome(name, gender) {
          this.name = name
          this.gender = gender
          this.dream = () => { console.log('探索未知,体验未知,改变世界'); }
      }

      const zdq = new Awesome('zdq', 'man')
      const tyl = new Awesome('tyl', 'man')
      console.log(zdq.dream === tyl.dream)  //false
  </script>

浪费内存

在构造函数生成对象的时候,当不同对象有一个相同的方法,如上的dream方法,那么zdq.dream和tyl.dream的方法是存储中不同的地址的,每次new都又生成了方法。

所以说这样就极大的浪费了内存,这个方法其实共用就行了。怎么实现共用呢?

使用原型prototype

原型

854

prototype是构造函数的一个属性,指向一个对象,如你打印Awesome.prototype,输出的也是一个对象。

问题:函数就是函数,咋函数也有属性了?

函数是一种特殊类型的对象。您自己编写的代码并不是实际的函数。 该函数是具有属性的对象,此属性是可调用的。

console.dir(Awesome) 

855

console.dir(Awesome.prototype)

856

也就是说只有是相同构造函数生成的不同对象,如果通过 构造函数名.prototype.方法名=function(){}的话

就可以共用这一个方法,这个方法被赋予给每一个此构造函数生成的对象,但是它们调用的话,就是调用同一个方法,避免了内存浪费。

<script>
       function Awesome(name, gender) {
           this.name = name
           this.gender = gender
         
       }
       Awesome.prototype.dream =() => { console.log('探索未知,体验未知,改变世界'); }
       const zdq = new Awesome('zdq', 'man')
       const tyl = new Awesome('tyl', 'man')
       console.log(zdq.dream === tyl.dream)  //true
   </script>

案例

857

分析:这里的需求就是在实例化数组的构造函数上加一个对象prototype的方法即可。

<script>
       // const arr = [1,2,3]
       const arr =new Array(1,2,8)
       Array.prototype.max = function(){
            // 原型函数里面的this 指向谁? 实例对象 arr
           return Math.max(...this)
       }
       console.log(arr.max());
   </script>

这里为什么写…this?this指向的就是调用者,调用者就是那个arr,三个点在数组里面就是展开运算符。

constructor属性

const恒定的,不变的;construct制造

constructor建造者,制造者,也就构造函数

858

console.log(Star.prototype.constructor === Star)  //true

也就是说在原型对象prototype里面有一个constructor属性,这个属性就是整个构造函数的代码

console.log(Star) 
//结果就是function  Star(name){
//this.name =name
//}

那我搞个这东西干嘛呢?吃饭没事干?

859

应用:

我们都知道prototype对象可以为构造函数挂载公共方法,但是一个一个添加太麻烦了,所以我们经常直接为prototype对象赋值,那么就会覆盖掉原有的对象内容,原有自带的constructor就会消失,所以需要手动再添加一个constructor属性指向构造函数

问题:

我手动添加constructor的话,就代表已经知道构造函数是谁了,为什么还要用constructor来知道构造函数是谁???

https://blog.csdn.net/carayq/article/details/120453930

https://wangdoc.com/javascript/oop/object.html#objectprototype__proto__

我们并不需要写 构造函数.prototype.constructor来获取,只需要通过对象.–proto–.constructor,当生成了很多个对象,就这个通过这个constructor快速知道此对象是由哪个构造函数生成。

思考

861

862

因为生成的每个对象都有一个属性__proto__指向原型对象,所以可以共用挂载的函数方法。

863

这里注意
1-prototype是原型对象,是一个对象
2-  __proto__是一个对象原型,它指向这个对象的原型对象,它是每个构造函数生成的对象的一个属性,所以
实例对象.__proto__===实例函数.prototype
3-有的浏览器控制台中会用prototype表示 __proto__
<script>
  function Star() {

  }
  const ldh = new Star()
  // 对象原型__proto__ 指向 改构造函数的原型对象
  console.log(ldh.__proto__)
  // console.log(ldh.__proto__ === Star.prototype)
  // 对象原型里面有constructor 指向 构造函数 Star
  console.log(ldh.__proto__.constructor === Star ===Star.prototpe.constructor) //true

</script>

864

__proto__.constructor和prototype.constructor的关系

865

原型继承

原型继承就是写一个对象,然后赋予给构造函数的prototype原型对象

<!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>Document</title>
</head>

<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>

</html>

866

为什么会这样呢?

因为男人女人两个构造函数引用赋予的prototype都是Person对象,这个对象赋予过去的是一个地址,指向同一片内存,一个通过操作改变如添加吸烟方法,另一个自然也改变。

怎么办?

很简单,我们让赋予给不同构造函数也就是男人女人两个构造函数的prototype的对象不同就行了,但是如果就男人女人两个构造函数还好说,如果有成千上万个要赋予相同方法和属性的构造函数怎么办呢?

再使用构造函数,可以构造出有相同的内容,不同地址的对象。

问题:

原先不是说prototype就是为了节省内存,共用相同的方法,你这又开辟不同的对象,还节省个啥?

我看的是黑马的就业班教程,es6就四天,后续会看codewhy王元红老师的2022课程进行查漏补缺

我的理解是构造函数woman和man的内存确实节省了,因为挂载了共用方法属性

但是为了不让男生添加吸烟方法,女生也被挂载上,所以必须牺牲内存去生成设置不同地址的对象。

构造函数分为父类和子类构造函数

如上的person为父类构造函数,woman和man是子类构造函数。

原型链

1-只要是对象,就都有–proto–,它指向生成这个对象的构造函数的原型对象prototype

2-1中的原型对象也是对象,它自然也有–proto–,指向更上一层的prototype

3-只要是原型对象,就有constructor

868

demo.constructor === demo.__proto__.constructor

867

为什么如上会相等?

869

因为有原型链的线路存在,demo.constructor === demo.proto.constructor找到的东西是一样的。

如arry.prototype.map(),如果在arry对象中没有map方法,事实上也确实没有,就会去array的prototype中寻找,就像我们自己定义的构造函数一样,如果再没有就会再查找上一层(事实上map方法就挂载在array.prototype)

instanceof运算符

<script>
   // function Objetc() {}
   console.log(Object.prototype)
   console.log(Object.constructor)
   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) //true ldh对象是由person构造函数生成的
   console.log(ldh instanceof Object) //true,ldh在object这条原型链上
   console.log(ldh instanceof Array) //false ldh对象不属于数组
   console.log([1, 2, 3] instanceof Array) //true 
   console.log(Array instanceof Object) //true 万物皆对象哈哈
 </script>

综合案例

870

面向过程的话,非常简单,只需要获取dom,然后设置事件就好了。但是我们这里需要联系面向对象,用构造函数实现。

871

什么意思呢

1-就是定义一个modal函数,它的作用是创造对象,也就是那些按钮的对象,如上的删除和登入

2-这个构造函数上面挂载上open和close方法。

<!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>
    .modal {
      width: 300px;
      min-height: 100px;
      box-shadow: 0 0 10px rgba(0, 0, 0, 0.2);
      border-radius: 4px;
      position: fixed;
      z-index: 999;
      left: 50%;
      top: 50%;
      transform: translate3d(-50%, -50%, 0);
      background-color: #fff;
    }

    .modal .header {
      line-height: 40px;
      padding: 0 10px;
      position: relative;
      font-size: 20px;
    }

    .modal .header i {
      font-style: normal;
      color: #999;
      position: absolute;
      right: 15px;
      top: -2px;
      cursor: pointer;
    }

    .modal .body {
      text-align: center;
      padding: 10px;
    }

    .modal .footer {
      display: flex;
      justify-content: flex-end;
      padding: 10px;
    }

    .modal .footer a {
      padding: 3px 8px;
      background: #ccc;
      text-decoration: none;
      color: #fff;
      border-radius: 2px;
      margin-right: 10px;
      font-size: 14px;
    }

    .modal .footer a.submit {
      background-color: #369;
    }
  </style>
</head>

<body>
  <button id="delete">删除</button>
  <button id="login">登录</button>

  <!-- <div class="modal">
    <div class="header">温馨提示 <i>x</i></div>
    <div class="body">您没有删除权限操作</div>
  </div> -->


  <script>
    // 1.  模态框的构造函数
    function Modal(title = '', message = '') {
      // 公共的属性部分
      this.title = title
      this.message = message
      // 因为盒子是公共的
      // 1. 创建 一定不要忘了加 this 
      this.modalBox = document.createElement('div')
      // 2. 添加类名
      this.modalBox.className = 'modal'
      // 3. 填充内容 更换数据
      this.modalBox.innerHTML = `
        <div class="header">${this.title} <i>x</i></div>
        <div class="body">${this.message}</div>
      `
      // console.log(this.modalBox)
    }
    // 2. 打开方法 挂载 到 模态框的构造函数原型身上
    Modal.prototype.open = function () {
      if (!document.querySelector('.modal')) {
        // 把刚才创建的盒子 modalBox  渲染到 页面中  父元素.appendChild(子元素)
        document.body.appendChild(this.modalBox)
        // 获取 x  调用关闭方法
        this.modalBox.querySelector('i').addEventListener('click', () => {
          // 箭头函数没有this 上一级作用域的this
          // 这个this 指向 m 
          this.close()
        })
      }
    }
    // 3. 关闭方法 挂载 到 模态框的构造函数原型身上
    Modal.prototype.close = function () {
      document.body.removeChild(this.modalBox)
    }

    // 4. 按钮点击
    document.querySelector('#delete').addEventListener('click', () => {
      const m = new Modal('温馨提示', '您没有权限删除')
      // 调用 打开方法
      m.open()
    })

    // 5. 按钮点击
    document.querySelector('#login').addEventListener('click', () => {
      const m = new Modal('友情提示', '您还么有注册账号')
      // 调用 打开方法
      m.open()
    })

  </script>
</body>

</html>

1.下面关于编程思想说法错误的是? (d) 分值1分

A: 面向过程就是分析出解决问题所需要的步骤,然后用函数把这些步骤一步一步实现,使用的时候再一个一个的依次调用就可以了

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

C: 面向过程编程性能比面向对象高,但是没有面向对象易维护、易复用、易扩展

D:面向对象编程 易维护、易复用、易扩展,性能也比面向过程高

回答正确+1分

答案解析:

面向对象性能一般情况下比面向过程低

2.下面是面向对象编程特性的是? (abc) 分值1分

A:封装性

B:继承性

C:多态性

D:优先级

回答正确+1分

3.原型(原型对象)是? (b) 分值1分

A:Object

B:prototype

C:__proto

D:consturctor

回答正确+1分

4.原型(原型对象)的作用说法错误的是? (d) 分值1分

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

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

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

D:原型经常挂载函数,但是比较耗费内存

回答正确+1分

答案解析:

原型对象上面挂在函数,可以实现共享,反而更加节省内存,因为不需要每次都创建这个函数

5.关于构造函数和原型对象里面this指向说法正确的是? (a) 分值1分

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

B:构造函数的this指向window

C: 原型对象的this指向构造函数

D:构造函数和实例对象里面没有this,跟箭头函数一样

回答正确+1分

6.下面关于constructor 属性说法正确的是? (abcd) 分值1分

A:每个原型对象里面都有个constructor 属性

B:原型对象里面都有个constructor 属性指向该原型对象的构造函数

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

D:原型对象constructor属性的使用场景:可以重新指回指向原来的构造函数

回答正确+1分

7.对象原型是? (c) 分值1分

A:Object

B:prototype

C:__ proto__

D:consturctor

回答正确+1分

8.为什么实例对象可以访问构造函数的原型对象中的属性和方法呢? (a) 分值1分

A:因为实例对象里面有对象原型 _ proto__ 它指向原型对象

B:因为实例对象有constructor 指向原型对象

C: 因为实例对象有 this指向原型对象

D:没啥原因,反正就是能….

回答正确+1分

9.下列选项中关于原型链说法正确的是? (abcd) 分值1分

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

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

C:如果没有就查找它的原型(也就是 proto指向的 prototype 原型对象)

D:如果还没有就查找原型对象的原型(Object的原型对象)依此类推一直找到 Object 为止(null)

回答正确+1分

10.下列选项中关于原型继承说法正确的是? (abc) 分值1分

A:核心是子类原型继承父类的实例: Man.prototype = new Person()

B:父类新增原型方法或是原型属性子类都可以访问到

C:Man.prototype = new Person() 其中 new Person() 生成一个对象,并且是独立的,互不影响

D:Man = new Person() 其实也可以的

回答正确+1分

答案解析:

不能直接 Man = new Person() 要不然就替换 Man函数了


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