面向对象(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) ...
相关文章
相关标签/搜索