目录
原型和原型链
constructor
通过原型链实现继承
ES6的class类
总结
首页 web前端 js教程 javascript中通过原型链实现继承的解析(附代码)

javascript中通过原型链实现继承的解析(附代码)

Aug 27, 2018 am 10:17 AM
html5 javascript

本篇文章给大家带来的内容是关于javascript中通过原型链实现继承的解析(附代码),有一定的参考价值,有需要的朋友可以参考一下,希望对你有所帮助。

原型和原型链

原型prototype,在创建新函数的时候,会自动生成,而prototype中也会有一个constructor,回指创建该prototype的函数对象。

__proto__是对象或者实例中内置的[[prototype]],其指向的是产生该对象的对象的prototype,在浏览器中提供了__proto__让我们可以访问,通过__proto__的指向形成的一个链条,就称做原型链,原型链的整个链路是:实例对象- ->构造函数的prototype- ->Object的prototype- ->null

我们在访问对象的属性或者方法的时候,首先从本对象寻找,如果本对象不存在该属性或者方法时,就会沿着原型链向上寻找,直至找到该属性或者方法,或者到null时停止。

这也解释了为什么数组对象上没有push,pop,shift,unshift等方法,却可以访问

constructor

constructor属性指向的是生成该函数(对象)的函数(对象),例如

var a = function(){};
var b = new a();
var c = {};
var d = [];
//以下皆为true
console.log(b.constructor === a) //因为实例b是由构造函数产生的
console.log(a.constructor === Function)//函数a实际是Function的实例,同理
console.log(c.constructor === Object)//空对象c是Object的实例
console.log(d.constructor === Array)//空对象c是Object的实例
console.log(Object.constructor === Function)//Object自身就是一个构造函数,同理
console.log(Array.constructor === Function)//Array自身也是一个构造函数
//---------------------------------------------------------------
//首先__proto__指向的是产生该对象的对象的prototype,
//也即a.prototype,prototype中也的constructor,回指创建该prototype的函数对象,也即函数a
console.log(b.__proto__.constructor === a)
登录后复制

这里顺便说一下instanceof,A instanceof B 是在 A 的原型链中找 B 的 prototype,找到返回 true,找不到返回 false

//有个奇怪的现象,下面都返回true,这是为什么呢?
//因为JS中一切都继承自Object,除了最顶层的null,
//所以在Function的原型链中能找到Object.prototype
console.log(Function instanceof Object)
//而Object自身就是一个构造函数,因此在Object的原型链中也能找到Function.prototype
console.log(Object instanceof Function)
登录后复制

通过原型链实现继承

由上面的分析,我们可以利用原型链实现继承的逻辑,继承是面向对象中的一个很重要的概念

function Dog(name){
    this.name = name;
    this.say1 = function(){
        console.log(this.name)
    }
}
Dog.prototype.say2 = function(){
    console.log(this.name)
}
Dog.prototype.test = 1
//say本来应该是所有Dog实例的共有方法,
//如果放在构造函数中,那么就会导致没办法数据共享,每一个实例都有自己的属性和方法的副本,这是对资源的极大浪费
//如果放在Dog.prototype中,那么利用原型链的特性,就可以让所有实例共用一个方法,
//需要注意的是,由于共用了一个方法,对属性的更改是对所有实例透明的

var dog1 = new Dog('lalala'); 
let dog2 = new Dog('wahaha');
dog1.test++;//2
dog2.test++;//3
console.log(dog1.say1 === dog2.say1)// false
console.log(dog1.say2 === dog2.say2)// true



//现在,我们可以尝试着去实现继承了
//我们是通过原型链去实现继承的,
//之前的原型链是:Dog实例 --> Dog函数 --> Object --> null
//那么现在的原型链需要改成 Dog实例 --> Dog函数 --> Dog父类(Animal函数) --> Object --> null
//第一种方案,改变Dog函数的prototype,让他指向Animal的实例
function Animal(){
    this.species = 'unknown';
}
Dog.prototype = new Animal();
//这里改变后会导致prototype中的constructor改变
Dog.prototype.constructor = Dog;

//第二钟方案,改变Dog函数的prototype,让他指向Animal的prototype
function Animal(){}
Animal.prototype.species = 'unknown';
Dog.prototype = Animal.prototype;
//这里改变后会导致prototype中的constructor改变
Dog.prototype.constructor = Dog;

