javascript学习笔记(五)原型和原型链详解


Posted in Javascript onOctober 08, 2014

私有变量和函数

在函数内部定义的变量和函数,如果不对外提供接口,外部是无法访问到的,也就是该函数的私有的变量和函数。

<script type="text/javascript">

    function Test(){

        var color = "blue";//私有变量

        var fn = function() //私有函数

        {
        }

    }

</script>

这样在函数对象Test外部无法访问变量color和fn,他们就变成私有的了:

var obj = new Test();

    alert(obj.color);//弹出 undefined

    alert(obj.fn);//同上

静态变量和函数

当定义一个函数后通过点号 “.”为其添加的属性和函数,通过对象本身仍然可以访问得到,但是其实例却访问不到,这样的变量和函数分别被称为静态变量和静态函数。

<script type="text/javascript">

    function Obj(){
    }

    Obj.num = 72;//静态变量

    Obj.fn = function()  //静态函数

    {
    } 
    alert(Obj.num);//72

    alert(typeof Obj.fn)//function
    var t = new Obj();

    alert(t.name);//undefined

    alert(typeof t.fn);//undefined

</script>

实例变量和函数

在面向对象编程中除了一些库函数我们还是希望在对象定义的时候同时定义一些属性和方法,实例化后可以访问,JavaScript也能做到这样

<script type="text/javascript">

    function Obj(){

                this.a=[]; //实例变量

                this.fn=function(){ //实例方法
                }

            }
            console.log(typeof Obj.a); //undefined

            console.log(typeof Obj.fn); //undefined
            var o=new Obj();

            console.log(typeof o.a); //object

            console.log(typeof o.fn); //function

</script>

为实例变量和方法添加新的方法和属性

<script type="text/javascript">

function Obj(){

                this.a=[]; //实例变量

                this.fn=function(){ //实例方法
                }

            }
            var o1=new Obj();

            o1.a.push(1);

            o1.fn={};

            console.log(o1.a); //[1]

            console.log(typeof o1.fn); //object
            var o2=new Obj();

            console.log(o2.a); //[]

            console.log(typeof o2.fn); //function

</script>

在o1中修改了a和fn,而在o2中没有改变,由于数组和函数都是对象,是引用类型,这就说明o1中的属性和方法与o2中的属性与方法虽然同名但却不是一个引用,而是对Obj对象定义的属性和方法的一个复制。

这个对属性来说没有什么问题,但是对于方法来说问题就很大了,因为方法都是在做完全一样的功能,但是却又两份复制,如果一个函数对象有上千和实例方法,那么它的每个实例都要保持一份上千个方法的复制,这显然是不科学的,这可肿么办呢,prototype应运而生。

基本概念

我们创建的每个函数都有一个prototype属性,这个属性是一个指针,指向一个对象,这个对象的用途是包含可以由特定类型的所有实例共享的属性和方法。那么,prototype就是通过调用构造函数而创建的那个对象实例的原型对象。

使用原型的好处是可以让对象实例共享它所包含的属性和方法。也就是说,不必在构造函数中添加定义对象信息,而是可以直接将这些信息添加到原型中。使用构造函数的主要问题就是每个方法都要在每个实例中创建一遍。

在JavaScript中,一共有两种类型的值,原始值和对象值。每个对象都有一个内部属性 prototype ,我们通常称之为原型。原型的值可以是一个对象,也可以是null。如果它的值是一个对象,则这个对象也一定有自己的原型。这样就形成了一条线性的链,我们称之为原型链。

含义

函数可以用来作为构造函数来使用。另外只有函数才有prototype属性并且可以访问到,但是对象实例不具有该属性,只有一个内部的不可访问的__proto__属性。__proto__是对象中一个指向相关原型的神秘链接。按照标准,__proto__是不对外公开的,也就是说是个私有属性,但是Firefox的引擎将他暴露了出来成为了一个共有的属性,我们可以对外访问和设置。

<script type="text/javascript">

    var Browser = function(){};

    Browser.prototype.run = function(){

        alert("I'm Gecko,a kernel of firefox");

    }
    var Bro = new Browser();

    Bro.run();

</script>

当我们调用Bro.run()方法时,由于Bro中没有这个方法,所以,他就会去他的__proto__中去找,也就是Browser.prototype,所以最终执行了该run()方法。(在这里,函数首字母大写的都代表构造函数,以用来区分普通函数)

当调用构造函数创建一个实例的时候,实例内部将包含一个内部指针(__proto__)指向构造函数的prototype,这个连接存在于实例和构造函数的prototype之间,而不是实例与构造函数之间。

<script type="text/javascript">

