web前端练习22----js中的原型对象prototype,原型链(重要)

Posted zhaihaohao1

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了web前端练习22----js中的原型对象prototype,原型链(重要)相关的知识,希望对你有一定的参考价值。

原型对象 原理解析:

我们创建的每一个函数,解析器都会向函数中添加一个属性 prototype
这个属性对应着一个对象,就是原型对象。

如果函数作为普通函数,调用 prototype 没有任何作用

当函数以构造函数的形式调用时,它所创建的对象都会有一个隐含的属性 __proto__,
我们可以通过 __proto__ 来访问原型对象

原型对象就相当于一个公共区域,同一个构造函数的实例都能访问到这个原型对象
我们可以将对象共有的内容,统一设置到原型对象中

创建构造函数时,可以将对象共有的属性和方法,统一添加到构造函数的原型对象中,
这样不用分别为每一个对象添加,也不会影响到全局作用域,就可以使每个对象都具有这些属性和方法了

当我们访问对象的一个属性或者方法时,他会先在对象自身中寻找,如果有则直接使用,
如果没有则会去原型对象中寻找,如果有则直接使用。
如果原型对象中也没有,则会在原型对象的原型对象中寻找,一层一层往上找,直到找到Object的原型对象,
如果没有,就没有这个属性或方法

图解:

 

下面写一些具体例子:

拿到原型对象:

        // 拿到原型对象:
        function method1() 
            // 构造函数
            function Person() 

            
            // 原型对象
            let yuanxing = Person.prototype;

            // 实例化对象
            let person1 = new Person();
            // 通过对象找到原型对象 person1.__proto__
            console.log(yuanxing == person1.__proto__);

            // 任何对象都可以找到原型对象 person2.__proto__
            let person2 = new Person();
            console.log(yuanxing == person2.__proto__);

        

调用对象属性和方法,先在自身找,自身没有在原型对象中找

        //调用对象属性和方法,先在自身找,自身没有在原型对象中找
        function method2() 
            // 构造函数
            function Person() 

            

            // 向原型对象中添加属性和方法
            Person.prototype.name = 'zhh';
            Person.prototype.method = function (args) 
                console.log('原型对象的方法>>>' + args);
            

            // 调用对象属性和方法,先在自身找,自身没有在原型对象中找
            let person1 = new Person();
            let person2 = new Person();

            console.log(person1.name = 'peson1');
            person1.method('person1调用');

            console.log(person2.name = 'perso2');
            person2.method('person2调用');

        

原型对象也是对象,所以它也有原型(就是原型对象的原型对象)
当我们使用一个对象的属性或方法时,会先在自身找,
自身有,则直接使用
自身没有,则去原型对象中寻找,如果原型对象中有,则使用
如果原型对象中也没有,则去 原型对象的原型对象 中去寻找。
以此类推向上寻找,直到找到Object的原型对象,Object 的原型对象,没有原型对象,
如果依然没找到则返回 unedfined

        function method3() 
            // 构造函数
            function Person() 
               
            
            let person = new Person();
            // person 对象中没有 toLocaleString() ; 调用的是原型对象中的方法
            person.toLocaleString();

            // 下面查找 toLocaleString() 在哪一级的原型对象里面

            // hasOwnProperty() 对象自身是否含有某个属性或方法
            // person 中是否有 toLocaleString 方法
            console.log(person.hasOwnProperty('toLocaleString'));

            // person的原型对象中是否有 toLocaleString 方法
            console.log(person.__proto__.hasOwnProperty('toLocaleString'));

            // person的原型对象的原型对象中是否有 toLocaleString 方法
            // 打印结果是true
            // 说明person.toLocaleString()调用的是person.__proto__.__proto__中的toLocaleString方法
            console.log(person.__proto__.__proto__.hasOwnProperty('toLocaleString'));
            

OK,讲完了

下面是一个综合的例子:

    <script>
        // 构造函数
        function Person() 

        
        // 拿到原型对象,原型对象是一个公共区域
        // 构造函数的实例,都能访问到里面的属性和方法
        let yuanxing = Person.prototype;

        // 原型里面添加属性和方法
        yuanxing.name = 'zhh';
        yuanxing.say = function () 
            console.log('原型对象中的方法');
        

        // 实例化对象
        let person1 = new Person();
        // 调属性,先在自身找,自身没有在原型对象里找
        console.log(person1.name);
        // 调方法,先在自身找,自身没有在原型对象里找
        person1.say();

        // 调属性或方法,自身没有,原型对象中也没有,一层一层往上找
        console.log(person1.toLocaleString());

    </script>

 

下面对原型对象进行深层次的解析

1、原型对象在堆栈存储

