2018.06.03
第一部份:導入
1、組織函數的屬性
funcion A(name) {
this.name = name; // 實例基礎屬性 (該屬性,強調私有,不同享)
this.arr = [1]; // 實例援用屬性 (該屬性,強調私用,不同享)
this.say = function() { // 實例援用屬性 (該屬性,強調復用,須要同享)
console.log('hello')
}
}
注重:數組和要領都屬於‘實例援用屬性’,然則數組強調私有、不同享的。要領須要復用、同享。
注重:在組織函數中,平常很少有數組情勢的援用屬性,大部份狀況都是:基礎屬性 + 要領。
2、原型對象的作用
原型對象的用處是為每一個實例對象存儲同享的要領和屬性,它僅僅是一個一般對象罷了。而且一切的實例是同享同一個原型對象,因而有別於實例要領或屬性,原型對象唯一一份。而實例有許多份,且實例屬性和要領是自力的。
在組織函數中:為了屬性(實例基礎屬性)的私有性、以及要領(實例援用屬性)的復用、同享。我們首倡:
- 將屬性封裝在組織函數中
- 將要領定義在原型對象上
funcion A(name) {
this.name = name; // (該屬性,強調私有,不同享)
}
A.prototype.say = function() { // 定義在原型對象上的要領 (強調復用,須要同享)
console.log('hello')
}
// 不引薦的寫法:[緣由](https://blog.csdn.net/kkkkkxiaofei/article/details/46474303)
A.prototype = {
say: function() {
console.log('hello')
}
}
第二部份:js 繼續—各種體式格局的優瑕玷
體式格局1、原型鏈繼續
- 中心:將父類實例作為子類原型
長處:要領復用
- 由於要領定義在父類的原型上,復用了父類組織函數的要領。比方say要領。
瑕玷:
- 建立子類實例的時刻,不能傳參數。
- 子類實例同享了父類組織函數的援用屬性,比方arr屬性。
function Parent() {
this.name = '父親'; // 實例基礎屬性 (該屬性,強調私有,不同享)
this.arr = [1]; // (該屬性,強調私有)
}
Parent.prototype.say = function() { // -- 將須要復用、同享的要領定義在父類原型上
console.log('hello')
}
function Child(like) {
this.like = like;
}
Child.prototype = new Parent() // 中心
let boy1 = new Child()
let boy2 = new Child()
// 長處:同享了父類組織函數的say要領
console.log(boy1.say(), boy2.say(), boy1.say === boy2.say); // hello , hello , true
// 瑕玷1:不能傳參數
// 瑕玷2:
console.log(boy1.name, boy2.name, boy1.name===boy2.name); // 父親,父親,true
boy1.arr.push(2); // 修改了boy1的arr屬性,boy2的arr屬性,也會變化,由於兩個實例的原型上(Child.prototype)有了父類組織函數的實例屬性arr;所以只需修改了boy1.arr,boy2.arr的屬性也會變化。 ---- 原型上的arr屬性是同享的。
console.log(boy2.arr); // [1,2]
注重:修改boy1的name屬性,是不會影響到boy2.name。由於name是基礎屬性,不是援用屬性。
體式格局2、借用組織函數
- 中心:借用父類的組織函數來加強子類實例,等於是複製父類的實例屬性給子類。
長處:實例之間自力。
- 建立子類實例,能夠向父類組織函數傳參數。
- 子類實例不同享父類組織函數的援用屬性。如arr屬性
瑕玷:
- 父類的要領不能復用
由於要領在父組織函數中定義,致使要領不能復用(由於每次建立子類實例都要建立一遍要領)。比方say要領。(要領應該要復用、同享)
- 子類實例,繼續不了父類原型上的屬性。(由於沒有用到原型)
function Parent(name) {
this.name = name; // 實例基礎屬性 (該屬性,強調私有,不同享)
this.arr = [1]; // (該屬性,強調私有)
this.say = function() { // 實例援用屬性 (該屬性,強調復用,須要同享)
console.log('hello')
}
}
function Child(name,like) {
Parent.call(this,name); // 中心
this.like = like;
}
let boy1 = new Child('小紅','apple');
let boy2 = new Child('小明', 'orange ');
// 長處1:可傳參
console.log(boy1.name, boy2.name); // 小紅, 小明
// 長處2:不同享父類組織函數的援用屬性
boy1.arr.push(2);
console.log(boy1.arr,boy2.arr);// [1,2] [1]
// 瑕玷1:要領不能復用
console.log(boy1.say === boy2.say) // false (申明,boy1和boy2
的say要領是自力,不是同享的)
// 瑕玷2:不能繼續父類原型上的要領
Parent.prototype.walk = function () { // 在父類的原型對象上定義一個walk要領。
console.log('我會走路')
}
boy1.walk; // undefined (申明實例,不能取得父類原型上的要領)
體式格局3、組合繼續
- 中心:經由歷程挪用父類組織函數,繼續父類的屬性並保存傳參的長處;然後經由歷程將父類實例作為子類原型,完成函數復用。
長處:
- 保存組織函數的長處:建立子類實例,能夠向父類組織函數傳參數。
- 保存原型鏈的長處:父類的實例要領定義在父類的原型對象上,能夠完成要領復用。
- 不同享父類的援用屬性。比方arr屬性
瑕玷:
- 由於挪用了2次父類的組織要領,會存在一份過剩的父類實例屬性,細緻緣由見文末。
- 注重:’組合繼續’這類體式格局,要記得修復Child.prototype.constructor指向
第一次Parent.call(this);從父類拷貝一份父類實例屬性,作為子類的實例屬性,
第二次Child.prototype = new Parent();建立父類實例作為子類原型,此時這個父類實例就又有了一份實例屬性,但這份會被第一次拷貝來的實例屬性屏蔽掉,所以過剩。
為啥是兩次?假如照樣,不清楚,能夠看文末,我會細緻解說!
function Parent(name) {
this.name = name; // 實例基礎屬性 (該屬性,強調私有,不同享)
this.arr = [1]; // (該屬性,強調私有)
}
Parent.prototype.say = function() { // --- 將須要復用、同享的要領定義在父類原型上
console.log('hello')
}
function Child(name,like) {
Parent.call(this,name,like) // 中心 第二次
this.like = like;
}
Child.prototype = new Parent() // 中心 第一次
<!--這裡是修復組織函數指向的代碼-->
let boy1 = new Child('小紅','apple')
let boy2 = new Child('小明','orange')
// 長處1:能夠傳參數
console.log(boy1.name,boy1.like); // 小紅,apple
// 長處2:可復用父類原型上的要領
console.log(boy1.say === boy2.say) // true
// 長處3:不同享父類的援用屬性,如arr屬性
boy1.arr.push(2)
console.log(boy1.arr,boy2.arr); // [1,2] [1] 能夠看出沒有同享arr屬性。
注重:為啥要修復組織函數的指向?
console.log(boy1.constructor); // Parent 你會發現實例的組織函數居然是Parent。
而實際上,我們願望子類實例的組織函數是Child,所以要記得修復組織函數指向。修復以下
Child.prototype.constructor = Child;
實在Child.prototype = new Parent()
console.log(Child.prototype.__proto__ === Parten.prototype); // true
體式格局4、組合繼續優化1
- 中心:
經由歷程這類體式格局,砍掉父類的實例屬性,如許在挪用父類的組織函數的時刻,就不會初始化兩次實例,防止組合繼續的瑕玷。
長處:
- 只挪用一次父類組織函數。
- 保存組織函數的長處:建立子類實例,能夠向父類組織函數傳參數。
- 保存原型鏈的長處:父類的實例要領定義在父類的原型對象上,能夠完成要領復用。
瑕玷:
- 修改組織函數的指向以後,父類實例的組織函數指向,同時也發生變化(這是我們不願望的)
- 注重:’組合繼續優化1’這類體式格局,要記得修復Child.prototype.constructor指向
緣由是:不能推斷子類實例的直接組織函數,究竟是子類組織函數照樣父類組織函數。
function Parent(name) {
this.name = name; // 實例基礎屬性 (該屬性,強調私有,不同享)
this.arr = [1]; // (該屬性,強調私有)
}
Parent.prototype.say = function() { // --- 將須要復用、同享的要領定義在父類原型上
console.log('hello')
}
function Child(name,like) {
Parent.call(this,name,like) // 中心
this.like = like;
}
Child.prototype = Parent.prototype // 中心 子類原型和父類原型,實質上是同一個
<!--這裡是修復組織函數指向的代碼-->
let boy1 = new Child('小紅','apple')
let boy2 = new Child('小明','orange')
let p1 = new Parent('小爸爸')
// 長處1:能夠傳參數
console.log(boy1.name,boy1.like); // 小紅,apple
// 長處2:
console.log(boy1.say === boy2.say) // true
// 瑕玷1:當修復子類組織函數的指向後,父類實例的組織函數指向也會隨着變了。
細緻緣由:由於是經由歷程原型來完成繼續的,Child.prototype的上面是沒有constructor屬性的,就會往上找,如許就找到了Parent.prototype上面的constructor屬性;當你修改了子類實例的construtor屬性,一切的constructor的指向都邑發生變化。
沒修復之前:console.log(boy1.constructor); // Parent
修復代碼:Child.prototype.constructor = Child
修復以後:console.log(boy1.constructor); // Child
console.log(p1.constructor);// Child 這裏就是存在的題目(我們願望是Parent)
體式格局5、組合繼續優化2 又稱 寄生組合繼續 — 圓滿體式格局
- 中心:
- 長處:圓滿i
- 瑕玷:—
function Parent(name) {
this.name = name; // 實例基礎屬性 (該屬性,強調私有,不同享)
this.arr = [1]; // (該屬性,強調私有)
}
Parent.prototype.say = function() { // --- 將須要復用、同享的要領定義在父類原型上
console.log('hello')
}
function Child(name,like) {
Parent.call(this,name,like) // 中心
this.like = like;
}
Child.prototype = Object.create(Parent.prototype) // 中心 經由歷程建立中心對象,子類原型和父類原型,就會隔脫離。不是同一個啦,有用防止了體式格局4的瑕玷。
<!--這裡是修復組織函數指向的代碼-->
Child.prototype.constructor = Child
let boy1 = new Child('小紅','apple')
let boy2 = new Child('小明','orange')
let p1 = new Parent('小爸爸')
注重:這類要領也要修復組織函數的
修復代碼:Child.prototype.constructor = Child
修復以後:console.log(boy1.constructor); // Child
console.log(p1.constructor);// Parent 圓滿
第三部份:其他 + 相干題目解答
1、Object.create() 或 Object.create(object, [,propertiesObject])
Object.create() 的第二參數,是可選的。
- Object.create() 的內部道理:
// 个中,o 是新建立對象的原型(對象)
function object(o) {
function F() {}
F.prototype = o
return new F()
}
注重:之前,Object.create()沒有湧現之前,就是採納的這類體式格局。
拜見《js高等程序設計》P170
- Object.create() 做了哪幾件事變?
- 建立空對象{}
- 指定空對象{}的原型為Object.create()的參數。
- new 與 Object.create() 的區分?
以下是我的個人見解,(若有不對,還請斧正):
new 發生的實例,優先獵取組織函數上的屬性;組織函數上沒有對應的屬性,才會去原型上查找;假如組織函數中以及原型中都沒有對應的屬性,就會報錯。
Object.create() 發生的對象,只會在原型上舉行查找屬性,原型上沒有對應的屬性,就會報錯。
let Base1 = function() {
this.a = 1
}
let o1 = new Base1()
let o2 = Object.create(Base1.prototype)
console.log(o1.a); // 1
console.log(o2.a); // undefined
let Base2 = function() {}
Base2.prototype.a = 'aa'
let o3 = new Base2()
let o4 = Object.create(Base2.prototype)
console.log(o3.a); // aa
console.log(o4.a); // aa
let Base3 = function() {
this.a = 1
}
Base3.prototype.a = 'aa'
let o5 = new Base3()
let o6 = Object.create(Base3.prototype)
console.log(o5.a); // 1
console.log(o6.a); // aa
2、new 的歷程
funciton Func(name) {
this.name = name
}
let p = new Func('小紅')
new 的歷程,做了啥?做了四件事。
- 建立一個空對象obj:let obj = new Object()
- 設置原型鏈
obj.__proto__ = Func.prototype
就是:將新對象的__proto__ 指向組織函數的prototype
- 將組織函數Func的this指向obj,並實行組織函數Func
let result = Func.call(obj)
就是:運用call或apply,將組織函數的this綁定到新對象,並實行組織函數
- 推斷組織函數Func的返回值範例
假如是援用範例,就返回這個援用範例的對象。假如是值範例或沒有return,則返回空對象obj。
if (typeof(result) === "object"){
func=result;
}
else{
func=obj; // 默許返回
}
注重:js中的組織函數,是不須要有返回值的,所以默許返回的是新建立的空對象obj
3、為啥‘組合繼續’這類體式格局,會實行兩次父類組織函數??
- 第一次:Child.prototype = new Parent()
‘new 的歷程’的第三步,實在就是實行了父類組織函數。
- 第二次:Parent.call(this,name,like)
call的作用是轉變函數實行時的上下文。比方:A.call(B)。實在,終究實行的照樣A函數,只不過是用B來挪用罷了。所以,你就懂了Parent.call(this,name,like) ,也就是實行了父類組織函數。