JS中原型与原型链
一. 普通对象与函数对象
JavaScript 中,万物皆对象!但对象也是有区别的。分为普通对象和函数对象,Object 、Function等 是 JS 自带的函数对象。下面举例说明。
- var o1 = {};
- var o2 =new Object();
- var o3 = new f1();
- function f1(){};
- var f2 = function(){};
- var f3 = new Function('str','console.log(str)');
- console.log(typeof Object); //function
- console.log(typeof Function); //function
- console.log(typeof f1); //function
- console.log(typeof f2); //function
- console.log(typeof f3); //function
- console.log(typeof o1); //object
- console.log(typeof o2); //object
- console.log(typeof o3); //object
二. 原型对象
- function Person(){}
- Person.prototype.name = 'test';
- Person.prototype.age = 28;
- Person.prototype.job = 'Software Engineer';
- Person.prototype.sayName = function() {
- alert(this.name);
- }
var person = new Person();
在 JavaScript 中,每当定义一个对象(函数也是对象)时候,对象中都会包含一些预定义的属性。其中每个函数对象都有一个prototype属性,这个属性指向函数的原型对象。而原型对象他本身就是一个普通对象(没有通过 new Function() 创建的对象都是普通对象),即原型对象就是Person.prototype,如果你还是害怕它,那就把它想想成一个字母 A:var A = Person.prototype。这里要强调一点,只有函数对象才会拥有prototype属性,但是每个对象都拥有__proto__属性(null除外)。
在上面我们给A添加了四个属性:name、age、job、sayName。其实它还有一个默认的属性:constructor。在默认情况下,所有的原型对象都会自动获得一个 constructor(构造函数)属性,这个属性(是一个指针)指向 prototype属性所在的函数(Person)。即:Person.prototype.constructor == Person。当我们创建对象var person = new Person()时,person可以继承原型对象Person.prototype的constructor属性,因此person.constructor == Person,注意person这个实例本身是没有constructor,实例的constructor是通过原型链(__proto__)获取原型对象上边的constructor。
- person.constructor == Person
- Person.prototype.constructor == Person
从这一角度,我们可以将Person.prototype理解为Person的一个实例。(但其实原型对象(Person.prototype)并不是构造函数(Person)的实例,而是构造函数的属性,而且是预定义添加的)。
- var A = new Person();
- Person.prototype = A;
但是有一个非常特别的原型对象:Function.prototype,它并不是普通对象,而是函数对象,而这个函数对象却没有prototype属性(前面所说的“每个函数对象都有一个prototype属性,这个属性指向函数的原型对象”,对Function.prototype并不适用)。
- function Person(){};
- console.log(typeof Person.prototype) //Object
- console.log(typeof Function.prototype) // Function,这个特殊
- console.log(typeof Object.prototype) // Object
- console.log(typeof Function.prototype.prototype) //undefined
Function.prototype为什么是函数对象呢?
- var A = new Function();
- Function.prototype = A;
上文提到过凡是通过凡是通过 new Function() 创建的对象都是函数对象,其他的都是普通对象。因为 A 是函数对象,所以Function.prototype是函数对象。
三. __proto__
JS 在创建对象(不论是普通对象还是函数对象)的时候,都有一个叫做__proto__的内置属性,用于指向创建它的构造函数的原型对象。对象 person有一个__proto__属性,创建它的构造函数是Person,构造函数的原型对象是Person.prototype ,所以:person.__proto__ == Person.prototype
- Person.prototype.constructor == Person;
- person.__proto__ == Person.prototype;
- person.constructor == Person;
类似的,Person.__proto__ == Function.prototype;Person.prototype.__proto__ == Object.prototype;Object.__proto__ == Function.prototype; Object.prototype.__proto__ == null,按照上述理解 Object.prototype是普通对象,而普通对象的构造函数是Object,那么Object.prototype.__proto__ == Object.prototype,从而在原型链上形成死循环无法终止,因此定义Object.prototype.__proto__ == null,null是原型链的顶端。
不过,要明确的真正重要的一点就是,这个连接存在于实例(person)与构造函数(Person)的原型对象(Person.prototype)之间,而不是存在于实例(person)与构造函数(Person)之间。
- var animal = function(){};
- var dog = function(){};
- animal.price = 2000;
- dog.prototype = animal;
- var tidy = new dog();
- console.log(dog.price) //undefined
- console.log(tidy.price) // 2000
实例(tidy)和 原型对象(dog.prototype)存在一个连接。这个连接存在于实例(tidy)与构造函数的原型对象(dog.prototype)之间,而不是存在于实例(tidy)与构造函数(dog)之间。
四. 函数对象
所有函数对象的__proto__都是指向Function.prototype,它是一个空函数。
- Number.__proto__ === Function.prototype // true
- Number.constructor == Function //true
- Boolean.__proto__ === Function.prototype // true
- Boolean.constructor == Function //true
- String.__proto__ === Function.prototype // true
- String.constructor == Function //true
- Object.__proto__ === Function.prototype // true
- Object.constructor == Function // true
- // 所有的构造器都来自于Function.prototype,甚至包括根构造器Object及Function自身
- Function.__proto__ === Function.prototype // true
- Function.constructor == Function //true
- Array.__proto__ === Function.prototype // true
- Array.constructor == Function //true
- RegExp.__proto__ === Function.prototype // true
- RegExp.constructor == Function //true
- Error.__proto__ === Function.prototype // true
- Error.constructor == Function //true
- Date.__proto__ === Function.prototype // true
- Date.constructor == Function //true
所有的构造器都来自于Function.prototype,甚至包括根构造器Object及Function自身。所有构造器都继承了Function.prototype的属性及方法。Function.__proto__ == Function.prototype,而前面说过,Function.prototype它不是普通对象,而是函数对象,那么Function.prototype.__proto__ == ?,按照上述,Function.prototype.__proto__ == Function.prototype,但又出现了原型链上的死循环,JS一直强调万物皆对象,函数对象也是对象,给他认个祖宗,指向 Object.prototype,Object.prototype.__proto__ == null,保证原型链能够正常结束。
- Function.prototype.__proto__ === Object.prototype // true
特别的,Math,JSON是以普通对象形式存在的。
Math.__proto__ === Object.prototype // true- Math.construrctor == Object // true
- JSON.__proto__ === Object.prototype // true
- JSON.construrctor == Object //true
四. 总结
原型和原型链是JS实现继承的一种模型。
原型链的形成是真正是靠__proto__而非prototype。
参考资料:https://www.jianshu.com/p/dee9f8b14771
JS中原型与原型链的更多相关文章
- 面试题常考&必考之--js中的难点!!!原型链,原型(__proto__),原型对象(prototype)结合例子更易懂
1>首先,我们先将函数对象认识清楚: 补充snow的另一种写法: var snow =function(){}; 2>其次:就是原型对象 每当我们定义一个函数对象的时候,这个对象中就会包含 ...
- js中的prototype原型解析
在典型的面向对象的语言中,如java,都存在类(class)的概念,类就是对象的模板,对象就是类的实例.但是在Javascript语言体系中,是不存在类(Class)的概念的,javascript中不 ...
- js中函数的原型
js中每一个构造函数都有一个prototype的属性,prototype指向一个对象,而这个对象的属性和方法都会被构造函数的实例所继承,因此,需要一些共享的属性和方法可以写在构造函数的原型中 1 用 ...
- js中构造函数的原型添加成员的两种方式
首先,js中给原型对象添加属性和方法. 方式一:对象的动态特效 给原型对象添加成员 语法:构造函数.prototype.方法名=function (){ } 方式二:替换原型对象(不是覆盖,而是替换, ...
- js中使用使用原型(prototype)定义方法的好处
经常在前端面试或是和其他同行沟通是,在谈到构造在JS定义构造函数的方法是最好使用原型的方式:将方法定义到构造方法的prototype上,这样的好处是,通过该构造函数生成的实例所拥有的方法都是指向一个函 ...
- js中__proto__(内部原型)和prototype(构造器原型)的关系
一.所有构造器/函数的__proto__都指向Function.prototype,它是一个空函数(Empty function) Number.__proto__ === Function.prot ...
- JS中构造函数与原型对象的同名属性,实例会取哪一个
构造函数与原型对象的同名属性,实例会取哪一个? 看了下面的过程,再回忆JS高程3里关于这部分的示意图.实例my在new的时候,本身就获得了a属性,所以my.a是1,倘若在new的时候如果没有赋予a属性 ...
- JS中定义对象原型的两种使用方法
第一种: function Person() { this.username = new Array(); this.password = "123"; } Person.prot ...
- JS中作用域和作用域链
1.执行环境(execution context) 执行环境定义了变量和函数有权访问的其他数据,决定了他们各自的行为.每个执行环境都有与之对应的变量对象(variable object),保存着该环境 ...
- JS原型和原型链
1 var decimalDigits = 2, 2 tax = 5; 3 4 function add(x, y) { 5 return x + y; 6 } 7 8 function su ...
随机推荐
- Qt 入门 ---- 布局管理
这是运行后的程序界面: 这是点击右上角"最大化"之后的程序界面: 接下来讲一下如何进行自动布局解决窗口拉伸问题. ① 原理: 在项目"设计"模式的左侧有如下两个 ...
- Java键盘获取数据
java录入键盘数据,整型.浮点型.布尔型.字符串. 通过导入java.util.Scanner实现各类操作 import java.util.Scanner;//导入包 public class H ...
- Pb代理工具之mitmproxy
mitmproxy 一 . mitmproxy介绍 mitmproxy 就是用于 MITM 的 proxy,MITM 即中间人攻击(Man-in-the-middle attack). 不同于 fid ...
- ts 学习笔记 - 类
目录 类 类的概念 类的用法 属性和方法 类的继承 存取器 静态属性 Typescript 中的用法 抽象类 类的类型 类与接口 类实现接口 接口继承接口 接口继承类 混合类型 类 类的概念 类 (c ...
- SQL SERVER 作业问题(SET 选项的设置不正确: 'QUOTED_IDENTIFIER'。),以及其它定时sql执行方式探索
在实时曲线测试平台中,需要用到实时测试数据作为依据,评估程序的可靠性.在编写sql server作业时,出现了一些问题,经过研究给予解决,供大家参考. 1.编写脚本如下: declare @i int ...
- Thinkphp 分页应用
$Table = M('Table'); $count = $Table ->where()->count(); $Page = new \Think\Page($count ...
- Django GIS SQL注入漏洞(CVE-2020-9402)
影响版本 Django 1.11.29之前的1.11.x版本.2.2.11之前的2.2.x版本和3.0.4之前的3.0.x版本中存在SQL注入漏洞 提示有admin.vuln.vuln2,3个页面,存 ...
- Linux--文件描述符、文件指针、索引节点
Linux -- 文件描述符 文件描述符 Fd 当进程打开文件或创建新文件时,内核会返回一个文件描述符(非负整数),用来指向被打开的文件,所有执行I/O操作的系统调用(read.write)都会通过文 ...
- 并发编程——线程中sleep(),yield(),join(),wait(),notify(),notifyAll()区别
前言 今天简单的讲一讲线程中sleep(),join(),yield(),wait(),notify(),notifyAll()这些方法的使用以及区别. 不过在讲这些方法之前,需要简单的介绍一下锁池和 ...
- 【C++】使用 libass,完成 Direct3D 11 下的字幕渲染
前言 前段时间曾经写过一个视频播放器:https://www.cnblogs.com/judgeou/p/14746051.html . 然而这个播放器却无法显示出外挂或者内封的字幕,这里要稍微解释一 ...