function Person(name){

                this.name=name;

            }
            Person.prototype.printName=function(){

                alert(this.name);

            }
            var person1=new Person('Byron');

            var person2=new Person('Frank');

</script>

Person的实例person1中包含了name属性,同时自动生成一个__proto__属性,该属性指向Person的prototype,可以访问到prototype内定义的printName方法,大概就是这个样子的:

javascript学习笔记(五)原型和原型链详解

再举个栗子:

<script type="text/javascript">

    function Animal(name)   //积累构造函数

    {

        this.name = name;//设置对象属性

    }
    Animal.prototype.behavior = function() //给基类构造函数的prototype添加behavior方法

    {  

        alert("this is a "+this.name);

    }
    var Dog = new Animal("dog");//创建Dog对象

    var Cat = new Animal("cat");//创建Cat对象
    Dog.behavior();//通过Dog对象直接调用behavior方法

    Cat.behavior();//output "this is a cat"
    alert(Dog.behavior==Cat.behavior);//output true;

</script>

可以从程序运行结果看出,构造函数的prototype上定义的方法确实可以通过对象直接调用到,而且代码是共享的。(可以试一下将Animal.prototype.behavior 中的prototype属性去掉,看看还能不能运行。)在这里,prototype属性指向Animal对象。

数组对象实例

再看个数组对象的实例。当我们创建出array1这个对象的时候,array1实际在Javascript引擎中的对象模型如下:

var array1 = [1,2,3];

javascript学习笔记(五)原型和原型链详解

array1对象具有一个length属性值为3,但是我们可以通过如下的方法来为array1增加元素:

array1.push(4);
push这个方法来自于array1的__proto__成员指向对象的一个方法(Array.prototye.push())。正是因为所有的数组对象(通过[]来创建的)都包含有一个指向同一个具有push,reverse等方法对象(Array.prototype)的__proto__成员,才使得这些数组对象可以使用push,reverse等方法。

函数对象实例

function Base() {  

    this.id = "base" 

}  

javascript学习笔记(五)原型和原型链详解

var obj = new Base();

这样代码的结果是什么,我们在Javascript引擎中看到的对象模型是:

javascript学习笔记(五)原型和原型链详解

new操作符具体干了什么呢?其实很简单,就干了三件事情。

var obj  = {};  

obj.__proto__ = Base.prototype;  

Base.call(obj);

原型链

原型链:当从一个对象那里调取属性或方法时,如果该对象自身不存在这样的属性或方法,就会去自己关联的prototype对象那里寻找,如果prototype没有,就会去prototype关联的前辈prototype那里寻找,如果再没有则继续查找Prototype.Prototype引用的对象,依次类推,直到Prototype.….Prototype为undefined(Object的Prototype就是undefined)从而形成了所谓的“原型链”。

<script type="text/javascript">

    function Shape(){

        this.name = "shape";

        this.toString = function(){

            return this.name;

        }

    }

    function TwoShape(){

        this.name = "2 shape";

    }

    function Triangle(side,height){

        this.name = "Triangle";

        this.side = side;

        this.height = height;

        this.getArea = function(){

            return this.side*this.height/2;

        }

    }
    TwoShape.prototype = new Shape();

    Triangle.prototype = new TwoShape();

</script>

这里,用构造器Shape()新建了一个实体,然后用它去覆盖该对象的原型。

<script type="text/javascript">

    function Shape(){

        this.name = "shape";

        this.toString = function(){

            return this.name;

        }

    }

    function TwoShape(){

        this.name = "2 shape";

    }

    function Triangle(side,height){

        this.name = "Triangle";

        this.side = side;

        this.height = height;

        this.getArea = function(){

            return this.side*this.height/2;

        }

    }
    TwoShape.prototype = new Shape();

    Triangle.prototype = new TwoShape();
    TwoShape.prototype.constructor = TwoShape;

    Triangle.prototype.constructor = Triangle;
    var my = new Triangle(5,10);

    my.getArea();

    my.toString();//Triangle

    my.constructor;//Triangle(side,height)

</script>

原型继承

原型继承:在原型链的末端,就是Object构造函数prototype属性指向的那个原型对象。这个原型对象是所有对象的祖先,这个老祖宗实现了诸如toString等所有对象天生就该具有的方法。其他内置构造函数,如Function,Boolean,String,Date和RegExp等的prototype都是从这个老祖宗传承下来的,但他们各自又定义了自身的属性和方法,从而他们的子孙就表现出各自宗族的那些特征。

ECMAScript中,实现继承的方法就是依靠原型链实现的。

