JS继承的一些见解

JS继承的一些见解

js在es6之前的继承是五花八门的。而且要在项目中灵活运用面向对象写法也是有点别扭,更多的时候还是觉得面向过程的写法更为简单,效率也高。久而久之对js的继承每隔一段时间就会理解出现困难。所以这次我要把对对象的理解写下来,这样应该就深刻一点了。

我们先来看看一个对象是怎么生成的

// 三种创建对象的方法
var obj = {}
var obj2 = new Object()
var obj3 = Object.create(null)
// 创建一个空字符串对象
var obj4 = new String()

obj.constructor === obj2.constructor        // true
obj.__proto__ === obj2.__proto__ === Object.prototype       // true
obj4.__proto__ === String.prototype         // true
obj4.__proto__.__proto__ === Object.prototype   // ture

这三种方法,前面两种是一样的。它们创建的空对象都具有原型,而第三种方式创建的是一个真正意义上的空对象,它不继承任何属性;而obj4呢是一个字符串对象。看下图的对比:
下面对象除了obj3都有个__proto__的隐性属性,这个属性指向的是该创建该实例的构造函数的原型。

这里obj和obj2虽然是空对象,不过它们具有Object构造函数的属性的方法,而obj4除了有obj拥有的属性和方法,它还具备了String拥有的属性和方法;而obj3是真正的空对象,它的__proto__指向的是null。

我们再将obj4全部展开看看:

再来看看它们之间的关系:

如上图,我举得例子是Object和String这两个原生具有的构造器。它们的关系和我们平时写的父类和子类之间的关系是一样的,所有的类最终都会指向Object。

在es5的时代,我们用到的继承最简单的就是原型链继承了

// 我们先创建一个父类
function Super (name) {
  this.name = name
  this.type = '我是父类'
}
Super.prototype.move = function () {
  console.log(this.name + '在跑')
}

// 子类继承父类
function Child (name) {
  this.name = name
}
Child.prototype = new Super()
// 原型链继承,子类原型的私有方法和属性要在继承之后添加,不然会被覆盖
Child.prototype.constructor = Child       // 修复对构造函数的指向
Child.prototype.eat = function () {
  // 做点啥
}

原型链继承就是将子类的原型等于父类的实例,然后再添加子类原型的属性和方法。这样的缺点就是只能继承一个父类。而且在创建子类的实例的时候,不能传参给父类。在做单一继承而且父类不需要传参的时候,这种写法是最好的选择了。

下面在介绍一种我个人觉得已经很不错的继承方式(组合继承)

// 子类继承父类
function Child (name) {
  Super.call(this)
  this.name = name
}
Child.prototype = new Super()
Child.prototype.constructor = Child       // 修复对构造函数的指向
Child.prototype.eat = function () {
  // 做点啥
}
  • 这种继承方式就是粗暴的将父类Super构造函数利用对象冒充的方式将父类的实例属性复制到子类里。在用原型继承的方式继承父类原型的属性和方法。
  • 这样的继承方式,你会发现子类的实例的属性和__proto__下都有父类的属性,而子类实例的把子类原型的覆盖了。
  • 这样的方式可以实现对多个父类(非原型部分)的继承。因为原型的继承是链式的,所以只能继承一个。

一般来说这种继承方式已经很好了,代码量少。继承性好,弊端也没有影响。这就是ES6之前的继承。下面再来看看ES6是怎么操作的。

ES6的class

  • ES6 提供了更接近传统语言的写法,引入了 Class(类)这个概念,作为对象的模板。通过class关键字,可以定义类。
  • 基本上,ES6 的class可以看作只是一个语法糖,它的绝大部分功能,ES5 都可以做到,新的class写法只是让对象原型的写法更加清晰、更像面向对象编程的语法而已。上面的代码用 ES6 的class改写,就是下面这样。

上面这两句话是引用阮一峰对ES6的class的简介(Class 的基本语法)。阮大神的ECMAScript 6 入门真的是对es6初学者居家必备的好东西。我在这里就谈谈我的理解好了。

// 写一个class
class Point {
  constructor(x, y) {
    this.x = x;
    this.y = y;
  }

  toString() {
    return '(' + this.x + ', ' + this.y + ')';
  }
}

var point = new Point(2, 3)       // new一个实例

下面看下point内部是怎样的,可以看出constructor,toString都是在“proto”内的。

在这里会等价于构造函数的什么写法呢

// 写一个class
function Point (x, y) {
  this.x = x
  this.y = y
}
Point.prototype.toString = function () {
  return '(' + this.x + ', ' + this.y + ')';
}

var point = new Point(2, 3)       // new一个实例

在不考虑class静态属性的情况下,可以简单理解为constructor内this下的新属性和方法都属于构造函数内的,而constructor外的方法都是prototype下的。

  • 在看看class的继承
class ChildPoint extends Point {
  constructor (x, y, z) {
    super(x, y)
    this.z = z
  }
  toString () {
    return '(' + this.x + ', ' + this.y + ','+ this.z +')';
  }
  changeX (x) {
    this.x = x
  }
}

var childPoint = new ChildPoint(2, 3, 4)      // new一个ChildPoint的实例

