目次
0.継承
1. プロトタイプ チェーンの継承 [オプション 1]
1.1 コードの解釈
1.2 コード コア分析
1.3 デフォルトのプロトタイプ
1.4 原型与继承的关系
1.4.1 instanceof
1.4.2 isPrototypeOf()
1.5 关于方法
1.6 原型链继承的缺陷
2. 盗用构造函数继承【方案二】
2.1 传递参数
2.2 盗用构造函数继承的缺陷
3. 组合继承【方案三】
4. 原型式继承【方案四】
4.1 方法一:object
4.2 方法二:Object.create()
5. 寄生式继承【方案五】
6. 寄生式组合继承【方案六】
6.1 组合式继承的缺陷
6.2 解决方法
7. 最後まで書く
ホームページ ウェブフロントエンド jsチュートリアル JavaScript で継承を実装する 6 つの方法について話しましょう

JavaScript で継承を実装する 6 つの方法について話しましょう

Nov 02, 2022 pm 08:05 PM
javascript

JavaScript で継承を実装する 6 つの方法について話しましょう

インタビュアー:「JavaScript で継承を実装するにはどのようなメソッドがあるのか​​教えていただけますか?」

緊張した新参者:「そうですね、継承を実装するにはクラス内で extends を使用します。そして...去っていきました..."

インタビュアー: "..."

....

あなたがこう言ったとき、ほとんどの人がこう言うと思います。継承というと、クラスでの継承を思い浮かべるでしょうが、実際、継承はクラスの特許ではありません。この記事では、Interviewer の問題を克服するのに役立つ、プロトタイプ チェーン、盗まれたコンストラクター、組み合わせなど、JavaScript でのいくつかの継承ソリューションをまとめます。 。

: この記事は、JS の高度な基礎を持っている学生に適しています (知らなくても大丈夫です。収集できますか?)。関係する知識ポイントは、プロトタイプ、プロトタイプ チェーン、コンストラクター、this ポイントなどです。記事に間違いや疑問がある場合は、コメント欄にメッセージを残して修正してください?

0.継承

継承は最も議論されているトピックです。オブジェクト指向プログラミングで。多くのオブジェクト指向言語は、インターフェイスの継承と実装の継承という 2 種類の継承をサポートしています。前者はメソッドのシグネチャのみを継承し、後者は実際のメソッドを継承します。関数には署名がないため、ECMAScript ではインターフェイスの継承はできません。実装の継承は ECMAScript でサポートされる唯一の継承方法であり、これは主にプロトタイプ チェーンを通じて実現されます。

1. プロトタイプ チェーンの継承 [オプション 1]

ECMA-262 では、ECMAScript の主な継承方法としてプロトタイプ チェーンが定義されています。基本的な考え方は、プロトタイプを通じて複数の参照型のプロパティとメソッドを継承することです。コンストラクター、プロトタイプ、インスタンス間の関係を再確認します。

  • 各コンストラクターには、プロトタイプ オブジェクトを指す prototype 属性があります。
  • すべてのプロトタイプ オブジェクトは自動的に取得されます。プロパティ名前付き constructor は、関連するコンストラクター

をポイントバックし、インスタンスはプロトタイプへの内部ポインターを持ちます。プロトタイプが別の型のインスタンスの場合はどうなるでしょうか?これは、プロトタイプ自体が別のプロトタイプへの内部ポインターを持ち、対応する他のプロトタイプも別のコンストラクターへのポインターを持っていることを意味します。これにより、インスタンスとプロトタイプの間に プロトタイプ チェーン が構築されます。これがプロトタイプチェーンの基本的な考え方です。

プロトタイプ チェーンの継承の実装には、次のコード パターンが含まれます。

// 定义 Person 构造函数
function Person() {
  this.name = 'CoderBin'
}

// 给 Person 的原型上添加 getPersonValue 方法(原型方法)
Person.prototype.getPersonValue = function() {
  return this.name
}

// 定义 Student 构造函数
function Student() {
  this.sno = '001'
}

// 继承 Person — 将 Peson 的实例赋值给 Student 的原型
Student.prototype = new Person()

Student.prototype.getStudentValue = function() {
  return this.sno
}

// 实例化 Student
let stu = new Student()