<script type="text/javascript">

    function Box(){             //被继承的函数叫做超类型(父类,基类)

        this.name = "Jack";

    }
    function Tree(){          //继承的函数叫做子类型(子类,派生类)

        this.age = 300;

    }

    //通过原型链继承,赋值给子类型的原型属性

    //new Box()会将box构造里的信息和原型里的信息都交给Tree

    Tree.prototype = new Box();//Tree继承了Box,通过原型,形成链条
    var tree = new Tree();

    alert(tree.name);//弹出 Jack

</script>

原型链的问题:原型链虽然很强大,可以用它来实现继承,但它也存在一些问题。其中最主要的问题来自包含引用类型的值原型。包含引用类型的原型属性会被所有实例共享;而这也正是为什么要在构造函数中,而不是在原型对象中定义属性的原因。在通过原型来实现继承时,原型实际上回变成另一个类型的实例。于是,原先的实例属性也就变成了原型的属性。

在创建子类型的实例时,不能向超类型的构造函数中传递参数。实际上,应该说是没有办法在不影响所有对象实例的情况下,给超类型的构造函数传递参数。再加上刚刚讨论的由于原型中包含引用类型值所带来的问题,实践中很少会单独使用原型链。

再举个栗子:

<script type="text/javascript">

    function Person(name)

    {

        this.name = name;//设置对象属性

    };
    Person.prototype.company = "Microsoft";//设置原型的属性

    Person.prototype.SayHello = function() //原型的方法

    {  

        alert("Hello,I'm "+ this.name+ " of " + this.company);

    };
    var BillGates = new Person("BillGates");//创建person对象

    BillGates.SayHello();//继承了原型的内容,输出"Hello,I'm BillGates of Microsoft"
    var Jobs = new Person("Jobs");

    Jobs.company = "Apple";//设置自己的company属性,掩盖了原型的company属性

    Jobs.SayHello = function()

    {

        alert("Hi,"+this.name + " like " + this.company);

    };

    Jobs.SayHello();//自己覆盖的属性和方法,输出"Hi,Jobs like Apple"

    BillGates.SayHello();//Jobs的覆盖没有影响原型,BillGates还是照样输出

</script>

看下面一个原型链例子:

<script type="text/javascript">

    function Year(){

        this.value = 21;

    }

    Year.prototype = {

        method:function(){
        }

    };
    function Hi(){
    };

    //设置Hi的prototype属性为Year的实例对象

    Hi.prototype = new Year();

    Hi.prototype.year = 'Hello World';
    Hi.prototype.constructor = Hi;
    var test = new Hi();//创建一个Hi的新实例
    //原型链

    test [Hi的实例]

        Hi.prototype [Year的实例]

            {year:'Hello World'}

            Year.prototype

                {method:……};

                object.prototype

                    {toString:...};
</script>

从上面例子中,test对象从Hi.prototype和Year.prototype中继承下来;因此他能访问Year的原型方法method,同时他能访问实例属性value

__ptoto__属性

__ptoto__属性(IE浏览器不支持)是实例指向原型对象的一个指针,它的作用就是指向构造函数的原型属性constructor,通过这两个属性,就可以访问原型里的属性和方法了。

Javascript中的对象实例本质上是由一系列的属性组成的,在这些属性中,有一个内部的不可见的特殊属性——__proto__,该属性的值指向该对象实例的原型,一个对象实例只拥有一个唯一的原型。

<script type="text/javascript">

    function Box(){        //大写,代表构造函数

        Box.prototype.name = "trigkit4";//原型属性

        Box.prototype.age = "21";

        Box.prototype.run = function()//原型方法

        {  

            return this.name + this.age + 'studying';

        }

    }
    var box1 = new Box();

    var box2 = new Box();

    alert(box1.constructor);//构造属性,可以获取构造函数本身,

                            //作用是被原型指针定位,然后得到构造函数本身

</script>  

__proto__属性和prototype属性的区别

prototype是function对象中专有的属性。
__proto__是普通对象的隐式属性,在new的时候,会指向prototype所指的对象;
__ptoto__实际上是某个实体对象的属性,而prototype则是属于构造函数的属性。__ptoto__只能在学习或调试的环境下使用。

原型模式的执行流程

1.先查找构造函数实例里的属性或方法,如果有,就立即返回。
2.如果构造函数的实例没有,就去它的原型对象里找,如果有,就立即返回

原型对象的

<script type="text/javascript">

    function Box(){        //大写,代表构造函数

        Box.prototype.name = "trigkit4";//原型属性

        Box.prototype.age = "21";

        Box.prototype.run = function()//原型方法

        {  

            return this.name + this.age + 'studying';

        }

    }
    var box1 = new Box();

    alert(box1.name);//trigkit4,原型里的值

    box1.name = "Lee";

    alert(box1.name);//Lee,就进原则
    var box2 = new Box();

    alert(box2.name);//trigkit4,原型的值,没有被box1修改

