假如沒有面向對象這類抽象觀點的小夥伴,發起先看一下我寫的
JS基本入門篇(三十四)—面向對象(一)👏👏👏👏
1.異常異常重要而又簡樸的觀點—原型鏈
想要以下為 f 增加一個say要領,有三種要領。
<script>
function Fn(){};
var f = new Fn();
</script>
要領一:相當於增加一個自定義屬性,此屬性是一個要領。
<script>
function Fn(){};
var f = new Fn();
f.say = function(){
console.log(1);
}
f.say(); //打印 1
</script>
要領二:為組織函數.prototype增加一個say要領。
<script>
function Fn(){};
var f = new Fn();
Fn.prototype.say = function(){
console.log(2);
}
f.say(); //打印 2
</script>
要領三:為Object.prototype增加一個say要領。
<script>
function Fn(){};
var f = new Fn();
Object.prototype.say = function(){
console.log(3);
}
f.say(); //打印 3
</script>
疑問🤔️:
要領二中掛在組織函數的要領,和要領三中掛在Object的要領, f 為何能查找的到???
剖析(此剖析一定要看懂,沒有看懂多看幾遍或許百度下):
是原型鏈的觀點。就是js內部的查找機制。起首要邃曉:
1.prototype 原型
當一個函數被說明的時刻,該函數下默許有一個屬性:prototype,該屬性的值是一個對象。
舉例說明:
<script>
function Fn(){}
var f = new Fn();
console.log( Fn.prototype );
</script>
效果如圖所示:
2.__proto__
當一個對象被建立的時刻,該對象會自動被增加上一個屬性:__proto__,他的值也是一個對象,而且該屬性 就是 當前這個對象的組織函數的prototype
舉例說明:
<script>
function Fn(){}
var f = new Fn();
console.log( f.__proto__ );
</script>
效果如圖所示:
3.對象.__proto__ === 組織函數.prototype
舉例說明
<script>
function Fn(){};
Fn.prototype.say=function () {
console.log(1);
};
var f = new CreatePreson();
f.say=function () {
console.log(2);
};
console.log( f.__proto__ );
console.log( Fn.prototype );
console.log( Fn.prototype === f.__proto__ );
</script>
效果如圖所示:
所以查找機製為:
挪用f.say( );時
if( 對象 f上面是不是say要領 ){//為真,實行if內部的代碼
則挪用f上面的say要領
}else if(Fn.prototype是不是有say要領){//為真,實行else if內部的代碼
第一步:f.__proto__ === Fn.prototype
由這個查找到f對應的組織函數的原型,即為 Fn.prototype。
第二步:檢察Fn.prototype是不是有say要領,有的話,則挪用Fn.prototype是上面的say要領。
}else if( Object.prototype是不是有say要領 ){
第一步:Fn.prototype.__proto__ === Object.prototype
由這個查找到Fn.prototype對應的組織函數的原型,即為 Object.prototype。
第二步:Object.prototype是不是有say要領,有的話,則挪用Object.prototype是上面的say要領。
}else{//假如以上都沒有say要領
會報錯。
}
舉例說明
<script>
function Fn() {
}
var f = new Fn();
f.say = function () {
console.log(1);
};
Fn.prototype.say = function () {
console.log(2);
};
Object.prototype.say = function () {
console.log(3);
};
f.say();//打印1。因為在f上面找到了,就不會往下繼續找了。
</script>
2.hasOwnProperty, constructor, instanceof
1.hasOwnPropert
🌹🌹🌹
作用
用來推斷某個對象是不是含有 指定的 本身屬性
語法
boolean object.hasOwnProperty(prop)
參數
object
要檢測的對象
prop
要檢測的屬性稱號。
注重:不會沿着原型鏈查找屬性,只查找本身屬性
假如以上筆墨都看不懂,能夠先看例子,再看筆墨。
<script>
//建立組織函數
function CreatPerson(name, age) {
this.name = name;
this.age = age;
}
CreatPerson.prototype.kind = "人類";
CreatPerson.prototype.say = function () {
console.log("我會措辭 ");
};
//天生對象,實例化的歷程
var p = new CreatPerson("Lily",28);
//挪用hasOwnProperty要領,檢察是不是是本身的屬性,不再在原型鏈上面找。
console.log(p.hasOwnProperty("name"));//true
console.log(p.hasOwnProperty("age"));//true
console.log(p.hasOwnProperty("kind"));//false
console.log(p.hasOwnProperty("say"));//false
</script>
2.constructor
🌹🌹🌹
函數的原型prototype的值是一個對象,初始化會有一個屬性為constructor,
對應的值為具有這個原型的函數
注重:prototype的值是能夠修改的,修改了prototype的值,
要手動將constructor指向函數
<script>
function Fn() {
console.log("組織函數");
}
console.log(Fn.prototype.constructor); // Fn(){console.log("組織函數");
//因為arr 是經由過程字面量的體式格局天生一個數組,然則函數內部照樣會經由過程new Array 天生arr對象
//所以Array是arr的組織函數
//arr沒有constructor,會依據原型鏈查找,找到JS內部的Array.prototype上的constructor要領。
//Array.prototype.constructor指向Array
var arr = [1, 2, 3];
console.log(arr.constructor); // Array() { [native code] }
//因為obj 是經由過程字面量的體式格局天生一個對象,然則函數內部照樣會經由過程new Object 天生obj對象
//所以Object是obj的組織函數
//obj沒有constructor,會依據原型鏈查找,找到JS內部的Object.prototype上的constructor要領。
//Object.prototype.constructor指向Object
var obj = {};
console.log(obj.constructor); //Object() { [native code] }
</script>
3.instanceof
🌹🌹🌹
instanceof
是一個二元運算符,返回布爾值
運算檢測 一個 對象原型 是不是 在要檢測的對象的原型鏈上
運用:object instanceof constructor
<script>
var arr = [];
console.log( typeof arr );//"object"
console.log( arr instanceof Array);//true
console.log( arr instanceof Object);//true
//str是字面量天生的,是由JS內部的String組織函數new出來的。
//然則str會馬上"壓扁"本身,讓本身不是對象。
//所以str都不是對象了,天然instanceof String 的獲得的值為fasle
//但str.indexOf(),str照樣能夠挪用indexOf()要領的緣由是,當它挪用要領的時刻,會從新將本身包裝成對象。
//運用完畢後會從新"壓扁"本身,讓本身不是對象。
var str = "123";
console.log( str instanceof Array );//false
console.log( str instanceof String);//false
console.log( str instanceof Object);//false
var obj = {};
console.log( obj instanceof Array );//false
console.log( obj instanceof Object);//true
// Array.prototype -> Object.prototype
</script>
3.this的指向
1.誰挪用就指向誰。
2.誰觸發就指向誰。
舉例說明1
<script>
function fn() {
console.log(this);
}
fn();//打印效果:Window 剖析:相當於 window.fn(); 所以指向window
document.onclick=fn;//打印效果:document 剖析: 由document的觸發,所以指向document
</script>
舉例說明2
<script>
var obj={
n:"k",
foo:function () {
console.log(this);
console.log(this.n);
}
};
obj.foo();
//運轉效果為:
//{n: "k", foo: ƒ}
//k
//剖析:obj.foo();是obj挪用foo對應的函數。所以this指向obj。
var b = obj.foo;
b();
//運轉效果為:
//Window {postMessage: ƒ, blur: ƒ, focus: ƒ, close: ƒ, frames: Window, …}
//undefined
//剖析:var b = obj.foo; ==== var b=function () { console.log(this); console.log(this.n);}
//變量b是Window 的自定義屬性,所以b(); === window.b();
//所以个中的this指向window,this.n === window.n
//因為window上面沒有n這個自定義屬性,則打印出來為 undefined
</script>
4.修改this指向的三種體式格局
1.call
1. 函數**`會`**馬上實行
2. 函數實行時刻,函數**`第一個參數`**是內部的**`this指向`**
3. **`第一個參數以後的參數,都是指 函數實行時刻 ,內部的實參`**
直接擼代碼,舉例說明
<script>
function Fn() {
console.log(this);
}
Fn.call();
//效果為:Window
// 1.函數會馬上實行
// 2.不傳入任何參數,this的指向穩定,照樣指向window。
Fn.call(document);
//效果為:#document
// 1.函數會馬上實行
// 2.括號中的內容 第一個參數 就是 函數實行時刻 ,內部的this指向
function Go(a,b) {
console.log(this);
console.log(a,b);
}
Go.call(document,2,3);
//效果為:
// #document
// 2 3
// 1.函數會馬上實行
// 2.括號中的內容 第一個參數 就是 函數實行時刻 ,內部的this指向
// 3.第一個參數以後的參數,都是指 函數實行時刻 ,內部的實參
</script>
2.bind
1. 函數**`不會`**馬上實行
2. 函數實行時刻,**函數第一個參數是內部的this指向**
3. **第一個參數以後的參數,都是指 函數實行時刻 ,內部的實參**
4. **`返回的是 修改了 this指向的新函數`**
與call的區分就是函數不會馬上實行。
舉例說明
<script>
function foo (a,b) {
console.log( this );
console.log( a,b );
}
var fn = foo.bind( document,2,3);// 函數 不會 馬上實行,返回的是 修改了 this指向的新函數
fn();//挪用以後才會實行 this指向的新函數
//運轉效果:
//#document
//2 3
</script>
3.apply
與call很類似,只是第二個參數值接收數組
舉例說明
<script>
function foo (a,b) {
console.log( this );
console.log( a,b );
}
foo.apply( document,[2,3] ); // 和call 類似 直接挪用 , 不過第二個參數接收數組
//運轉效果:
//#document
//2 3
</script>
5.數組的檢測
因為由typeof打印出來,數組和對象的效果都是object。有時刻我們須要推斷變量是不是是數組
。
要領一:
var arr = [1,2,3];
console.log( arr.toString() );//1,2,3
Array.prototype.toString = Object.prototype.toString;//從新賦值Array.prototype.toString的要領。然則下次在別的狀況挪用Array.prototype.toString,此要領已被從新掩蓋。所以不太好
console.log( arr.toString() );//[object Array]
var arr = [1,2,3];
console.log( Object.prototype.toString.call(arr) );
// 運用 Object.prototype.toString
// 同時 修改內部的this指向 arr
console.log( arr );//[object Array]
要領二:
6.繼續
繼續
在JavaScript中,繼續就是讓一個對象(子類)具有另一個對象(父類)的屬性/要領(另有原型上的屬性和要領)。个中準繩就是:
1.子類的修改不能影響父類
2.子類能夠 在 父類 基本上 增加本身的屬性 和 要領
1.經由過程prototype 賦值 (行不通,然則照樣要看行不通的緣由)
舉例說明:上代碼
<script>
function CreatPerson() {}
CreatPerson.prototype.say = function () {
console.log("我會說漢語");
};
function Coder(){}
// 此處 子類 的 prototype和父類的 prototype 指的是 同一個對象。
// 的確是繼續CreatPerson的原型上面的要領,然則當Coder.prototype重寫say要領
// CreatPerson.prototype中的say要領也會被改寫
Coder.prototype = CreatPerson.prototype;
Coder.prototype.say=function () {//
console.log("我會說漢語,還會碼代碼");
};
var person = new CreatPerson();
person.say();
var coder = new Coder();
coder.say();
</script>
2.原型鏈繼續
子類的原型 = 父類的實例
注重 : 在為 子類 原型 賦值的時刻去修改 constructor
弊病 : 子類組織函數內的地點的修改會修改其他子類。
因為一切子類組織函數的原型同享一個實例。
舉例說明
<script>
function CreatPerson() {
this.age=18;
this.arr=[1,2,3];
}
CreatPerson.prototype.say=function () {
console.log("我會說漢語");
};
CreatPerson.prototype.eat=function () {
console.log("我想用飯");
};
function Coder() {}
Coder.prototype = new CreatPerson();//子類組織函數內的地點的修改會修改其他子類。因為一切子類組織函數的原型同享一個實例
Coder.prototype.constructor=Coder;//在為 子類 原型 賦值的時刻去修改 constructor
Coder.prototype.say=function () {
console.log("我會說漢語,還會碼代碼");
};
var person=new CreatPerson();
var coder1=new Coder();
person.say();//我會說漢語
coder1.say();//我會說漢語,還會碼代碼
coder1.eat();//我想用飯
//----------能夠繼續父類,修改子類也不會影響到父類。然則子類修改會影響到子類-------------------
var coder2=new Coder();
coder2.age=10;
//coder2.age -> Coder.prototype.age === new CreatPerson().age
// 存儲的是值,Coder.prototype.age的轉變,只會影響當前對象的age
// 別的子類影響不到
coder2.arr.push(4);
//coder2.arr -> Coder.prototype.arr === new CreatPerson().arr
// 存儲的是地點,Coder.prototype.arr 修改,new CreatPerson().arr 取到的內容就是修改后的內容
console.log(coder1.age);//18
console.log(coder1.arr);//[1, 2, 3, 4]
console.log(coder2.age);//10
console.log(coder2.arr);//[1, 2, 3, 4]
console.log(person.age);//18
console.log(person.arr);//[1, 2, 3]
</script>
對原型鏈繼續遇到問題的處理的計劃一:
改成:
function Coder() {
this.arr=[1,2,3];//如許查找的時刻,對象上面就有了,不會查找到上一層,既不會修改到。
}
剖析:此要領貧苦,假如父類有許多自定義屬性都是對象或許要領,那末子類都要從新複製一遍。
對原型鏈繼續遇到問題的處理的計劃二:
借用組織函數
在子類中實行父類的組織函數
修改子類組織函數中的 this指向
只能繼續父類組織函數中的要領和屬性
繼續不到父類組織函數原型鏈中的要領和屬性
改成:
function Coder() {
CreatPerson.call(this);// // 此處的 this 指的 是 Coder 的 實例
}
總結:經由過程原型鏈繼續的準確寫法。
<script>
//父類組織函數
function CreatPerson( name ) {
this.age = 18;
this.arr = [123];
this.name = name;
}
CreatPerson.prototype.say = function () {
console.log("我會說漢語");
};
//子類組織函數
function Corder(name,job) {
CreatPerson.call(this,name);//繼續父類上非原型上的屬性和要領。
this.job=job;//子類擴大的自定義屬性
}
Corder.prototype=new CreatPerson();//繼續父類上原型上的屬性和要領。
Coder.prototype.constructor=Coder;//在為 子類 原型 賦值的時刻去修改 constructor
Corder.prototype.say=function () {//重寫父類上面的say要領,並不修改父類的say要領
console.log("我會說漢語,我是程序員!!!");
};
//父類的對象實例化
console.log("------------ 父類1 -----------");
var person=new CreatPerson("jack");
console.log(person.age);//18
console.log(person.name);//jack
//子類1的對象實例化
console.log("------------ 子類1 -----------");
var corder1=new Corder("rose","worker");
corder1.arr=[234];
corder1.say();//我會說漢語,我是程序員!!!
console.log(corder1.age);//18
console.log(corder1.arr);//[234]
console.log(corder1.name);//rose
console.log(corder1.job);//worker
//子類2的對象實例化
console.log("------------ 子類2 -----------");
var corder2=new Corder("Mary","corder");
console.log(corder2.age);//18
console.log(corder2.arr);//[123]子類與子類之間的 自定義屬性 沒有受到影響
console.log(corder2.name);//Mary
console.log(corder2.job);//corder
corder2.say();//我會說漢語,我是程序員!!!
console.log("------------ 父類1 -----------");
person.say();///我會說漢語 父類原型上面的要領 沒有受到影響
console.log(person.arr);//[123] 父類自定義屬性 沒有受到影響
</script>
提示本身: Coder.prototype.constructor=Coder;//在為 子類 原型 賦值的時刻去修改 constructor。 不要遺忘修改子類的constructor。
3.拷貝式繼續
1. 完成拷貝式繼續起首要知道怎樣拷貝對象。所以先來拷貝對象
<script>
//拷貝對象的內容
function cloneFn( sourse ) {
var obj= (Object.prototype.toString.call(sourse).
indexOf("Array")!==-1)?[]:{};//假如對象是數組,就應該建立數組。假如黑白數組的對象,就應該建立對象。
for(var attr in sourse){
if(typeof sourse[attr] ==="object"&& sourse[attr] !==null){//假如對象內的鍵值照樣對象,舉行更深一步的拷貝
obj[attr]=cloneFn( sourse[attr] );
}else{
obj[attr]=sourse[attr];
}
}
return obj;
}
var a={
abc:1,
abc2:2,
arr:[1,23,4]
};
var clone=cloneFn( a );
clone.abc=9;
clone.arr.push(5);//不會影響a中的arr
console.log(clone);//{abc: 9, abc2: 2, arr: Array(4)}
console.log(a.abc);//1
console.log(a.arr);//[1, 23, 4]
</script>
2.拷貝繼續
<script>
//拷貝對象的內容
function cloneFn( sourse ) {
var obj= (Object.prototype.toString.call(sourse).
indexOf("Array")!==-1)?[]:{};//假如對象是數組,就應該建立數組。假如黑白數組的對象,就應該建立對象。
for(var attr in sourse){
if(typeof sourse[attr] ==="object"&& sourse[attr] !==null){//假如對象內的鍵值照樣對象,舉行更深一步的拷貝
obj[attr]=cloneFn( sourse[attr] );
}else{
obj[attr]=sourse[attr];
}
}
return obj;
}
//父類組織函數
function CreatPerson( name ) {
this.age = 18;
this.arr = [123];
this.name = name;
}
CreatPerson.prototype.say = function () {
console.log("我會說漢語");
};
//子類組織函數
function Corder(name,job) {
CreatPerson.call(this,name);//繼續父類上非原型上的屬性和要領。
this.job=job;//子類擴大的自定義屬性
}
Corder.prototype=cloneFn(CreatPerson.prototype);//拷貝父類上原型上的屬性和要領。
Coder.prototype.constructor=Coder;//在為 子類 原型 賦值的時刻去修改 constructor
Corder.prototype.say=function () {//重寫父類上面的say要領,並不修改父類的say要領
console.log("我會說漢語,我是程序員!!!");
};
//父類的對象實例化
console.log("------------ 父類1 -----------");
var person=new CreatPerson("jack");
console.log(person.age);//18
console.log(person.name);//jack
//子類1的對象實例化
console.log("------------ 子類1 -----------");
var corder1=new Corder("rose","worker");
corder1.arr=[234];
corder1.say();//我會說漢語,我是程序員!!!
console.log(corder1.age);//18
console.log(corder1.arr);//[234]
console.log(corder1.name);//rose
console.log(corder1.job);//worker
//子類2的對象實例化
console.log("------------ 子類2 -----------");
var corder2=new Corder("Mary","corder");
console.log(corder2.age);//18
console.log(corder2.arr);//[123]子類與子類之間的 自定義屬性 沒有受到影響
console.log(corder2.name);//Mary
console.log(corder2.job);//corder
corder2.say();//我會說漢語,我是程序員!!!
console.log("------------ 父類1 -----------");
person.say();///我會說漢語 父類原型上面的要領 沒有受到影響
console.log(person.arr);//[123] 父類自定義屬性 沒有受到影響
</script>