console.log(stu.getPersonValue()) // CoderBin
ログイン後にコピー

1.1 コードの解釈

上記のコードは、2 つのコンストラクター、Person と Student を定義します。これら 2 つのコンストラクターは、それぞれプロパティとメソッドを定義します。

これら 2 つの型の主な違いは、Student は Person のインスタンスを作成し、それを独自のプロトタイプ Student.prototype に割り当てることによって Person を継承することです。

この割り当てでは、Student の元のプロトタイプを書き換えて、Person のインスタンスに置き換えます。これは、Person インスタンスにアクセスできるすべてのプロパティとメソッドが Student.prototype にも存在することを意味します。この方法で継承を実装した後、コードは、新しいメソッドを Student.prototype に追加します。これは、Person のインスタンスです。最後に、Student のインスタンスが作成され、その継承された getPersonalValue() メソッドが呼び出されます。

次の図は、サブクラスのインスタンスと 2 つのコンストラクターおよびそれらに対応するプロトタイプとの関係を示しています。

JavaScript で継承を実装する 6 つの方法について話しましょう

1.2 コード コア分析

#この例で継承を実装する鍵となるのは、Student がデフォルトのプロトタイプを使用せず、それを新しいオブジェクトに置き換えることです。この新しいオブジェクトはたまたま Person のインスタンスです。このようにして、Student インスタンスは person インスタンスからプロパティとメソッドを継承するだけでなく、person プロトタイプにもフックされます。したがって、stu は (内部 [[Prototype]] 経由) Student.prototypeStudent.prototype (内部 [[Prototype]] 経由で Person のインスタンスとして) を指します。 ) person.prototype を指します。

注 1: getPersonValue() メソッドは引き続き Person.prototype オブジェクト上にあり、name 属性は Student.prototype 上にあります。 。これは、getPersonValue() がプロトタイプ メソッドであり、name がインスタンス プロパティであるためです。 Student.prototype は Person のインスタンスになったので、名前が保存されます。

注 2: Student.prototype のコンストラクター プロパティは person を指すように書き換えられるため、stu.constructor も person を指します。

1.3 デフォルトのプロトタイプ

実際、プロトタイプ チェーンには別のリンクがあります。デフォルトでは、すべての参照型は、やはりプロトタイプ チェーンを通じて Object から継承します。関数のデフォルトのプロトタイプは Object のインスタンスです。これは、このインスタンスが Object.prototype を指す内部ポインターを持っていることを意味します。これが、カスタム型が toString() や valueOf() を含むすべてのデフォルト メソッドを継承できる理由です。したがって、前の例には追加の継承層があります。

次の図は、完全なプロトタイプ チェーンを示しています。

JavaScript で継承を実装する 6 つの方法について話しましょう

Student 继承 Person ,而 Person 继承 Object 。在调用 stu.toString() 时,实际上调用的是保存在 Object.prototype 上的方法。

1.4 原型与继承的关系

原型与实例的关系可以通过两种方式来确定。

1.4.1 instanceof

第一种方式是使用instanceof操作符,如果一个实例的原型链中出现过相应的构造函数,则instanceof返回 true 。如下例所示:

console.log(stu instanceof Object)    // true
console.log(stu instanceof Person)    // true
console.log(stu instanceof Student)   // true
ログイン後にコピー

从技术上讲,stu 是 Object、Person 和 Student 的实例,因为 stu 的原型链中包含这些构造函数的原型。结果就是 instanceof 对所有这些构造函数都返回 true 。

1.4.2 isPrototypeOf()

确定这种关系的第二种方式是使用isPrototypeOf()方法。原型链中的每个原型都可以调用这个方法,如下例所示,只要原型链中包含这个原型,这个方法就返回 true 。

console.log(Object.prototype.isPrototypeOf(stu))    // true
console.log(Person.prototype.isPrototypeOf(stu))    // true
console.log(Student.prototype.isPrototypeOf(stu))   // true
ログイン後にコピー

1.5 关于方法

子类有时候需要覆盖父类的方法,或者增加父类没有的方法。为此, 这些方法必须在原型赋值之后再添加到原型上。来看下面的例子:

// 定义 Person 构造函数
function Person() {
  this.name = 'CoderBin'
}