</script>

构造函数的

<script type="text/javascript">

    function Box(){                 

        this.name = "Bill";

    }
    Box.prototype.name = "trigkit4";//原型属性

    Box.prototype.age = "21";

    Box.prototype.run = function()//原型方法

    {  

            return this.name + this.age + 'studying';

    }
    var box1 = new Box();

    alert(box1.name);//Bill,原型里的值

    box1.name = "Lee";

    alert(box1.name);//Lee,就进原则

</script>
Javascript 相关文章推荐
原生javascript实现获取指定元素下所有后代元素的方法
Oct 28 Javascript
ECMAScript6块级作用域及新变量声明(let)
Jun 12 Javascript
js实现的简单图片浮动效果完整实例
May 10 Javascript
浅谈JavaScript中的分支结构
Jul 01 Javascript
jQuery实现的导航下拉菜单效果示例
Sep 05 Javascript
JQueryEasyUI之DataGrid数据显示
Nov 23 Javascript
jquery获取file表单选择文件的路径、名字、大小、类型
Jan 18 jQuery
初学node.js中实现删除用户路由
May 27 Javascript
javascript实现超好看的3D烟花特效
Jan 01 Javascript
如何在Node和浏览器控制台中打印彩色文字
Jan 09 Javascript
js实现页面导航层级指示效果
Aug 25 Javascript
详解Javascript实践中的命令模式
May 05 Javascript
JavaScript中获取高度和宽度函数总结
Oct 08 #Javascript
5个可以帮你理解JavaScript核心闭包和作用域的小例子
Oct 08 #Javascript
JavaScript中获取样式的原生方法小结
Oct 08 #Javascript
吐槽一下我所了解的Node.js
Oct 08 #Javascript
Node.js 的异步 IO 性能探讨
Oct 08 #Javascript
JS中的form.submit()不能提交表单的错误原因
Oct 08 #Javascript
js数值计算时使用parseInt进行数据类型转换(jquery)
Oct 07 #Javascript
You might like
PHP 编写的 25个游戏脚本
2009/05/11 PHP
PHP获取MAC地址的函数代码
2011/09/11 PHP
php对称加密算法示例
2014/05/07 PHP
php获取网页上所有链接的方法
2015/04/03 PHP
浅谈PDO的rowCount函数
2015/06/18 PHP
微信小程序发送订阅消息的方法(php 为例)
2019/10/30 PHP
JavaScript下申明对象的几种方法小结
2008/10/02 Javascript
JavaScript中string转换成number介绍
2014/12/31 Javascript
jQuery设置和移除文本框默认值的方法
2015/03/09 Javascript
javascript监听页面刷新和页面关闭事件方法详解
2017/01/09 Javascript
javascript中this用法实例详解
2017/04/06 Javascript
Angular CLI 安装和使用教程
2017/09/13 Javascript
Element-UI Table组件上添加列拖拽效果实现方法
2018/04/14 Javascript
vue 指令之气泡提示效果的实现代码
2018/10/18 Javascript
利用JS代码自动删除稿件的普通弹幕功能
2019/09/20 Javascript
tensorflow识别自己手写数字
2018/03/14 Python
Python使用re模块实现信息筛选的方法
2018/04/29 Python
用Django写天气预报查询网站
2018/10/21 Python
python3+selenium实现qq邮箱登陆并发送邮件功能
2019/01/23 Python
使用python list 查找所有匹配元素的位置实例
2019/06/11 Python
Python从文件中读取指定的行以及在文件指定位置写入
2019/09/06 Python
Python之Class&amp;Object用法详解
2019/12/25 Python
解决Python中报错TypeError: must be str, not bytes问题
2020/04/07 Python
Django URL参数Template反向解析
2020/11/24 Python
pandas按条件筛选数据的实现
2021/02/20 Python
html5实现九宫格抽奖可固定抽中某项奖品
2020/06/15 HTML / CSS
天猫国际进口超市直营:官方直采,一站购齐
2017/12/11 全球购物
澳大利亚排名第一的儿童在线玩具商店:Toy Galaxy
2018/10/06 全球购物
意大利男装网店:Vrients
2019/05/02 全球购物
Optimalprint加拿大:在线打印服务
2020/04/03 全球购物
TecoBuy澳大利亚:在线电子和小工具商店
2020/06/25 全球购物
介绍一下Java的安全机制
2012/06/28 面试题
C语言编程题
2015/03/09 面试题
学习保证书范文
2014/04/30 职场文书
民族学专业职业生涯规划范文:积跬步以至千里
2014/09/11 职场文书
运动会三级跳加油稿
2015/07/21 职场文书