国产xxxx99真实实拍_久久不雅视频_高清韩国a级特黄毛片_嗯老师别我我受不了了小说

資訊專欄INFORMATION COLUMN

JS的原型鏈和繼承

anRui / 722人閱讀

摘要:是對象或者實(shí)例中內(nèi)置的,其指向的是產(chǎn)生該對象的對象的在瀏覽器中提供了讓我們可以訪問,通過的指向形成的一個鏈條,就稱做原型鏈,原型鏈的整個鏈路是實(shí)例對象構(gòu)造函數(shù)的的。

原型和原型鏈

原型prototype,在創(chuàng)建新函數(shù)的時候,會自動生成,而prototype中也會有一個constructor,回指創(chuàng)建該prototype的函數(shù)對象。

__proto__是對象或者實(shí)例中內(nèi)置的[[prototype]],其指向的是產(chǎn)生該對象的對象的prototype,在瀏覽器中提供了__proto__讓我們可以訪問,通過__proto__的指向形成的一個鏈條,就稱做原型鏈,原型鏈的整個鏈路是:實(shí)例對象- ->構(gòu)造函數(shù)的prototype- ->Object的prototype- ->null

我們在訪問對象的屬性或者方法的時候,首先從本對象尋找,如果本對象不存在該屬性或者方法時,就會沿著原型鏈向上尋找,直至找到該屬性或者方法,或者到null時停止。

這也解釋了為什么數(shù)組對象上沒有push,pop,shift,unshift等方法,卻可以訪問。

constructor

constructor屬性指向的是生成該函數(shù)(對象)的函數(shù)(對象),例如

var a = function(){};
var b = new a();
var c = {};
var d = [];
//以下皆為true
console.log(b.constructor === a) //因?yàn)閷?shí)例b是由構(gòu)造函數(shù)產(chǎn)生的
console.log(a.constructor === Function)//函數(shù)a實(shí)際是Function的實(shí)例,同理
console.log(c.constructor === Object)//空對象c是Object的實(shí)例
console.log(d.constructor === Array)//空對象c是Object的實(shí)例
console.log(Object.constructor === Function)//Object自身就是一個構(gòu)造函數(shù),同理
console.log(Array.constructor === Function)//Array自身也是一個構(gòu)造函數(shù)
//---------------------------------------------------------------
//首先__proto__指向的是產(chǎn)生該對象的對象的prototype,
//也即a.prototype,prototype中也的constructor,回指創(chuàng)建該prototype的函數(shù)對象,也即函數(shù)a
console.log(b.__proto__.constructor === a)

這里順便說一下instanceof,A instanceof B 是在 A 的原型鏈中找 B 的 prototype,找到返回 true,找不到返回 false

//有個奇怪的現(xiàn)象,下面都返回true,這是為什么呢?
//因?yàn)镴S中一切都繼承自O(shè)bject,除了最頂層的null,
//所以在Function的原型鏈中能找到Object.prototype
console.log(Function instanceof Object)
//而Object自身就是一個構(gòu)造函數(shù),因此在Object的原型鏈中也能找到Function.prototype
console.log(Object instanceof Function)
通過原型鏈實(shí)現(xiàn)繼承

由上面的分析,我們可以利用原型鏈實(shí)現(xiàn)繼承的邏輯,繼承是面向?qū)ο笾械囊粋€很重要的概念

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本來應(yīng)該是所有Dog實(shí)例的共有方法,
//如果放在構(gòu)造函數(shù)中,那么就會導(dǎo)致沒辦法數(shù)據(jù)共享,每一個實(shí)例都有自己的屬性和方法的副本,這是對資源的極大浪費(fèi)
//如果放在Dog.prototype中,那么利用原型鏈的特性,就可以讓所有實(shí)例共用一個方法,
//需要注意的是,由于共用了一個方法,對屬性的更改是對所有實(shí)例透明的

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



//現(xiàn)在,我們可以嘗試著去實(shí)現(xiàn)繼承了
//我們是通過原型鏈去實(shí)現(xiàn)繼承的,
//之前的原型鏈?zhǔn)牵篋og實(shí)例 --> Dog函數(shù) --> Object --> null
//那么現(xiàn)在的原型鏈需要改成 Dog實(shí)例 --> Dog函數(shù) --> Dog父類(Animal函數(shù)) --> Object --> null
//第一種方案,改變Dog函數(shù)的prototype,讓他指向Animal的實(shí)例
function Animal(){
    this.species = "unknown";
}
Dog.prototype = new Animal();
//這里改變后會導(dǎo)致prototype中的constructor改變
Dog.prototype.constructor = Dog;

//第二鐘方案,改變Dog函數(shù)的prototype,讓他指向Animal的prototype
function Animal(){}
Animal.prototype.species = "unknown";
Dog.prototype = Animal.prototype;
//這里改變后會導(dǎo)致prototype中的constructor改變
Dog.prototype.constructor = Dog;

//第三種方案,調(diào)用apply或call,將Animal的this綁定到Dog中
function Animal(){
    this.species = "unknown";
}
function Dog(name){
    Animal.apply(this, arguments);
    this.name = name;
}

//第四種方法,通過Object.create()方法實(shí)現(xiàn)繼承,過濾掉了父類實(shí)例屬性,Dog.prototype中就沒有了Animal的實(shí)例化數(shù)據(jù)了
//這種方法也是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;
//這里改變后會導(dǎo)致prototype中的constructor改變
Dog.prototype.constructor = Dog;