// 给 Person 的原型上添加 getPersonValue 方法(原型方法)
Person.prototype.getPersonValue = function() {
  return this.name
}

// 定义 Student 构造函数
function Student() {
  this.sno = '001'
}

// 继承 Person
Student.prototype = new Person()

// 新方法 —— 1
Student.prototype.getStudentValue = function() {
  return this.sno
}

// 覆盖已有的方法 —— 2
Student.prototype.getPersonValue = function() {
  return 'Bin'
}

// 实例化 Student
let stu = new Student()

console.log(stu.getPersonValue()) // Bin
ログイン後にコピー

在上面的代码中,注释1、2的部分涉及两个方法。

  • 第一个方法 getStudentValue() 是 Student 的新方法,
  • 第二个方法 getPersonValue() 是原型链上已经存在但在这里被遮蔽的方法。

后面在 Student 实例上调用 getPersonValue() 时调用的是2这个方法。而 Person 的实例仍然会调用最初的方法。

重点一:上述两个方法都是在把原型赋值为 Person 的实例之后定义的。

重点二:另一个要理解的重点是,以对象字面量方式创建原型方法会破坏之前的原型链,因为这相当于重写了原型链。下面是一个例子:

// 定义 Person 构造函数
function Person() {
  this.name = 'CoderBin'
}

// 给 Person 的原型上添加 getPersonValue 方法(原型方法)
Person.prototype.getPersonValue = function() {
  return this.name
}

// 定义 Student 构造函数
function Student() {
  this.sno = '001'
}

// 继承 Person
Student.prototype = new Person()

// 通过对象字面量添加新方法,这会导致上一行无效!!!
Student.prototype = {
  getStudentValue() {
    return this.sno
  },
  someOtherMethod() {
    return 'something'
  }
}

// 实例化 Student
let stu = new Student()

console.log(stu.getPersonValue())  // TypeError: stu.getPersonValue is not a function
ログイン後にコピー

在这段代码中,子类的原型在被赋值为 Person 的实例后,又被一个对象字面量覆盖了。覆盖后的原型是一个Object 的实例,而不再是 Person 的实例。因此之前的原型链就断了。Student 和 Person 之间也没有关系了。

1.6 原型链继承的缺陷

原型链虽然是实现继承的强大工具,但它也有问题。

主要问题出现在原型中包含引用值的时候。前面在谈到原型的问题时也提到过,原型中包含的引用值会在所有实例间共享,这也是为什么属性通常会在构造函数中定义而不会定义在原型上的原因。在使用原型实现继承时,原型实际上变成了另一个类型的实例【1】。这意味着原先的实例属性摇身一变成为了原型属性。下面的例子揭示了这个问题:

// 定义 Person 构造函数
function Person() {
  this.letters = ['a', 'b', 'c']
}

// 定义 Student 构造函数
function Student() {
  this.sno = '001'
}

// 继承 Person
Student.prototype = new Person()

let stu1 = new Student()
let stu2 = new Student()

stu1.letters.push('d')

console.log(stu1.letters)  // ['a', 'b', 'c', 'd']
console.log(stu2.letters)  // ['a', 'b', 'c', 'd']
ログイン後にコピー

代码解析: 在这个例子中,Person 构造函数定义了一个 letters 属性,其中包含一个数组(引用值)。每个 Person 的实例都会有自己的 letters 属性,包含自己的数组。但是,当 Student 通过原型继承 Person 后,Student.prototype变成了 Person 的一个实例,因而也获得了自己的 letters 属性。这类似于创建了Student.prototype.letters 属性。最终结果是,Student 的所有实例都会共享这个 letters 属性。这一点通过 stu1.letters 上的修改也能反映到 stu2.letters 上就可以看出来。

原型链的第二个问题是,子类型在实例化时不能给父类型的构造函数传参【2】。事实上,我们无法在不影响所有对象实例的情况下把参数传进父类的构造函数。再加上之前提到的原型中包含引用值的问题,就导致原型链基本不会被单独使用。

2. 盗用构造函数继承【方案二】

