這篇文章為大家帶來了javascript中繼承和原型鏈的相關知識,其中包括建構函式、原型以及class語法糖的相關問題,希望對大家有幫助。
this關鍵字添加的成員;只能透過實例化物件以後透過實例化物件進行存取;而靜態成員是函數本身上新增的成員,只能透過建構函數來訪問。
//创造一个构造函数let Father = function(name,age){ //实例成员 this.name = name; this.age = age; this.method = "我是一个实例成员";} //静态成员Father.like = "mother"; //检验实例对象是否能够被构造函数直接访问console.log(Father.method); //undefinedconsole.log(Father.like); //mother //实例化一个对象let father = new Father("小王",27); //检验静态对象是否能够被实例化对象访问console.log(father.name); //小王console.log(father.age); //27console.log(father.like); //undefined2.2 實例化物件的過程透過
new#關鍵字可以透過建構函式實現一個實例化對象,那麼在具體實例化的過程中發生了什麼呢?大致可以劃分為以下步驟:
(1) 建立一個空物件son {}(2) 為son 準備原型鏈連接son.__proto__ = Father.prototype
Father.call(this)
son.name
return this,此時的新物件就擁有了建構函式的方法和屬性了
//函数内部直接定义的方法let Father = function(){ this.read = function(){ console.log("我是内部定义的read方法!"); }}//通过淺析JavaScript的繼承與原型鏈添加的方法Father.prototype.look = function(){ console.log("我是通过淺析JavaScript的繼承與原型鏈定义的look方法!");} //实例化对象进行检验let father1 = new Father();let father2 = new Father(); father1.read(); //我是内部定义的read方法!father2.read(); //我是内部定义的read方法!console.log(father1.read === father2.read); //falsefather1.look(); //我是通过淺析JavaScript的繼承與原型鏈定义的look方法!father2.look(); //我是通过淺析JavaScript的繼承與原型鏈定义的look方法!console.log(father1.look === father2.look); /true可以發現,函數內部直接定義的方法在每實例化一個新的物件以後,都會為這個方法分配一個新的記憶體空間,而透過原型添加的方法便會共享一個空間。 一個小問題:所有實例化物件的屬性都是共享的嗎? 不存在記憶體空間的問題,判斷時看其值是否相同;
let Father = function(name){ this.name = name;}let father1 = new Father("小王"); let father2 = new Father("小红"); console.log(father1.name === father2.name); //falselet father1 = new Father("小王"); let father2 = new Father("小王"); console.log(father1.name === father2.name); //true因此我們可以總結定義建構子的基本規則,即
公共屬性定義到建構函數裡面,公共方法我們放到原型物件身上。
Father.prototype.method 可以共用方法,不會反應開啟空間儲存方法。
是每個物件都有的屬性,prototype是每個函數特有的方法;
屬性都會指向自身建構函式的prototype;
=== Function.prototype;
=== null 也就是原型鏈的終點;
__proto__原型的存在每個物件都有
__proto__原型的存在
let Father = function(name){ this.name = name;}let father = new Father("老王");console.log(father.__proto__ === Father.prototype); //true //验证上述说法中的第二条4.3 原型鏈圖 結合寫在最前面的幾點,理解上圖應該問題不大了,圖中圈起來的部分就是駭人聽聞的原型鏈。 4.4 原型鏈的尋找方式
function Star(name) { this.name = name; //(1)首先看obj对象身上是否有dance方法,如果有,则执行对象身上的方法 this.dance = function () { console.log(this.name + '1'); }}//(2)如果没有dance方法,就去构造函数原型对象prototype身上去查找dance这个方法。Star.prototype.dance = function () { console.log(this.name + '2');}; //(3)如果再没有dance方法,就去Object原型对象prototype身上去查找dance这个方法。Object.prototype.dance = function () { console.log(this.name + '3');}; //(4)如果再没有,则会报错。let obj = new Star('小红');obj.dance();
有两种添加方法,第一种为上面的写法,直接通过 构造函数.prototype.方法名 进行添加;第二种为重定义构造函数的prototype,但是此种情况会丢失掉原有的constructor构造器,所以一定要再连接回去,例子如下:
function Star(name) { this.name = name;}Star.prototype = { dance:function(){ console.log("重定义prototype"); }}Star.prototype.constructor = Star;
另外,类似于Array、String这些内置的类是不能这么处理的。
这里就长话短说,首先我们要明确继承需要继承哪些东西,在前文中我们提到了定义构造函数的基本规则,即**公共属性定义到构造函数里面,公共方法我们放到原型对象身上。**我们所需要继承的东西也不外乎就这二者,公共属性的继承可以通过call()或者apply()进行this的指向定义,而公共方法可以通过原型对象的赋值进行处理,因此我们很容易想到如下的方法:
//定义一个父类function Father(name) { this.name = name;}Father.prototype.dance = function () { console.log('I am dancing');};//定义一个子类function Son(name, age) { Father.call(this, name); this.age = age;}//通过赋值的方法连接Son.prototype = Father.prototype;//为子类添加方法Son.prototype.sing = function () { console.log('I am singing');}; let son = new Son('小红', 100); //此时父类也被影响了console.log(Father.prototype) //{dance: ƒ, sing: ƒ, constructor: ƒ}
很显然,当我们只想修改子类里面的方法时,显然上述方法不太合适;因此 我们可以尝试new一个新的父类出来,代码如下:
function Father(name) { this.name = name;}Father.prototype.dance = function () { console.log('I am dancing');};function Son(name, age) { Father.call(this, name); this.age = age;}Son.prototype = new Father();Son.prototype.sing = function () { console.log('I am singing');};let son = new Son('小红', 100);console.log(Father.prototype) //{dance: ƒ, constructor: ƒ}
对于以前了解过面向对象编程的程序员来讲,上述关于继承的写法属实让人有些难以接受,因此在es6里面新增了一个语法糖来更方便更便捷地书写继承,这里就直接上代码了;
class Father { constructor(name) { this.name = name; } dance() { console.log("我是" + this.name + ",我今年" + this.age + "岁," + "我在跳舞"); }}class Son extends Father { constructor(name, age) { super(name); this.age = age; } sing() { console.log("我是" + this.name + ",我今年" + this.age + "岁," + "我在唱歌"); }}let obj = new Son('小红', 19); obj.sing();obj.dance();
分析一下上面代码,首先一个类(构造函数)里面依旧为两部分,即公共属性和公共方法,constructor() 里面存放了该构造函数的公共属性,后面接着的便是公共方法,extends 关键字表示继承的是哪个类,super() 便是将里面父类里面相应的公共属性拿出来,这样看下来便可以将代码规整许多。
相关推荐:javascript学习教程
以上是淺析JavaScript的繼承與原型鏈的詳細內容。更多資訊請關注PHP中文網其他相關文章!