【面试题】关于JavaScript实现继承的六大方案,你都了解过吗?

这篇具有很好参考价值的文章主要介绍了【面试题】关于JavaScript实现继承的六大方案,你都了解过吗?。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

【面试题】关于JavaScript实现继承的六大方案,你都了解过吗?

大厂面试题分享 面试题库

前后端面试题库 (面试必备) 推荐:★★★★★

地址:前端面试题库  web前端面试题库 VS java后端面试题库大全文章来源地址https://www.toymoban.com/news/detail-434577.html

前言

面试官:“你说说 JavaScript 中实现继承有哪几种方法?”

紧张的萌新:“额,class 中用 extends 实现继承,然后...没了...”

面试官:“...”

······

想必绝大部分人一说继承就会想到类中的继承吧,但其实继承可不是 class 的专利,本文将总结📕 JavaScript 中关于继承的几种方案,其中包括原型链,盗用构造函数、组合式等等,助你力压面试官💪。

注意:本文比较适合具备一定 JS 进阶基础的同学(不会也没关系,收藏就会了😄),涉及知识点有:原型、原型链、构造函数、this指向等。如果文中有不对、疑惑的地方,欢迎在评论区留言指正🌻

0. 继承

继承是面向对象编程中讨论最多的话题。很多面向对象语言都支持两种继承:接口继承和实现继承。前者只继承方法签名,后者继承实际的方法。 接口继承在 ECMAScript中 是不可能的,因为函数没有签名。实现继承是 ECMAScript 唯一支持的继承方式,而这主要是通过原型链实现的。

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 代码解读

以上代码定义了两个构造函数:Person 和 Student。这两个构造函数分别定义了一个属性和一个方法。

这两个类型的主要区别是 Student 通过创建 Person 的实例并将其赋值给自己的原型 Student.prototype 实现了对 Person 的继承。

这个赋值重写了 Student 最初的原型,将其替换为 Person 的实例。这意味着 Person 实例可以访问的所有属性和方法也会存在于Student.prototype。这样实现继承之后,代码紧接着又给Student.prototype,也就是这个 Person 的实例添加了 一个新方法。最后又创建了 Student 的实例并调用了它继承的getPersonValue()方法。

下图展示了子类的实例与两个构造函数及其对应的原型之间的关系:

【面试题】关于JavaScript实现继承的六大方案,你都了解过吗?

1.2 代码核心解析

这个例子中实现继承的关键,是 Student 没有使用默认原型,而是将其替换成了一个新的对象。这个新的对象恰好是 Person 的实例。这样一来,Student 的实例不仅能从 Person 的实例中继承属性和方法,而且还与 Person 的原型挂上了钩。于是 stu(通过内部的 [[Prototype]] )指向Student.prototype,而Student.prototype(作为 Person 的实例又通过内部的 [[Prototype]] )指向Person.prototype

注意1:getPersonValue() 方法还在Person.prototype对象上,而 name 属性则在Student.prototype上。这是因为 getPersonValue() 是一个原型方法,而 name 是一个实例属性。Student.prototype现在是 Person 的一个实例,因此 name 才会存储在它上面。

注意2:由于 Student.prototype 的 constructor 属性被重写为指向 Person,所以 stu.constructor 也指向 Person 。

1.3 默认原型

实际上,原型链中还有一环。默认情况下,所有引用类型都继承自 Object ,这也是通过原型链实现的。任何函数的默认原型都是一个 Object 的实例,这意味着这个实例有一个内部指针指向Object.prototype。这也是为什么自定义类型能够继承包括 toString() 、valueOf() 在内的所有默认方法的原因。因此前面的例子还有额外一层继承关系。

下图展示了完整的原型链。

【面试题】关于JavaScript实现继承的六大方案,你都了解过吗?

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()方法正常有效。寄生式组合继承可以算是引用类型继承的最佳模式。

大厂面试题分享 面试题库

前后端面试题库 (面试必备) 推荐:★★★★★

地址:前端面试题库  web前端面试题库 VS java后端面试题库大全

