原文地址:http://www.cnblogs.com/allenxing/p/3527654.html

前言

荒废了好几天,在宿舍闷了几天了,一直想着回家放松,什么也没搞,论文就让老师催吧。不过,闲的没事干的感觉真是不好,还是看看书,写写博客吧,今天和大家说说函数的原型。

原型是什么

第一次看到这个的时候,没太理解这个概念,其实也就是一个概念呗,没啥神秘的。书上说每个函数都有一个prototype属性(原型属性),这个属性是一个指针,指向一个对象(原型对象),这个对象包含这个函数创建的实例的共享属性和方法。也就是说原型对象中的属性和方法是所有实例共享的,打住,那我们就先创建一个函数看看,原型是什么东东

var test = function(){}
console.log(test.prototype);//Object {}

看来,是这的有这么一个属性,可以看出是一个对象,但是默认的是一个空的对象,既然是一个对象,那我们就可以给它添加属性和方法喽,试试看

var test = function (){}
test.prototype.name = "hainan";
test.prototype.age = "25";
test.prototype.getName = function(){console.log(this.name);}
console.log(test.prototype);//Object {}

我们成功的添加了属性和方法,骄傲吧,对象中的属性一会我们在解释,现在看看我们修改之后的原型对象,有啥用呢?刚才我们说过,原型对象中的属性和方法是这个函数new出来实例所共享的,那我们就new实例出来试试

var test = function test(){}
test.prototype.name = "hainan";
test.prototype.age = "25";
test.prototype.getName = function(){console.log(this.name);}
console.log(test.prototype);//Object {}
var o1 = new test();
console.log(o1.getName());
console.log(o1.name);
var o2 = new test();
console.log(o2.getName());
console.log(o2.name);

看上面的图,我们new出来两个实例,这两个实例中并没有name对象和getName()方法,但是我们却使用了该属性和方法,就是因为函数的原型对象中存在这个属性和方法,并且是每个实例都可以使用的,这就是原型的神秘之处。也就是说以后我们想在每一个实例中添加属性和方法,那我们就把这个共有的属性或方法直接添加到原型对象上就可以了。

理解原型对象

原型对象

现在我们知道了,函数有一个prototype属性,这个属性是一个指针,指向一个原型对象,这个原型对象中的属性和方法是这个函数的实例所共有的。现在我们看看这个原型对象中的属性,看这幅图

我们给一个原型属性添加属性之后,这个对象就不是空得了,看第一个图,上面打印出来的Object是空的,其实这个对象不是空的,只是有些属性没有被枚举出来,看下图

嗯,这样就对了,无论啥时候,只要创建一个新函数,就为给这个函数创建一个prototype属性,在默认的情况下,所有的原型对象会自动添加一个constructor属性,从名字就可以看出来应该指向构造函数,看图

指向了构造函数,也就是test.prototype.constructor === test。

属性中还有一个__prototype__属性,我们先放一下,我们还是看new出来那个实例的图,这次我把属性展开大家看看

var test = function test(){}
test.prototype.name = "hainan";
test.prototype.age = "25";
test.prototype.getName = function(){console.log(this.name);}
console.log(test.prototype);//Object {}
var o1 = new test();
console.log(o1);

我们分别打印了test的原型对象和一个实例对象,我们可以看到原型对象和实例对象中都有一个__proto__对象,但是指向不同,原型对象中的__proto__指向Object,而实例中的__proto__指向内部明叫test的对象,展开这个对象可以看到就是原型对象。就是说每一个实例中有一个__proto__属性指向原型对象。是不是有点晕呢,画个图看看先

是不是清楚点了呢,每一个实例的内部都包含一个内部属性__proto__,指向了构造函数的原型,就是这个属性连接了实例和原型对象之间的关系,并且我们知道实例中不包含name属性和getName方法,但是我们却使用了getName(),就是通过这个__proto__属性查找的。

