构造函数、原型对象prototype、实例、隐式原型__proto__的理解
(欢迎一起探讨,如果有什么地方写的不准确或是不正确也欢迎大家指出来~)
PS:
- 建议将构造函数中的方法都定义到构造函数的原型中,那么由该构造函数创建的实例的方法都会指向同一个方法。(在构造函数内部声明的话,每创建一个实例都会重新实例化函数,那么每个实例中的函数的指向是不同的;如果定义在全局作用域中,且有很多方法,这样毫无封装性可言。)
一、属性和方法
构造函数可以定义三种属性和方法:
对象属性/对象方法:构造函数内定义的属性/方法
静态属性/静态方法:构造函数的静态属性/方法
原型属性/原型方法:构造函数的原型属性/方法
实例:
function Shape() {
// 对象属性和对象方法
this.color = 'red';
this.show = function(){
console.log('show')
}
}
// 静态属性和静态方法
Shape.type = 'auto'
Shape.printing = function(){
console.log('Shape')
}
// 原型属性和原型方法
Shape.prototype.area = 'unknow'
Shape.prototype.move = function() {
console.info('move');
};
// 实例
var rect = new Shape();
console.log(rect.color) // red
console.log(rect.show()) // show
console.log(rect.area) // unknow
console.log(rect.move()) // move
console.log(Shape.type) //auto
console.log(Shape.printing()) //Shape
console.log(rect.type) // undefined
console.log(rect.printing) // undefined
构造函数的对象属性和对象方法、原型属性和原型方法,对应的实例都可以调用;
构造函数的静态属性和静态方法,对应的实例获取不到,会提示undefined,但是可由构造函数本身调用。
调用属性、方法 | 对象属性和对象方法 | 原型属性和原型方法 | 静态属性和静态方法 |
---|---|---|---|
构造函数 | undefined | undefined | 可以得到 |
实例 | 可以得到 | 可以得到 | undefined |
二、prototype、constructor、__proto__
在以上的基础上,再实例化一个新的对象和普通函数。
var circle = new Shape();
function test(){
console.log('test')
}
console.log(Shape.prototype) // {area: "unknow", move: ƒ, constructor: ƒ}
console.log(Shape.constructor) // ƒ Function() { [native code] }
console.log(Shape.__proto__) // ƒ () { [native code] }
console.log(Shape.prototype.constructor) // ƒ Shape() { ... }
console.log(Shape.prototype.constructor === Shape) // true 构造函数的显式原型的构造器指向构造函数
// Shape的constructor(构造器)指向Function,__proto__(隐式原型)指向Function的原型ƒ () { [native code] }。
// 检测
console.log(Shape.__proto__ === Function.prototype) // true
console.log(rect.prototype) // undefined
console.log(rect.constructor) // ƒ Shape() { ... }
console.log(rect.__proto__) // {area: "unknow", move: ƒ, constructor: ƒ}
console.log(circle.prototype) // undefined
console.log(circle.constructor) // ƒ Shape() { ... }
console.log(circle.__proto__) // {area: "unknow", move: ƒ, constructor: ƒ}
// 综上可以知道,
// 实例的原型均为undefined
// 实例(rect、circle)的隐式原型__proto__指向构造函数(Shape)的显式原型prototype
// 实例(rect、circle)的构造器constructor指向构造其的构造函数(Shape),代表是Shape的实例
// 而构造函数(Shape)是函数Function的实例,所以它的构造器是函数Function
// 那么构造函数(Shape)的隐式原型__proto__指向的是函数Function的显式原型prototype
console.log(test.prototype) // {constructor: ƒ} test的原型指向一个包含construct和__proto__的对象
console.log(test.constructor) // ƒ Function() { [native code] }
console.log(test.__proto__) // ƒ () { [native code] }
// 那么同理,普通函数的构造器指向Function;普通函数的隐式原型__ptoto__指向Function的显式原型prototype
console.log(test.__proto__ === Function.prototype) // true
所以,
指向 | prototype显式原型 | constructor构造器 | __proto__ 隐式原型 |
prototype.constructor(显式原型的构造器) |
---|---|---|---|---|
构造函数 | 构造函数的原型 | Function | ƒ () { [native code] } | 其本身 |
实例 | undefined | 构造其本身的构造函数 | 构造其本身的构造函数的原型 | Cannot read property 'constructor' of undefined |
普通函数 | 一个包含construct和__proto__ 的对象 |
Function | ƒ () { [native code] } | 其本身 |
综上可以知道
实例的构造器constructor都指向构造其本身的构造函数。
实例的隐式原型
__proto__
,均指向构造其本身的构造函数的显式原型prototype。构造函数也是函数,它的隐式原型
__proto__
指向Function的显式原型。
验证一下。
console.log(Shape.__proto__ === Shape.constructor.prototype) // true
console.log(Shape.__proto__ === Function.prototype) // true
console.log(rect.__proto__ === rect.constructor.prototype) // true
console.log(rect.__proto__ === Shape.prototype) // true
console.log(circle.__proto__ === circle.constructor.prototype) // true
console.log(circle.__proto__ === Shape.prototype) // true
console.log(test.__proto__ === test.constructor.prototype) // true
console.log(test.__proto__ === Function.prototype) // true
可能会有人觉得实例也是函数,所以会有疑问:实例的原型为什么是undefined
来验证一下
// test的原型和rect、circle的原型可能会有疑问
console.log(typeof test) // "function"
console.log(typeof rect + '---' + typeof circle) // "object---object"
每个函数都有prototype属性。
实例都有一个constructor(构造函数)属性,该属性指向构造它的构造函数。
调用构造函数创建一个新实例后,该实例的内部将包含一个指针(内部属性),指向构造函数的原型对象。即__proto__
属性。
默认情况下,所有原型对象prototype都会自动获得一个constructor属性,这个属性包含一个指向原型对象prototype属性的函数的指针。如:Shape.prototype.constructor === Shape
构造函数的原型对象prototype就是通过调用构造函数而创建的那个对象实例的隐式原型。
使用原型对象的好处是可以让所有对象实例共享它所包含的属性和方法。(重点在 共享 )
PS:
几个方法:
显式原型相关:
可以通过 hasOwnProperty 判断一个对象是否包含自定义属性而不是原型链上的属性。
in 操作符会在通过对象能够访问给定属性时返回true,无论该属性存在于实例还是原型中。
同时使用 hasOwnProperty 和 in 操作符,就可以确定该属性到底是存在于对象中,还是存在于原型中。
// hasOwnProperty
console.log(rect.hasOwnProperty('color')) // true
console.log(rect.hasOwnProperty('area')) // false
// in操作符
console.log('color' in rect); // true
console.log('area' in rect); // true
隐式原型相关:
可以通过 isPrototypeOf 来确定对象之间的关系(__proto__
)
可以通过 Object.getPrototypeOf() 得到__proto__
的值。
// isPrototypeOf
console.log(Shape.prototype.isPrototypeOf(rect)) // true
console.log(Shape.prototype.isPrototypeOf(test)) // false
// Object.getPrototypeOf()
console.log(Object.getPrototypeOf(rect) === Shape.prototype); // true
console.log(Object.getPrototypeOf(test) === Shape.prototype); // false
原型链:
通过原型添加的属性和方法,实例中虽然不包含这些属性和方法,但是却可以使用在构造函数的原型中定义的属性和方法。
实例通过隐式原型__proto__
去该构造函数的原型中去找属性和方法,如果没有,该构造函数的原型通过它的隐式原型__proto__
去找构造它的构造函数的原型中去找,以此类推。这个通过__proto__
属性连接起来的链条就是原型链。
原型链最终得到的值是null;Object的原型对象是原型链的尽头。
// 隐式原型
console.log(circle.__proto__ === Shape.prototype) // true
console.log(circle.__proto__.__proto__ === Object.prototype) // true
console.log(circle.__proto__.__proto__.__proto__) // null
/*
* 以上代码可以理解为
* circle.__proto__ === Shape.prototype
* Shape.prototype.__proto__ === Object.prototype
* Object.prototype.__proto__ === null
*/
额外:
那么Function呢?
console.log(Function.constructor === Function) // true
console.log(Function.constructor === Object) // false
// 根据以上输出,可以认为它的__proto__就是它的prototype
console.log(Function.__proto__ === Function.prototype) // true 这句说明Function是Function的实例对象
另外,Function和Object是什么关系呢?
console.log(Object.constructor === Function) // true
console.log(Object.constructor === Object) // false
console.log(Object.__proto__ === Function.prototype) // true 这句说明Object是Function的实例对象
// 那么Object也是函数
另外,instanceof的例子
// =====instanceof 用来检测 构造函数的 prototype 属性是否出现在某个实例对象的原型链上
console.log(Object instanceof Function) // true
// 因为Object.__proto__ === Function.prototype
console.log(Object instanceof Object) // true
// 因为Object.__proto__ === Function.prototype,Function.prototype.__proto__ === Object.prototype
console.log(Function instanceof Function) // true
// 因为 Function.__proto__ === Function.prototype
console.log(Function instanceof Object) // true
// 因为 Function.__proto__ === Function.prototype,Function.prototype.__proto__ === Object.prototype
参考:
https://segmentfault.com/q/1010000000249140
构造函数、原型对象prototype、实例、隐式原型__proto__的理解的更多相关文章
- 275 原型与原型链:显式原型prototype ,隐式原型__proto__,隐式原型链,原型链_属性问题,给原型对象添加属性/方法
1.所有函数都有一个特别的属性 prototype : 显式原型属性 [普通构造函数的实例对象没有prototype 属性,构造函数有__proto__属性,原型对象有__proto__属性 ] 2. ...
- JS 原型链 prototypt 和隐式原型 _proto_
prototype(原型) : 对象的一个属性,此属性使您有能力向对象添加属性和方法,当访问对象不存在属性是会自动到 prototype 中找 _proto_(隐式原型): 此对象构造函数(类)的原 ...
- JavaScript 隐式原型(_proto_)与显示原型(prototype)
作者:苏墨橘链接:https://www.zhihu.com/question/34183746/answer/59043879来源:知乎著作权归作者所有.商业转载请联系作者获得授权,非商业转载请注明 ...
- [js高手之路]原型对象(prototype)与原型链相关属性与方法详解
一,instanceof: instanceof检测左侧的__proto__原型链上,是否存在右侧的prototype原型. 我在之前的两篇文章 [js高手之路]构造函数的基本特性与优缺点 [js高手 ...
- JS高阶---显式原型和隐式原型
前言: 1.函数对象即函数的prototype原型属性指向原型对象,在创建函数时便存在,默认为空Object 2.实例对象的__proto__隐式原型在实例化创建实例时产生,值等于构造函数的显式pro ...
- js高级——构造函数,实例对象和原型对象——prototype、__proto__和constructor构造器
一.前言 了解JavaScript面向对象,需要先了解三个名词: 构造函数,实例对象和原型对象. 注意:JavaScript中没有类(class)的概念,取而代之的是构造函数,两者类似却又有很大的差别 ...
- Javascript 构造函数、原型对象、实例之间的关系
# Javascript 构造函数.原型对象.实例之间的关系 # 创建对象的方式 # 1.new object() 缺点:创建多个对象困难 var hero = new Object(); // 空对 ...
- [js高手之路]使用原型对象(prototype)需要注意的地方
我们先来一个简单的构造函数+原型对象的小程序 function CreateObj( uName, uAge ) { this.userName = uName; this.userAge = uAg ...
- 原型链 | 显示原型、隐式原型 | 构造关系constructor | instanceof
1.原型关系 prototype 显式原型:prototype 隐式原型:__proto__ 1. 每个函数function都有一个prototype,即显式原型(属性)2. 每个实例对象都有一个__ ...
随机推荐
- 【题解】HNOI2004敲砖块
题目传送门:洛谷1437 决定要养成随手记录做过的题目的好习惯呀- 这道题目乍看起来和数字三角形有一点像,但是仔细分析就会发现,因为选定一个数所需要的条件和另一个数所需要的条件会有重复的部分,所以状态 ...
- HDOJ(HDU).2159 FATE (DP 带个数限制的完全背包)
HDOJ(HDU).2159 FATE (DP 带个数限制的完全背包) 题意分析 与普通的完全背包大同小异,区别就在于多了一个个数限制,那么在普通的完全背包的基础上,增加一维,表示个数.同时for循环 ...
- 2017-7-19-每日博客-关于Linux下的CentOS中文件夹基本操作命令.doc
CentOS中文件夹基本操作命令 文件(夹)查看类命令 ls--显示指定目录下内容 说明:ls 显示结果以不同的颜色来区分文件类别.蓝色代表目录,灰色代表普通文件,绿色代表可执行文件,红色代表压缩文件 ...
- 微服务学习一:idea中springboot集成mybatis
一直都想学习微服务,这段时间在琢磨这块的内容,个人之前使用eclipse,现在用intellij idea来进行微服务的开发,个人感觉intellij idea比eclipse更简洁更方便,因为int ...
- JQuery学习一
第一个JQuery程序 <!DOCTYPE html> <html xmlns="http://www.w3.org/1999/xhtml"> <he ...
- 论C++11 中vector的N种遍历方法
随着C++11标准的出现,C++标准添加了许多有用的特性,C++代码的写法也有比较多的变化. vector是经常要使用到的std组件,对于vector的遍历,本文罗列了若干种写法. (注:本文中代码为 ...
- 如何更有效使用 Rational AppScan 扫描大型网站,第 1 部分: 工作原理及技术分析
Rational AppScan 工作原理 Rational AppScan(简称 AppScan)其实是一个产品家族,包括众多的应用安全扫描产品,从开发阶段的源代码扫描的 AppScan sourc ...
- 社会网络分析——Social Network Analysis
什么是社会网络分析,英文social network analysis.现在这个分析越来越时髦,也越来越显现其在社会科学的研究价值.我在2000年的时候受祝建华老师的邀请到香港城市大学作研究,接触到 ...
- 【20151105noip膜你赛】bzoj3652 bzoj3653
题目仿佛在讽刺我... 第一题: 题解: 考虑枚举区间右端点,维护所以左到当前的 and 和 or .注意 and 每次变化至少有一个二进制位从1变 0,or 每次至少有一个位从0变 1,所以最多有l ...
- 【BZOJ4884】太空猫 [DP]
太空猫 Time Limit: 1 Sec Memory Limit: 256 MB[Submit][Status][Discuss] Description 太空猫(SpaceCat)是一款画面精 ...