console.log(childPoint.x)                // 2
console.log(childPoint.y)                // 3
console.log(childPoint.z)                // 4
console.log(childPoint.toString())       // 2,3,4
childPoint.changeX(5)
console.log(childPoint.x)                // 5
console.log(childPoint.__proto__ === ChildPoint.prototype)    // true
console.log(childPoint.__proto__.__proto__ === Point.prototype)         // true
console.log(childPoint.constructor === ChildPoint)    // true

这里class是通过extends继承父类的,而子类的constructor方法内需要调用super()方法,这相当与调用了父类的constructor()方法。

下面看下childPoint的内部情况:

从图片和上面打印的情况可以看出,es6的继承和组合继承很是相似。唯一的不同点就是es6的继承没有组合继承产生的多余的一份实例属性在原型里。看起来很顺眼。不过相对的es6的不能多继承(虽然组合继承只能多继承多个构造函数,并不能继承多个原型)。

其实组合继承还可以进一步升级成es6继承那个样子的(寄生组合继承):

// 我们先创建一个父类
function Super (name) {
  this.name = name
  this.type = '我是父类'
}
Super.prototype.move = function () {
  console.log(this.name + '在跑')
}

// 子类继承父类
function Child (name) {
  Super.call(this)
  this.name = name
}
(function() {
  // 创建一个没有Super实例的中间类
  var MiddleSuper = function() {}
  MiddleSuper.prototype = Super.prototyoe       // 跟Super共享原型
  // 在这里MiddleSuper和Super的区别就是有没有实例了。
  // 然后在正常进行组合继承的操作
  Child.prototype = new MiddleSuper()
})()
Child.prototype.constructor = Child       // 修复对构造函数的指向
Child.prototype.eat = function () {
  // 做点啥
}

经过这一系列复杂的操作后我们就得到了一份和es6一样的结构了。不过es6只需要一个extends,而es6之前我们就得长篇大论的写才能实现,而且还特别绕。所以一句话,快去学es6吧。

  • 该文纯属个人见解,有什么问题请指出。

本文参考了:

原文地址:https://www.cnblogs.com/suyuanli/p/8866918.html

时间: 2024-10-10 13:41:04

JS继承的一些见解的相关文章

js继承的常用方式

写在前面的话:这篇博客不适合对面向对象一无所知的人,如果你连_proto_.prototype...都不是很了解的话,建议还是先去了解一下JavaScript面向对象的基础知识,毕竟胖子不是一口吃成的. 我们都知道面向对象语言的三大特征:继承.封装.多态,但JavaScript不是真正的面向对象,它只是基于面向对象,所以会有自己独特的地方.这里就说说JavaScript的继承是如何实现的. 学习过Java和c++的都知道,它们的继承通过类实现,但JavaScript没有类这个概念,那它通过什么机

JS继承的实现方式

前言 JS作为面向对象的弱类型语言,继承也是其非常强大的特性之一.那么如何在JS中实现继承呢?让我们拭目以待. JS继承的实现方式 既然要实现继承,那么首先我们得有一个父类,代码如下: // 定义一个动物类 function Animal (name) { // 属性 this.name = name || 'Animal'; // 实例方法 this.sleep = function(){ console.log(this.name + '正在睡觉!'); } } // 原型方法 Animal

js继承的实现

js继承有5种实现方式: 1.继承第一种方式:对象冒充   function Parent(username){     this.username = username;     this.hello = function(){       alert(this.username);     }   }   function Child(username,password){     //通过以下3行实现将Parent的属性和方法追加到Child中,从而实现继承     //第一步:this.

js继承有5种实现方式

js继承有5种实现方式:1.继承第一种方式:对象冒充  function Parent(username){    this.username = username;    this.hello = function(){      alert(this.username);    }  }  function Child(username,password){    //通过以下3行实现将Parent的属性和方法追加到Child中,从而实现继承    //第一步:this.method是作为一

JS继承——原型的应用

前面我们知道JS是基于对象编程的一种脚本语言,在JS本着一切皆对象的原则,对象之间也涉及到了继承,不过这里的继承与我们以往学习过的继承有所不同,它运用的是对象的原型,来构造一个原型链来实现对超类对象的继承. 1.如何实现对象继承 function Box() { //Box 构造<span style="font-family:Arial;font-size:18px;">,超类对象</span> this.name = 'Lee'; } Desk.protot

5种JS继承方法

<!DOCTYPE html> <html> <head> <meta charset=" utf-8"> <meta name="author" content="http://www.jb51.net/" /> <title>5种JS继承方法</title> <script type="text/javascript"> //1

JS 继承的方式

JS 继承的方式 1.使用call的方式 2. code如下 <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Title</title> </head> <body> <script type="text/javascript"> //继承的第二种实

js继承之call,apply和prototype随谈

在js中,call,apply和prototype都可以实现对象的继承,下面我们看一个例子: function FatherObj1() { this.sayhello = "I am join"; this.show = function () { alert("I am FatherObj1"); }; this.getall = function () { if (arguments) alert("GetAll:" + arguments

js继承的实现 by DY

js继承有5种实现方式: 1.继承第一种方式:对象冒充   function Parent(username){     this.username = username;     this.hello = function(){       alert(this.username);     }   }   function Child(username,password){     //通过以下3行实现将Parent的属性和方法追加到Child中,从而实现继承     //第一步:this.