Skip to content

[js] 第83天 举例说明js如何实现继承? #586

Open
@haizhilin2013

Description

@haizhilin2013
Collaborator

第83天 举例说明js如何实现继承?

Activity

ghost

ghost commented on Jul 8, 2019

@ghost

JavaScript 使用基于原型链的继承。访问一个对象的属性时若该属性在对象上不存在,则会沿原型链向下搜寻每个原型对象。

每个构造器都拥有 prototype 属性,代表该构造器的原型对象,初始为一个 Object 实例。所有用此构造器 new 出的对象都可以访问该对象的属性。

function Foo(bar) { this.bar = bar }
Foo.prototype.baz = 'test'
let qux = new Foo('hello')
qux.bar // => 对象本身的属性:'hello'
qux.baz // => 对象原型链上 Foo 的原型属性:'test'
// qux 的原型链:(qux ->) Foo.prototype -> Object.prototype

如果一个构造器 A 的 prototype 属性是另一个构造器 B 的实例,那么 B 的原型链会被接到 A 上,此时我们就说 A 继承了 B,A 实例可以访问所有 B 原型链上的属性。

function Foo2() {}
Foo2.prototype.baz2 ='test2'
Foo2.prototype = new Foo() // 将 Foo 的原型链接到 Foo2 上。Foo 来自上一个例子
qux = new Foo2()
qux.baz // 原型链上 Foo 的原型属性:'test'
qux.baz2 // 原型链上 Foo2 的原型属性:'test2'
// qux 的原型链:(qux ->) Foo2.prototype -> Foo.prototype -> Object.prototype
nowherebutup

nowherebutup commented on Jul 8, 2019

@nowherebutup

js常采用寄生组合式继承,每个对象特有的属性在构造函数重新创建,每个对象共用的属性方法写在原型对象上,利用每个对象的_proto_属性沿着原型链往原型找与键对应的属性,找到则返回对应的属性;
ES6中增加class语法糖,本质上没什么区别,但是统一了ES6之前五花八门的继承写法

  class Person{
    constructor(name){
      this.name = name;
    }
    sayHi(){
      console.log(`my name is ${this.name}`);
    }
  }

  class Man extends Person{
    constructor(name,age){
      super(name);
      this.age = age;
    }
    sayAge(){
      console.log(`I am ${this.name}, I am ${this.age} years old`);
    }
  }

  let p_man = new Man('tom',19);
  p_man.sayAge(); // I am tom, I am 19 years old
  p_man.sayHi(); // my name is tom
AnsonZnl

AnsonZnl commented on Jul 8, 2019

@AnsonZnl
Contributor

JavaScript 基于原型链实现的继承,简单来说就是通过对象的__proto__实现的向上查找。
比如你从未定义过toSing()方法,但是你却可以在任何地方使用它,原因就是当你使用xx.toString()时,他会先在自身查找看看没有这个方法,如果没有就根据__proto__寻找他的原型对象,看看他的原型对象上有没有....知道找到为止。
如何证明呢,做个试验:

var arr = [1,2,3 ];
console.log(arr.toString());//1,2,3

可以肯定的是,新创建的arr上没有定义toString()方法,我们知道数组的构造函数是Array(),可以重写一下toSing()方法:


Array.prototype.toString = function(){ 
   return 'Hello world' 
}
var arr = [1,2,3 ];
console.log(arr.toString());//'Hello world'

91562582654_ pic

MrZse

MrZse commented on Jul 9, 2019

@MrZse

JS实现继承的方式有以下几种:

1.通过构造函数实现继承:

function Parent1 () {
  this.name= '111';
}
function Child1 () {
  Parent1.call(this);
  this.type='222';
} 

通过这种方式,只能继承定义在父类构造函数内的属性与方法,定义在prototype原型对象内的属性与方法则无法继承,因此对其改进。

2.通过原型对象进行继承:

function Parent2 () {
  this.name= '111';
}
function Child2 () {
  this.type='222';
} 
Child2.prototype = new Parent2();

通过这种方式,则既能继承构造函数内的属性与方法,也能继承原型链上的属性与方法。但是,由于令其原型对象指向父类的一个实例对象,使得所有子类的实例对象所访问到的属性指向同一个对象,所以会出现改变一个子类实例对象的父类中的属性,另一个子类对象的属性也跟着改变。因此我们有下一个方法来改进:

3.组合方法进行继承:

function Parent3 () {
  this.name= '111';
}
function Child3 () {
  Parent3.call(this);
  this.type='222';
} 
Child3.prototype = new Parent3();

但是这种方法,使得父类的构造函数执行了两次,为了减少父类的构造函数的不必要的多次执行,如下修改代码。

4.组合方法进行继承优化:

function Parent4 () {
  this.name= '111';
}
function Child4 () {
  Parent4.call(this);
  this.type='222';
} 
Child4.prototype = Parent4.prototye;

这样解决了前面提到的问题,但是这样简单粗暴的继承,使子类的原型对象指向了父类的原型对象,会导致当子类实例对象通过constructor属性获取其构造函数时,获得的是父类的构造函数(因为constructor属性在prototype属性中被继承),因此再进行优化:

5.组合方法进行继承优化2:

function Parent5 () {
  this.name= '111';
}
function Child5 () {
  Parent5.call(this);
  this.type='222';
} 
Child5.prototype = Object.create(Parent5.prototype);
Child5.prototype.constructor = Child5;

由于Object.create()这个api的特性,父类的原型对象会继承在子类的原型对象的原型对象上,实现了子类原型对象与父类原型对象的隔离,这时再给子类的原型对象的constructor属性赋值。为什么直接在第四种方式的后面直接赋值呢?因为这是子类与父类的原型对象指向同一个对象,修改属性会同时修改子类与父类的原型对象。
这样5种实现继承的方法各自的优缺点都明了了。

