【JavaScript】多态(Symbol),迭代器接口,getter/setter,继承,ES6继承,instanceof

文章介绍了JavaScript中的多态概念,通过Symbol来创建唯一标识符,并探讨了迭代器接口的实现,包括如何实现数组的迭代器方法。此外,讨论了Getter/Setter的作用,以及静态方法在类中的应用。文章还涵盖了继承的概念,包括如何使用`extends`关键字和`super`关键字,并讲解了如何通过`Object.defineProperty`来定义和管理对象的属性描述符。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

❤️ Author: 老九
☕️ 个人博客:老九的CSDN博客
🙏 个人名言:不可控之事 乐观面对
😍 系列专栏:

多态

  • 不同的数据类型进行同一个操作,表现出不同的行为,就是多态的体现。
    在这里插入图片描述
  • 补充:for of可以遍历可迭代对象,例如数组,字符串,集合(Set),这些可迭代对象是实现了迭代器接口,也就是具有Symbol.iterator属性的对象,该属性返回一个迭代器对象,该对象有一个next方法,可以一次访问可迭代对象的每个元素,for of循环会自动调用迭代器对象next方法。
    typeof类型只有这些是返回特殊的,其他的都是返回object(null也是返回object) 在这里插入图片描述

symbol

  • Symbol()是JavaScript中的一个原始数据类型,表示一个唯一的标识符。它的主要作用是创建一个独一无二的属性名,防止属性名冲突。每个通过Symbol()函数创建的符号值都是唯一的,即使它们的描述相同。
  • Symbol不会隐式转换为字符串(a + ’‘ 这样是不行的);它是原型类型,typeof对它的返回是symbol;symbol函数调用时不能加new;symbol自带一个方法是for
<script>
  var map = new Map()
  function SymbolFor(key){
    if(map.has(key)){
      return map.get(key)
    }else{
      var s = Symbol(key)
      map.set(key,s)
      return s
    }
  }
</script>

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

  • 通过xxx.description可以查看创建xxx时候传入的字符串
    在这里插入图片描述
  • 设置对象里的变量名字
    在这里插入图片描述
  • 并且symbol只能通过中括号的形式访问
    在这里插入图片描述

迭代器接口

  • Symbol中有一个iterator接口 ,迭代器的基础结构是obj对象里有一个Symbol.iterator的属性,这个属性值是一个函数,这个函数返回一个对象,这个对象里面有一个next的方法,因为是个闭包,a的值由外层作用域来存储;
  • 这个对象自动放在for of中,然后for of自动调用这个对象进行循环遍历
  • 迭代器接口是多态的重要体现

在这里插入图片描述
在这里插入图片描述

  • 迭代之后没值了之后,value:undefined,done:true
    在这里插入图片描述

实现数组的迭代器方法

  • 对象实现了迭代器接口就称iterable,也就是a是可迭代对象
  • 调用迭代器接口返回的对象叫iterator,也就是a[Symbol.iterator]是迭代器,这个方法叫做迭代器接口
<script>
  a[Symbol.iterator] = function(){
    var len = 0
    return {
      next : ()=>{
        return {
          value : this[len++],
          done : len > this.length
        }
      }
    } 
  }
</script>

练习

  • 在for(var n of 10)的上面写一段代码,让它能够从1到10输出
<script>
  Number.prototype[Symbol.iterator] = function(){
    var target = this
    var curr = 1
    return {
      next() {
        return{
          value : curr++,
          done : curr > target + 1
        }
      }
    }
  }

  for(var n  of 10){
    console.log(n)
  }
</script>