大家应该发现了test的原型对象中也有一个__proto__属性,这个属性指向谁呢,我们来分析一下。我们知道了__proto__属性存在一个实例中并指向的是一个原型对象,现在就是说test的原型对象时某一个对象的实例喽,因为它有一个__proto__属性嘛。那test的原型对象是哪个对象的实例呢?我们知道javascript所有的对象都是基于Object这个对象的,都是Object的实例,我们大胆的猜测就是Oject的实例,我们展开这个属性看看就知道我们猜的对不对了,看图

嗯,我们看到了__proto__属性指向的对象中存在一些方法,这些方法就是我们前面介绍的Object对象的方法,好牛,我们猜对了。我们刚才说了一下,实例可以共享原型的方法和属性,也就是test的原型可以使用Object原型中方法,而test的实例可以使用test的原型中的方法,也就是说test的实例可以使用Object原型中的方法,嗯,就是这样,我们试一下,看代码,使用一个简单的函数试一下就知道了

var test = function test(){}
test.prototype.name = "hainan";
test.prototype.age = "25";
test.prototype.getName = function(){console.log(this.name);}
var o1 = new test();
console.log(o1.toString());

我们使用了Object原型中的方法,我们再一次猜对了。现在我们把上面的图补充完整

现在就完整了,这就是这个例子的完整原型图形。我们以前说过,所有的应用类型都是继承Object,所有函数的默认原型都是Object的实例,因此默认原型都包含一个默认指针指向Object.prototype。这就是我们所说的原型链,继承就是通过原型链实现的,这就是所有的自定义类型都会继承toString()和valueOf()等默认方法的根本原因。Object是所有引用类型的父类,可以这么理解。

isPrototypeOf

使用方法a.isprototypeOf(b),判断对象a是否是实例b__proto__指向的原型对象,如果是返回true,否则返回false。看个例子

var test = function test(){}
test.prototype.name = "hainan";
test.prototype.age = "25";
test.prototype.getName = function(){console.log(this.name);}
var o1 = new test();
console.log(test.prototype.isPrototypeOf(o1));//true

hasOwnProperty

这个方法是检测一个属性是否存在实例中,存在原型中会返回false,看例子

var test = function test(){}
test.prototype.name = "hainan";
test.prototype.age = "25";
test.prototype.getName = function(){console.log(this.name);}
var o = new test();
console.log(o.hasOwnProperty("name"));//false
o.name = "xing";//给实例添加属性
console.log(o.hasOwnProperty("name"));//true

属性查找

对象实例可以访问保存在原型中的值,但是不能重写原型中的值。每次要读取某一个属性时,都会执行一次搜索:首先在对象本身开始查找,如果查找到了就返回这个属性,如果没有找到,则继续搜索__proto__指向的原型对象,如果还没有找到,则继续搜索原型对象中__proto__指向的原型对象,这样一直迭代下去,这就是原型链的作用。看例子

var test = function test(){}
test.prototype.name = "hainan";
test.prototype.age = "25";
test.prototype.getName = function(){console.log(this.name);}
var o = new test();
console.log(o.name);//hainan
o.name = "xing";//给实例添加属性
console.log(o.name);//xing
delete o.name;//删除实例的name属性
console.log(o.name);//hainan

重写原型对象

我们已经知道怎么给一个原型对象添加属性和方法,但是大家都会想到一个简单地方法来一起添加,就像这样

var test = function test(){}
test.prototype = {
name : "hainan",
age : "25",
getName : function(){return this.name}
};
var o = new test(); o.getName();//"hainan"
console.log(o.constructor == test);//false

我们思考一下,上面的代码test.prorotype现在已经指向了一个新的对象,已经不是原来那个默认的原型对象了,原来的默认原型对象我们之前只是添加属性并没有重写它,所有他的内部属性还是存在了,现在我们重写了这个对象,即prototype指向了一个新的对象了,那他原来的属性constructor就没有了,如果我们以后会使用这个属性,那我们应该人为的设置,例如

var test = function test(){}
test.prototype = {
constructor : test,
name : "hainan",
age : "25",
getName : function(){return this.name}
};
var o = new test(); o.getName();//"hainan"
console.log(o.constructor == test);//true

