JavaScript原型与原型链

106 阅读5分钟

JavaScript万物皆由对象,一切皆空!!!

1.设计模式

  1. 设计模式概念:软件开发人员在软件开发过程中面临的一般问题的解决方案
  2. 原型模式:原型模式是一种创建型设计模式, 用于创建重复的对象,同时又能保证性能

1.1面向对象

  1. 特征:继承、封装、多态

  2. java和js继承的区别:

    继承:继承属于类与类之间的关系,继承的目的是为了实现数据共享;

    js:是一种基于原型的面向对象的编程语言,js通过构造函数模拟类,通过原型来实现继承;

    java:是一种面向对象的编程语言,类实现继承;

2.原型链和原型

2.1理论

prototype、_proto_

  1. prototype(显式原型):所有的函数的一个属性prototype,这个属性指向函数的原型对象;
  2. _proto_(隐式原型):每个对象都有一个__proto__属性,指向该实例对象对应的原型对象;
  3. constructor:原型对象有一个constructor属性,指向该原型对象对应的构造函数

构造函数、实例、原型对象

  1. 构造函数:用来创建对象的函数;
  2. 实例:实例就是通过构造函数创建出来的对象;
  3. 原型:每个函数在被创建的时候,都会默认有一个prototype属性,prototype的属性值是一个对象,也就是构造函数的原型;
    /**
    ①写一个函数:
    */
    function Bird(kind,color){
        this.kind=kind;
        this.color=color;
    };
    console.log(Bird.prototype,'原型对象');//原型对象
    console.log(Bird.prototype.constructor,'指向函数');//指向函数
    /**
    ②构建一个实例:
    */
    let a=new Bird('猫头鹰','灰色');
    console.log(a,'实例对象');//实例对象
    console.log(a.__proto__,'指向原型对象');//指向原型对象
    console.log(a.__proto__.constructor,'指向函数');//指向函数/**
    ③构造函数、原型、实例
    */
    console.log(a.__proto__===Bird.prototype);
    console.log(a.__proto__.constructor===Bird.prototype.constructor);

4. 关系图:

构造函数、原型、实例关系.png

2.2找原型链

2.2.1数据类型

数组
  /**
    创建数组实例
    */
    let arr=new Array(1,8,0);
    console.log(arr);
    console.log(Array.prototype,'构造函数的原型对象');
    //①查看实例arr的原型
    console.log(arr.__proto__,'原型');
    console.log(arr.__proto__.constructor,'原型的指向');//Array
    console.log(arr.__proto__===Array.prototype);
    //②查看实例arr的原型的原型
    console.log(arr.__proto__.__proto__,'原型的原型');
    console.log(arr.__proto__.__proto__.constructor,'原型的原型的指向');//Object
    console.log(arr.__proto__.__proto__===Object.prototype);
    //③原型链的终点
    console.log(arr.__proto__.__proto__.__proto__,'终点');

数组原型链.png

字符串
/**
    创建字符串实例
    */
    let str=new String('43');
    console.log(str);
    console.log(String.prototype,'构造函数的原型对象');
    //①查看实例str的原型
    console.log(str.__proto__,'原型');
    console.log(str.__proto__.constructor,'原型的指向');
    console.log(str.__proto__===String.prototype);//String
    //②查看实例str的原型的原型
    console.log(str.__proto__.__proto__,'原型的原型');
    console.log(str.__proto__.__proto__.constructor,'原型的原型的指向');//Object
    console.log(str.__proto__.__proto__===Object.prototype);
    //③原型链的终点
    console.log(str.__proto__.__proto__.__proto__,'终点');

字符串原型链.png

DOM
 <body>
        <div>div标签</div>
        <p>P标签</p>
        <a href="#">a标签</a>
        <script>
            let div = document.querySelector('div');
            let p = document.querySelector('p');
            let a = document.querySelector('a');
            //①找原型
            console.log(div.__proto__, 'div第一层的原型');
            console.log(p.__proto__, 'p的原型');
            console.log(a.__proto__, 'a的原型');
            //②原型的指向
            console.log(div.__proto__.constructor, 'div的原型指向');
            console.log(p.__proto__.constructor, 'p的原型指向');
            console.log(a.__proto__.constructor, 'a的原型指向');
            //③原型的原型
            console.log(div.__proto__.__proto__, 'div第二层原型');
            console.log(p.__proto__.__proto__, 'p的原型的原型');
            console.log(a.__proto__.__proto__, 'a的原型的原型');
            //④原型的原型的指向
            console.log(div.__proto__.__proto__.constructor, 'div的原型的原型指向');
            //⑤原型的原型的原型
            console.log(div.__proto__.__proto__.__proto__, 'div第三层原型');
            console.log(div.__proto__.__proto__.__proto__.__proto__, 'div第四层原型');
            console.log(div.__proto__.__proto__.__proto__.__proto__.__proto__, 'div第五层原型');
            console.log(div.__proto__.__proto__.__proto__.__proto__.__proto__.__proto__, 'div第六层原型');
            console.log(div.__proto__.__proto__.__proto__.__proto__.__proto__.__proto__.__proto__, 'div第七层原型');
        </script>
    </body>

