原型、继承等相关问题

Youky ... 2021-10-11 前端
  • JS
About 3 min

# 原型、继承等相关问题

# 对原型链的理解

JS 中,每个函数都有一个prototype属性指向自己的原型对象,原型对象则有一个constructor属性指向该函数。

当通过new调用一个函数时,这个函数就作为了构造函数,它返回一个实例对象,这个对象有一个__proto__属性,也指向函数的原型对象。

因此,每个对象的__proto__都指向其原型对象,形成了一条链,也就是原型链几乎所有对象都是位于顶端的Object的实例,Objectproto指向null,作为原型链的结束。

什么对象不是 Object 的实例呢?

对于这个问题,可以参考这篇文章 (opens new window)

简单来说就是自己进行构造:

const obj = Object.create(null);
Object.prototype.isPrototypeOf(obj); // false
1
2

# this

# this 的指向问题

  1. 对象的方法内的 this,通过对象调用函数时,指向调用的对象

  2. 直接调用的函数,指向全局对象windowglobal

  3. 通过 new 调用构造函数,内部的 this 指向新创建的对象

  4. 箭头函数:没有 this,内部作用域为函数声明时所在的上下文

    var x = 11;
    var obj = {
      x: 22,
      say: () => {
        // 定义时的上下文为obj对象,obj的执行上下文是window
        console.log(this.x); //输出的值为11,即window.x
      },
    };
    
    var obj2 = {
      x: 22,
      say: function() {
        console.log(x); // 11定义时的上下文为obj2对象,obj2的执行上下文是window
        console.log(this.x); // 22
      },
    };
    
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16

# 改变 this 的指向

三种改变的方法:

  1. apply:将所有参数作为一个数组传入

  2. call:将参数一个个传入

  3. bind:将参数一个个传入。

    bind 与前者的区别,调用后返回一个函数,而不是直接执行。

箭头函数因为没有 this,这三种方法不起作用

# new

三种创建对象的方式:

  1. 字面量
  2. new
  3. Object.create

    接收两个参数,分别是新创建对象的原型对象,以及在新对象上添加的属性

# new 一个对象的过程

  1. 创建一个空对象obj,将obj.__proto__指向构造函数的prototype
  2. this指向obj
  3. 执行构造函数中的代码
  4. 若函数没有用 return 返回其他对象,return obj

# 三者创建对象的区别

  • 通过字面量或 new 创建的对象,原型指向 Object.prototype,会继承 Object 的属性和方法
  • Object.create(null)创建的对象,其原型指向 null,即本身为原型链的顶端

# 继承的 5 种实现方式

  1. 在构造函数中使用 call 调用父类构造函数
    • 缺点:只能继承父类自身的属性,其原型对象中的属性无法继承
function Parent1() {
  this.name = "parent1";
}
function Child1() {
  Parent1.call(this);
  this.type = "child1";
}
1
2
3
4
5
6
7
  1. 原型链继承
    • 可以复用父类构造函数原型上的属性
    • 缺点:共享父类构造函数的引用类型的属性
function Parent2() {
  this.name = "parent2";
  this.play = [1, 2, 3];
}
function Child2() {
  this.type = "child2";
}
Child2.prototype = new Parent2();
1
2
3
4
5
6
7
8
  1. 组合继承
    • 可复用方法,不会共用引用属性
    • 缺点:子类的对象,其 constructor 属性指向父类构造函数
function Parent3() {
  this.name = "parent3";
  this.play = [1, 2, 3];
}
function Child3() {
  Parent3.call(this);
  this.type = "child3";
}
Child3.prototype = new Parent3();
1
2
3
4
5
6
7
8
9
  1. 寄生组合继承
function Parent2() {
  this.name = "parent2";
  this.play = 1;
}
function Child2() {
  this.type = "child2";
  Parent2.call(this);
}
Child2.prototype = Object.create(Parent2.prototype, {
  constructor: {
    value: Child2,
  },
});
1
2
3
4
5
6
7
8
9
10
11
12
13
  1. ES6 的 extend
Last update: October 16, 2022 21:28
Contributors: youky7 , Youky