为了解决原型包含引用值导致的继承问题,一种叫作“盗用构造函数” (constructor stealing)的技术在开发社区流行起来(这种技术有时也称作“对象伪装”或“经典继承”)。基本思路很简单:在子类构造函数中调用父类构造函数。 因为毕竟函数就是在特定上下文中执行代码的简单对象,所以可以使用apply()call()方法以新创建的对象为上下文执 行构造函数。来看下面的例子:

// 定义 Person 构造函数
function Person() {
  this.letters = ['a', 'b', 'c']
}

// 定义 Student 构造函数
function Student() {
  // 继承 Person — 使用 call() 方法调用 Person 构造函数
  Person.call(this)
}

let stu1 = new Student()
let stu2 = new Student()

stu1.letters.push('d')

console.log(stu1.letters)  // ['a', 'b', 'c', 'd']
console.log(stu2.letters)  // ['a', 'b', 'c']
ログイン後にコピー

代码解析: 示例中继承 Person 那一行代码展示了盗用构造函数的调用。通过使用call() (或 apply() )方法,Person 构造函数在为 Student 的实例创建的新对象的上下文中执行了。这相当于新的 Student 对象上运行了 Person() 函数中的所有初始化代码。结果就是每个实例都会有自己的 letters 属性。

2.1 传递参数

相比于使用原型链,盗用构造函数的一个优点就是可以在子类构造函数中向父类构造函数传参。来看下面的例子:

// 定义 Person 构造函数
function Person(name) {
  this.name = name
}

// 定义 Student 构造函数
function Student(name) {
  // 继承 Person
  Person.call(this, name)
  // 实例属性
  this.age = 18
}

let stu = new Student('CoderBin')

console.log(stu.name)   // CoderBin
console.log(stu.age)     // 18
ログイン後にコピー

代码解析:在这个例子中,Person 构造函数接收一个参数 name ,然后将它赋值给一个属性。在 Student 构造函数中调用 Person 构造函数时传入这个参数,实际上会在 Student 的实例上定义 name 属性。为确保 Person 构造函数不会覆盖 Student 定义的属性,可以在调用父类构造函数之后再给子类实例添加额外的属性。

2.2 盗用构造函数继承的缺陷

盗用构造函数的主要缺点,也是使用构造函数模式自定义类型的问题:必须在构造函数中定义方法,因此函数不能重用。此外,子类也不能访问父类原型上定义的方法,因此所有类型只能使用构造函数模式。由于存在这些问题,盗用构造函数基本上也不能单独使用。

3. 组合继承【方案三】

组合继承 (有时候也叫伪经典继承)综合了原型链和盗用构造函数,将两者的优点集中了起来。基本的思路是:使用原型链继承原型上的属性和方法,而通过盗用构造函数继承实例属性。 这样既可以把方法定义在原型上以实现重用,又可以让每个实例都有自己的属性。来看下面的例子:

// 定义 Person 构造函数
function Person(name) {
  this.name = name
  this.letters = ['a', 'b', 'c']
}

// 在 Person 的原型上添加 sayName 方法
Person.prototype.sayName = function() {
  console.log(this.name + ' 你好~')
}

// 定义 Student 构造函数
function Student(name, age) {
  // 继承属性
  Person.call(this, name)
  this.age = age
}

// 继承方法
Student.prototype = new Person()

// 在 Student 的原型上添加 sayAge 方法
Student.prototype.sayAge = function() {
  console.log(this.age)
}

let stu1 = new Student('CoderBin', 18)
let stu2 = new Student('Bin', 23)

stu1.letters.push('d')

// 输出 stu1 的信息
console.log(stu1.letters)   // [ 'a', 'b', 'c', 'd' ]
stu1.sayName()               // CoderBin 你好~
stu1.sayAge()                 // 18

// 输出 stu2 的信息
console.log(stu2.letters)   // [ 'a', 'b', 'c']
stu2.sayName()               // Bin 你好~
stu2.sayAge()                 // 23
ログイン後にコピー

代码解析:在这个例子中,Person 构造函数定义了两个属性,name 和 letters ,而它的原型上也定义了一个方法叫 sayName() 。Student 构造函数调用了 Person 构造函数,传入了 name 参数,然后又定义了自己的属性 age 。

此外,Student.prototype 也被赋值为 Person 的实例。 原型赋值之后,又在这个原型上添加了新方法sayAge() 。这样,就可以创建两个 Student 实例,让这两个实例都有自己的属性,包括 letters , 同时还共享相同的方法。