//第三种方案,调用apply或call,将Animal的this绑定到Dog中
function Animal(){
    this.species = 'unknown';
}
function Dog(name){
    Animal.apply(this, arguments);
    this.name = name;
}

//第四种方法,通过Object.create()方法实现继承,过滤掉了父类实例属性,Dog.prototype中就没有了Animal的实例化数据了
//这种方法也是ES6中Class被babel编译成ES5所用的方法
function Animal(){
    this.species = 'unknown';
}
function Dog(name){
    Animal.apply(this, arguments);
    this.name = name;
}
//这里模拟了 Dog.prototype = Object.create(Animal.prototype)
var f = function(){};
f.prototype = Animal.pototype;
Dog.prototype = new f();
Dog.__proto__ = Animal;
//这里改变后会导致prototype中的constructor改变
Dog.prototype.constructor = Dog;


//现在就能访问到Animal中的species属性了
var dog = new Dog('lalala');
dog.species;//unknown
登录后复制

以上这些就是利用原型链实现继承的一些方法

ES6的class类

有了以上的知识,我们就可以研究一下ES6的class类了,这个语法糖能让我们更容易的实现类和继承,其提供了extends,static,super等关键字

//这是es6的代码实现
class Parent {
  static l(){
    console.log(222)
  }
  constructor(m){
    this.m = m
  }
  get(){
    return this.m;
  }
}
class Child extends Parent {
  constructor(n){
    super(4);
    this.n = n;
  }
  get(){
    return this.n
  }
  set(a){
    this.n = a;
  }
}

//这是利用babel编译之后的es5的实现
//_createClass是一个自执行函数,作用给构造函数绑定静态方法和动态方法
//对于静态的static关键字声明的变量,会直接绑定在函数对象上,作为静态属性(方法)
//对于在class中声明的函数方法,则会绑定在构造函数的prototype上,通过Object.definePropety方法
var _createClass = function () {
  function defineProperties(target, props) {
    for (var i = 0; i < props.length; i++) {
      var descriptor = props[i];
      descriptor.enumerable = descriptor.enumerable || false;
      descriptor.configurable = true;
      if ("value" in descriptor) descriptor.writable = true;
      Object.defineProperty(target, descriptor.key, descriptor);
    }
  }
  return function (Constructor, protoProps, staticProps) {
    if (protoProps) defineProperties(Constructor.prototype, protoProps);
    if (staticProps) defineProperties(Constructor, staticProps);
    return Constructor;
  };
}();

//如果父函数没有返回值或者返回值不为object或者function,则返回子类的this
function _possibleConstructorReturn(self, call) {
  if (!self) {
    throw new ReferenceError("this hasn't been initialised - super() hasn't been called");
  }
  return call && (typeof call === "object" || typeof call === "function") ? call : self;
}

//_inherits就是extends关键字发挥的作用,实现了继承的功能。利用&&的短路特性,对superClass做了容错性处理,然后将子类Object.create()传了两个参数,一个参数是父类superClass.prototype,作用在上面解释继承的方法时讲过了,第二个参数是一个键值对,key代表着属性,value则和Object.definePropety中descriptor一样,这里改变constructor的目的,也在解释继承时讲过了,最后将subClass.__proto__指向superClass
function _inherits(subClass, superClass) {
  //...省略
  subClass.prototype = Object.create(superClass && superClass.prototype, {
    constructor: {
      value: subClass,
      enumerable: false,
      writable: true,
      configurable: true
    }
  });
  if (superClass) Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass;
}

//_classCallCheck是保证构造函数不能被当成普通函数调用,需要用new关键字
function _classCallCheck(instance, Constructor) {
  if (!(instance instanceof Constructor)) {
    throw new TypeError("Cannot call a class as a function");
  }
}

var Parent = function () {
  _createClass(Parent, null, [{
    key: "l",
    value: function l() {
      console.log(222);
    }
  }]);

  function Parent(m) {
    _classCallCheck(this, Parent);

    this.m = m;
  }

  _createClass(Parent, [{
    key: "get",
    value: function get() {
      return this.m;
    }
  }]);

  return Parent;
}();

