• JS 面向对象


    js 面向对象

    对象理解

    属性类型

    属性定义方法:ie8不支持

    • Object.defineProperty(obj, propertyName, 包含修改内容的对象)

      • 数据属性的四个特征:
        [[Configurable]] 可否通过delete删除属性并重新定义
        [[Enumerable]] 可否枚举/通过for-in返回属性
        [[Writable]] 可写
        [[Value]]
      • 修改访问器属性的两个函数
        gettersetter 函数
    • Object.definePropertyies(obj, {修改的属性})

    特性读取

    Object.getOwnPropertyDescriptor()---返回一个对象

    创建对象模式

    工厂模式

    定义函数,接收参数作为属性值,新建一个对象,并返回赋值后的对象。

    function createCat(name, age, color){
      var obj = new Object();
      obj.name = name;
      obj.age = age;
      obj.color = color;
      return obj;
    }
    var cat1 = createCat("mimi", 2, "yellow");
    

    构造函数模式

    function Cat(name, age, color){
        this.name = name;
        this.age = age;
        this.color = color;
        this.getName = function(){ return this.name; };
    }
    var cat2 = new Car("lucky", 1, "white");
    var cat3 = new Car("cathy", 1, "black");
    

    !!!new 操作符的步骤:

    1. 创建一个空对象
    2. this 指向新对象(构造函数的作用域赋给这个对象)
    3. 执行构造函数给对象添加属性
    4. 返回新对象

    问题:
    每个实例都会新建 Function 实例,实例的机制相同,但是作用域和标识符解析不同,是不必要的开销。
    解决:将方法定义在外部。

    function Cat(name, age, color){
        this.name = name;
        this.age = age;
        this.color = color;
        this.getName = getName;
    }
    function getName()
    { 
        return this.name;
    }
    

    原型模式

    function Person(){};
    Person.prototype.name = "jack";
    

    通过 Person.prototype 可以给原型添加属性和方法,也可以通过定义对象字面量一次添加多个,但是会破坏 constructor 指向,可以再修改回来。而且通过定义对象字面量重写原型对象会导致原型链断开:

    function Person(){};
    var person = new Person();//先实例化
    Person.prototype = {//再重写原型对象
        constructor: Person,
        name:john,
        age: 20,
        getName: function(){}
    };
    //person.getName()会报错,因为person指向的是旧的原型对象,其中没有这个方法
    

    组合模式

    在构造函数中定义非引用数值属性,在原型中定义共享属性 constructor 和方法。

    function Person(name, age, eyeColor){
        this.name = name;
        this.age = age;
        this.eyeColor = eyeColor;
    };
    Person.prototype = {
        constructor: Person,
        getName: function(){}
    };
    

    动态原型模式

    function Person(name, age, eyeColor){
        this.name = name;
        this.age = age;
        this.eyeColor = eyeColor;
        if(typeof  this.getName != "function"){//只有getName不存在的时候才会调用,就是初次调用构造函数的时候
            Person.prototype.getName = function(){};
        }
    };
    

    寄生构造函数模式

    function createCat(name, age, color){
      var obj = new Object();
      obj.name = name;
      obj.age = age;
      obj.color = color;
      return obj;
    }
    var cat1 = new createCat("mimi", 2, "yellow");
    

    对象和构造函数无关系

    稳妥构造函数模式

    原型与原型链

    原型对象

    • 创建一个函数时会自动创建一个原型对象,只有函数拥有 prototype 属性,【prototype】指向函数的【原型对象】
    • prototype 原型对象中有【constructor属性】指回向【构造函数】。
    • : ES6 的箭头函数没有 prototype 属性,但是有 __proto__ 属性。

    原型

    • prototype 是【基于构造函数构造的实例对象】的隐式原型([[prototype]],部分浏览器可以通过 __proto__ 属性访问),即 [[prototype]] 指向【其构造函数的原型对象】。

      • 从构造函数A实例化对象a,那么【a的原型】就是【A的原型对象prototype】。
    • 所有的引用类型都有 __proto__ 属性

    原型链与继承

    原型链是继承的一种实现方法:利用原型使【引用类型A】继承【引用类型B】的属性和方法
    引用类型值属性会被所有实例共享

    • 有构造函数A、B,B继承自A,C为B的一个实例
      【实例 C 的 __proto__ 】指向【B prototype 原型对象】,【B prototypeconstructor 属性】指向【构造函数B】,【B prototype__proto__ 】指向【A prototype 原型对象】。

    实际上是 __proto__ 连接【实例】和【原型对象】形成原型链。

    • C.__proto__ === B.prototype //true
    • C.__proto__.__proto__ === A.prototype //true
    //没有修改 constructor
    function Animal(){
        this.name = 'animal';
    }
    function Cat(){
        this.name = 'Cat';
    }
    function ChinaCat(){
        this.name='chinacat';
    }
    Cat.prototype = new Animal();   //继承Animal
    ChinaCat.prototype = new Cat(); //继承Cat
    //插入 constructor 修改语句
    var a = new ChinaCat();
    

    结果如图:

    此时

    • 【实例a】, 【ChinaCat原型对象】,【Cat原型对象】的 constructor 都会指向【function Animal(){}
      因为 a.__proto__ 指向 ChinaCat 原型对象,ChinaCat 原型对象的__proto__ 指向 Cat 原型对象,Cat 原型对象的__proto__ 指向 Animal 原型对象,Animal 原型对象中的 constructor 指向【function Animal(){}】.
      注:实例 a 的 prototype 为空,因为 prototype 是函数有的属性
    //插入constructor修改语句
    Cat.prototype.constructor = Cat;
    ChinaCat.prototype.constructor = ChinaCat;
    var a = new ChinaCat();
    

    此时

    • a.constructor 】指向其【构造函数 function ChinaCat(){} 】,【a.__proto__ 】等于其原型对象中的__proto__ 所指向的【Cat原型对象】,
    • ChinaCat原型对象】

    ES5继承

    绑定构造函数

    缺点:父类型中定义的方法不可继承,对子类型不可见,只能通过构造函数,无法进行函数复用。

    function Parent(){
    	this.color = ['red','blue','yellow'];
    }
    function Child(){
    	parent.call(this); //parent.apply(this,arguments);
    }
    var ins = new Child();
    ins.color.push('white');//ins.color = ['red','blue','yellow','white'];
    

    原型链继承

    缺点:无法向父类构造函数中传递参数;子类原型链上定义的方法有先后顺序问题;引用类型值的原型属性会被共享。
    需要注意的一点是要修改constructor的指向。

    function Animal(species) {
        this.species = species;
    }
    Animal.prototype.func = function() {
        console.log("Animal");
    };
    function Cat() {}
    /*func方法是无效的, 因为后面原型链被重新指向了Animal实例*/
    Cat.prototype.func = function() {
        console.log("Cat");
    };
    Cat.prototype = new Animal();//原型继承
    //Cat.prototype.constructor属性会指向Animal,而不是Cat,需要手动修改
    Cat.prototype.constructor = Cat; // 修复: 将Cat.prototype.constructor重新指向Cat
    var cat = new Cat();
    cat.func(); // output: Animal
    console.log(cat.species); // undefined,不能传递参数
    

    组合继承

    • 用原型链实现对原型属性和方法的继承
    • 借用构造函数实现对实例属性的继承。

    缺点:调用了两次父类构造函数

    function Animal(type){//构造函数,定义属性
        this.type = type;
        this.deployment = [];
    }
    Animal.prototype.getType= function(){//原型对象定义方法
        return this.type;
    };
    //继承属性
    function Cat(name, age){//构造函数,继承并定义自己的属性
        Animal.call(this, "cat"); 
        this.name = name;
        this.age = age;
    }
    //继承方法
    Cat.prototype = new Animal();   //第一次调用   获得Animal的实例属性typedeployment
    Cat.prototype.constructor = Cat;    //修改constructor指向
    //实例化对象
    var cat1 = new Cat("lucky",2);//第二次在Cat构造函数中调用  会重新获得实例属性,从而屏蔽原型中的同名属性
    cat1.deployment.push("china");
    var cat2 = new Cat("lucy",1);//第二次在Cat构造函数中调用  会重新获得实例属性,从而屏蔽原型中的同名属性
    cat2.deployment.push("canada");
    //
    console.log(cat1);
    console.log(cat2);
    

    实际上,就有了两组相同的属性:一组在cat1/cat2实例中,一组在Cat原型对象中。

    寄生组合式继承

    解决组合继承的问题,是理想的继承方法

    • 用构造函数继承属性
    • 通过原型链混成形式继承方法
    function inherentPro(subType, superType){
        var prototype = object(superType.prototype);
        prototype.constructor = subType;
        subType.prototype = prototype;
    }
    inherentPro(Cat, Animal);//替换Cat.prototype = new Animal(); 
    
  • 相关阅读:
    第一篇博客
    【面试大系】PHP程序员的知识盘点
    【PHP资源】PHP 资源大全
    【前端经纬】将页面元素定位
    大爱卡农三百年
    【夯实PHP基础】PHP标准库 SPL
    【http抓包】记录一次抓手机app的接口
    【算法】PHP实现冒泡排序和快速排序--防遗忘
    Apache的初中级面试题
    【生活感悟系列】感悟在一瞬间(不断完善中)
  • 原文地址:https://www.cnblogs.com/qiuqiubai/p/12539291.html
Copyright © 2020-2023  润新知