首页 > 其他分享 >原型链的理解

原型链的理解

时间:2022-09-25 20:00:07浏览次数:42  
标签:console log Person 理解 原型 prototype name

使用构造函数创建一个实例

<script>
    function Person(age, name) {
      this.age = age
      this.name = name
      this.sing = function () {
        console.log("我会唱歌")
      }
    }
    const student = new Person(22, "张三")
    console.log(student)
    console.log(Person.age)
    console.log(student.age)
    Person.sex = "女"
    console.log(Person.sex)
    console.log(student.sex)
  </script>

构造函数中有实例成员和静态成员

实例成员是通过内部this添加的,如此例中的age,name,sing;它们只能通过实例对象访问,不能通过构造函数访问

console.log(Person.age)//undefined
console.log(student.age)//22

静态成员是直接在函数本身上添加的,只能通过构造函数访问,不能通过实例对象访问

console.log(Person.sex)//女
console.log(student.sex)//undefined

prototype

每一个构造函数都有一个prototype属性,这是一个指针,指向原型对象,我们可以把方法直接定义在prototype对象上,这样所有的实例都可以共享这个方法

原型对象默认拥有一个 constructor 属性,指向它的构造函数

<script>
  /*Person 构造函数*/
  function Person(name, age) {
    this.name = name;
    this.age = age;
  }
  /*构造函数的原型上添加方法*/
  Person.prototype.sayHello = function () {
    console.log(`大家好,我是${this.name}今年${this.age}岁了`);
  };
  /*构造函数的原型上添加方法*/
  Person.prototype.study = function () {
    console.log(`我${this.name}要学习了`);
  };
  let p1 = new Person("小明", 23); // p1为 构造函数Person new出来的实例对象
  /*实例对象上的属性会屏蔽(遮蔽)原型上同名的属性*/
  p1.study = function () {
    console.log(`我${this.name}正在学习web前端开发课程`);
  };
</script>

 

 

原型对象默认拥有一个 constructor 属性,指向它的构造函数

console.log(Person.prototype.constructor === Person); // true

每个对象实例都有一个隐藏的属性__proto__,被称为隐式原型,指向它的构造函数的原型

console.log(p1.__proto__ === Person.prototype); // true

对象实例可以共享原型上面的所有属性和方法

p1.sayHello(); // 大家好,我是小明今年23岁了

实例自身的属性会屏蔽(遮蔽)原型上同名的属性,实例上没有的属性就会去原型上去找

p1.study(); // 我小明正在学习web前端开发课程

 原型链

  • JavaScript 中所有的对象都是由它的原型对象继承而来。
  • 而原型对象自身也是一个对象,它也有自己的原型对象,这样层层上溯,就形成了一个类似链表的结构,这就是原型链

 以下代码的原型链结构图:

<script>
  /*Person 构造函数*/
  function Person(name, age) {
    this.name = name;
    this.age = age;
  }
  /*构造函数的原型上添加方法*/
  Person.prototype.sayHello = function () {
    console.log(`大家好,我是${this.name}今年${this.age}岁了`);
  };
  /*构造函数的原型上添加方法*/
  Person.prototype.study = function () {
    console.log(`我${this.name}要学习了`);
  };
  let p1 = new Person("小明", 23); // p1为 构造函数Person new出来的实例对象
  /*实例对象上的属性会屏蔽(遮蔽)原型上同名的属性*/
  p1.study = function () {
    console.log(`我${this.name}正在学习web前端开发课程`);
  };
  console.log(p1.__proto__ === Person.prototype); //true
  console.log(Person.prototype.constructor === Person); //true
  console.log(Person.prototype.__proto__ === Object.prototype); //true
  console.log(Object.prototype.constructor === Object); //true
  console.log(Object.prototype.__proto__); //null
</script>

所有原型链的终点都是 Object.prototype

Objec.prototype 指向的原型对象同样拥有原型Object.prototype.__proto__,不过它的原型是 null ,而 null 则没有原型

原型链的查找

  • 当访问一个对象的某个属性时,会先在这个对象本身属性上查找,如果这个对象本身没有这个属性时,它就会去他的__proto__隐式原型上去找(即它的构造函数的 prototype)。
  • 如果还找不到,就去原型的原型(即构造函数的prototype的__proto__)上去找,....一直找到最顶层(Object.prototype)为止。
  • 如果还没有找到,则返回 undefined。