var Child = function (_Parent) {
  _inherits(Child, _Parent);

  function Child(n) {
    _classCallCheck(this, Child);
    
    //由于在_inherits中将subClass(child).__proto__指向了superClass(Parent),所以这里即是Parent.call(this,4),即这里执行的是super函数,super也可以调用父类的静态方法,
    //如果父函数没有返回值或者返回值不为object或者function,则返回子类的this    
    var _this = _possibleConstructorReturn(this, (Child.__proto__ || Object.getPrototypeOf(Child)).call(this, 4));

    _this.n = n;
    return _this;
  }

  _createClass(Child, [{
    key: "set",
    value: function set(a) {
      this.n = a;
    }
  }]);

  return Child;
}(Parent);
登录后复制

总结

  • 通过以上分析,对原型和原型链有了更加深入和清晰的了解,也熟悉了constructor和instanceof的用法,加深了基于原型链的继承方式的了解,理清了这块知识。

  • 在对ES6的class通过babel编译后的源码的分析中,也了解到了Object.create和Object.setPrototypeOf的用法,挖掘了如何去模拟super,extends和static的实现。

相关推荐:

JavaScript面向对象-基于原型链实现继承

JavaScript的继承与原型链

以上是javascript中通过原型链实现继承的解析(附代码)的详细内容。更多信息请关注PHP中文网其他相关文章!

本站声明
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系admin@php.cn

热AI工具

Undresser.AI Undress

Undresser.AI Undress

人工智能驱动的应用程序,用于创建逼真的裸体照片

AI Clothes Remover

AI Clothes Remover

用于从照片中去除衣服的在线人工智能工具。

Undress AI Tool

Undress AI Tool

免费脱衣服图片

Clothoff.io

Clothoff.io

AI脱衣机

AI Hentai Generator

AI Hentai Generator

免费生成ai无尽的。

热门文章

R.E.P.O.能量晶体解释及其做什么(黄色晶体)
3 周前 By 尊渡假赌尊渡假赌尊渡假赌
R.E.P.O.最佳图形设置
3 周前 By 尊渡假赌尊渡假赌尊渡假赌
R.E.P.O.如果您听不到任何人,如何修复音频
3 周前 By 尊渡假赌尊渡假赌尊渡假赌

热工具

记事本++7.3.1

记事本++7.3.1

好用且免费的代码编辑器

SublimeText3汉化版

SublimeText3汉化版

中文版,非常好用

禅工作室 13.0.1

禅工作室 13.0.1

功能强大的PHP集成开发环境

Dreamweaver CS6

Dreamweaver CS6

视觉化网页开发工具

SublimeText3 Mac版

SublimeText3 Mac版

神级代码编辑软件(SublimeText3)

HTML 中的表格边框 HTML 中的表格边框 Sep 04, 2024 pm 04:49 PM

HTML 表格边框指南。在这里,我们以 HTML 中的表格边框为例,讨论定义表格边框的多种方法。

HTML 中的嵌套表 HTML 中的嵌套表 Sep 04, 2024 pm 04:49 PM

这是 HTML 中嵌套表的指南。这里我们讨论如何在表中创建表以及相应的示例。

HTML 左边距 HTML 左边距 Sep 04, 2024 pm 04:48 PM

HTML 左边距指南。在这里,我们讨论 HTML margin-left 的简要概述及其示例及其代码实现。

HTML 表格布局 HTML 表格布局 Sep 04, 2024 pm 04:54 PM

HTML 表格布局指南。在这里,我们详细讨论 HTML 表格布局的值以及示例和输出。

HTML 有序列表 HTML 有序列表 Sep 04, 2024 pm 04:43 PM

HTML 有序列表指南。在这里我们还分别讨论了 HTML 有序列表和类型的介绍以及它们的示例

在 HTML 中移动文本 在 HTML 中移动文本 Sep 04, 2024 pm 04:45 PM

HTML 中的文本移动指南。在这里我们讨论一下marquee标签如何使用语法和实现示例。

HTML 输入占位符 HTML 输入占位符 Sep 04, 2024 pm 04:54 PM

HTML 输入占位符指南。在这里,我们讨论 HTML 输入占位符的示例以及代码和输出。

HTML onclick 按钮 HTML onclick 按钮 Sep 04, 2024 pm 04:49 PM

HTML onclick 按钮指南。这里我们分别讨论它们的介绍、工作原理、示例以及各个事件中的onclick事件。

See all articles