【日拱一卒】JavaScript类与继承

这是我参与8月更文挑战的第11天,活动详情查看: 8月更文挑战” 

前言

由浅入深,深入浅出。梳理一下JavaScript类与继承。

什么是原型

在不同的编程语言中,设计者也利用各种不同的语言特性来抽象描述对象。最为成功的流派是使用“类”的方式来描述对象,这诞生了诸如 C++、Java 等流行的编程语言。这个流派叫做基于类的编程语言。

JavaScript是基于原型的编程语言,它们利用原型来描述对象。

基于类:

在这类语言中,总是先有类,再从类去实例化一个对象。类与类之间又可能会形成继承、组合等关系。类又往往与语言的类型系统整合,形成一定编译时的能力。

基于原型:

关注对象实例的行为,划分到最近的使用方式相似的原型对象。

基于原型的面向对象系统,通过“复制”的方式来创建新对象。

( 原型:一系列对象行为的集合。原型更强调的是行为。)

在 JavaScript 之前,原型系统就更多与高动态性语言配合,并且多数基于原型的语言提倡运行时的原型修改。

原型系统的“复制操作”有两种实现思路:

一个是并不真的去复制一个原型对象,而是使得新对象持有一个原型的引用;

另一个是切实地复制对象,从此两个对象再无关联。

JavaScript采用的是第一个。

JavaScirpt原型

整体上JavaScript1.1提出了原型相关概念,1.2引入了__proto__伪属性,ES3规范中删除了伪属性之后就没有改动了。

JavaScript 1.1 原型的实例

// 定义出作为方法被使用的函数
function ptSum(pt2) {
  return new Point(this.x + pt2.x, this.y + pt2.y);
}
function ptDistance(pt2) {
  return Math.sqrt(Math.pow(pt2.x - this.x, 2) + Math.pow(pt2.y - this.y, 2));
}

// 定义 Point 构造函数
function Point(x, y) {
  // 创建并初始化新对象的数据属性
  this.x = x;
  this.y = y;
}

// 添加方法到共享的原型对象
Point.prototype.sum = ptSum;
Point.prototype.distance = ptDistance;

var origin = new Point(0, 0); // 创建 Point 对象
复制代码

原型的特点:

  • 访问对象属性时,如果这个属性的名称在「与对象构造函数相关联的原型」上已被定义,那么将返回原型对象的属性值。
  • 对原型对象属性的添加或修改,对于通过「与原型相关联的构造函数」创建的现有对象,是立即可见的。
  • 为对象属性赋值时,会遮盖在「与对象构造函数相关联的原型」上定义的同名属性值。

————————————————————————————————

ES6中,为我们提供了一系列内置函数,以便更为直接地访问操纵原型:

  • Object.create 根据指定的原型创建新对象,原型可以是 null;

  • Object.getPrototypeOf 获得一个对象的原型;

  • Object.setPrototypeOf 设置一个对象的原型。

    // ES6中 原型API相关案例
    var cat = {
    say(){
    console.log(“meow~”);
    },
    jump(){
    console.log(“jump”);
    }
    }
    // 这段代码创建了一个“猫”对象

    var tiger = Object.create(cat, {
    say:{
    writable:true,
    configurable:true,
    enumerable:true,
    value:function(){
    console.log(“roar!”);
    }
    }
    })
    // 根据猫做了一些修改创建了虎
    // 用 Object.create 来创建另外的猫和虎对象
    var anotherCat = Object.create(cat);

    anotherCat.say();

    var anotherTiger = Object.create(tiger);

    anotherTiger.say();
    // 我们可以通过“原始猫对象”和“原始虎对象”来控制所有猫和虎的行为。

    console.log(Object.getPrototypeOf(anotherCat) === cat);

    console.log(Object.getPrototypeOf(anotherTiger) === cat);
    console.log(Object.getPrototypeOf(anotherTiger) === tiger);

PS:关于__proto__伪属性,参考MDN文档,不推荐使用,逐步被淘汰。这里就不花过多的精力去探讨。可能大多数博客资料都还有这个__proto__伪属性的身影。根据自身需要学习吧。

类与继承

通过new 构造函数创建对象,通过prototype共享方法和属性。

ES6之前

在没有ES6中的Object.create、Object.setPrototypeOf时,我们只能通过new 和 构造函数 去指定对象的原型。

// 通过new实现的类与继承
function c1(){
    // 直接在构造器中修改 this,给 this 添加属性
    this.p1 = 1;
    this.p2 = function(){
        console.log(this.p1);
    }
} 
var o1 = new c1;
o1.p2();// 1



function c2(){
}
// 修改构造器的 prototype 属性指向的对象,它是从这个构造器构造出来的所有对象的原型
c2.prototype.p1 = 1;
c2.prototype.p2 = function(){
    console.log(this.p1);
}

var o2 = new c2;
o2.p2(); // 1
复制代码

ES6中的类

class Rectangle {
  constructor(height, width) {
    this.height = height;
    this.width = width;
  }
  // Getter
  get area() {
    return this.calcArea();
  }
  // Method
  calcArea() {
    return this.height * this.width;
  }
}
复制代码

ES6中的继承

class Animal { 
  constructor(name) {
    this.name = name;
  }
  
  speak() {
    console.log(this.name + ' makes a noise.');
  }
}

class Dog extends Animal {
  constructor(name) {
    super(name); // call the super class constructor and pass in the name parameter
  }

  speak() {
    console.log(this.name + ' barks.');
  }
}

let d = new Dog('Mitzie');
d.speak(); // Mitzie barks.
复制代码

PS:

怀着忐忑的心理,梳理类与集成。因为这一块并没有多少实战经验作为基础,所以必然要举一些实例,并且围绕这些实例展开梳理。一方面是知识上的梳理,一方面也是案例上的梳理。

在这之前,还是得有思路上的梳理,以免造成内容上的堆砌。JavaScript1.0是没有继承这个概念的,是基于对象的一门语言,之后1.1函数对象有了prototype属性,再之后1.2才引入了__proto__伪属性。

ES3并没有相关的修改,ES5函数prototype属性的bind方法更新了,对象的Setter,getter,原操作,ES6(ECMAScript2015)正式引入了类,把JavaScript类与继承进行了封装,并且之后再没有较大的更新。

总体来看,核心内容还是JavaScirpt对象、函数、原型链、以及类与继承这些内容。单从年限来看,学习资料应该是很多的。重点还是放在原型链和类与集成的实现上,找到合适的案例,加深理解。

本文挑选了一些简单的案例,做一个简单的梳理,主要是熟悉JavaScirpt中类与继承的核心概念。稳扎稳打把,连续查阅资料,整个人都有些许浮躁,先把这些最简单的基建打好,再往深处探寻。

© 版权声明
THE END
喜欢就支持一下吧
点赞0 分享