看下面一段代码:

    <script>
        // 创建函数时内部语句
        // this.prototype = ;
        function Dog() 
            this.test1 = function() 
                console.log('test1方法');
            
        
        // 拿到原型对象,并给原型对象添加方法
        Dog.prototype.test2 = function() 
            console.log('test2方法');
        

        // 实例化对象时内部语句
        // this.__proto__=Dog.prototype
        var dog = new Dog();
        dog.test1();
        dog.test2();
        console.log(dog.toString());
        dog.test3();
    </script>

上面代码,堆栈是示意图,(很重要,这个图比上面Person示意图要详细)

2、构造函数和原型对象的相互指向

构造函数对象的 prototype 属性,指向原型对象
原型对象中的 constructor 指向构造函数对象

看下面一段代码:

    <script>
        // 构造函数和原型对象的相互指向
        // 构造函数
        function Type() 

        
        // 拿到原型对象,
        // 构造函数对象的 prototype 属性,指向原型对象
        // 原型对象就是一个 Object 的实例
        let yuanxing = Type.prototype;
        yuanxing.name = function() ;
        // 原型对象中的 constructor 指向构造函数对象
        console.log(yuanxing.constructor == Type);

        
    </script>

图解:

3、几个重要概念和结论
I、两个概念:
1.显式原型对象  函数对象.prototype
2.隐式原型对象  实例对象.__proto__


II、Function 和Object 的三个重要结论
1、函数的原型对象都是Object的实例(但Object除外)
2、所有的函数对象都是 Function 的实例 (Function 是他自己的实例)
3、Object 的原型对象是原型链的尽头

上面3个结论的验证:

1、函数的原型对象都是Object的实例(但Object除外) 

        /* 
         * 1、函数的原型对象都是Object的实例(但Object除外)   
         */
        // Person的原型对象,是Object 实例
        console.log(Person.prototype instanceof Object);
        // Object的原型对象不是Object 实例
        console.log(Object.prototype instanceof Object);
        // Function的原型对象,是 Object 实例
        console.log(Function.prototype instanceof Object);

2所有的函数对象都是 Function 的实例 (Function 是他自己的实例)

/* 
         * 2所有的函数对象都是 Function 的实例 (Function 是他自己的实例)
         */

        
            //  例如:Dog函数对象,内部就是 new Function()得到的
            //        所以 Dog 就是 Function 的实例
            function Dog() 

            
            //  实例化对象
            let dog = new Dog();
            //  对象中添加方法
            dog.method = function() 
                console.log('对象中添加方法');
            
            dog.method();
        


        
            // 上面的代码可以写成这样
            //  Dog是构造函数对象
            let Dog = new Function();
            //  实例化对象
            let dog = new Dog();
            //  对象中添加方法
            dog.method = function() 
                console.log('对象中添加的方法2');
            
            dog.method();
        

        // Function是自己的实例
        // Function.prototype 的Function是函数对象
        // Function.__proto__ 的Function是实例对象
        console.log(Function.prototype == Function.__proto__);

3.Object 的原型对象是原型链的尽头

        /* 
         * 3.Object 的原型对象是原型链的尽头
         */
        console.log(Object.prototype.__proto__);

根据上面的三条结论验证 instanceof  判断时原型链的指向

例子1:

    // 案例1
    function Foo() 

    
    let foo = new Foo();
    console.log(foo instanceof Foo);
    console.log(foo instanceof Object);

console.log(foo instanceof Foo); 结果是true,示意图如下:

console.log(foo instanceof Object);结果true,示意图如下:

例子2

    // 案例2
    console.log(Object instanceof Function);
    console.log(Object instanceof Object);
    console.log(Function instanceof Function);
    console.log(Function instanceof Object);

    function Fooo()
    console.log(Object instanceof Fooo);

console.log(Object instanceof Function); 结果true,示意图如下:

console.log(Object instanceof Object);结果是true,示意图如下:

console.log(Function instanceof Function);结果是true,示意图如下:

console.log(Function instanceof Object);结果是true,示意图如下:

    function Fooo()

    console.log(Object instanceof Fooo);结果是 false 结果如下:

OK,原型对象全部讲完了

参考视频

http://www.gulixueyuan.com/my/course/58

http://www.gulixueyuan.com/course/194

js继承机制的设计思想

http://www.ruanyifeng.com/blog/2011/06/designing_ideas_of_inheritance_mechanism_in_javascript.html

 

 

 

 

 

 

 

 

以上是关于web前端练习22----js中的原型对象prototype,原型链(重要)的主要内容,如果未能解决你的问题,请参考以下文章

web前端面试题JavaScript第一弹,个人整理部分面试题汇总

深入理解JS原型与原型链

web前端练习20----es6新语法7,生成器对象 Generator

好程序员web前端教程分享前端javascript练习题之promise

web前端练习28----Dom4,事件(事件对象,事件冒泡,事件委派,事件传播,事件绑定,事件移除及案例练习)

web前端练习28----Dom4,事件(事件对象,事件冒泡,事件委派,事件传播,事件绑定,事件移除及案例练习)