摘要:也就是說,并不知道,等是屬于哪個對象的哪個構(gòu)造函數(shù)或者類。構(gòu)造函數(shù)模式與原型模式相結(jié)合的模式。給新建的對象,添加屬性,建立與構(gòu)造函數(shù)之間的聯(lián)系。另一種就是構(gòu)造函數(shù)繼承了。
前面講完原型鏈,現(xiàn)在來講繼承,加深理解下。
一、對象的相關知識什么是對象? 就是一些無序的 key : value 集合, 這個value 可以是 基本值,函數(shù),對象。(注意 key 和 value 之間 是冒號 : ,每個 key : value 之間 是逗號 , )
var person = { name: "zdx", age: 18, get: function(){ return "名字:" + this.name + "年紀:" + this.age; } }
這時的 person 就是一個對象
讀取對象的屬性,有兩種方式:點 和 [" "],(使用中括號,里面是有引號的)
person.name //"zdx" person["age"] //18
之前說過,對象的創(chuàng)建 有三種 方式: 字面量,new,Object.create;
但是,這種簡單的創(chuàng)建,并不能滿足,我們實際開發(fā)的要求。
比如,每個人的名字,年紀不一樣,我們不可能,每個人都寫一個對象,那不得炸了。
var personA = { name: "A", age: 28 } var personB = { name: "B", age: 22 }
所以,這時候,我們就需要一些創(chuàng)建模式。
工廠模式呢,就是我們寫一個方法,然后通過這個方法復制出我們需要的對象。我們需要多少個,就復制多少個。(注意這里是用方法(函數(shù)) 來生成對象)
var createPerson = function(name,age){ var obj = {}, obj.name = name, obj.age = age }
然后我們就可以這樣使用了。
var personA = createPerson("A", 28); var personB = createPerson("B", 22);
但是呢,這種模式,創(chuàng)建的對象,無法歸類。也就是說,并不知道 personA,personB 等 是 屬于哪個對象的(哪個構(gòu)造函數(shù)或者類)。
具有相同特性的一類事物,把它總結(jié)成一個 類;比如,人類,有男人,女人,小孩。把他們總結(jié)成了人類。
比如:
var obj = {}; obj instanceof Object; //true //instanceof 方法 可以判斷 前者是否是 后者的實例對象 var arr = []; arr instanceof Array; //true
于是,構(gòu)造函數(shù)模式來了。
要想知道,男人,女人 屬于什么類, 人類? 獸類? 這時候就需要 構(gòu)造函數(shù)(類)了。
function Person(name, sex){ //注意,我們通常把構(gòu)造函數(shù)(類)的首字母大寫! this.name = name; this.sex = sex; } var personA = new Person("張三", "男"); var personB = new Person("李四", "女"); console.log( personA instanceof Person ); //true console.log( personB instanceof Person ); //true
這時候 創(chuàng)建出來的對象, 就知道 它屬于哪個構(gòu)造函數(shù)(類)了。
這里為啥用了 this 呢?new 一個函數(shù), 為啥會創(chuàng)建出 一個實例對象(我們把 new 出來的對象 稱為 實例對象,簡稱實例)呢?
這步,之前的原型鏈中說過,如果你懂了,可以簡單過一眼,增強記憶。
這個呢,就要理解new 到底干了啥呢:
新建一個對象;
將該對象,即實例指向構(gòu)造函數(shù)的原型;
將構(gòu)造函數(shù)的this,指向該新建的對象;
返回該對象,即返回實例。下面,手寫一個方法 實現(xiàn)new 的功能。
function New(func){ //func 指傳進來的構(gòu)造函數(shù) var obj = {}; //這里就直接新建一個空對象了,不用new Object了,效果一樣的,因為我感覺這里講實現(xiàn)new的功能 再用 new 就不太好。 if(func.prototype != null){ obj.__proto__ = func.prototype; } func.apply(obj,Array.prototype.slice.call(arguments, 1)); //把func構(gòu)造函數(shù)里的this 指向obj對象(你可以理解成func構(gòu)造函數(shù)的this 替換成 obj對象); //Array.prototype.slice.call(arguments, 1);這個就是把New 函數(shù),func 之后傳進來的參數(shù),轉(zhuǎn)成數(shù)組。 //把該參數(shù)數(shù)組,傳入func構(gòu)造函數(shù)里,并執(zhí)行func構(gòu)造函數(shù),比如:屬性賦值。 return obj; }
驗證下:
function Person(name, sex){ this.name = name; this.sex = sex; this.getName = function(){ return this.name; } } var p = New(Person,"周大俠啊", "男"); console.log( p.getName() ); //"周大俠啊" console.log( p instanceof Person ); //true
去掉new 的寫法就是這樣:
function Person(name, sex){ var obj = {}; obj.__proto__ = Person.prototype; obj.name = name; obj.sex = sex; obj.getName = function(){ return obj.name; } return obj; } var p = Person("周大俠啊", "男"); console.log( p.getName() ); //"周大俠啊" console.log( p instanceof Person ); //true
使用 new 就簡化了步驟。
這種模式,也有弊端,你看出來了嗎? 有木有發(fā)現(xiàn),每個實例對象 都有 相同 的 getName 方法,這樣,是不是就但占資源了呢,100個實例對象,就新建了 100 個 getName 方法。
原型模式開始!
之前的原型鏈說過,屬性、方法的讀取是沿著原型鏈查找的,也就是說,在構(gòu)造函數(shù)的原型對象上 創(chuàng)建的 屬性、方法,它的實例對象都能夠使用。
function Person(){}; Person.prototype.name = "周大俠啊"; Person.prototype.sex = "男"; Person.prototype.getName = function(){ return Person.prototype.name; } var p = new Person(); console.log( p.getName() ); //"周大俠啊" console.log( p.hasOwnProperty("getName") ); //false getName 不是 p實例的屬性
這樣,創(chuàng)建的實例對象 都能使用 getName ,并且,也不會給每個實例對象,添加該屬性。
不過,你發(fā)現(xiàn)了嗎,這樣創(chuàng)建的實例對象,都是固定的屬性值, 一更改,所有的實例對象獲取的值,也都改變了。
那么說了半天,這都是啥呢,別急,正因為上面做的鋪墊,才有更好的 方法。我覺得,眼尖的同學,可能已經(jīng)知道了。
構(gòu)造函數(shù)模式 與 原型模式 相結(jié)合的模式。
構(gòu)造函數(shù)模式,可以創(chuàng)建 每個 實例對象 自己的屬性, 而原型模式,可以共享,同一個方法。
所以,我們一般,把對象自己的屬性、方法 ,用構(gòu)造函數(shù)模式創(chuàng)建; 公共的方法,用原型模式 創(chuàng)建。
function Person(name, age){ this.name = name; this.age = age; } Person.prototype.getName = function(){ return this.name; } var p = new Person("周大俠啊", 22); p.getName();
這樣就可以完美的創(chuàng)建對象了。
要是你有余力,可以想想,這里的 this.name 中 this 是怎么回事。
但是,上面這個還可以優(yōu)化,要是 Person.prototype 上有很多方法, 這種寫法就很不好,一般,我們用這種寫法:
function Person(name, age){ this.name = name; this.age = age; } //新建一個對象,給該對象添加 方法, 然后把該對象 賦值給Person.prototype ,該對象就成為 Person (構(gòu)造函數(shù))的原型對象了。 Person.prototype = { constructor : Person, //給新建的對象,添加constructor 屬性,建立與構(gòu)造函數(shù)之間的聯(lián)系。 getName : function(){ return this.name; }, getAge : function(){ return this.age; } } var p = new Person("周大俠啊", 22); p.getName();
這里的 this 又看懂是怎么回事了嗎? 那我就簡單說一下 this 的知識吧。
關于 this 估計都聽說過 誰調(diào)用它,this就指向誰,這種說法,不嚴謹,this 的指向,是 在函數(shù)調(diào)用(運行)時確定的!
上面的:
Person.prototype = { constructor : Person, getName : function(){ return this.name; }, } var p = new Person("周大俠啊", 22); p.getName();
this 在getName 函數(shù)里,而getName 真正運行 的地方 是 p.getName,p 是調(diào)用者,所以, this 就指向 p(換句話說,這時的this 就是 p)。
你要理解 精髓, 函數(shù)調(diào)用(運行)時 確定的調(diào)用者 之間的關系。
看這個:
var a = 10; var b = { a : 20, say : function(){ console.log(this.a); } } var c = b.say; c(); //10 非嚴格模式下,獨立調(diào)用(無調(diào)用者)this 指向全局對象
包含 this 的函數(shù) 正在調(diào)用(運行) 的時候 是 c(); 此時,無調(diào)用者,指向全局對象。
上面做了辣么多鋪墊,終于要開始繼承的講解啦!什么是繼承呢,其實呢,就是你 想要使用 別人的屬性,或者方法; 這時候就要利用繼承來實現(xiàn)。
既然是 得到別人的屬性,方法,就是繼承,那么不就是 除了 Object.prototype 其他都是繼承了? dui ! 你說的沒錯...哈哈
所以,繼承的一種就是基于 原型鏈的了 ,就是屬性的查找,讀取。
另一種就是 構(gòu)造函數(shù)繼承了。
首先來一個需要被繼承的目標
//父類: function Person(name) { this.name = name; } Person.prototype = { constructor: Person, getName : function() { return this.name; } }
//子類: function Son(name, age) { Person.call(this, name); //call 方法,把前面函數(shù)的this 指向 給定的對象,第二個參數(shù)開始,傳入 參數(shù),并執(zhí)行函數(shù)。 this.age = age; } //就相當于 function Son(name, age) { //Person(name); 此時 Person 里的 this 等于當前的this this.age = age; } //等同于 function Son(name, age) { this.name = name, this.age = age }
下面檢驗一下:
var s1 = new Son("周大俠啊", 22); console.log(s1.name); //"周大俠啊" console.log(s1.getName()); //報錯,這是Person原型對象上的方法
這種是,new 一個父類實例,然后把son.prototype 添加到原型鏈上去。
//new 一個 Person 實例 賦給 Son.prototype Son.prototype = new Person(); //給子類的原型加方法: Son.prototype.get = function() { return this.name + this.age; }
上面的 Son.prototype = new Person() 實際上就是這樣的
//內(nèi)部就是 var obj = {}; obj.__proto__ = Person.prototype; Son.prototype = obj //把 obj 賦給 Son.prototype 。而obj 這個實例 又指向了Person的原型對象, //這樣,Son 就添加到了 Person 的原型鏈上了。
所以:
//給子類的原型加方法: Son.prototype.get = function() { return this.name + this.age; } //就是: obj.get = function(){ //通過給obj添加方法 然后賦值給Son的原型對象 return this.name + this.age; };
驗證:
var s2 = new Son("周大俠啊", 22); console.log(s2.get()); //"周大俠啊22" console.log(s2.getName()); //"周大俠啊"
function create(proto, options){ //proto表示父類的原型對象,options表示給子類添加的屬性 var obj = {}; obj.__proto__ = proto; return Object.defineProperties(obj,options); //Object.defineProperties方法直接在一個對象上定義新的屬性或修改現(xiàn)有屬性,并返回該對象。所以這里就直接return了 } //繼承 就這樣寫了 Son.prototype = create(Person.prototype,{ constructor: { //這種格式的賦值寫法, 是 defineProperties 方法規(guī)定的寫法。 value: Son }, get: { value: function(){ return this.name + this.age; } } }) //驗證一下 var s3 = new Son("zdx",22); console.log(s3.getName()); //"zdx" console.log(s3.get()); //"zdx22"
Object.defineProperties具體用法點這里
這里是它具體用法
可以直接使用它來完成繼承:
Son.prototype = Object.create(Person.prototype,{ constructor: { value: Son }, get: { value: function(){ return this.name + this.age; } } }) //驗證一下 var s4 = new Son("zdx",22); console.log(s4.getName()); //zdx console.log(s4.get()); //zdx22
文章版權(quán)歸作者所有,未經(jīng)允許請勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請注明本文地址:http://systransis.cn/yun/94966.html
摘要:情況沒有明確作用對象的情況下,通常為全局對象例如函數(shù)的回調(diào)函數(shù),它的就是全局對象。正因如此,機器可以作為這類對象的標志,即面向?qū)ο笳Z言中類的概念。所以機器又被稱為構(gòu)造函數(shù)。原型鏈也就是繼承鏈。 JS面向?qū)ο蠖?this/原型鏈/new原理 阮一峰JavaScript教程:面向?qū)ο缶幊?阮一峰JavaScript教程:實例對象與 new 命令 阮一峰JavaScript教程:this 關...
摘要:每一個由構(gòu)造函數(shù)創(chuàng)建的對象都會默認的連接到該神秘對象上。在構(gòu)造方法中也具有類似的功能,因此也稱其為類實例與對象實例一般是指某一個構(gòu)造函數(shù)創(chuàng)建出來的對象,我們稱為構(gòu)造函數(shù)的實例實例就是對象。表示該原型是與什么構(gòu)造函數(shù)聯(lián)系起來的。 本文您將看到以下內(nèi)容: 傳統(tǒng)構(gòu)造函數(shù)的問題 一些相關概念 認識原型 構(gòu)造、原型、實例三角結(jié)構(gòu)圖 對象的原型鏈 函數(shù)的構(gòu)造函數(shù)Function 一句話說明什么...
摘要:實際上,我們通常認為是自有類型的唯一成員。比較運算符的操作數(shù)可能是任意類型。結(jié)果只有,例得到操作值等價的布爾值真值為,假值為等同于,經(jīng)常稱為強制轉(zhuǎn)換。結(jié)果返回布爾值的用法是中唯一一個不等于任何值的包括它自己。 說起 js 類型轉(zhuǎn)換,都是頭疼吧,暈暈的,但是不行啊,這東西很重要滴! 基礎知識 JavaScript的數(shù)據(jù)類型分為六種,分別為null, undefined, boolean,...
摘要:此時產(chǎn)生了閉包。導致,函數(shù)的活動對象沒有被銷毀。是不是跟你想的不一樣其實,這個例子重點就在函數(shù)上,這個函數(shù)的第一個參數(shù)接受一個函數(shù)作為回調(diào)函數(shù),這個回調(diào)函數(shù)并不會立即執(zhí)行,它會在當前代碼執(zhí)行完,并在給定的時間后執(zhí)行。 上一節(jié)說了執(zhí)行上下文,這節(jié)咱們就乘勝追擊來搞搞閉包!頭疼的東西讓你不再頭疼! 一、函數(shù)也是引用類型的。 function f(){ console.log(not cha...
摘要:創(chuàng)建一個新的對象即實例對象把新對象的指向后面構(gòu)造函數(shù)的原型對象。簡單來驗證一下等同與對象沒有原型對象的原型對像等同于構(gòu)造函數(shù)是等同于,構(gòu)造函數(shù)是七原型鏈的作用其實,原型鏈的根本作用就是為了屬性的讀取。 首先說一下,函數(shù)創(chuàng)建的相關知識 在JavaScript中,我們創(chuàng)建一個函數(shù)A(就是聲明一個函數(shù)), 那么 js引擎 就會用構(gòu)造函數(shù)Function來創(chuàng)建這個函數(shù)。所以,所有的函數(shù)的con...
閱讀 2996·2021-11-25 09:43
閱讀 3640·2021-08-31 09:41
閱讀 1256·2019-08-30 15:56
閱讀 2146·2019-08-30 15:55
閱讀 3007·2019-08-30 13:48
閱讀 2824·2019-08-29 15:15
閱讀 995·2019-08-29 15:14
閱讀 2665·2019-08-28 18:26