最后:组合继承弥补了原型链和盗用构造函数的不足,是 JavaScript 中使用最多的继承模式。而且组合继承也保留了instanceof操作符和isPrototypeOf()方法识别合成对象的能力。

4. 原型式继承【方案四】

2006年,Douglas Crockford(JSON之父) 写了一篇文章:《JavaScript中的原型式继承》(“Prototypal Inheritance in JavaScript”)。这篇文章介绍了 一种不涉及严格意义上构造函数的继承方法。他的出发点是即使不自定义类型也可以通过原型实现对象之间的信息共享。文章最终给出了一个函数:

function object(o) {
  function F() {}
  F.prototype = o
  return new F()
}
ログイン後にコピー

这个object() 函数会创建一个临时构造函数,将传入的对象赋值给这个构造函数的原型,然后返回这个临时类型的一个实例。

4.1 方法一:object

本质上,object() 是对传入的对象执行了一次浅复制。 来看下面的例子:

function object(o) {
  function F() {}
  F.prototype = o
  return new F()
}

let person = {
  name: 'CoderBin',
  letters: ['a', 'b', 'c']
}

let p1 = object(person)
let p2 = object(person)

p1.name = 'p1'
p1.letters.push('d')

p2.name = 'p2'
p2.letters.push('e')

console.log(person.letters)   // [ 'a', 'b', 'c', 'd', 'e' ]
ログイン後にコピー

代码解析:在这个例子中,person 对象定义了另一个对象也应该共享的信息,把它传给 object() 之后会返回一个新对象。这个新对象的原型是 person ,意味着它的原型上既有原始值属性又有引用值属性。这也意味着 person.letters 不仅是 person 的属性,也会跟 p1 和 p2 共享。这里实际上克隆了两个 person 。

Crockford推荐的原型式继承适用于这种情况:你有一个对象,想在它的基础上再创建一个新对象。你需要把这个对象先传给 object() ,然后再对返回的对象进行适当修改。

4.2 方法二:Object.create()

ECMAScript5 通过增加Object.create()方法将原型式继承的概念规范化了。这个方法接收两个参数:作为新对象原型的对象,以及给新对象定义额外属性的对象(第二个可选)。在只有一个参数时,Object.create() 与这里的object()方法效果相同:

let person = {
  name: 'CoderBin',
  letters: ['a', 'b', 'c']
}

let p1 = Object.create(person)
let p2 = Object.create(person)

p1.name = 'p1'
p1.letters.push('d')

p2.name = 'p2'
p2.letters.push('e')

console.log(person.letters)   // [ 'a', 'b', 'c', 'd', 'e' ]
ログイン後にコピー

Object.create()的第二个参数与Object.defineProperties()的第二个参数一样:每个新增属性都通过各自的描述符来描述。以这种方式添加的属性会遮蔽原型对象上的同名属性。比如:

let person = {
  name: 'CoderBin',
  letters: ['a', 'b', 'c']
}

let p1 = Object.create(person, {
  name: {
    value: 'CoderBin'
  }
})

console.log(p1.name)
ログイン後にコピー

原型式继承非常适合不需要单独创建构造函数,但仍然需要在对象间共享信息的场合。但要记住,属性中包含的引用值始终会在相关对象间共享,跟使用原型模式是一样的。

5. 寄生式继承【方案五】

与原型式继承比较接近的一种继承方式是寄生式继承 (parasitic inheritance),也是Crockford首倡的一种模式。寄生式继承背后的思路类似于寄生构造函数和工厂模式:创建一个实现继承的函数,以某种方式增强对象,然后返回这个对象。基本的寄生继承模式如下:

function inheritPrototype(o) {
  let clone = Object.create(o)  // 通过调用函数创建一个新对象
  clone.sayHi = function() {     // 以某种方式增强这个对象
    console.log('Hi~')
  }
  return clone  // 返回这个对象
}
ログイン後にコピー

代码解析:在这段代码中,inheritPrototype() 函数接收一个参数,就是新对象的基准对象。这个对象 o 会被传给Object.create()函数,然后将返回的新对象赋值给 clone 。接着给 clone 对象添加一个新方法 sayHi() 。最后返回这个对象。可以像下面这样使用 inheritPrototype() 函数:

