• js 面向对象


    编程思想

    • 基本思想是使用对象,类,继承,封装等基本概念来进行程序设计
    • 优点
      • 易维护
        • 采用面向对象思想设计的结构,可读性高,由于继承的存在,即使改变需求,那么维护也只是在局部模块,所以维护起来是非常方便和较低成本的
      • 易扩展
      • 开发工作的重用性、继承性高,降低重复工作量。
      • 缩短了开发周期

    一般面向对象包含:继承,封装,多态,抽象

    1. 对象形式的继承

    浅拷贝

    var Person = {
        name: 'poetry',
        age: 18,
        address: {
            home: 'home',
            office: 'office',
        }
        sclools: ['x','z'],
    };
    
    var programer = {
        language: 'js',
    };
    
    function extend(p, c){
        var c = c || {};
        for( var prop in p){
            c[prop] = p[prop];
        }
    }
    extend(Person, programer);
    programer.name;  // poetry
    programer.address.home;  // home
    programer.address.home = 'house';  //house
    Person.address.home;  // house
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25

    从上面的结果看出,浅拷贝的缺陷在于修改了子对象中引用类型的值,会影响到父对象中的值,因为在浅拷贝中对引用类型的拷贝只是拷贝了地址,指向了内存中同一个副本

    深拷贝

    function extendDeeply(p, c){
        var c = c || {};
        for (var prop in p){
            if(typeof p[prop] === "object"){
                c[prop] = (p[prop].constructor === Array)?[]:{};
                extendDeeply(p[prop], c[prop]);
            }else{
                c[prop] = p[prop];
            }
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11

    利用递归进行深拷贝,这样子对象的修改就不会影响到父对象

    extendDeeply(Person, programer);
    programer.address.home = 'poetry';
    Person.address.home; // home
    
    • 1
    • 2
    • 3

    利用call和apply继承

    function Parent(){
        this.name = "abc";
        this.address = {home: "home"};
    }
    function Child(){
        Parent.call(this);
        this.language = "js"; 
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8

    ES5中的Object.create()

    var p = { name : 'poetry'};
    var obj = Object.create(p);
    obj.name; // poetry
    
    • 1
    • 2
    • 3

    Object.create()作为new操作符的替代方案是ES5之后才出来的。我们也可以自己模拟该方法:

    //模拟Object.create()方法
    function myCreate(o){
        function F(){};
        F.prototype = o;
        o = new F();
        return o;
    }
    var p = { name : 'poetry'};
    var obj = myCreate(p);
    obj.name; // poetry
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10

    目前,各大浏览器的最新版本(包括IE9)都部署了这个方法。如果遇到老式浏览器,可以用下面的代码自行部署

     if (!Object.create) {
        Object.create = function (o) {
           function F() {}
          F.prototype = o;
          return new F();
        };
      }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

    2. 类的继承

    Object.create()

    function Person(name, age){}
    Person.prototype.headCount = 1;
    Person.prototype.eat = function(){
        console.log('eating...');
    }
    function Programmer(name, age, title){}
    
    Programmer.prototype = Object.create(Person.prototype); //建立继承关系
    Programmer.prototype.constructor = Programmer;  // 修改constructor的指向
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9

    调用父类方法

    function Person(name, age){
        this.name = name;
        this.age = age;
    }
    Person.prototype.headCount = 1;
    Person.prototype.eat = function(){
        console.log('eating...');
    }
    
    function Programmer(name, age, title){
        Person.apply(this, arguments); // 调用父类的构造器
    }
    
    
    Programmer.prototype = Object.create(Person.prototype);
    Programmer.prototype.constructor = Programmer;
    
    Programmer.prototype.language = "js";
    Programmer.prototype.work = function(){
        console.log('i am working code in '+ this.language);
        Person.prototype.eat.apply(this, arguments); // 调用父类上的方法
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22

    3. 封装

    • 命名空间
      • js是没有命名空间的,因此可以用对象模拟
    var app = {};  // 命名空间app
    //模块1
    app.module1 = {
        name: 'poetry',
        f: function(){
            console.log('hi robot');
        }
    };
    app.module1.name; // "poetry"
    app.module1.f();  // hi robot
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10

    对象的属性外界是可读可写 如何来达到封装的额目的?答:可通过闭包+局部变量来完成

    • 在构造函数内部声明局部变量 和普通方法
    • 因为作用域的关系 只有构造函数内的方法
    • 才能访问局部变量 而方法对于外界是开放的
    • 因此可以通过方法来访问 原本外界访问不到的局部变量 达到函数封装的目的
    function Girl(name,age){
    	var love = '小明';//love 是局部变量 准确说不属于对象 属于这个函数的额激活对象 函数调用时必将产生一个激活对象 love在激活对象身上   激活对象有作用域的关系 有办法访问  加一个函数提供外界访问
    	this.name = name;
    	this.age = age;
    	this.say = function () {
    		return love;
    	};
    
    	this.movelove = function (){
    		love = '小轩'; //35
    	}
    
    } 
    
    var g = new Girl('yinghong',22);
    
    console.log(g);
    console.log(g.say());//小明
    console.log(g.movelove());//undefined  因为35行没有返回
    console.log(g.say());//小轩
    
    
    
    function fn(){
    	function t(){
    		//var age = 22;//声明age变量 在t的激活对象上
    		age = 22;//赋值操作 t的激活对象上找age属性 ,找不到 找fn的激活对象....再找到 最终找到window.age = 22;
    				//不加var就是操作window全局属性
    	
    	}
    	t();
    }
    console.log(fn());//undefined
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33

    4. 静态成员

    面向对象中的静态方法-静态属性:没有new对象 也能引用静态方法属性

    function Person(name){
        var age = 100;
        this.name = name;
    }
    //静态成员
    Person.walk = function(){
        console.log('static');
    };
    Person.walk();  // static
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9

    5. 私有与公有

    function Person(id){
        // 私有属性与方法
        var name = 'poetry';
        var work = function(){
            console.log(this.id);
        };
        //公有属性与方法
        this.id = id;
        this.say = function(){
            console.log('say hello');
            work.call(this);
        };
    };
    var p1 = new Person(123);
    p1.name; // undefined
    p1.id;  // 123
    p1.say();  // say hello 123
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17

    6. 模块化

    var moduleA;
    moduleA = function() {
        var prop = 1;
    
        function func() {}
    
        return {
            func: func,
            prop: prop
        };
    }(); // 立即执行匿名函数
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11

    7. 多态

    多态:同一个父类继承出来的子类各有各的形态

    function Cat(){
    	this.eat = '肉';
    }
    
    function Tiger(){
    	this.color = '黑黄相间';
    }
    
    function Cheetah(){
    	this.color = '报文';
    }
    
    function Lion(){
    	this.color = '土黄色';
    }
    
    Tiger.prototype =  Cheetah.prototype = Lion.prototype = new Cat();//共享一个祖先 Cat
    
    var T = new Tiger();
    var C = new Cheetah();
    var L = new Lion();
    
    console.log(T.color);
    console.log(C.color);
    console.log(L.color);
    
    
    console.log(T.eat);
    console.log(C.eat);
    console.log(L.eat);
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30

    8. 抽象类

    在构造器中 throw new Error(''); 抛异常。这样防止这个类被直接调用

    function DetectorBase() {
        throw new Error('Abstract class can not be invoked directly!');
    }
    
    DetectorBase.prototype.detect = function() {
        console.log('Detection starting...');
    };
    DetectorBase.prototype.stop = function() {
        console.log('Detection stopped.');
    };
    DetectorBase.prototype.init = function() {
        throw new Error('Error');
    };
    
    // var d = new DetectorBase();
    // Uncaught Error: Abstract class can not be invoked directly!
    
    function LinkDetector() {}
    LinkDetector.prototype = Object.create(DetectorBase.prototype);
    LinkDetector.prototype.constructor = LinkDetector;
    
    var l = new LinkDetector();
    console.log(l); //LinkDetector {}__proto__: LinkDetector
    l.detect(); //Detection starting...
    l.init(); //Uncaught Error: Error
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
  • 相关阅读:
    分布式计算模型Mapreduce实践与原理剖析(一)
    基于react18+arco+zustand通用后台管理系统React18Admin
    【基础篇】六、基于SpringBoot来整合SSM的案例(下)
    PaddlePaddle - 初体验
    C++自定义swap函数实现交换两个变量的值兼谈参数传递
    故障解析丨Clone节点导致主从故障
    JackJson多态
    SpringBoot 使用WebSocket打造在线聊天室
    2022年IC行业还缺人吗?
    类与类的加载
  • 原文地址:https://blog.csdn.net/php_martin/article/details/125855306