面向對象(OOP)和THIS綜合

面向對象(OOP)和THIS綜合複習

一. OOP面向對象編程

在JS中面向類/實例進行程序設計,就是經典的面向對象編程javascript

  • 類和實例的建立(構造函數模式)
  • prototype / _proto_ (原型和原型鏈)
  • 函數的三種角色
  • 基於內置類原型擴展方法,實現鏈式寫法
  • 借用內置類原型上的方法,實現一些特殊的需求(例如:把類數組轉換爲數組)
  • 細小的知識點:instanceof / constructor / hasOwnProperty ....
  • 類的繼承封裝和多態
  • ......

構造函數java

function Fn(n,m){
    let plus=n+m,
        minus=n-m;
    this.x=plus;
    this.y=minus;
    this.print=function(){
        console.log(this.x+this.y);
    }
}
let f1=new Fn(30,10);
f1.print();
console.log(f1.plus); //=>undefined
console.log(f1 instanceof Fn); //=>true

let f2=new Fn; //=>沒有傳遞任何的實參
console.log(f1.print===f2.print); //=>false
/* * 構造函數執行 * 1.開闢一個新的私有做用域 * 2.形參賦值&變量提高 * 3.瀏覽器在當前做用域中建立一個實例對象@A,而且讓THIS指向它 * 4.代碼執行 this=>當前類的實例@A * this.xxx=xxx都是給當前實例@A設置的私有屬性 * 除此以外的私有變量等和@A這個實例沒有必然的關係 * 5.即便咱們不設置RETURN,瀏覽器也會默認把實例@A返回,而外面的f1/f2接收到的就是返回的實例,因此也說f1/f2是Fn這個類的實例(若是手動返回的是引用類型值,會以用戶返回的爲主,也就是返回的再也不是Fn的實例,若是返回基本類型值,對原有的操做無影響) */

Fn(10,20);
window.print();
/* * 普通函數執行 * 1.造成私有的棧內存(私有的做用域scope) * 2.形參賦值&變量提高 n=10 m=20 * 3.代碼執行 this=>window * 4.沒有RETURN返回值 */
複製代碼

原型:prototype 和 原型鏈:_proto_面試

  • 每個函數都自帶一個屬性:prototype,它的屬性值是一個對象
  • prototype這個對象中有一個默認的屬性:constructor,存儲函數自己
  • 每個對象都自帶一個屬性:_proto_,屬性值是所屬類的prototype
    • 普通對象、數組、正則、日期等都是對象
    • 類的實例是對象 (基本數據類型值雖然是所屬類實例,但不是對象)
    • prototype原型屬性值也是對象
    • 函數自己也是一個對象
function Fn() {
    this.x = 100;
    this.y = 200;
    this.getX = function () {
        console.log(this.x);
    }
}
Fn.prototype.getX = function () {
    console.log(this.x);
};
Fn.prototype.getY = function () {
    console.log(this.y);
};
let f1 = new Fn;
let f2 = new Fn;
console.log(f1.getX === f2.getX);
console.log(f1.getY === f2.getY);
console.log(f1.__proto__.getY === Fn.prototype.getY);
console.log(f1.__proto__.getX === f2.getX);
console.log(f1.getX === Fn.prototype.getX);
console.log(f1.constructor);
console.log(Fn.prototype.__proto__.constructor);
f1.getX(); //=>this:f1 =>console.log(f1.x) =>100
f1.__proto__.getX(); //=>this:f1.__proto__ =>console.log(f1.__proto__.x) =>undefined
f2.getY(); //=>this:f2
Fn.prototype.getY(); //=>this:Fn.prototype
複製代碼

原型和原型鏈

函數三種角色編程

  • 普通函數(閉包做用域、做用域鏈)
  • 構造函數(類、實例、原型、原型鏈)
  • 普通對象(鍵值對)

三種角色之間沒有必然的聯繫數組

//=>JS中運算符的優先級:https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Operators/Operator_Precedence
function Foo() {
    getName = function () {
        console.log(1);
    };
    return this;
}
Foo.getName = function () {
    console.log(2);
};
Foo.prototype.getName = function () {
    console.log(3);
};
var getName = function () {
    console.log(4);
};
function getName() {
    console.log(5);
}
Foo.getName();
getName();
Foo().getName();
getName();
new Foo.getName();
new Foo().getName();
new new Foo().getName();
複製代碼

函數三種角色

基於內置類原型擴展方法,實現鏈式寫法瀏覽器

let ary = [12,13,13,12,24,13,12];
//=>ary是Array數組類的實例,因此能夠調取Array.prototype上的方法,sort方法中的this是ary,當前要排序的數組實例(底層理解:sort是內置方法,它能夠實現排序,ary.sort(...)本意:ary先基於__proto__找到Array.prototype上的sort方法,而且把sort方法執行,方法中的this是ary,sort方法在執行的時候,會把this對應的數組進行排序處理)
ary.sort((a,b)=>a-b);

/* //=>slice執行的時候,方法中的this是誰,就至關於把誰克隆成一份全新的數組出來 Array.prototype.slice=function(){ //this:當前須要操做的這個數組 let newAry=[]; for(let i=0;i<this.length;i++){ newAry.push(this[i]); } return newAry; }; */
let newAry = ary.slice(0);
newAry = Array.prototype.slice.call(ary,0);