let person = {
  name: 'CoderBin',
  letters: ['a', 'b', 'c']
}

let p1 = inheritPrototype(person)
p1.sayHi()  // Hi~
ログイン後にコピー

代码解析:这个例子基于 person 对象返回了一个新对象。新返回的 p1 对象具有 person 的所有属性和方法,还有一个新方法叫 sayHi() 。寄生式继承同样适合主要关注对象,而不在乎类型和构造函数的场景。Object.create()函数不是寄生式继承所必需的,任何返回新对象的函数都可以在这里使用。

注意: 通过寄生式继承给对象添加函数会导致函数难以重用,与构造函数模式类似。

6. 寄生式组合继承【方案六】

组合继承其实也存在效率问题。最主要的效率问题就是父类构造函数始终会被调用两次:一次在是创建子类原型时调用,另一次是在子类构造函数中调用。本质上,子类原型最终是要包含超类对象的所有实例属性,子类构造函数只要在执行时重写自己的原型就行了。

6.1 组合式继承的缺陷

再来看一看这个组合继承的例子:

// 定义 Person 构造函数
function Person(name) {
  this.name = name
  this.letters = ['a', 'b', 'c']
}

// 在 Person 的原型上添加 sayName 方法
Person.prototype.sayName = function() {
  console.log(this.name)
}

// 定义 Student 构造函数
function Student(name, age) {
  Person.call(this, name)   // 第一次调用 Person()
  this.age = age
}

Student.prototype = new Person()  // 第二次调用 Person()

// 让 Student 的原型指回 Student
Student.prototype.constructor = Student

// 在 Student 的原型上添加 sayAge 方法
Student.prototype.sayAge = function() {
  console.log(this.age)
}

let stu = new Student('CoderBin', 18)

console.log(stu)
// 输出:Student { name: 'CoderBin', letters: [ 'a', 'b', 'c' ], age: 18 }

console.log(Student.prototype)
// 输出:
// Person {
//   name: undefined,
//   letters: [ 'a', 'b', 'c' ],      
//   constructor: [Function: Student],
//   sayAge: [Function (anonymous)]   
// }
ログイン後にコピー

代码解析:代码中注释的部分是调用 Person 构造函数的地方。在上面的代码执行后,Student.prototype上会有两个属性:name 和 letters 。它们都是 Person 的实例属性,但现在成为了 Student 的原型属性。在调用 Student 构造函数时,也会调用 Person 构造函数,这一次会在新对象上创建实例属性 name 和 letters 。这两个实例属性会遮蔽原型上同名的属性。

所以,执行完上面的代码后,有两组 name 和 letters 属性:一组在实例上,另一组在 Student 的原型上。这是调用两次 Person 构造函数的结果。

6.2 解决方法

寄生式组合继承通过盗用构造函数继承属性,但使用混合式原型链继承方法。基本思路是不通过调用父类构造函数给子类原型赋值,而是取得父类原型的一个副本。说到底就是使用寄生式继承来继承父类原型,然后将返回的新对象赋值给子类原型。寄生式组合继承的基本模式如下所示:

function inheritPrototype(subType, superType) {
  let prototype = Object.create(superType.prototype)   // 创建对象
  prototype.constructor = subType                             // 增强对象
  subType.prototype = prototype                               // 赋值对象
}
ログイン後にコピー

代码解析:这个 inheritPrototype() 函数实现了寄生式组合继承的核心逻辑。这个函数接收两个参数:子类构造函数和父类构造函数。在这个函数内部,第一步是创建父类原型的一个副本。然后,给返回的prototype 对象设置 constructor 属性,解决由于重写原型导致默认 constructor 丢失的问题。最后将新创建的对象赋值给子类型的原型。如下例所示,调用 inheritPrototype() 就可以实现前面例子中的子类型原型赋值:

// 定义 Person 构造函数
function Person(name) {
  this.name = name
  this.letters = ['a', 'b', 'c']
}

// 在 Person 的原型上添加 sayName 方法
Person.prototype.sayName = function() {
  console.log(this.name)
}