原型动态性

看个例子先

var test = function test(){}
var o = new test();
test.prototype.name = "hainan";
console.log(o.name);

我们一般的时候肯定是先设置原型在创建实例,这在任何情况下都是没有问题的。我们创建实例是在给原型添加属性之后,即使这样我们也可以在实例中使用这个属性,这就是原型的动态性。这是由于在原型中查找值的过程是一次搜索,所有你修改的属性可以立即在实例中得到体现。但是重写一个函数的原型就不是这样了,看例子

var test = function test(){}
var o = new test();
o.getName();//TypeError: Object #<test> has no method 'getName'
test.prototype = {
constructor : test,
name : "hainan",
age : "25",
getName : function(){return this.name}
};

出现了错误,也就是我先创建了一个实例,在重写函数的原型对象这是不行的。原因是这样的,由于实例中的__proto__指针只是指向原型,而不是构造函数,上面的这段代码中,我们创建实例的时候,这个实例中的__proto__指向的是函数的默认原型对象,当我们重写了这个函数的原型对象时,虽然函数的prototype属性指向了新的对象,但是实例中的已经创建好了,它__proto__并没有改变,这个属性还是指向的是默认的原型对象,所有它的内部没有这个方法。但是如果在重写原型之后创建一个实例的话,这个新的实例的__proto__指向的就是新的原型对象了,像这样

var test = function test(){}
test.prototype = {
constructor : test,
name : "hainan",
age : "25",
getName : function(){return this.name}
};
var o = new test();
o.getName();//"hainan"

原生对象的原型

原生对象和我们自定义对象一样,都存在原型,原生对象的方法都存在原型中,这样我们创建一个新的对象实例时,这些对象就会拥有这些方法,当然我们可扩展原型对象,这样我们以后new出来的原型对象的实例就会共享这个方法或属性了。

console.log(String.prototype.slice);//function

我们扩展一个Array类型,增加一个函数contains(),判断数组中是否包含一个值

Array.prototype.contains = function(){
var length = arguments.length;
if(length != 1) return;
for(var i=0;l=this.length,i<l;++i ){
if(arguments[0] === this[i]) return true;
}
return false;
}
var arr = [1,2,3];
console.log(arr.contains(1));//true
console.log(arr.contains(4));//false

这就成了。

PS:给大家截一个图大家看看,不懂的可以在下面讨论下

小结

就先写到这吧,大家有不懂的可以在下面讨论吧,我不懂的话我再去问大神,大伙要是觉得写得乱的话推荐去看看《javascript高级程序设计》,那上面写得比较好。小伙伴们都要回家了吧,提前祝大家春节快乐,马上有钱,立马变土豪。

