JS高级——原型链

构造函数数组

  1. 构造函数是特殊的函数,里面没有returen返回值
  2. new的过程当中,会自动将对象返回,不须要return
  3. new的过程当中,会执行函数中的代码,会将建立的对象赋值给构造函数中的this

基本概念浏览器

  1. 经过同一个构造函数建立的对象都会关联一个神秘的对象,能够经过构造函数.prototype进行访问,这个神秘对象被称为原型对象
  2. 这个原型对象能够被用来作继承用,js中的继承有好几种,包括混入继承,经典继承,还有原型继承
  3. 经过构造函数建立出来的对象,不只拥有构造函数中的属性,还拥有原型对象中建立出来的属性
  4. 实例化后的对象也能够经过__proto__进行访问原型对象,可是只是调试时使用,不推荐正式代码中使用

继承方式安全

原型基本

<script>
    function Person(name, age) {
        this.name = name;
        this.age = age;
    }
    //不只拥有构造函数中的属性,还拥有原型中建立出来的属性
    Person.prototype.gender = 'male';
    var p = new Person('qx', 18);
    console.log(p.name);//qx
    console.log(p.age);//18
    console.log(p.gender);//male
</script>

混入继承

<script>
    var o = {}
    var obj = {
        name: "张三",
        age: 18,
        sayHello: function () {
            console.log("Hello world");
        }
    }
    //混入式继承
    for (var k  in obj) {
        o[k] = obj[k];
    }
    console.log(o);
</script>

经典继承

一、最先的原理函数

<script>
    //经过替换原型,使得被建立出来对象也拥有传入对象的属性
    function jicheng(obj) {
        var o = {};
        o.__proto__ = obj;
        return o;
    }

    var o = jicheng({name: "张三"});
    console.log(o);
</script>

二、create方法this

<script>
    var o = {
        name: "周三"
    };

    var obj = Object.create(o);
    console.log(obj.name);
</script>

三、create方法存在兼容性问题spa

<script>
    var obj = {
        name:"周三"
    };

    //检测浏览器的能力,若是没有Object.create方法就给他添加一个(不推荐使用)
    if(Object.create){
        var o = Object.create(obj);
    }else{
        Object.create = function () {
            function F() {
            }
            F.prototype = obj;
            var o = new F();
        }
        var o = Object.create(obj);
    }
</script>
<script>
    //本身定义个函数
    function create(obj) {
        if (Object.create) {
            return Object.create(obj);
        } else {
            function F() {
            }

            F.prototype = obj;
            return new F();
        }
    }
</script>

原型对象prototype

  • 原型对象能够经过构造函数.prototype得到
  • 原型对象中的属性和方法,能够提供给那些经过此构造函数建立的对象使用,达到了全局使用的做用
  • 原型对象被替换,若是替换的属性与原有构造函数的属性相冲突,那么被建立的对象,依然首先访问的是构造函数中的属性
<script>
    function Person(name, age) {
        this.name = name;
        this.age = age;
    }
    Person.prototype.say=function () {
        console.log('chi');
    }
    var p=new Person('wx',18);
    p.say();//chi
</script>
  • 原型对象添加属性和方法还能够经过如下方式,用{}来追加,{}这是字面量,同时表明了一个字面量对象,Person.prototype能够直接点出{}里面的属性和方法
<script>
    function Person(name, age) {
        this.name = name;
        this.age = age;
    }
    Person.prototype={
        say:function () {
            console.log('chi');
        },
        play:function () {
            console.log('wan');
        }
    };
    var p=new Person('wx',18);
    p.say();//chi
    p.play();//wan
</script>
  • 原型对象在没有被替换前会输出一个constructor,其指向就是本身的构造函数,可是一旦被替换就会变成失去本来的构造函数,确实有,可是其指向的是Object的构造函数,最好最好的方式是在原型对象被替换以后,再次添加一个constructor属性,而且指向本身原先的构造函数
  • 补充下,为何原型对象被替换后,其构造函数属性指向的是Object的构造函数?由于Person.protype,被称为原型对象,既然是对象,确定也是经过某一个构造函数建立出来的,结果就是Object构造函数,因此Person.prototype有Object的构造函数

<script>
    function Person(name, age) {
        this.name = name;
        this.age = age;
    }

    console.dir(Person.prototype);
    Person.prototype = {
        say: function () {
            console.log('chi');
        },
        play: function () {
            console.log('wan');
        }
    };
    Person.prototype.constructor = Person;
    console.dir(Person.prototype);
</script>

原型继承3d

将animal这个对象赋值给Person.prototype,与上面的经过字面量{}道理是同样的,毕竟animal也是一个对象,里面的属性也是被{}包裹的调试

<script>
    function Animal() {
        this.eat = 'chifan';
    }

    var animal = new Animal();

    function Person() {
        this.read = 'dushu';
    }

    var p1 = new Person();

    //person显示是具备animal的属性,毕竟人也是动物,具体方法是让person的原型被替换成animal
    //替换后,经过person构造函数建立出来的对象,不只具备person的属性,还具备animal的属性

    Person.prototype = animal;//Person.prototype = new Animal()这样也行

    var p2 = new Person();
    console.log(p1);
    console.log(p2);
</script>

 

输出结果:code

  1. p1与p2输出的都是Person类,因此都具备read属性
  2. p1与p2输出的原型__proto__中的constructor构造函数属性不同,p1指向的是Person构造函数,p2指向的是Animal构造函数并且具备eat属性
  3. p1与p2输出的原型__proto__中的原型proto__指向都是一致的,都为Object类