// 定义 Student 构造函数
function Student(name, age) {
  Person.call(this, name)
  this.age = age
}
// 调用 inheritPrototype() 函数,传入 子类构造函数 和 父类构造函数
inheritPrototype(Student, Person)

// 在 Person 的原型上添加 sayAge 方法
Student.prototype.sayAge = function() {
  console.log(this.age)
}

let stu = new Student('CoderBin', 18)

console.log(stu)
// 输出:Student { name: 'CoderBin', letters: [ 'a', 'b', 'c' ], age: 18 }

console.log(Student.prototype)
// 输出
// Person {
//   constructor: [Function: Student],
//   sayAge: [Function (anonymous)]   
// }
ログイン後にコピー

这里只调用了一次 Person 构造函数,避免了Student.prototype上不必要也用不到的属性,因此可以说这个例子的效率更高。而且,原型链仍然保持不变,因此instanceof操作符和isPrototypeOf()方法正常有效。寄生式组合继承可以算是引用类型继承的最佳模式。

7. 最後まで書く

ここまでJavaScriptにおける継承の6つの方法をまとめましたが、ここまで粘っていただければ継承はできると思います。十分に知識を習得しました。もちろん、JS には、このポインティングなど、他にも非常に重要な知識ポイントがあります。 をクリックすると、この ✍ の 4 つのバインディング ルールを理解するのに役立つ記事が表示されます。

[推奨学習: JavaScript 上級チュートリアル]

以上がJavaScript で継承を実装する 6 つの方法について話しましょうの詳細内容です。詳細については、PHP 中国語 Web サイトの他の関連記事を参照してください。

このウェブサイトの声明
この記事の内容はネチズンが自主的に寄稿したものであり、著作権は原著者に帰属します。このサイトは、それに相当する法的責任を負いません。盗作または侵害の疑いのあるコンテンツを見つけた場合は、admin@php.cn までご連絡ください。

ホットAIツール

Undresser.AI Undress

Undresser.AI Undress

リアルなヌード写真を作成する AI 搭載アプリ

AI Clothes Remover

AI Clothes Remover

写真から衣服を削除するオンライン AI ツール。

Undress AI Tool

Undress AI Tool

脱衣画像を無料で

Clothoff.io

Clothoff.io

AI衣類リムーバー

Video Face Swap

Video Face Swap

完全無料の AI 顔交換ツールを使用して、あらゆるビデオの顔を簡単に交換できます。

ホットツール

メモ帳++7.3.1

メモ帳++7.3.1

使いやすく無料のコードエディター

SublimeText3 中国語版

SublimeText3 中国語版

中国語版、とても使いやすい

ゼンドスタジオ 13.0.1

ゼンドスタジオ 13.0.1

強力な PHP 統合開発環境

ドリームウィーバー CS6

ドリームウィーバー CS6

ビジュアル Web 開発ツール

SublimeText3 Mac版

SublimeText3 Mac版

神レベルのコード編集ソフト(SublimeText3)

WebSocket と JavaScript を使用してオンライン音声認識システムを実装する方法 WebSocket と JavaScript を使用してオンライン音声認識システムを実装する方法 Dec 17, 2023 pm 02:54 PM

WebSocket と JavaScript を使用してオンライン音声認識システムを実装する方法 はじめに: 技術の継続的な発展により、音声認識技術は人工知能の分野の重要な部分になりました。 WebSocket と JavaScript をベースとしたオンライン音声認識システムは、低遅延、リアルタイム、クロスプラットフォームという特徴があり、広く使用されるソリューションとなっています。この記事では、WebSocket と JavaScript を使用してオンライン音声認識システムを実装する方法を紹介します。

WebSocket と JavaScript: リアルタイム監視システムを実装するための主要テクノロジー WebSocket と JavaScript: リアルタイム監視システムを実装するための主要テクノロジー Dec 17, 2023 pm 05:30 PM

WebSocketとJavaScript:リアルタイム監視システムを実現するためのキーテクノロジー はじめに: インターネット技術の急速な発展に伴い、リアルタイム監視システムは様々な分野で広く利用されています。リアルタイム監視を実現するための重要なテクノロジーの 1 つは、WebSocket と JavaScript の組み合わせです。この記事では、リアルタイム監視システムにおける WebSocket と JavaScript のアプリケーションを紹介し、コード例を示し、その実装原理を詳しく説明します。 1.WebSocketテクノロジー