到了这里,关于【面试题】关于JavaScript实现继承的六大方案,你都了解过吗?的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • Javascript 如何实现继承?

    简单来说 js 实现继承的方式有很多种,比如 原型链继承、借用构造函数继承、组合继承、寄生继承、寄生组合继承、ES6 新增的 extends 继承 一般我开发中用的比较多的就是 原型链继承 还有 class 类函数继承。 其他的基本用得少,主要是平时看一些技术博客类文章了解过一些

    2024年02月16日
    浏览(32)
  • JavaScript ES6实现继承

    1 对象的方法补充 2 原型继承关系图 3 class方式定义类 4 extends实现继承 5 extends实现继承 6 多态概念的理 function 创建的名称如果开头是大写的,那这个创建的不是函数,是创建了类。 可以把class创建的类当做是function创建的类的一种语法糖。但是在直接使用的方面是有不同之处

    2024年02月16日
    浏览(44)
  • web前端Javascript—7道关于前端的面试题

    本文主要是web前端Javascript—的面试题,附上相关问题以及解决答案,希望对大家web前端Javascript闭包的学习有所帮助。 每个JavaScript 程序员都必须知道闭包是什么。在 JavaScript 面试中,你很可能会被问到的问题 以下是 7 个有关 JavaScript的面试题,比较有挑战性。不要查看答案

    2024年02月03日
    浏览(95)
  • JavaScript里实现继承的几种方式

    JavaScript 中的继承可以通过以下几种方式来实现: 1、原型链继承 :通过将子类的原型对象指向父类的实例来实现继承。这种方式的优点是实现简单,缺点是父类的私有属性和方法子类是不能访问的。   2、借用构造函数继承 :通过在子类的构造函数中调用父类的构造函数来

    2023年04月23日
    浏览(50)
  • 一文让你搞懂javascript如何实现继承

    一、本文想给你聊的东西包含一下几个方面:(仅限于es6之前的语法哈,因为es6里面class这用上了。。) 1.原型是啥?原型链是啥? 2.继承的通用概念。 3.Javascript实现继承的方式有哪些?   二、原型是啥?原型链是啥? 1.原型是函数本身的prototype属性。 首先js和java不

    2024年02月04日
    浏览(53)
  • 一篇文章告诉你JavaScript 如何实现继承

    JavaScript 在编程语言界是个特殊种类,它和其他编程语言很不一样,JavaScript 可以在运行的时候动态地改变某个变量的类型。 比如你永远也没法想到像isTimeout这样一个变量可以存在多少种类型,除了布尔值true和false,它还可能是undefined、1和0、一个时间戳,甚至一个对象。 如

    2024年02月10日
    浏览(36)
  • 第4集丨JavaScript 使用原型(prototype)实现继承——最佳实战2

    书接上集,在上集中我们给出了一个需求说明,要求利用现学的知识实现原型( prototype )继承,并且我们给出了三种实现方式。但是这三种方式各自有优缺点,都不能很好的满足要求,那是否还有其他更好的实现方式呢? 在看本文之前,诸位可以自己思考下?带着问题去学

    2024年02月12日
    浏览(43)
  • 第6集丨JavaScript 使用原型(prototype)实现继承——最佳实战3

    对于继承应用来说,主要目标是将一些现有的功能归为己有。也就是说,我们在新建一个对象时,通常首先应该继承于现有对象,然后在为其添加额外的方法和属性。对此,我们可以通过一个函数调用来完成,并且在其中混合使用我们刚才所讨论的两种方式: 使用原型继承的

    2024年02月16日
    浏览(48)
  • 第3集丨JavaScript 使用原型(prototype)实现继承——最佳实战1

    在 JavaScript 中, 继承机制的基础是 原型 ,(包括内部原型 __proto__ 和 prototype )。当访问一个对象的属性时, JavaScript 引擎是这么搜索的:如果在本对象中找不到一个属性时,就会去其原型对象中找,如果原型对象中还没找到的话,就去到原型对象中的原型中去找,直到顶级

    2024年02月15日
    浏览(70)
  • 关于GPT的20个知识,你都知道吗?

    1. GPT全称是什么?  GPT全称是Generative Pre-trained Transformer。 2. GPT是由谁研发的? GPT是由OpenAI的研究人员研发的。 3. GPT的目的是什么? GPT的目的是通过无监督的预训练 obtain 语言理解能力,然后应用于下游的NLP任务。 4. GPT是什么类型的模型? GPT属于transformer模型家族,是一种基于注意

    2024年02月08日
    浏览(40)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包