原型链的理解

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

复制代码
<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>

 



posted @   天青色等烟雨灬  阅读(46)  评论(0编辑  收藏  举报
(评论功能已被禁用)
相关博文:
阅读排行:
· 分享一个免费、快速、无限量使用的满血 DeepSeek R1 模型,支持深度思考和联网搜索!
· 25岁的心里话
· 基于 Docker 搭建 FRP 内网穿透开源项目(很简单哒)
· 闲置电脑爆改个人服务器(超详细) #公网映射 #Vmware虚拟网络编辑器
· ollama系列01:轻松3步本地部署deepseek,普通电脑可用
点击右上角即可分享
微信分享提示