结果分析:

  1. p1与p2都是经过构造函数Person建立出来的,因此都具备read属性
  2. p1与p2建立的中间,执行了 Person.prototype= new Animal() 代码,因此p2的原型被替换了,致使p2的原型__proto__中指向的是Animal类,其构造函数固然也就是Animal,因此也具备eat属性
  3. p1与p2追踪到最后其实都是属于Object类,再追踪的话,Objec类的原型就是null
  4. 须要注意的是在执行 Person.prototype= new Animal() 代码以后,全部被建立出来Person对象的原型对象的构造函数constructor将再也不指向本来的Person,而是Animal,因此最好将改过来,Person.prototype.constructor=Person

复杂原型继承

<script>
    function Yuanzi() {
        this.des = '进化';
    }

    function Animal() {
        this.skill = '捕猎';
    }

    function Human() {
        this.say = '说话';
    }

    Animal.prototype = new Yuanzi();
    Animal.prototype.constructor = Animal;
    Human.prototype = new Animal();
    Human.prototype.constructor = Human;


    var h = new Human();
    console.log(h);
</script>

 

打印对象h:

  1. h是一个Human类,原型对象指向Animal类,原型对象的构造函数又指向Human
  2. Animal类的原型对象指向的是Yuanzi,其构造函数又指向Animal
  3. h因此拥有了Human类,Animal类的全部属性

构造器的做用

  • 构造器其实就是构造函数,实例化后的对象是能够经过对象.constructor的方式访问本身的构造函数的,这个属性实际上是实例化后的对象自己没有,可是原型对象上有,那么它为何被建立出来呢?
  • 其实很简单的,由于构造函数能够被看做是类,在简单类型中,var a=‘hello’ 或者 var b=1 他们的typeof类型都是能够找到的对应的内置对象(类),那么复杂类型呢?好比一个经过Person构造函数建立的对象经过typeof打印结果是object,经过Animal构造函数建立出来的对象经过typeof打印结果也是object,那么这些对象的类究竟是什么呢?答案是constructor,有了它就可让对象知道建立本身的构造函数是什么,等同于知道了本身属于什么类

原型链基本概念

一、每一个构造函数都有原型对象

二、每一个对象都会有构造函数

三、每一个构造函数的原型都是一个对象

四、那么这个原型对象也会有构造函数

五、那么这个原型对象的构造函数也会有原型对象

六、这样就会造成一个链式的结构,称为原型链

七、经过修改原型链结构实现的继承,就叫作原型继承 

八、三角关系到成立的必要性之一是 实例化对象p原型的原型与Object构造函数都是指向同一个对象(在内存中的内容与地址都是同样的)

consolo.log (p.__proto__.__proto__===Object.prototype)//true

属性搜索基本原则

  1. 当访问一个对象的成员的时候,会如今自身找有没有,若是找到直接使用,
  2. 若是没有找到,则去当前对象的原型对象中去查找,若是找到了直接使用
  3. 若是没有找到,继续找原型对象的原型对象,若是找到了,直接使用
  4. 若是没有找到,则继续向上查找,直到Object.prototype,若是仍是没有,就报错

 原型对象的替换

上面负责原型继承的模式是进行的原型的替换,这样虽然方便了,可是也是有问题的

(1)给原型对象替换赋值,原型对象就会失去constructor属性

<script>
    function Person(name, age) {
        this.name = name;
        this.age = age;
    }

    var p = new Person('qx', 18);
    console.log(Person.prototype);
    Person.prototype = {
        say: function () {
            console.log(this.name);
        }
    };
    console.log(Person.prototype);
</script>

(2)固然即便替换了原型对象,可是实例化后的对象依然具备constructor属性,而这个属性属于对象的原型对象的原型对象,从上图能够看出,以及下图也看出,替换先后打印的结果是不同的。替换后,访问的是对象的原型对象的原型的构造函数

<script>
    function Person(name, age) {
        this.name = name;
        this.age = age;
    }

    var p = new Person('qx', 18);
    console.log(Person.prototype.constructor);
    Person.prototype = {
        say: function () {
            console.log(this.name);
        }
    };
    console.log(Person.prototype.constructor);
</script>

(3)对原型对象进行替换,其实能够看做是将一个对象赋值给了一个普通对象,相似var a={},咱们打印a,consolo.dir(a),结果是a并无构造函数的属性

(4)解决办法就是,替换了原型对象,必须在替换的对象上面加上constructor属性,而且赋值指向的是自身构造函数,和上面的复杂原型继承是同样的

<script>
    function Person() {

    }
    Person.prototype = {
        constructor: Person;
    }
</script>

扩展内置对象

  • 确实能够经过修改内置对象的原型对象添加一些方法,可是直接添加是不安全的,安全的方式是经过一个中介,理由是内置对象是系统默认的,直接修改它是不安全的,让一个中介拥有内置对象的全部属性,再经过这个对象的原型对象添加方法是保险的
<script>
    function MyArray() {
        // this.name = '我是一个数组';
    }

    MyArray.prototype = new Array();
    var arr1 = new MyArray();
    arr1.push(4)
    console.log(arr1);
    MyArray.prototype.say = function () {
        console.log('添加一个说的方法');
    }
    var arr2=new MyArray();
    arr2.say();
</script>
相关文章
相关标签/搜索