<script>
  Object.prototype.sayHello = function () {
    console.log(`大家好,我是${this.name}`);
  };
  class People {
    constructor(name, age) {
      this.name = name;
      this.age = age;
    }
    eat() {
      console.log(`${this.name}正在吃饭`);
    }
  }
  //Student类继承People类
  class Student extends People {
    constructor(name, age, id) {
      super(name, age);
      this.id = id;
    }
    eat() {
      console.log(`${this.name}正在吃肉肉`);
    }
    study() {
      console.log(`${this.name}正在学习`);
    }
  }
  const s1 = new Student("小明", 15, "0001");
  s1.run = function () {
    console.log(`${this.name}每天都要跑步`);
  };
  s1.run(); //小明每天都要跑步  自身找到,以自身为主
  s1.study(); //小明正在学习  自身没有,沿原型链查找,在Student.prototype中找到
  s1.eat(); //小明正在吃肉肉  自身没有,沿原型链查找,在Student.prototype中找到,就不再向上找了。
  s1.sayHello(); //大家好,我是小明   自身没有,沿原型链查找,在Object.prototype中找到
</script>

重写原型带来的问题

  • 在已经创建了实例的情况下重写原型,会切断现有实例与新原型之间的联系
  • 如果要重写原型,一定要在重写原型后,再创建实例。
<script>
  function Person(name) {
    this.name = name;
  }
  let p1 = new Person("小明");
  Person.prototype.eat = function () {
    console.log(`${this.name}在吃饭`);
  };
  // 重写原型
  Person.prototype = {
    name: "小明",
    sayHello() {
      console.log(`大家好,我是${this.name}`);
    },
  };
  p1.eat(); // 小明在吃饭
  p1.sayHello(); // p1.sayHello is not a function
</script>

在这个例子中,Person 的实例在重写原型对象之前创建的,在调用 p1.eat()时会输入正确的信息。

但是在调用 p1.sayHello 时候,会报错。是因为 p1 指向的原型还是最初的原型,而这个最初的原型上并没有 sayHello 这个方法,而且 eat 这个方法。

重写原型对象时,单独指定 constructor

<script>
  function Person() {}
  //重写原型,在prototype中需要重新指定constructor的值
  Person.prototype = {
    constructor: Person,
  };
  console.log(Person.prototype.constructor === Person); //true
</script>

 



标签:console,log,Person,理解,原型,prototype,name
From: https://www.cnblogs.com/qianduan-Wu/p/16603025.html

相关文章

  • ExecutorService、Callable、Future实现有返回结果的多线程原理解析
    原创/朱季谦在并发多线程场景下,存在需要获取各线程的异步执行结果,这时,就可以通过ExecutorService线程池结合Callable、Future来实现。我们先来写一个简单的例子——publ......
  • 我对软件工程的理解
    软件本身具有许多特点,比如:复杂性,一致性、不可见性和可变性,这些特性使得软件的编写、维护和运行等都比较困难。为了应对这些问题,产生了许多针对软件运维的基本原理和技术,这......
  • C#教程 - 深入理解C#与.NET
    更新记录转载请注明出处:2022年9月25日发布。2022年9月10日从笔记迁移到博客。CILTypeDef类型定义TypeRef引用其他程序集的类型Assembly......
  • static静态变量的理解
    静态变量类型说明符是static。静态变量属于静态存储方式,其存储空间为内存中的静态数据区(在静态存储区内分配存储单元),该区域中的数据在整个程序的运行期间一直占用这些存......
  • 初识设计模式 - 原型模式
    简介对于大部分系统来说,创建对象包括申请内存、给成员变量赋值等过程,这些操作耗费的时间基本可以忽略不计。如果对象中的数据需要经过复杂的计算才能得到(比如排序、计算......
  • 06. 不急于开始原型设计;我知道你很兴奋!
    06.不急于开始原型设计;我知道你很兴奋!设计思维不仅仅适用于用户体验设计师。我很感激我的开发者们单枪匹马地开发了一个产品;你在家庭中扮演父母双方的角色。你必须做出......
  • 深入理解左值、右值
    深入理解左值、右值作者:高性能架构探索链接:https://www.zhihu.com/question/428340896/answer/2353437577恰好之前写过一篇类似文章,发表于公众号【高性能架构探索】......
  • 深入理解Redis
         Redis在互联网架构存储系统中是使用最为广泛的中间件。基于内存实现了多中数据结构,通常被用作与内存数据库、缓存、消息队列和流引擎。Redis提供多种数据结构......
  • [Jetpack Compose] popUpTo 的一些理解
    开发应用时遇到一个需求:无论处于哪个页面,按返回键时都弹出退出应用的提示。我用了BackHandler处理返回事件,发现只有处于主页时才可触发回调,于是思考应该是导航相关的问......
  • 软件工程理解
     什么是软件?通俗的说,软件是一个集合,是程序+数据+文档的集合。并不是大部分普通人认为的软件仅仅是程序。软件工程是一门研究用工程化方法构建和维护有效的、实用的和高质......