(转)【javascript基础】原型与原型链的更多相关文章

  1. javascript基础学习系列-原型链模式

    1.demo代码如下: 2.画图如下: 3.规则: 1)每一个函数数据类型(普通函数/类)都有一个天生自带的属性:prototype(原型),并且这个属性是一个对象数据类型的值 2)并且prototy ...

  2. 【javascript基础】4、原型与原型链

    前言 荒废了好几天,在宿舍闷了几天了,一直想着回家放松,什么也没搞,论文就让老师催吧.不过,闲的没事干的感觉真是不好,还是看看书,写写博客吧,今天和大家说说函数的原型. 原型是什么 第一次看到这个的时 ...

  3. JavaScript基础之原型对象和原型链

    原型对象 原型对象简单来说就是函数的原型所指向的对象.前面说原型的时候,说了Object.prototype所指对象就是Object(函数)的原型对象.在每个函数的原型对象中,默认会有construc ...

  4. 前端知识体系:JavaScript基础-原型和原型链-实现继承的几种方式以及他们的优缺点

    实现继承的几种方式以及他们的优缺点(参考文档1.参考文档2.参考文档3) 要搞懂JS继承,我们首先要理解原型链:每一个实例对象都有一个__proto__属性(隐式原型),在js内部用来查找原型链:每一 ...

  5. 《JavaScript 闯关记》之原型及原型链

    原型链是一种机制,指的是 JavaScript 每个对象都有一个内置的 __proto__ 属性指向创建它的构造函数的 prototype(原型)属性.原型链的作用是为了实现对象的继承,要理解原型链, ...

  6. JavaScript高级内容:原型链、继承、执行上下文、作用域链、闭包

    了解这些问题,我先一步步来看,先从基础说起,然后引出这些概念. 本文只用实例验证结果,并做简要说明,给大家增加些印象,因为单独一项拿出来都需要大篇幅讲解. 1.值类型 & 引用类型 funct ...

  7. JavaScript原型与原型链,原型的实际应用

    原型链是js面向对象的基础,非常重要. 一,创建对象的几种方法: 1,字面量 var o1 = { name:'o1' }; 2,构造函数 var M = function(name){ this.n ...

  8. 前端总结·基础篇·JS(一)原型、原型链、构造函数和字符串(String)

    前端总结系列 前端总结·基础篇·CSS(一)布局 前端总结·基础篇·CSS(二)视觉 前端总结·基础篇·CSS(三)补充 前端总结·基础篇·JS(一)原型.原型链.构造函数和字符串(String) 前 ...

  9. 林大妈的JavaScript基础知识(三):JavaScript编程(3)原型

    在一般的编程语言中,我们使用继承来复用代码,做成良好的数据结构.而在JavaScript中,我们使用原型来实现以上的需求.由于JavaScript专注于对象而摒弃了类,我们要明白原型和继承的确是有差异 ...

随机推荐

  1. git源站安装

    ##下载源站 wget https://www.kernel.org/pub/software/scm/git/git-2.15.0.tar.xz ##安装依赖组件 yum install curl- ...

  2. linux pwd命令查看当前路径命令

    命令简介: 该命令用来显示目前所在的工作目录.指令英文原义:print work directory执行权限 :All User指令所在路径:/usr/bin/pwd 或 /bin/pwd 命令语法: ...

  3. linux(centOs)下memcached安装

    1.libevent安装.为啥先安装它?因为不先装,memcached这座房子就没打地基: yum install libevent-devel 敲回车后出现: Loaded plugins: fas ...

  4. Python自然语言处理(1):初识NLP

    由于我们从美国回来就是想把医学数据和医学人工智能的事认真做起来,所以我们选择了比较扎实的解决方法,想快速出成果的请绕道.我们的一些解决方法是:1.整合公开的所有医学词典,尽可能包含更多的标准医学词汇: ...

  5. usbip install

    # README for usbip-utils## Copyright (C) 2011 matt mooney <mfm@muteddisk.com>#               2 ...

  6. mongodb与SQL常见语句对照

    inert into users value(3,5) db.users.insert({a:3,b:5})     select a,b from users db.users.find({}, { ...

  7. java普通类、抽象类、接口、面向对象理解

    1.面向对象编程: 面向对象编程的语言不止java一种,还有: simula 67(第一个面向对象语言,支持单继承和一定含义的多态和部分动态绑定): Smalltalk(第一个支持动态类型的语言,支持 ...

  8. 二维码名片的格式 - vcard(非常好,可直接添加到手机通讯录)

    分享到 一键分享 QQ空间 新浪微博 百度云收藏 人人网 腾讯微博 百度相册 开心网 腾讯朋友 百度贴吧 豆瓣网 搜狐微博 百度新首页 QQ好友 和讯微博 更多... 百度分享   登录|注册     ...

  9. Python新利器之pipenv(转)

    pipenv 都包含什么? pipenv 是 Pipfile 主要倡导者.requests 作者 Kenneth Reitz 写的一个命令行工具,主要包含了Pipfile.pip.click.requ ...

  10. DB2 数据类型转换

    db2数据类型转换: 1.CAST() CAST (<expression> AS <data_ type>[ length ]) 2.CONVERT() CONVERT (& ...