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的實現。

轉載于:https://juejin.im/post/5b82c49251882542fc41db19

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/news/282317.shtml
繁體地址,請注明出處:http://hk.pswp.cn/news/282317.shtml
英文地址,請注明出處:http://en.pswp.cn/news/282317.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

Android 的滑動分析以及各種實現

一、滑動效果的產生滑動一個View&#xff0c;本質區別就是移動一個View。改變當前View所在的坐標&#xff0c;原理和動畫相似不斷改變坐標位置實現。實現View的滑動就必須監聽滑動的事件&#xff0c;并且根據事件傳入的坐標&#xff0c;動態且不斷改變View的坐標&#xff0c;從…

微軟產品 .NET 6 遷移之旅

“.NET性能不行&#xff01;”“.NET有什么像樣的產品嗎&#xff01;&#xff1f;”“升級到.NET 6有什么好處&#xff01;&#xff1f;”……聽人扯淡還不如看看微軟自己是怎么做的。本文將匯總一下微軟的開發博客——這些博客均涉及微軟將產品和服務遷移到.NET 6的成果。博客…

Navicat 連接 RDS數據庫

場景介紹&#xff1a; 隨著業務量的逐漸增加&#xff0c;公司的數據庫壓力也會逐漸增大&#xff0c;使用自己購買的esc創建的mysql的話&#xff0c;還得考慮相應的dba維護&#xff0c;也比較繁瑣&#xff0c;說不定還做的并不完美&#xff0c;這時&#xff0c;RDS就派上用場了&…

bzoj1045 糖果傳遞

Description 有n個小朋友坐成一圈&#xff0c;每人有ai個糖果。每人只能給左右兩人傳遞糖果。每人每次傳遞一個糖果代價為1。 Input 第一行一個正整數nn<1000000&#xff0c;表示小朋友的個數&#xff0e;接下來n行&#xff0c;每行一個整數ai&#xff0c;表示第i個小朋友得…

BEGINNING SHAREPOINT#174; 2013 DEVELOPMENT 第9章節--client對象模型和REST APIs概覽 client對象模型API范圍...

BEGINNING SHAREPOINT 2013 DEVELOPMENT 第9章節--client對象模型和REST APIs概覽 client對象模型API范圍 本章之前提到過。client對象模型應用中一個不足就是缺乏對SP APIs和訪問功能的支持不足。轉載于:https://www.cnblogs.com/yutingliuyl/p/6748382.html

為.NET應用添加截圖功能

本文介紹了 .NET 實現截圖功能的思路和過程&#xff0c;如果你僅想了解最后的解決方案&#xff0c;可以直接查看文章末尾。截圖的功能我們應該都經常使用&#xff0c;在開發軟件時&#xff0c;我們有時也或多或少需要提供這方面的功能&#xff0c;無論是為用戶更方便提供遠程診…

K8S集群Master高可用實踐

本文將在前文基礎上介紹k8s集群的高可用實踐&#xff0c;一般來講&#xff0c;k8s集群高可用主要包含以下幾個內容&#xff1a;1、etcd集群高可用2、集群dns服務高可用3、kube-apiserver、kube-controller-manager、kube-scheduler等master組件的高可用 其中etcd實現的辦法較為…

[轉載]智能科普:VR、AR、MR的區別

智能科普&#xff1a;VR、AR、MR的區別 http://news.zol.com.cn/553/5534833.html news.zol.com.cn 2015-11-23 16:00近日&#xff0c; 獲得谷歌5億美元融資的技術公司Magic Leap在WSJD展會中放出了一段實錄視頻&#xff0c;引起不小騷動。如今&#xff0c;也有媒體稱他們為MR公…

PHP項目中,記錄錯誤日志

一、場景介紹&#xff1a; 環境&#xff1a;LNMP 我們通常是通過nginx的錯誤日志來分析分錯的&#xff0c;也就是我們在各個server中定義的error_log。 比如下面這樣&#xff0c;就是將錯誤日志定義在/etc/nginx/logs/error/www.xiaobudiu.top.log&#xff0c;發生錯誤&#xf…

