JS的原型链和继续

原型和原型链

原型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的完成。
    原文作者:charlie
    原文地址: https://segmentfault.com/a/1190000016155985
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