//現(xiàn)在就能訪問到Animal中的species屬性了
var dog = new Dog("lalala");
dog.species;//unknown

以上這些就是利用原型鏈實(shí)現(xiàn)繼承的一些方法

ES6的class類

有了以上的知識,我們就可以研究一下ES6的class類了,這個語法糖能讓我們更容易的實(shí)現(xiàn)類和繼承,其提供了extends,static,super等關(guān)鍵字

//這是es6的代碼實(shí)現(xiàn)
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的實(shí)現(xiàn)
//_createClass是一個自執(zhí)行函數(shù),作用給構(gòu)造函數(shù)綁定靜態(tài)方法和動態(tài)方法
//對于靜態(tài)的static關(guān)鍵字聲明的變量,會直接綁定在函數(shù)對象上,作為靜態(tài)屬性(方法)
//對于在class中聲明的函數(shù)方法,則會綁定在構(gòu)造函數(shù)的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;
  };
}();

//如果父函數(shù)沒有返回值或者返回值不為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關(guān)鍵字發(fā)揮的作用,實(shí)現(xiàn)了繼承的功能。利用&&的短路特性,對superClass做了容錯性處理,然后將子類Object.create()傳了兩個參數(shù),一個參數(shù)是父類superClass.prototype,作用在上面解釋繼承的方法時講過了,第二個參數(shù)是一個鍵值對,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是保證構(gòu)造函數(shù)不能被當(dāng)成普通函數(shù)調(diào)用,需要用new關(guān)鍵字
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),即這里執(zhí)行的是super函數(shù),super也可以調(diào)用父類的靜態(tài)方法,
    //如果父函數(shù)沒有返回值或者返回值不為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);
總結(jié)

通過以上分析,對原型和原型鏈有了更加深入和清晰的了解,也熟悉了constructor和instanceof的用法,加深了基于原型鏈的繼承方式的了解,理清了這塊知識。

在對ES6的class通過babel編譯后的源碼的分析中,也了解到了Object.create和Object.setPrototypeOf的用法,挖掘了如何去模擬super,extends和static的實(shí)現(xiàn)。

文章版權(quán)歸作者所有,未經(jīng)允許請勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。

轉(zhuǎn)載請注明本文地址:http://specialneedsforspecialkids.com/yun/97051.html

相關(guān)文章

  • 剖析JS原型鏈和繼承

    摘要:接下來我們來聊一下的原型鏈繼承和類。組合繼承為了復(fù)用方法,我們使用組合繼承的方式,即利用構(gòu)造函數(shù)繼承屬性,利用原型鏈繼承方法,融合它們的優(yōu)點(diǎn),避免缺陷,成為中最常用的繼承。 JavaScript是一門面向?qū)ο蟮脑O(shè)計語言,在JS里除了null和undefined,其余一切皆為對象。其中Array/Function/Date/RegExp是Object對象的特殊實(shí)例實(shí)現(xiàn),Boolean/N...

    darkerXi 評論0 收藏0
  • JS面向?qū)ο?em>的程序設(shè)計之繼承實(shí)現(xiàn)-組合繼承

    摘要:實(shí)現(xiàn)思路使用原型鏈實(shí)現(xiàn)對原型方法和方法的繼承,而通過借用構(gòu)造函數(shù)來實(shí)現(xiàn)對實(shí)例屬性的繼承。繼承屬性繼承方法以上代碼,構(gòu)造函數(shù)定義了兩個屬性和。 JS面向?qū)ο蟮某绦蛟O(shè)計之繼承的實(shí)現(xiàn)-組合繼承 前言:最近在細(xì)讀Javascript高級程序設(shè)計,對于我而言,中文版,書中很多地方翻譯的差強(qiáng)人意,所以用自己所理解的,嘗試解讀下。如有紕漏或錯誤,會非常感謝您的指出。文中絕大部分內(nèi)容引用自《Java...

    antz 評論0 收藏0
  • 深入淺出面向?qū)ο蠛?em>原型【概念篇3】—— 原型鏈和繼承

    摘要:由一個問題引發(fā)的思考這個方法是從哪兒蹦出來的首先我們要清楚數(shù)組也是對象,而且是對象的實(shí)例也就是說,下面兩種形式是完全等價的只不過是一種字面量的寫法,在深入淺出面向?qū)ο蠛驮透拍钇恼吕铮覀兲岬竭^類會有一個屬性,而這個類的實(shí)例可以通過屬性訪 1.由一個問題引發(fā)的思考 let arr1 = [1, 2, 3] let arr2 = [4, 5, 6] arr1.c...

    levinit 評論0 收藏0
  • JS高程筆記 - 繼承

    摘要:下面來看一個例子繼承屬性繼承方法在這個例子中構(gòu)造函數(shù)定義了兩個屬性和。組合繼承最大的問題就是無論什么情況下都會調(diào)用兩次超類型構(gòu)造函數(shù)一次是在創(chuàng)建子類型原型的時候另一次是在子類型構(gòu)造函數(shù)內(nèi)部。 組合繼承 組合繼承(combination inheritance),有時候也叫做偽經(jīng)典繼承,指的是將原型鏈和借用構(gòu)造函數(shù)的技術(shù)組合到一塊,從而發(fā)揮二者之長的一種繼承模式。其背后的思路是使用原型鏈...

    fsmStudy 評論0 收藏0

發(fā)表評論

0條評論

最新活動
閱讀需要支付1元查看
<