• JS原型链模式和继承模式


    原型链模式

    实例识别:
    构造函数模式中拥有了类和实例的概念,并且实例和实例之间是相互独立开的

    function CreateJsPerson(name, age){
        this.name = name;
        this.age = age;
    }
    CreateJsPerson.prototype.writeJs = function(){
            console.log("my name is " + this.name + ", i can                write js ~~")
        }
    

    基于构造函数模式的原型模式解决了,方法或者属性共有的问题,想让谁共有就把他放在CreateJsPerson.prototype上即可

    js中规定的

    prototype

    1. 每一个函数数据类型(普通函数,类)都有一个天生自带的属性:prototype(原型),并且这个属性是一个对象数据类型的值

    constructor

    1. 并且在prototype上浏览器天生给它加了一个属性constructor(构造函数),属性值是当前函数(类)本身

    _ proto _

    1. 每一个对象数据类型(普通的对象,实例,prototype..)也天生自带一个属性: _ _proto _ _,属性值是当前实例所属类的原型(prototype)
    function Fn(){
        this.x = 100;
    }
    Fn.prototype.getX = function(){
        console.log(this.x);
    }
    var f1 = new Fn;
    var f2 = new Fn;
    
    console.log(Fn.prototype.constructor === Fn); // true
    console.log(f1 instanceof Object); // true
    
    • f1 instanceof Object -> true 因为f1通过_ _ protp _ _ 可以向上级查找,不管查找多少级都可以查找到Object
    • 在Object.prototype上没有_ _protp _ _ 这个属性
      5780d495bccfd76c04ed1ad6e283a75b.png

    原型链查找机制

    1. 通过 对象名.属性名 的方式获取属性值的时候, 首先在对象的私有的属性上进行查找, 如果私有中存在这个属性,则获取的是私有的属性值;
    2. 如果私有的没有,则通过__proto__找到所属类的原型, 类的原型上定义的属性和方法都是当前实例公有的属性和方法, 原型上存在的话, 获取的是共有的属性值;
    3. 如果原型上也没有,则继续通过原型上的__proto__继续向上查找, 一直找到Obejct.prototype为止
    console.log(f1.getX === f2.getX); // true
    console.log(f1.__proto__.getX === f2.getX);// true
    console.log(f1.getX === Fn.prototype.getX); // true
    

    在IE浏览器中,原型模式也是同样的原理,但是IE浏览器怕你通过__proto__把公有的修改,禁止我们修改__proto__

    批量设置共有属性

    1. 起别名
    function Fn(){
        this.x = 100;
    }
    var pro = Fn.prototype;
    pro.getX = function(){}
    pro.getY = function(){}
    
    1. 重构原型对象的方式
    function Fn(){
        this.x = 100;
    }
    Fn.prototype = {
        getX: function(){},
        getY: function(){}
    }
    var f = new Fn;
    console.log(f.constructor)' // Object
    

    只有浏览器天生给Fn.prototype开辟的堆内存里面才有constructor,而我们自己开辟的这个堆内存没有这个属性,
    这样constructor指向就不再是Fn而是Object

    为了和原来的保持一致,我们需要手动的增加constructor的指向

    Fn.prototype = {
       constructor: Fn
    }
    

    会把之前已经存在于原型上的属性和方法给替换掉, 用这种方式修改内置类的话, 浏览器会给屏蔽掉

    Array.prototype = {
        constructor: Array,
        unique: function(){}
    }
    console.dir(Array.prototype);
    

    但是可以使用prototype属性,一个个修改内置的方法,如果方法名和原来内置的重复了, 会把内置的修改掉, 在内置类的原型上增加方法, 名命都需要加特殊的前缀

    Array.prototype.sort = function(){
        console.log("lemon");
    }
    var ary = [1, 2, 2, 1, 2, 3, 4];
    ary.sort();
    console.log(ary);
    

    常用的六种继承模式

    for in 循坏在遍历的时候, 可以把自己私有的和在它所属原型上扩展的属性和方法都遍历到.

    Object.prototype.aaa = function(){};
    var obj = {name: 'lemon', age: 22};
    for(let key in obj){
        console.log(key)
    }
    

    可以使用以下方法,进行判断处理

    for(let key in obj){
        if(obj.propertyIsEnumerable(key)){
            console.log(key);
        }
    }
    for(let key in obj){
        if(obj.hasOwnProperty(key)){
            console.log(key);
        }
    }
    

    Object.create(proObj)

    创建一个新的对象, 但是要把proObj作为这个对象的原型, 在IE6-IE8不兼容(ECMAScript5)

    var obj = {
        getX: function(){}
    }
    var obj2 = Object.create(obj)
    console.dir(obj2)
    obj.getY = function(){}
    console.dir(obj2)
    

    自己实现一个create

    var obj = {
        getX: function(){}
    }
    function object(o){
        function Fn(){}
        Fn.prototype = o;
        return new Fn();
    }
    var newObj = object(obj);
    

    原型链继承

    原型继承是JS中最常用的一种继承方式, 子类B想要继承父类A中的所有的属性和方法(私有+公有), 只需要让B.prototype = new A;
    特点: 它是把父类中的私有+公有的都继承到了子类原型上(共有的)
    核心: 原型继承并不是把父类中的属性和方法克隆一份一模一样的给B,而是让B和A之间增加了原型链的连接, 以后B的实例n想要A中的getX方法,需要一级一级的向上查找来使用

    function A(){
        this.x = 100;
    }
    A.prototype.getX = function(){
        console.log(this.x)
    }
    function B(){
        this.y = 200;
    }
    B.prototype = new A
    

    缺点: 不安全, 可以通过子类或子类的实例,更改父类原型链上的属性和方法, 对A的实例和子类造成影响

    var b = new B
    var a = new A
    b.__proto__.__proto__.getX = 3000;
    console.log(a.getX)
    B.prototype.__proto__.getX = 2000;
    console.log(a.getX)
    

    call继承

    借用构造函数, 伪造对象继承和经典继承
    call:调用一个对象的一个方法,用另一个对象替换当前对象。例如:B.call(A, args1,args2);即A对象调用B对象的方法。

    function A(){
        this.x = 100;
    }
    A.prototype.getX = function(){
        console.log(this.x);
    }
    function B(){
        A.call(this)
    }
    var b = new B;
    console.log(b.x)
    

    缺点:
    父类在原型链中定义的函数不能被子类访问,也就是说所有的函数都必须写在构造函数内部,无法复用

    冒充对象

    把父类私有和公有的属性和方法,克隆一份一模一样的给子类私有的

    function A(){
        this.x = 100;
    }
    A.prototype.getX = function(){
        console.log(this.x);
    }
    function B(){
        var temp = new A;
        for(var key in temp){
            this[key] = temp[key];
        }
    }
    var b = new B;
    console.log(b.x)
    

    混合模式继承

    子私有 = 父私有, 子公有 = 父私有 + 父公有

    function A(){
        this.x = 100;
    }
    A.prototype.getX = function(){
        console.log(this.x);
    }
    function B(){
        A.call(this)
    }
    B.prototype = new A;
    B.prototype.constructor = B;
    var b = new B;
    console.log(b.x)
    

    寄生组合式继承

    子私有 = 父私有, 子公有 = 父公有

    function A(){
        this.x = 100;
    }
    A.prototype.getX = function(){
        console.log(this.x);
    }
    function B(){
        A.call(this)
    }
    B.prototype = Object.create(A.prototype);
    B.prototype.constructor = B;
    var b = new B;
    console.log(b.x)
    

    中间类继承法,不兼容

    function avgFn(){
        arguments.__proto__ = Array.prototype;
        arguments.sort(function (a, b) {
            return a - b;
        })
        arguments.pop()
        arguments.shift()
        return eval(arguments.join("+")) / arguments.length;
    }
    avgFn(10, 20 ,30 ,10 ,30, 40, 40); // 26
    
  • 相关阅读:
    Python模块、包、异常、文件(案例)
    jQuery DataTable 删除数据后重新加载
    Python|面向对象
    python开发的学生管理系统
    使用JDK开发WebServrice案例
    Python入门(案例)
    Spring总结以及在面试中的一些问题
    Web Services简单介绍
    Canvas实现文字粒子化,并且绕轴旋转(完善)
    HTML5 Canvas画数字时钟
  • 原文地址:https://www.cnblogs.com/xiaoxu-xmy/p/13644998.html
Copyright © 2020-2023  润新知