function fn(){
    //=>arguments類數組集合(實參集合):不是Array的實例,它就是一個對象而已,不能直接使用數組中的方法 (=>把類數組轉換爲數組)
    let ary = Array.prototype.slice.call(arguments,0);
    ary = [].slice.call(arguments,0);
    
    //=>借用數組原型上的FOREACH方法,實現給類數組進行循環(內置方法中的THIS是誰,其實當前方法就在操做誰)
    [].forEach.call(arguments,item=>{
        
    });
}
fn(10,20,30,40,50);
複製代碼

內置方法不少,可是不必定徹底夠用 ,不少時候咱們須要本身向內置類的原型上擴展方法來實現一些需求閉包

~function(){
    function unique(){
        //注意:this是誰就給誰去重
        let temp={};
        for(let i=0;i<this.length;i++){
            let item=this[i];
            if(typeof temp[item]!=="undefined"){
                //this.splice(i,1);
                this[i]=this[this.length-1];
                this.length--;
                i--;
                continue;
            }
            temp[item]=item;
        }
        temp=null;
        //注意:返回的結果若是仍是數組,則繼續能夠調取Array.prototype上的其它方法,實現「鏈式寫法」
        return this;
    }
    //往內置類原型上擴展方法:爲了防止不修改原有內置的方法,咱們擴展的方法名要增長對應的前綴
    Array.prototype.myUnique = unique;
}();
let ary = [12,13,13,12,24,13,12];
ary.myUnique().reverse().push('A');
//Array.prototype.myUnique.call(ary);
複製代碼

2、THIS問題

THIS函數執行的主體:誰執行的app

THIS是誰和函數在哪執行和在哪定義都不要緊,想要分清執行主體,記住如下規律便可函數

  • 給元素的某個事件綁定方法,當事件觸發方法執行的時候,方法中的THIS是當前元素自己
  • 方法執行,看方法名前面是否有點,有點,點前面是誰,THIS就是誰;沒有點THIS是WINDOW(JS在嚴格模式下,沒有點THIS是UNDEFINED)
    • "use strict" 開啓嚴格模式
    • 自執行函數、回調函數等方法中的THIS通常是WINDOW
  • 在構造函數執行過程當中,構造函數體中的THIS是當前類的實例
  • 使用CALL/APPLY/BIND能夠改變函數中的THIS指向
  • ES6箭頭函數中沒有本身的THIS,所用的THIS是繼承上下文中的
function fn(n,m){
    this.total=n+m;
}
let obj={name:'OBJ'};

fn(10,20); //=>this:window
//obj.fn(20,30); //=>報錯:obj中沒有fn屬性
document.body.onclick=fn; //=>點擊後FN中的this:BODY
document.body.onclick=function(){
   //=>this:BODY
   fn(30,40); //=>this:window 
};

fn.call(); //=>this:window 不傳或者傳遞null/undefined都是
fn.call(obj,10,20); //=>this:obj
fn.apply(obj,[10,20]);//=>this:obj APPLY要求傳遞的參數是數組
document.body.onclick=fn.bind(obj,10,20); //=>BIND是預處理THIS,此時的FN尚未執行,只是把THIS改爲了OBJ,點擊BODY的時候才執行的 =>「柯理化函數」(預處理機制)
複製代碼

構造函數中的THISui

function Fn(){
    this.x=100;
    this.y=200;
}
Fn.prototype.sum=function(){
    console.log(this.x+this.y);
};
let f = new Fn; //=>Fn中的this:f 當前類的實例
f.sum(); //=>this:f
Fn.prototype.sum(); //=>this:Fn.prototype
f.__proto__.sum(); //=>this:f.__proto__
複製代碼
//=>ES6的寫法
class Fn{
    constructor(){
        //=>this:當前Fn類的實例
        this.x=100;
    	this.y=200;
    }
    //=>直接寫的方法就是放到原型上的
    sum(){console.log(this.x+this.y);}
    //=>STATIC修飾的都是把Fn當作普通對象設置的鍵值對
    static unique(){}
}
Fn.prototype.name='珠峯';
Fn.age=10;

let f = new Fn;
f.sum();
Fn.unique();
//Fn();//=>Uncaught TypeError: Class constructor Fn cannot be invoked without 'new'
複製代碼

箭頭函數中的THIS

window.name='WINDOW';
let obj={
    name:'OBJ',
    fn:()=>{
        console.log(this.name);
    }
};
obj.fn();//=>this:window
obj.fn.call(obj);//=>this:window

//==================
document.body.onclick=function(){
    //=>this:BODY
    let _this=this;
    _this.count=0;
    
    /*setTimeout(function(){ //=>this:window _this.count++; },1000);*/
    
    setTimeout(()=>{
        //=>this:沒有本身的THIS,繼承下上文中的,也就是BODY
        this.count++;
    },1000);
}
複製代碼

關於THIS這塊的問題:

  • 重寫Function.prototype中內置call/apply方法(bind能夠本身先研究)
  • fn1.call.call.call(fn2) ...這道阿里面試題
  • 獲取數組中的最大值和最小值:Math.min.apply(Math,ary) ...
相關文章
相關標籤/搜索