Getter/Setter

  • get,set可以实现不用一直维护对应的属性(size),下面这样写虽然在对象里写的是一个方法,但是读的话是按照属性来读的
  • 使用Getter和Setter方法和直接赋值之间的区别在于,Getter和Setter方法允许对属性的访问和修改进行自定义操作,并且可以对属性的值进行验证和计算。直接赋值则是将属性的值直接存储在对象的属性中。
    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述
  • 那么如何给一个已经存在的对象,付给一个属性,这个属性有getter和setter,通过Object.defineProperty(对象名,属性名(字符串),方法)
    在这里插入图片描述
  • 那么如何让if(a == 1 && a = = 2 && a = = 3)能够成立,通过Object.defineProperty方法,将window设置一个a属性添加一个get方法
    在这里插入图片描述

static

  • 该函数为静态函数,在构造函数里,如果加上static的函数,可以直接使用,不需要调用构造函数的实例
  • 构造函数是这个类型的命名空间,与这类型有关的一些函数或者常量都可以以静态属性或静态方法的形式挂在构造函数上
    在这里插入图片描述
  • 如果设置成了静态方法,js中就不能够通过实例对象进行调用方法了
class A {
  static baz() {
    console.log('baz');
  }
}

const a = new A();
a.baz(); // 抛出 TypeError: a.baz is not a function

在这里插入图片描述

  • 补充:String.from方法,String.of方法,这些都是静态方法
    from这个方法里面的值可以传入类数组对象,或者迭代器对象;of方法就是直接创建一个数组。
    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述
  • 写在of里和写在括号里现在都一样了,所有of用的就不多了

继承

  • 谈到编程方式,有函数式编程,函数被视为一等公民,就是将代码抽象为一个一个函数相互传递,函数可以作为参数传递给其他函数,也可以作为返回值(高阶函数)。面向对象编程,就是抽象出一个一个类,然后变成对象,面像对象编程有三大特性。
  • 封装:将属性和方法封装到一个类中,可以称之为封装的过程;
  • 继承:可以减少重复代码的数量(老师学生类,相同的东西抽取到父类,子类继承父类),也是多态的前提
  • 多态:不同对象在执行时候表现出来不同的形态,通过多态可以编写通用的代码
  • extend,跟java差不多,要注意在super之前,是不能使用this的,super是绑定了父类的构造函数,然后通过父类才产生出了子类
<script>
  class Creature {
    constructor(age) {
      this.age = age
    }
    grow() {
      this.age++
    }
  }

  class Animal extends Creature {
    constructor(age, name) {
      //super就是调用父类的构造函数
      //父类的构造函数运行完,可以认为this已被“构造”为一个Creature
      super(age)
      this.name = name
      this.x = 0
      this.y = 0
    }
    moveTo(x, y) {
      this.x = x
      this.y = y
    }
  }
</script>
  • 基本关系是:有一个类A,B继承自A,那么A有一个独自的原型属性,B有一个独自的原型属性,B的一个原型是A,B的原型属性的原型是A的原型属性
  • 继承要实现对父类实例方法的复用(通过构造函数的prototype属性之间的继承来实现);对父类构造函数的复用(super);对父类静态方法的继承(通过构造函数之间的原型继承来实现)
  • 下面这几行代码,就类似于实现了extends的东西,._proto_和setPrototypeOf(把谁的原型属性设置为谁的原型属性)是一样的作用,通过setPrototypeOf,将父类原型属性赋值给子类原型属性,实现方法的复用,最终实现继承
    在这里插入图片描述
<!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>
      function Person(name, age, height) {
        this.name = name;
        this.age = age;
        this.height = height;
      }
      Person.prototype.running = function () {
        console.log("running");
      };
      Person.prototype.eating = function () {
        console.log("eating");
      };

      function Student(name, age, height, sno, score) {
        //传递参数
        Person.call(this, name, age, height);
        this.sno = sno;
        this.score = score;
      }
      //传递方法到子类原型属性中
      Object.setPrototypeOf(Student.prototype, Person.prototype);
      Student.prototype.studying = function () {
        console.log("studying");
      };

      var stu1 = new Student("why", 18, 1.88, 111, 100);
      var stu2 = new Student("kobe", 30, 1.88, 112, 80);
      console.log(stu1, stu2);
    </script>
  </body>