持續集成指南:GitLab 的 CI/CD 工具配置與使用

1前言寫代碼這項工作&#xff0c;本質就是將工作自動化&#xff0c;減少手工操作提供效率&#xff0c;因為人的本質都是懶狗&#xff0c;程序員也不能例外&#xff0c;為了各種意義的效率提升&#xff08;懶&#xff09;&#xff0c;我們需要持續集成工具&#xff0c;將代碼測試…

php 錯誤日志 redis' already loaded in Unknown on line 0

環境介紹&#xff1a;LNMP 報錯信息&#xff1a;注&#xff1a;這個php_errors.log 是我在php.ini 中定義的錯誤日志路徑 問題原因&#xff1a; 報錯信息給出的意思是&#xff1a;redis和memcache 模塊已經加載過問題解決&#xff1a; php加載模塊有兩種方式&#xff0c;一種是…

第一周作業

我的Git賬號&#xff1a;AI1452349541 和代碼圖 這是我在電腦和手機上下的網易有道詞典 &#xff0c; C也下了。 ***學習內容總結*** 感覺任務并不是很難&#xff0c;有些任務沒完成是 因為還沒買電腦不好弄&#xff0c;下周電腦一定到位。 ***遇到的問題…

升級MariaDB為10.1版本

2019獨角獸企業重金招聘Python工程師標準>>> CentOS中升級mariadb為10.1GA版本。 1、如果有&#xff0c;停止服務 systemctl stop mariadb 2、卸載原來的數據庫服務 yum -y remove mari* 3、刪除數據庫文件 rm -rf /var/lib/mysql/* 4.創建/etc/yum.repos.d/MariaDB…

第一篇文章

第一次寫博客。歡迎各位大牛捧場轉載于:https://www.cnblogs.com/clnchanpin/p/6753665.html

羊了個羊的Ignite大會又來啦

據說最近羊了個羊非常火啊&#xff5e;可惜沒有時間精力研究。不過&#xff0c;薅微軟羊毛的機會我是一定不會錯過的&#xff0c;這不&#xff0c;薅羊毛的機會來了&#xff0c;哈哈哈。作為經常薅微軟羊毛的老司機&#xff0c;今天收到了微軟的郵件&#xff0c;告知有新的羊毛…

清除谷歌瀏覽器的dns緩存

谷歌地址欄輸入&#xff1a; chrome://net-internals/#dns出現下面界面&#xff1a;找到DNS選項&#xff0c;選擇clear host cache即可效果&#xff1a;這樣&#xff0c;谷歌瀏覽器上的dns緩存就清理掉了。應用場景&#xff1a; 本地環境和線上環境用的是一個host&#xff0c;這…

生產YUM源搭建

企業內部YUM源搭建轉載于:https://www.cnblogs.com/xiangtanglaojing/p/7603581.html

什么樣的代碼稱得上是好代碼?

“軟件自有其美感所在” --《重構》圖片&#xff1a;崇禮瀚海梁的山花 拍攝于2022年8月13日 攝影師&#xff1a;劉先生這篇內容寫作于4年前&#xff08;2018年&#xff09;&#xff0c;是自己多年軟件開發工作的一點感悟&#xff0c;現在看來雖有偏頗&#xff0c;但總體思想方…

Coding and Paper Letter(十四)

2019獨角獸企業重金招聘Python工程師標準>>> 資源整理。 1 Coding: 1.R語言包ungeviz&#xff0c;ggplot2的拓展包&#xff0c;專門用來作不確定性的可視化。 ungeviz 2.計算機圖形學相關開源項目。 計算機圖形學光線追蹤開源項目C源碼。 computer graphics ray tra…

QBC運算符含義

HQL運算符 QBC運算符 含義 Restrictions.eq() 等于 <> Restrictions.not(Exprission.eq()) 不等于 > Restrictions.gt() …