摘要:原型鏈構造函數原型實例的關系每個構造函數都有一個原型對象,原型對象都包含一個指向構造函數的指針,實例有一個指向原型對象的指針構造函數原型對象構造函數構造函數操作符實例對象構造函數實例對象原型對象如果試
原型鏈
構造函數/原型/實例 的關系
每個構造函數都有一個原型對象,原型對象都包含一個指向構造函數的指針,實例有一個指向原型對象的指針
構造函數 --(prototype)--> 原型對象 --(constructor)--> 構造函數
構造函數 --(new 操作符)--> 實例對象 --(constructor)--> 構造函數
實例對象 --(__proto__)--> 原型對象
如果試圖使用某個對象(實例)的某個屬性或方法,會首先在對象內部尋找該屬性,如果找不到去該對象的原型(instance.__proto__)里面去找,如果還找不到就繼續沿著 __proto__ 這個鏈條往上找,直到找到 Object.prototype 為止
javascript 里面一切皆對象,所以都可以從這個鏈條去出發
JavaScript 的繼承不同于傳統面向對象是靠類實現繼承,而是通過原型鏈實現繼承
ES5 繼承拷貝式繼承(通過深拷貝實現繼承)
原型式繼承
缺點:只能繼承原型方法
借用構造函數繼承
缺點:只能繼承實例屬性
組合式繼承
缺點:無論在什么情況下,都會調用兩次構造函數(創建父類實例的時候,在子類構造函數內不調用父類構造函數時)
組合寄生式繼承 (比較完美的繼承,但不能繼承父類靜態方法、靜態屬性)
function Parent() {} function Child() { // 繼承父類實例屬性 Parent.call(this) // 如果父類有參數寫在 this 后面 } // 繼承父類原型方法 Child.prototype = Object.create(Parent.prototype) // 修正子類原型的 constructor 指向 Child.prototype.constructor = Child
Object.create(proto, [propertiesObject]) MDN
創建一個新對象,使用現有的對象來提供新創建的的對象的 __proto__ Object.create(null) // 創建一個沒有原型的空對象 第二個參數可添加屬性描述符 js高級程序設計用一下代碼代替的這個方法 function createObject(P) { var F = function() {} F.prototype = P.prototype return new F() }
為什么要修正子類原型的 constructor 指向? 阮一峰
簡單總結一下: 任何一個 prototype 對象都有一個 constructor 屬性,指向它的構造函數 更重要的是,每一個實例也有一個 constructor 屬性,默認調用 prototype 的 constructor 屬性 如果沒有修正的那行代碼,結果如下 var c = new C() c.constructor === Child // false Child.prototype.constructor === Child // false c.constructor === Parent // true Child.prototype.constructor === Parent // true 這顯然會導致繼承鏈的混亂(c 明明是用構造函數Child生成的),因此我們必須手動糾正ES6 繼承
ES6 的繼承本質上還是借助原型鏈實現繼承
// 借助 class extends 關鍵字 class Parent { static sayAge() { return "18" } constructor(name) { this.name = "name" } } class Child extends Parent { constructor(name, age) { /** * 如果寫 constructor 必須調用 super 方法,這是因為子類自己的 this 對象,必須先通過父類構造函數完成塑造 * 不寫 super 就得不到 this對象,new 的時候就會報錯 * Uncaught ReferenceError: Must call super constructor in derived class before accessing "this" or returning from derived constructor * * ES5 實質上先創造子類的實例對象 this,然后再將父類的方法添加到 this 上面 (Parent.call(this)) * ES6 實質上先將父類實例對象的屬性和方法,加到 this 上面(必須先調用 super 方法),然后用子類構造函數修改 this */ super(name, age) this.age = age } } // 注意點:es6 的繼承可以繼承父類的靜態方法和靜態屬性,而ES5的繼承不行 // 經過 extends 之后,Child.__proto__ ==== Parent // true // Parent.__proto__ 返回 f() { [native code] } // Parent.__proto__.__proto__ === Object.prototypeBabel 轉碼后的 ES6 繼承代碼
// 為方便觀看,對代碼做了一些美化和省略處理 "use strict"; // 檢查子類是否調用了 super 方法 function _possibleConstructorReturn(self, call) { if (call && (_typeof(call) === "object" || typeof call === "function")) { return call; } return _assertThisInitialized(self); } function _assertThisInitialized(self) { if (self === void 0) { throw new ReferenceError( "this hasn"t been initialised - super() hasn"t been called" ); } return self; } // 獲取子類的原型鏈指向的對象即父類 function _getPrototypeOf(o) { _getPrototypeOf = Object.setPrototypeOf ? Object.getPrototypeOf : function _getPrototypeOf(o) { return o.__proto__ || Object.getPrototypeOf(o); }; return _getPrototypeOf(o); } // 核心繼承方法 function _inherits(subClass, superClass) { // ... // 同 es5 繼承的那一段 subClass.prototype = Object.create(superClass.prototype, { constructor: { value: subClass, // 修正 constructor 指向 writable: true, configurable: true } }); // 實現靜態屬性和方法的繼承 原理為:Child.__proto__ = Parent // 即子類(子類現在相當于實例)的在往上的 prototype = Parent,即子類可以使用父類的靜態屬性和方法 if (superClass) _setPrototypeOf(subClass, superClass); } function _setPrototypeOf(o, p) { _setPrototypeOf = Object.setPrototypeOf || function _setPrototypeOf(o, p) { o.__proto__ = p; return o; }; return _setPrototypeOf(o, p); } // ... 省略類的創建及檢測等方法 var Parent = /*#__PURE__*/ (function() { _createClass(Parent, null, [ { key: "sayAge", value: function sayAge() { return "18"; } } ]); function Parent(name) { _classCallCheck(this, Parent); this.name = "name"; } return Parent; })(); var Child = /*#__PURE__*/ (function(_Parent) { _inherits(Child, _Parent); function Child(name, age) { var _this; _classCallCheck(this, Child); // 下面一行代碼即 調用 super 的效果,如果不調用 super 子類將沒有 this _this = _possibleConstructorReturn(this, _getPrototypeOf(Child).call(this, name, age)); /*** * 如果注釋源碼中的 super 將編譯為如下代碼 * _this.age = age; * return _possibleConstructorReturn(_this); * 因為沒有調用 super 子類還沒有 this,所以下一行直接報錯 * * 如果源碼中不寫 _this.age = age * 將直接進入 _assertThisInitialized 方法,然后報錯,沒有調用 super 方法 */ _this.age = age; return _this; } return Child; })(Parent); var c = new Child(); // 如果不用 babel轉碼,直接在瀏覽器里運行,不寫 super,結果如下和 babel 轉義后報錯信息不一樣 // 原生報錯信息: Uncaught ReferenceError: Must call super constructor in derived class before accessing "this" or returning from derived constructor
原生構造函數不可通過 extends 實現繼承 ES6阮一峰
因為子類拿不到 原生父類內部的對象,即是通過 call 也不行
new 運算符上面說了繼承,那產生實例的操作符 new 是什么原理?
var obj = {} obj.__proto__ = Child.prototype F.call(obj) // 1. 創建一個空對象 // 2. 將這個空對象的 __proto__ 屬性 指向了構造函數的 prototype 屬性 上 ==> 繼承原型屬性方法 // 3. 將構造函數的 this 指針替換成了 obj(實例),再調用 構造函數 ===> 繼承實例屬性方法與原型鏈有關的幾個方法
hasOwnProperty : 該方法只會查找對象本身是否有某屬性,不會去原型鏈上尋找
A.isPropertyOf(instanceA) : 判斷 A 是不是 instanceA 的原型對象
instanceof : 判斷對象是不是某個構造函數的實例
__proto__ 只是瀏覽器廠商的私有實現,規范并不支持,規范支持Object.getPrototypeOf 和 Object.setPrototypeOf
文章版權歸作者所有,未經允許請勿轉載,若此文章存在違規行為,您可以聯系管理員刪除。
轉載請注明本文地址:http://specialneedsforspecialkids.com/yun/105935.html
摘要:今天閑來無事,看見幾行小字。又說所有對象,繼承終是。強行押韻一波這首詩的意思就是說的我今天沒有什么事情,然后無意中又在網上看到了任何對象都是從對象繼承而來的這句話。一時興起,便去驗證這句話。 今天閑來無事,看見幾行小字。又說所有對象,繼承終是Obj。—— 強行押韻一波 這首詩的意思就是說的我今天沒有什么事情,然后無意中又在網上看到了任何對象都是從Object對象繼承而來的這句話。一時興...
摘要:在繼承的構造函數中,我們必須如上面的例子那么調用一次方法,它表示構造函數的繼承,與中利用繼承構造函數是一樣的功能。 showImg(https://segmentfault.com/img/remote/1460000009078532); 在實際開發中,ES6已經非常普及了。掌握ES6的知識變成了一種必須。盡管我們在使用時仍然需要經過babel編譯。 ES6徹底改變了前端的編碼風格,...
摘要:聲明會提升,但是不會被初始化賦值,所以優先初始化賦值,則會進入暫時性死區,類似,變量內部啟動嚴格模式的所有方法包括靜態方法和示例方法都沒有原型對象,所以也沒有,不能使用來調用必須使用來調用內部無法重寫類名 class聲明會提升,但是不會被初始化賦值,所以優先初始化賦值,則會進入暫時性死區,類似let,const變量 const bar = new Bar(); // ok funct...
摘要:組合式繼承是最常用的繼承模式,但組合繼承使用過程中會被調用兩次一次是創建子類型的時候,另一次是在子類型構造函數的內部。 首先需要了解原型鏈機制: 原型鏈作為實現繼承的主要方法,其基本思想就是利用原型讓一個引用類型繼承另 一個引用類型的屬性和方法. 構造函數、原型、實例之間的關系: 每個構造函數都有一個原型對象(prototype),原型對象都包含一個指向構造函數的指針(constr...
摘要:對象擴展簡潔表示法屬性表達式值用中括號包起來,就是個表達式跟的功能是一樣的數組也是引用類型,值雖然都是空,但指向不同的內存地址實現對象的拷貝淺拷貝只拷貝對象自身的屬性,如果對象有繼承,繼承的屬性不會被拷貝只拷貝可枚舉屬性,不可枚舉屬性不會被 對象擴展 簡潔表示法 { let o = 1,k = 2; let es5 = { o: o, k...
閱讀 2919·2023-04-25 19:08
閱讀 1416·2021-11-16 11:45
閱讀 1964·2021-10-13 09:40
閱讀 4128·2021-09-30 09:47
閱讀 2415·2019-08-30 15:44
閱讀 2261·2019-08-30 13:03
閱讀 1387·2019-08-30 12:56
閱讀 1890·2019-08-26 14:04