</html>

  • 如果是想用父类的方法,但是自己想添加一些小功能,可以通过super调用,super有两个用处。
    在这里插入图片描述

对象方法

  • obj.hasOwnProperty,判断自己的对象上是否有对应的属性(不是在原型上的属性)
  • in,判断某个属性是否在对象上或者对象对应的原型上面
// 创建一个对象
let person = {
  name: 'Alice',
  age: 30
};

// 使用 in 操作符检查属性是否存在于对象中
console.log('name' in person); // 输出 true,因为 name 属性存在于 person 对象中
console.log('job' in person); // 输出 false,因为 job 属性不存在于 person 对象中

// 创建一个新对象,以 person 为原型
let employee = Object.create(person);

// 使用 in 操作符检查属性是否存在于对象的原型链上
console.log('name' in employee); // 输出 true,因为 name 属性存在于 employee 对象的原型链上(即 person 对象)
console.log('job' in employee); // 输出 false,因为 job 属性不存在于 employee 对象或其原型链上

instanceof

  • 用于检查一个对象是否属于某个类或其子类的实例。原理就是判断实例对象的constructor上是否指向对应的类(构造函数)
    在这里插入图片描述
  • instanceof只能判断对象,2可以包装为一个对象,然后再进行判断。任何东西都是对象;instanceof可以判断是不是来自于父类或者是祖先的实例
  • 总结:类型判断有:(三种,在下面的图片显示),第一个只能判断原始类型,对于对象和null都返回object,第二个只能判断系统自带的对象类型,第三只能判断对象类型,可判断内置对象,还能判断类的继承关系,不能判断原始类型
    在这里插入图片描述
  • 补充:Object和Function的原型和原型属性的关系,object是function的实例,function也是object的实例在这里插入图片描述

属性描述符

如果想对一个对象的其中的属性进行比较精准的操作控制,可以使用属性描述符
Object.defineProperty()方法可以接受三个参数
在这里插入图片描述

<script>
  //大括号里是属性描述符,有四项
  Object.defineProperty(a,'baaa',{
    //这里还可以写get和set,如果写了get和set,就不要写value,如果写了value,就不要写get和set
    value : 999,
    //判断该类型是否在for in或者Object.keys()返回该属性,是否可以枚举,默认是false
    enumerable : true,
    //属性的值是否可以修改
    writable:false,
    //属性是否可以再次被Object.defineProperty函数定义,默认是false
    configurable: false
  })
//通过Object.getOwnPropertyDescriptor(obj,'xxx'),这个函数可以获取某个对象的属性描述
</script>
  • 还有一个Object.defineProperties的属性,可以一次设置多个属性描述符
  • create方法的第二个参数是跟Properties的用法是一样的,Object.create适用于创建一个新对象的方法
const myObj = { x: 1, y: 2 };
const obj3 = Object.create(myObj);

在这里插入图片描述

  • Object还有一些其他的方法,比如keys和values,可以获得对象的属性组成的数组或者是值组成的数组
    在这里插入图片描述
  • assign属性,将后面的所有对象的属性都赋值给第一个参数的对象,只是一个浅复制,用的值是同一个内存地址,如果第二个参数是y :2,第三个参数是y:8,那么最后给到第一个参数的值就是y:8
  • entries方法,把一个对象的每个属性名和属性值放在一个数组里,然后返回一个大数组
    在这里插入图片描述
  • fromEntries方法,将上面的entries的值传入就返回一个对象
    在这里插入图片描述
  • Object.preventExtensions(xxx),这个方法可以让一个对象不能增加新的属性了,但删除一个属性是可以的
  • 在这里插入图片描述
    在这里插入图片描述
  • seal方法,是将对象封闭,对象不可增加和删除属性了,但属性是可以修改的
    在这里插入图片描述
    在这里插入图片描述
  • freeze方法, 将对象冻结,对象不能增加,不能删除,也不能修改属性的值
  • 在这里插入图片描述