JavaScript と WebSocket を使用してリアルタイムのオンライン注文システムを実装する方法 JavaScript と WebSocket を使用してリアルタイムのオンライン注文システムを実装する方法 Dec 17, 2023 pm 12:09 PM

JavaScript と WebSocket を使用してリアルタイム オンライン注文システムを実装する方法の紹介: インターネットの普及とテクノロジーの進歩に伴い、ますます多くのレストランがオンライン注文サービスを提供し始めています。リアルタイムのオンライン注文システムを実装するには、JavaScript と WebSocket テクノロジを使用できます。 WebSocket は、TCP プロトコルをベースとした全二重通信プロトコルで、クライアントとサーバー間のリアルタイム双方向通信を実現します。リアルタイムオンラインオーダーシステムにおいて、ユーザーが料理を選択して注文するとき

WebSocketとJavaScriptを使ったオンライン予約システムの実装方法 WebSocketとJavaScriptを使ったオンライン予約システムの実装方法 Dec 17, 2023 am 09:39 AM

WebSocket と JavaScript を使用してオンライン予約システムを実装する方法 今日のデジタル時代では、ますます多くの企業やサービスがオンライン予約機能を提供する必要があります。効率的かつリアルタイムのオンライン予約システムを実装することが重要です。この記事では、WebSocket と JavaScript を使用してオンライン予約システムを実装する方法と、具体的なコード例を紹介します。 1. WebSocket とは何ですか? WebSocket は、単一の TCP 接続における全二重方式です。

JavaScript と WebSocket: 効率的なリアルタイム天気予報システムの構築 JavaScript と WebSocket: 効率的なリアルタイム天気予報システムの構築 Dec 17, 2023 pm 05:13 PM

JavaScript と WebSocket: 効率的なリアルタイム天気予報システムの構築 はじめに: 今日、天気予報の精度は日常生活と意思決定にとって非常に重要です。テクノロジーの発展に伴い、リアルタイムで気象データを取得することで、より正確で信頼性の高い天気予報を提供できるようになりました。この記事では、JavaScript と WebSocket テクノロジを使用して効率的なリアルタイム天気予報システムを構築する方法を学びます。この記事では、具体的なコード例を通じて実装プロセスを説明します。私たちは

簡単な JavaScript チュートリアル: HTTP ステータス コードを取得する方法 簡単な JavaScript チュートリアル: HTTP ステータス コードを取得する方法 Jan 05, 2024 pm 06:08 PM

JavaScript チュートリアル: HTTP ステータス コードを取得する方法、特定のコード例が必要です 序文: Web 開発では、サーバーとのデータ対話が頻繁に発生します。サーバーと通信するとき、多くの場合、返された HTTP ステータス コードを取得して操作が成功したかどうかを判断し、さまざまなステータス コードに基づいて対応する処理を実行する必要があります。この記事では、JavaScript を使用して HTTP ステータス コードを取得する方法を説明し、いくつかの実用的なコード例を示します。 XMLHttpRequestの使用

JavaScriptでinsertBeforeを使用する方法 JavaScriptでinsertBeforeを使用する方法 Nov 24, 2023 am 11:56 AM

使用法: JavaScript では、insertBefore() メソッドを使用して、DOM ツリーに新しいノードを挿入します。このメソッドには、挿入される新しいノードと参照ノード (つまり、新しいノードが挿入されるノード) の 2 つのパラメータが必要です。

JavaScript と WebSocket: 効率的なリアルタイム画像処理システムの構築 JavaScript と WebSocket: 効率的なリアルタイム画像処理システムの構築 Dec 17, 2023 am 08:41 AM

JavaScript は Web 開発で広く使用されているプログラミング言語であり、WebSocket はリアルタイム通信に使用されるネットワーク プロトコルです。 2 つの強力な機能を組み合わせることで、効率的なリアルタイム画像処理システムを構築できます。この記事では、JavaScript と WebSocket を使用してこのシステムを実装する方法と、具体的なコード例を紹介します。まず、リアルタイム画像処理システムの要件と目標を明確にする必要があります。リアルタイムの画像データを収集できるカメラ デバイスがあるとします。

See all articles