imccode

imccode commented on Jul 9, 2019

@imccode

组合继承

function Parent() {
    this.params = 1
}
Parent.prototype.pro = "Parent"

function Child() {
    Parent.apply(this)
}
Child.prototype = new Parent()

var newChild = new Child()
newChild.params = 2
newChild.pro = "Child"

var newParent = new Parent()

console.log(newParent.params, newParent.pro)
console.log(newChild.params, newChild.pro)
xjt31012

xjt31012 commented on Jul 9, 2019

@xjt31012

使用es6里的class和extends继承
class childFunction extends parentFunction{}

songlovena

songlovena commented on Jan 25, 2021

@songlovena

@ ES5继承

es5的继承是根据原型链继承的;

function Person(name) {
    this.name = name;
}
Person.prototype.eat = function() {
    console.log('Person eat');
}

//Boy继承Person
function Boy(name, age) {
    Person.call(this, name);
    this.age = age;
}
Boy.prototype = Object.create(Person.prototype);
Boy.prototype.play = function() {
    console.log('Boys Play');
}
Boy.prototype.constructor = Boy;

@ ES6继承

es6的class其实只是一种语法糖;定义一个class类,使用typeof判断类型,输出function;因为在es6之前是没有类的概念的,只有构造函数创建实例对象。但是其他面向对象语言是有类的概念。因此在es6就新增可使用关键字class来定义类,这种写法只是让对象原型的写法更加清晰、更像面向对象编程的语法。在class中使用extends、super两个关键字来实现继承。

class Person{
    //...
}
console.log(typeof Person); //function
//constructor里面的东西类似于写在原生构造函数内部的
//其他地方的等同于写在原型上面的
class Person {
    constructor(name) {
        this.name = name;
    }
    eat() {
        console.log('Perosn eat');
    }
}

class Boy extends Person {
    constructor(name, age) {
        super(name); //继承Person的属性和方法
        this.age = age;
    }
    play() {
        console.log('Boy Play');
    }
}

console.log(typeof Boy); //function
smile-2008

smile-2008 commented on Feb 23, 2021

@smile-2008

@ ES5继承

es5的继承是根据原型链继承的;

function Person(name) {
    this.name = name;
}
Person.prototype.eat = function() {
    console.log('Person eat');
}

//Boy继承Person
function Boy(name, age) {
    Person.call(this, name);
    this.age = age;
}
Boy.prototype = Object.create(Person.prototype);
Boy.prototype.play = function() {
    console.log('Boys Play');
}
Boy.prototype.constructor = Boy;

@ ES6继承

es6的class其实只是一种语法糖;定义一个class类,使用typeof判断类型,输出function;因为在es6之前是没有类的概念的,只有构造函数创建实例对象。但是其他面向对象语言是有类的概念。因此在es6就新增可使用关键字class来定义类,这种写法只是让对象原型的写法更加清晰、更像面向对象编程的语法。在class中使用extends、super两个关键字来实现继承。

class Person{
    //...
}
console.log(typeof Person); //function
//constructor里面的东西类似于写在原生构造函数内部的
//其他地方的等同于写在原型上面的
class Person {
    constructor(name) {
        this.name = name;
    }
    eat() {
        console.log('Perosn eat');
    }
}

class Boy extends Person {
    constructor(name, age) {
        super(name); //继承Person的属性和方法
        this.age = age;
    }
    play() {
        console.log('Boy Play');
    }
}

console.log(typeof Boy); //function
xiaoqiangz

xiaoqiangz commented on Jun 22, 2022

@xiaoqiangz

// 构造函数继承
function Person(name, age) {
this.name = name
this.age = age
this.sayName = function() {
console.log(this.name)
}
}
function SubPerson(name, age){
Person.call(this, name, age)
}
var p1 = new SubPerson('xx', 11)
console.log(p1)
p1.sayName()

// 原型链继承
function Car(){

}
Car.prototype.name = 'byd'
Car.prototype.type = '电动车'
Car.prototype.sayName = function() {
console.log(this.name)
}

function SubCar(name) {
this.name = name
}
SubCar.prototype = new Car()
var c1 = new SubCar('bmw')
console.log(c1)
c1.sayName()

// 组合继承
function Person(name, age) {
this.name = name
this.age = age
this.sayName = function() {
console.log(this.name)
}
}
function SubPerson(name, age){
Person.call(this, name, age)
}
SubPerson.prototype = new Person()
SubPerson.prototype.constructor = SubPerson

// 原型继承 类似Object.create()对对象的浅拷贝
function createObject(o) {
function f() {}
f.prototype = o
return new f()
}

// es6 extend
class Fruits{
constructor(name, size){
this.name = name
this.size = size || 'big'
}
sayName() {
console.log(this.name)
}
}

class Apple extends Fruits{
constructor(level, name, size='small') {
super(name, size)
this.level = level
}
}
var apple = new Apple(1, 'apple')
apple.sayName()
console.log(apple)

panpanxuebing

panpanxuebing commented on Dec 16, 2024

@panpanxuebing
function Super () {
  this.property = true
  this.frend = ['Bob', 'Tom']
}

Super.prototype.say = function () {
  console.log('hello')
}

function Sub () {
  Super.call(this)
  this.subProperty = true
}

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

// Sub.prototype = new Super()
Sub.prototype = object(Sub.prototype)
Sub.prototype.construtor = Sub

Sub.prototype.speak = function () {
  console.log('hi')
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    jsJavaScript

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

      Development

      No branches or pull requests

        Participants

        @smile-2008@haizhilin2013@xiaoqiangz@xjt31012@panpanxuebing

        Issue actions

          [js] 第83天 举例说明js如何实现继承? · Issue #586 · haizlin/fe-interview