在这里插入图片描述

  • isExtensible,isSealed,isFrozen这些方法可以判断对象是否可以扩展,已经封闭,已经冻结了

练习

  • Set实现iterator和from种传入迭代器,和map
<script>
  class Group {
    static fromArray(array) {
      var group = new Group()
      for (let i = 0; i < array.length; i++) {
        group.add(array[i])
      }
      return group
    }
    static fromIterable(iterable) {
      var group = new Group()
      var iterator = iterable[symbol.iterator]()
      var iterated = iterator.next()
      while (!iterated.done) {
        group.add(iterated.value)
        var iterated = iterator.next()
      }
      return group
    }
    constructor(iterable = []) {
      this._elements = []
      for (var item of iterable) {
        this.add(item)
      }
    }
    [Symbol.iterator]() {
      var i = 0
      return {
        next: () => {
          return {
            value: this._elements[i++],
            done: i > this._elements.length,
          }
        }
      }
    }
    add(val) {
      if (!this.has(val)) {
        this._elements.push(val)
      }
      return this
    }
    has(val) {
      if (this._elements.indexOf(val) >= 0) {
        return true
      }
      return false
    }
    delete(val) {
      var idx = this._elements.indexOf(val)
      if (idx >= 0) {
        this._elements.splice(idx, 1)
        return true
      }
      return false
    }
    size() {
      return this._elements.length
    }
  }

  class Map {
    #keys = []
    #vals = []
    constructor(entries = []) {
      for (var [key, val] of entries) {
        this.set(key, val)
      }
    }
    #keyIndex(key) {
      return this.#keys.indexOf(key)
    }
    forEach(action) {
      for (var i = 0; i < this.#keys.length; i++) {
        var key = this.#keys[i]
        var val = this.#vals[i]
        action(val, key, this)
      }
    }
    [Symbol.iterator]() {
      var i = 0
      return {
        next: () => {
          var key = this.#keys[i]
          var val = this.#vals[i]
          var entry = [key, val]
          i++
          return {
            value: entry,
            done: i > this.size
          }
        }
      }
    }
    get(key) {
      var keyIdx = this.#keyIndex(key)
      if (keyIdx >= 0) {
        return this.#vals[keyIdx]
      }
    }

    set(key, val) {
      var keyIdx = this.#keyIndex(key)
      if (keyIdx >= 0) {
        this.#vals[keyIdx] = val
      } else {
        this.#keys.push(key)
        this.#vals.push(val)
      }
      return this
    }

    has(key) {
      var keyIdx = this.#keyIndex(key)
      if (keyIdx >= 0)
        return true
      return false
    }

    delete(key) {
      var keyIdx = this.#keyIndex(key)
      if (keyIdx >= 0) {
        this.#keys.splice(keyIdx, 1)
        this.#vals.splice(keyIdx, 1)
        return true
      }
      return false
    }

    clear() {
      this.#keys = []
      this.#vals = []
    }
    get size() {
      return this.#keys.length
    }
  }
</script>


  • 如果想实现数组去重
    在这里插入图片描述

  • 补充,如果有一个自有对象中有一个属性是hasOwnProperty:true的属性,但是我想要Object中的hasOwnProperty属性怎么办
    在这里插入图片描述

  • 两种办法:第一种: Object.hasOwn(map,‘one’);第二种:Object.prototype.hasOwnProperty.call(map,‘one’)
    在这里插入图片描述

————————————————————————
♥♥♥码字不易,大家的支持就是我坚持下去的动力♥♥♥
版权声明:本文为CSDN博主「亚太地区百大最帅面孔第101名」的原创文章

评论 8
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

李小浦

你的鼓励将是我创作的最大动力

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

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

打赏作者

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

抵扣说明:

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

余额充值