DOM原型链.png

2.2.2 instanceof

语法:object instanceof constructor

作用:用于检测构造函数的 prototype 属性是否出现在某个实例对象的原型链上

   /**
    ①字符串
    */
    let a='abc';
    let b=new String('abc');
    console.log(a==b)//true;
    console.log(a===b)//false;
    console.log(b instanceof Object);
    console.log(a instanceof Object);
    console.log(a instanceof String);
    console.log(b instanceof String);
    /**
    ②数组
    */
    let a=[1,8,0];
    let b=new Array(1,8,0);
    console.log(b instanceof Object);
    console.log(a instanceof Object);
    console.log(b instanceof Array);
    console.log(a instanceof Array);
    /**
    ②null和undefined
    */
    let a=null;
    let b=undefined;
    console.log(b instanceof Object);
    console.log(a instanceof Object);
    /**
    ECMAScript 规范认为(https://262.ecma-international.org/5.1/#sec-4.3.9)
    If x is null and y is undefined, return true.
    If x is undefined and y is null, return true.
    */
    console.log(a==b);//true
ToObject原则(补充)

JS中使用字符串可以直接赋值字符串字面量,或者是new String("252")

那这两中定义字符串的区别在哪?

当我们尝试访问一个primitive值的属性时,JS引擎内部会调用一个内置[[toObject]] 方法,将字面量的”252”转为一个[[PrimitiveValue]]为”252”的String对象,然后从其原型链中尝试查找需要访问的属性,使用结束后再释放掉这个String对象。

    let a='22';
    let b=new String('22');
    console.log(a);
    console.log(b);

简而言之就是把构造函数创建的字符串通过ToObject算法转换成了字面量定义的值。

ECMAScript 规范规范:tc39.es/ecma262/

ToObject算法原则.png

题目应用
function Box() { this.name = '我是Box'; }
function Desk() { this.age = 100; }
Desk.prototype = new Box();//通过原型链继承     
var desk = new Desk();
console.log(desk instanceof Box);//true
console.log(Desk.prototype instanceof Box);//true
console.log(Desk.prototype instanceof Desk);//false

原型链关系题目1.png

3总结

原型继承

  1. 概念:把父对象作为子对象构造函数的原型;

  2. 代码演示:

  //父对象
        var parent={
          home:{
            plate:'150号',
            stree:'香榭丽舍大道'
          },
          money:1000000
        }
        //子对象
        function Son(name,age){
          this.name=name;
          this.age=age;
        }
        var result=new Son('zzh',18);
        console.log(result,'没继承parent时候的实例对象');
        /**
        原型继承
        */
        Son.prototype=parent;
        Son.prototype.construct=Son;//可选
        var result=new Son('zzh',18);
        //Son.prototype.constructor=Son;//可选
        console.log(result,'已经继承parent属性的实例对象');
        console.log(Son.prototype.constructor);

原型继承题目关系图.png

访问原型链的规则

就近原则:对象先访问自己的属性,自己没有就找原型的,原型没有就找原型的原型,一直到原型链终点null,如果还找不到,属性则获取undefined。

    function Person(name,age){
        this.name=name;
        this.age=age
    };
    Person.prototype.country='中国';
    Person.prototype.eat=function(){
        console.log('吃零食')
    }
    let p1=new Person('张三',18);
    /**
    得到的结果
    */
    console.log(p1.name);//张三
    console.log(p1.age);//18
    console.log(p1.country);//中国
    console.log(p1.height);//undefined
    p1.eat();//吃零食
    p1.learn();//TypeError: p1.learn is not a function

4问题

  1. 原型是什么?

    每个函数都有一个属性prototype(显式原型),这个属性指向函数的原型对象;每个对象都有一个属性_proto_(隐式原型),这个属性指向该实例对象对应的原型对象;

  2. 原型链是什么?原型链的作用?

    JS中万物皆对象,对象与对象之间有关联,当我们寻找一个对象的属性的时候,会先去找它自己的属性有没有这个属性,如果自己没有,就会顺着它的原型属性上面找,看有没有这个属性,如果没有,则继续找下去,这个关系就形成了一条链式,专业术语叫原型链,而原型链是JS用来实现继承的一种方式;

  3. 为什么[] instanceof Object会等于true?

    因为instanceof就是在原型链上面找有没有出现过,如果出现过为true,反之false,而在数组的原型链中,第二层原型链中出现过对象构造函数,所以[] instanceof Object等于true;

  4. 原型链访问原则是什么?终点是什么?

    原型链的访问原则就是,先去找对象本来的属性,如果没有出现再去找原型上面的属性,如果还是没有找到,那就去找原型的原型属性,最后找到null的时候,原型链访问结束;原型链的终点是null;