摘要:在節中,我們學習到了通過構造函數創建對象的三個重要步驟,其中的一步是把構造函數的對象設置為創建對象的原型。利用而不是直接用創建一個實例對象的目的是,減少一次調用父構造函數的執行。
JavaScript語言不像面向對象的編程語言中有類的概念,所以也就沒有類之間直接的繼承,JavaScript中只有對象,使用函數模擬類,基于對象之間的原型鏈來實現繼承關系,
ES6的語法中新增了class關鍵字,但也只是語法糖,內部還是通過函數和原型鏈來對類和繼承進行實現。
JavaScript對象上都有一個內部指針[[Prototype]],指向它的原型對象,而原型對象的內部指針[[Prototype]]也指向它的原型對象,直到原型對象為null,這樣形成的鏈條就稱為原型鏈。
這樣在訪問對象的屬性時,會現在自己的屬性中查找,如果不存在則會到上一層原型對象中查找。
注意:
根據 ECMAScript 標準,someObject.[[Prototype]] 符號是用于指派 someObject 的原型。這個等同于 JavaScript 的 proto 屬性(現已棄用)。從 ECMAScript 6 開始, [[Prototype]] 可以用Object.getPrototypeOf()和Object.setPrototypeOf()訪問器來訪問。
例如:
var obj2 = { height: 170 } var obj3 = { name: "obj3" } Object.setPrototypeOf(obj3, obj2); console.log(obj3.height); // 170 var isproto = Object.getPrototypeOf(obj3) === obj2; console.log(isproto); // true1.2 不同方法創建對象與生成原型鏈 1.2.1 使用 Object.create 創建對象
ECMAScript 5 中引入了一個新方法:Object.create()。可以調用這個方法來創建一個新對象。新對象的原型就是調用 create 方法時傳入的第一個參數。
例如:
var a = {a: 1}; // a ---> Object.prototype ---> null var b = Object.create(a); // b ---> a ---> Object.prototype ---> null console.log(b.a); // 1 (繼承而來) var c = Object.create(b); // c ---> b ---> a ---> Object.prototype ---> null var d = Object.create(null); // d ---> null console.log(d.hasOwnProperty); // undefined, 因為d沒有繼承Object.prototype1.2.2 使用構造函數創建對象
在 JavaScript 中,構造函數其實就是一個普通的函數,一般函數名首字母大寫。當使用 new 操作符 來作用這個函數時,它就可以被稱為構造方法(構造函數)。
例如:
function Person (name, age) { this.name = name; this.age = age; } Person.prototype = { sayName: function () { console.log(this.name); } } var person1 = new Person("yangyiliang", 23); person1.sayName(); // yangyiliang
使用構造函數創建對象,經歷了如下三個關鍵步驟:
var temp = {}; //1 創建空對象 Person.call(temp, "yangyiliang", 23); //2 以空對象為this執行構造函數 Object.setPrototypeOf(temp, Person.prototype); //3 將構造函數的prototype 設置為空對象的原型 return temp;1.2.3 使用字面量方法創建對象
使用字面量方法創建的對象,根據對象的類型,他們的原型都會指向相應JavaScript內置構造函數的prototype,和直接使用內置構造函數創建對象生成的原型鏈相同,例如:
var o = {a: 1}; // o這個對象繼承了Object.prototype上面的所有屬性 // 所以可以這樣使用 o.hasOwnProperty("a"). // hasOwnProperty 是Object.prototype的自身屬性。 // Object.prototype的原型為null。 // 原型鏈如下: // o ---> Object.prototype ---> null var a = ["yo", "whadup", "?"]; // 數組都繼承于Array.prototype // (indexOf, forEach等方法都是從它繼承而來). // 原型鏈如下: // a ---> Array.prototype ---> Object.prototype ---> null function f(){ return 2; } // 函數都繼承于Function.prototype // (call, bind等方法都是從它繼承而來): // f ---> Function.prototype ---> Object.prototype ---> null2 繼承
在面向對象的語言當中,繼承關系應該指的是父類和子類之間的關系,子類繼承父類的屬性和方法,在JavaScript當中是父構造函數和子構造函數之間的關系。
類本身是對象的抽象形式,類的使用價值最后也是在于通過它能夠創建對象,
所以子類能夠繼承父類的屬性和方法的意義,就是通過子類創建出來的對象能夠繼承通過父類創建出來的對象的屬性和方法。
而這種對象之間的繼承關系,就是通過原型鏈實現。
在1.2.2節中,我們學習到了通過構造函數創建對象的三個重要步驟,其中的一步是把構造函數的prototype對象設置為創建對象的原型。
因此我們將父類的實例對象作為子類的prototype即能夠達到繼承的目的,如下圖所示:
繼承的實現
function Person (name, age) { this.name = name; this.age = age } Person.prototype.sayName = function () { console.log("my name is " + this.name); } function Student (name, age, school) { Person.call(this, name, age); this.school = school; } Student.prototype = Object.create(Person.prototype); Student.prototype.saySchool = function () { console.log("my school is " + this.school); }
上面代碼實現的繼承,遵循了幾個原則:
1、因為構造函數創建的對象將公用同一個原型,所以將每個對象獨有的屬性寫在構造函數中,將對象之間可以公用的方法寫在構造函數的prototype中,也就是對象的原型中
2、子構造函數繼承父構造函數做了兩個地方的工作,一是在子構造函數中利用call,調用父構造函數的方法,二是利用Object.create方法創建一個以父構造函數的prototype為原型的對象。
利用Object.create而不是直接用new 創建一個實例對象的目的是,減少一次調用父構造函數的執行。
3、先通過prototype屬性指向父構造函數的實例,然后再向prototype添加想要放在原型上的方法。
最后上一張js高級程序設計第三版中的一張源于原型鏈繼承的圖
利用class實現繼承下面利用ES6引入的新語法糖,class、extends關鍵字對上述實現繼承的代碼進行改寫:
class Person { constructor (name, age) { this.name = name; this.age = age; } sayName () { console.log("my name is " + this.name); } } class Student extends Person { constructor (name, age, school) { super(name, age); this.school = school; } saySchool () { console.log("my school is " + this.school); } }
class里的constructor 對應原來的構造函數
class里面的其他方法都是寫在原來構造函數的prototype中的
子類直接通過extends 關鍵字進行繼承
子類中可以通過super來調用父類中的方法
本文部分內容來自 https://developer.mozilla.org...
后續
function A() { } //函數默認會有一個prototype對象并且具有constructor屬性指向他本身 var a = new A() a instanceof A
function A() { } function B() { } var proto = {} B.prototype = proto A.prototype = proto var a = new A() a instanceof B //true a instanceof Object //true instanceof 是遍歷a 的原型鏈 尋找是否有和 B.prototype 是同一個對象的__proto__ 如果找到就為true
文章版權歸作者所有,未經允許請勿轉載,若此文章存在違規行為,您可以聯系管理員刪除。
轉載請注明本文地址:http://m.specialneedsforspecialkids.com/yun/86638.html
摘要:忍者級別的函數操作對于什么是匿名函數,這里就不做過多介紹了。我們需要知道的是,對于而言,匿名函數是一個很重要且具有邏輯性的特性。通常,匿名函數的使用情況是創建一個供以后使用的函數。 JS 中的遞歸 遞歸, 遞歸基礎, 斐波那契數列, 使用遞歸方式深拷貝, 自定義事件添加 這一次,徹底弄懂 JavaScript 執行機制 本文的目的就是要保證你徹底弄懂javascript的執行機制,如果...
摘要:有了原型鏈,就有了繼承,繼承就是一個對象像繼承遺產一樣繼承從它的構造函數中獲得一些屬性的訪問權。這里其實就是一個原型鏈與繼承的典型例子,開發中可能構造函數復雜一點,屬性定義的多一些,但是原理都是一樣的。 作用域、原型鏈、繼承與閉包詳解 注意:本章講的是在es6之前的原型鏈與繼承。es6引入了類的概念,只是在寫法上有所不同,原理是一樣的。 幾個面試常問的幾個問題,你是否知道 insta...
摘要:有了原型鏈,就有了繼承,繼承就是一個對象像繼承遺產一樣繼承從它的構造函數中獲得一些屬性的訪問權。這里其實就是一個原型鏈與繼承的典型例子,開發中可能構造函數復雜一點,屬性定義的多一些,但是原理都是一樣的。 作用域、原型鏈、繼承與閉包詳解 注意:本章講的是在es6之前的原型鏈與繼承。es6引入了類的概念,只是在寫法上有所不同,原理是一樣的。 幾個面試常問的幾個問題,你是否知道 insta...
摘要:函數式編程前端掘金引言面向對象編程一直以來都是中的主導范式。函數式編程是一種強調減少對程序外部狀態產生改變的方式。 JavaScript 函數式編程 - 前端 - 掘金引言 面向對象編程一直以來都是JavaScript中的主導范式。JavaScript作為一門多范式編程語言,然而,近幾年,函數式編程越來越多得受到開發者的青睞。函數式編程是一種強調減少對程序外部狀態產生改變的方式。因此,...
閱讀 797·2021-10-09 09:44
閱讀 702·2019-08-30 13:55
閱讀 3158·2019-08-29 15:07
閱讀 3225·2019-08-29 13:09
閱讀 2418·2019-08-29 11:10
閱讀 1295·2019-08-26 14:05
閱讀 3601·2019-08-26 13:57
閱讀 2210·2019-08-23 16:42