一、首先来看下javascript组成:ECMAScirpt(核心)+BOM+DOMjavascript
二、如何理解javascript的面向对象?html
面向对象有三个基本特性:封装、继承、多态。其中,对象系统中的继承特性有三种实现方案:基于类的(class-based)、基于原型(prototype-based)的、基于元类(metaclass-based)的。Javascript 是面向对象语言,比 Java 还要完全的面向对象。在 Javascript没有使用常见的类继承体系,而是使用“构造器”来实现对象的类型化。构造器就是函数,惯例是首字母大写函数名来表明这是一个构造器,用 new
+构造器名字
来建立实例对象,在构造器(及其原型属性对象)内部,this
指代实例对象。Javascript采用“原型继承”而不是“类型继承”,经过对象的原型链进行属性(方法)的查找。java
三、从对象类型开始理解: git
3.一、JavaScript 是一门完全的面向对象的语言,所以有必要从面向对象的概念着手 , 探讨一下面向对象中的几个概念:github
a、一切事物皆对象web
b、对象具备封装和继承特性chrome
c、对象与对象之间使用消息通讯,各自存在信息如何隐藏 ,经过一种叫作 原型(prototype)的方式来实现面向对象编程的。编程
来看下对象类型系统数组
上图能够看出:javascript对象系统图解浏览器
a、javascript数据类型可分两大类:“值类型”和“引用类型” 。这两中类型可使用typeof方法加以区分
b、引用类型 即整个对象类型系统。大体分为原生对象+宿主对象+引擎扩展对象。其中原生对象包括内置对象和arguments,
c、对象类型:全文下来讨论的都是这两类对象----普通实例对象(object)和函数对象(function) (区分这两类对象 即:typeof 返回object /function)
注:本文主要讨论两种引用类型建立以及它们之间关系:对象(object/function)的建立以及的联系。
3.二、其中,ECMAScirpt定义数据六种基本类型:undefined boolean string number object function(引用类型),
3.三、理解两种引用类型之间关系 最重要的两点是:函数就是对象;对象是经过函数(即构造函数)建立的。
四、如何建立对象
上面说到两种引用类型之间的关系时谈到,函数就是对象,对象又是经过构造函数建立的。怎么感受那么绕呢,这个问题像是先有鸡仍是先有蛋的问题。这种问题能解释清楚吗?固然,但得一步步来吧,javascript中一切皆是对象,那就先从对象是如何建立的提及吧
建立对象的方法有两种,一种是使用new 操做符后跟Object 类型的构造函数 ,另外一种则是对象字面量方法(建立对象的一种快捷方式:简化包含大量属性的对象的建立过程,即语法糖)。
// 构造函数方法--建立 var obj = new Object();//< == > obj = {} obj.name = 'foo'; obj.method = function(){}; //对象字面量方法--建立 var obj = { name:'foo', method:function{console.log(this.name);} }
就这么简单,没那么简单!!实际这中间发生了什么?
对象是如何建立的, 即实例化一个对象的过程,都是经过 new 构造器来实现的。来看下下面这段代码
var name = 'tom'
var Person = function(name,age){ this.name = name; this.age = age; } Person.prototype.say = function(){
console.log(this.name);
} var p = new Person('jerry','10');
console.log(p);//
p.say();//jerry
console.log('一、全局window做用域下的变量name:'+name);//tom ---new Person 并无修改全局变量。
var p1 = Person('carry','12'); //若是把构造函数看成普通函数调用状况下,this 指向当前运行环境window。
console.log(p1);//undefined
//p1.say();
console.log('二、全局window做用域下的变量name:'+name);//carry ---全局对象被修改
a、var p ={};
b、p.__proto__ = Person.prototype;//原型链
c、Person.call(p,arg1,arg2);//初始化p对象
d、返回p对象
这种状况下,若是把构造函数看成普通函数调用状况下,有什么不一样呢?
a、this 指向window(便可能修改全局变量)
b、不建立对象也不返回对象。//返回默认undefined
本文接下来将重点讨论几种经过建立对象模式,这样咱们就可以更深入理解对象与函数之间的关系的,同时对比这几种模式的优缺点。
思考:基于Object构造函数,建立了一个对象,该对象包含两个属性,其中一个为方法。若是须要不少相似obj的实例,那就会有许多重复的代码。
所以建立对象引入新的模式,其中包括了几种经典的模式;工厂模式,构造函数模式,原型模式,混合构造函数/原型模式,动态原型模式,寄生构造函数模式等。
4.1 工厂模式
/*工厂模*/ function person(name,age){ var obj = new Object();//经过Object构造器建立实例对象。 obj.name = name; obj.age = age; obj.say = function(){ console.log(this.name); }; return obj; } var person1 = person('zhangsan','18'); var person2 = person('lisi','3');
console.log(instanceOf person1)//object
console.log(instanceof person2)//object
缺:a、每次调用person函数,都会经过该函数内部的对象obj建立新的对象,而后返回,除此以外,这个为了建立新对象而存在的内部对象obj没有其余的用途。
b、另外,没法判断工厂模式建立的对象的类型(即 没法经过instanceOf等判断区分,都为Object 对象的一个实例 )。
4.二、构造函数模式
var Car = function (model, year, miles) { this.model = model; this.year = year; this.miles = miles; this.run =function(){ console.log(this.miles); } }; var baoma = new Car("Tom", 2009, 20000); var benchi = new Car("Dudu", 2010, 5000); console.log(baoma.constructor == Car);//constructor位于构造函数原型中,并指向构造函数,结果为true console.log(baoma instanceof Car);//经过instanceof操做符,判断baoma是否为构造函数car的实例 console.log(baoma.run == benchi.run);//false
优: 对比工厂模式,能够发现,这里并不须要建立中间对象(obj),也没有返回值。另外,能够将构造函数的实例标识为一种特定的类型(instanceOf Car),这就解决了工厂模式建立对象识别的问题(经过检查实例的constructor属性,或利用instanceof操做符检查该实例是否经过某个构造函数建立)。
缺:不过经过构造函数建立仍然会有本身的问题,实际上,run方法在每一个实例上都会被从新建立一次,须要注意的是,经过new 构造函数,实例化建立的方法且并不相等,便可以经过比较baoma.run == benchi.run 获得 false 就能判断这两个方法并不相等。
next: 所以,虽然咱们能够考虑将方法移到构造器外部(变为全局函数)来解决这个问题。可是 在全局下建立的全局函数实际上只能被经由Person建立的实例调用,这就有点名存实亡了,控制使用权达不到仅限于当前对象的效果。
首先来理解下函数:ECMAScript 中,咱们声明的每一个函数都是Function构造器(后面统一:实际为构造函数)的一个实例,便可以理解为,咱们声明(不管是全局仍是局部)的每个函数都是new Function()建立出来的一个函数对象。
这里稍微跑下题:函数对象的有如下两种属性:
内部属性(便可以理解为函数执行过程当中能访问的局部变量) arguments this, arguments类数组包含属性(callee);
自身属性:length(函数但愿接收的参数个数)和prototype ,javascript中的每个函数(function)都包含一个指向prototype属性的指针。
(大部分浏览器中实例对象能够经过__proto__访问内部属性),prototype属性实际上也是一个对象,其中包含了由某种引用类型(Object/Function/自定义构造器)建立的全部实例共享的属性和方法。如toString() ,valueOf();
这里强调下:大部分对象是能够经过for in 来访问其属性的,不过,虽然prototype 是一个对象,但它是不可枚举的,即不能够经过for in 来遍历访问
此外还有两个非继承而来的方法:call()、apply():做用是在指定的做用域执行函数,即从新设置函数内部属性 上下文执行环境this,固然也可使用ECMAScript 5提供的bind 方法从新设置函数执行的上下文环境。
好回归正题^^
4.3.1 定义prototype属性
function Person() {} Person.name ='tom'; Person.prototype.friends = ['jerry','miqi','carry']; Person.prototype.logName = function() { console.log(this.name); } } var person1 = new Person(); person1.logName();//'tom' for(i in person1) {console.log(i);}
以上代码作了这几件事情:
1.定义了一个构造器Person,Person函数自动得到一个prototype属性,该属性默认只包含一个指向Person的constructor属性(固然大部分浏览器还会增长一个__proto__属性)。
来看看浏览器中运行是什么状况,卤煮随手在控制台打印一些代码并截图。从下图能够看到函数的prototype属性上包含constructor、__proto__这两个属性:constructor展开又有这两个属性,__proto__展开有一堆其它属性+constructor:一层层指向,可以往下一直展开无穷尽也。头大没有,反正卤煮是晕了。后面再详细讨论这些属性与对象和函数直接的关系和由来。
2.经过Person.prototype添加三个属性,其中一个做为方法;
3.建立一个Person的实例,随后在实例上调用了logName()方法。!!!这里须要注意的是logName()方法的调用过程:
a.在person1实例上查找logName()方法,发现没有这个方法,因而追溯到person1的原型prototype
b.在person1的原型上查找logame()方法,有这个方法,因而调用该方法 。所以基于这样一个查找过程,咱们也能够经过在实例上定义原型中的同名属性,来阻止该实例访问原型上的同名属性,须要注意的是,这样作并不会删除原型上的同名属性,仅仅是阻止实例访问。
c、接下来,利用for-in循环枚举出实例能够访问到的全部属性(不论该属性存在于实例或是原型中),注意:这与上面提到的prototype 不可遍历访问并不冲突。至于为何卤煮也不清楚^^。
好了,原型模式建立对象过程大概理解了,
提问:那怎么区分判断某个属性到底存在于实例上,仍是存在于原型中?
咱们能够看到__proto__列举出了对象Object构造器的原型上继承过来的方法:hasOwnProperty(),isPrototypeOf(),toString(),valueOf()等
答1: 这里咱们能够利用hasOwnProperty()方法只有当属性存在于实例中,才会返回true:
console.log(person1.hasOwnProperty('name'));//true
答2:另外判断属性是否存在与某个实例对象,也能够经过同时使用in操做符和hasOwnProperty()方法来判断某个属性存在于实例中仍是存在于原型中:
console.log(('friends' in person1) && !person1.hasOwnProperty('friends'));
//注释:先判断person1是否能够访问到friends属性,若是能够,再判断这个属性是否存在于实例当中(注意前面的!),若是不存在于实例中,就说明这个属性存在于原型中。
注意:hasOwnProperty来自Object的原型,是javascript中惟一一个在处理属性时不查找原型链的方法。
4.3.2 字面量方式(语法糖)重写prototype属性
前面提到,原型属性prototype也是对象,因此咱们能够用对象字面量表示法书写原型,以前为原型添加代码的写法能够修改成:
function Person(){//使用对象字面量 重写Person 的原型 prototype属性
}
Person.prototype = { name: 'tom', friends: ['jerry','miqi','carry'], logName: function() { console.log(this.name); } }
var person1 = new Person(); //person1.logName();//'tom'
//对象字面量重写原型以后
console.log(person1.constructor);//function Object(){[native code]}构造函数
console.log(person1.constructor == Object);//true
console.log(person1.constructor == Person);//false
console.log(person1 instanceof Person);//true
console.log(person1 instanceof Object);//true
因为对象字面量语法重写了整个prototype原型,原先经过建立构造函数(new Object())时默认取得的constructor属性会指向Object构造函数,
不过,instanceof操做符仍会返回但愿的结果:console.log(person1 instanceof Person);//true
所以,必要时能够在原型中手动设置constructor的值来解决这个问题。
Person.prototype = { constructor: Person, ...... }
从上面代码也能够看出全部对象都是Object的一个实例
经过原型模式建立也一样存在一些问题:若是在建立对象实例以后修改原型对象prototype某个属性或者重写整个原型对象,那么经过原型模式建立出来的对象在访问原型上方法时会出现什么样的结果呢?
4.3.3 修改原型对象某个属性
结论是:对原型的修改会当即在全部对象实例中反映出来:
var person1 = new Person(); Person.prototype.name = 'tidy';//修改原型name属性 console.log(person1.name);//'tidy'
实例和原型之间的链接仅仅是一个指针,而不是一个原型的拷贝。因此其实是在实例和原型上一次搜索过程,对原型对象的所作的任何修改都会在全部对象实例中反映出来,就算在建立实例以后修改原型,也是如此。
4.3.4 若是在建立对象实例以后重写原型对象,状况又会如何?
function Person() {}; var person1 = new Person1();//建立的实例引用的是最初的原型 //重写了原型 Person.prototype = { friends: ['小花','二狗子','二愣子'] } var person2 = new Person();//这个实例引用新的原型 console.log(person2.friends); console.log(person1.friends);
以上代码在执行到最后一行时会出现未定义错误,若是咱们用for-in循环枚举person1中的可访问属性时,会发现,里头空无一物,可是person2却能够访问到原型上的friends属性。 !重写原型切断了现有原型与以前建立的全部对象实例的联系,以前建立的对象实例的原型还在,只不过是旧的。
//建立person1时,原型对象还未被重写,所以,原型对象中的constructor仍是默认的Person() console.log(person1.constructor);//Person() //可是person2的constructor指向Object() console.log(person2.constructor);//Object()
须要注意的是,原型模式忽略了为构造函数传递参数的过程,全部的实例都取得相同的属性值。同时,原型模式还存在着一个很大的问题,就是原型对象中的引用类型值会被全部实例共享,对引用类型值的修改,也会反映到全部对象实例当中。
function Person() {}; Person.prototype = { friends:['小花','二狗子','二愣子'] }
var person1 = new Person();
var person2 = new Person(); person1.friends.push('傻妞'); console.log(person2.friends);//['小花','二狗子','二愣子','傻妞']
从上述代码能够看出:修改person1的引用类型值friends,意味着person2中的friends也会发生变化,实际上,原型中保存的friends实际上只是一个指向堆中friends值的指针(这个指针的长度是固定的,保存在栈中),实例经过原型访问引用类型值时,也是按指针访问,而不是访问各自实例上的副本(这样的副本并不存在)。
上面讨论了构造函数和原型模式建立对象优点和缺点,发现能够将构造函数和原型模式的优势结合起来,弥补各自的不足,利用构造函数传递初始化参数,在其中定义实例属性,利用原型定义公用方法和公共属性,该模式应用最为普遍。
function Person(name,age) { this.name = name; this.age = age; this.friends = ['ajiao','jianjian','pangzi']; } Person.prototype = constructor: Person, logName: function() { console.log(this.name); } } var person1 = new Person('evansdiy','22'); var person2 = new Person('amy','21'); person1.logName();//'evansdiy' person1.friends.push('haixao'); console.log(person2.friends.length);//3
原型动态模式将须要的全部信息都封装到构造函数中,经过if语句判断原型中的某个属性是否存在,若不存在(在第一次调用这个构造函数的时候),执行if语句内部的原型初始化代码。
function Person(name,age) { this.name = name; this.age = age; if(typeof this.logName != 'function') { Person.prototype.logName = function() { console.log(this.name); }; Person.prototype.logAge = function() { console.log(this.age); }; }; } var person1 = new Person('evansdiy','22');//初次调用构造函数,此时修改了原型 var person2 = new Person('amy','21');//此时logName()方法已经存在,不会再修改原型
须要注意的是,该模式不能使用对象字面量语法书写原型对象(这样会重写原型对象)。若重写原型,那么经过构造函数建立的第一实例能够访问的原型对象不会包含if语句中的原型对象属性。
function Person(name,age) { this.name = name; this.age = age; if(typeof this.logName != 'function') { Person.prototype = { logName: function() { console.log(this.name); }, logAge: function() { console.log(this.Age); } } }; } var person1 = new Person('evansdiy','22'); var person2 = new Person('amy','21'); person2.logName();//'amy' person1.logName();//logName()方法不存在
须要说明的是,各模式都有本身的应用场景,无所谓优劣。
五、如何区分这些对象?
上面讨论 对象如何建立的问题,接下来讨论的是如何区分这些对象(无论是object,仍是function),这些对象有什么本质性区别。(注:本文讨论仅限于原生对象)
思考一: 有几类对象?
思路:JavaScript规范里写着有三种对象: 原生对象, 内置对象(属于原生对象); 宿主对象(本文不具体讨论,这是运行环境(浏览器)有关)
上文提到:在 Javascript没有使用常见的类继承体系,而是使用“构造器”来实现对象的类型化。从对象类型系统能够看出,原生对象中包含13内置对象,9个构造器。
所以能够简单理解为:两类对象
一、函数function:普通函数/构造函数(构造器)——由Function构造器建立的实例函数对象。
2、普通实例对象object:---由自定义构造函数或内置构造器建立
思考二:有几类构造函数?
思路:根据对象分类都是由构造函数,大体可分为:自定义构造器,内置构造器(包括Function 和Object、)
思考三:这些对象都有什么属性,对象与构造器之间有什么联系,属性是如何访问/继承(原型链概念)过来的?
思路:先来理解三个概念:原型、内部初始化属性、构造属性
一、prototype,每个函数(构造器)都有一个显式的prototype属性,它表明了对象的原型(Function.prototype是个例外,没有prototype属性)。
__ptoto__
属性(IE浏览器不支持)是实例对象指向原型对象的一个指针,它的做用就是指向构造函数的原型属性prototype,经过这两个属性,就能够访问原型里的属性和方法了。
Javascript
中的对象实例本质上是由一系列的属性
组成的,在这些属性中,每一个实例对象都有一个的内部隐藏属性[[prototype]](浏览器经过__proto__把这个属性暴露出来了),指向于它所对应构造器的的原型对象(chrome、firefox中名称为__proto__,而且能够被访问到)。原型链正是基于__proto__才得以造成。
三、constructor: 返回建立此对象的构造函数的引用,即指向对象的构造函数
思考四:__proto__ 是什么?
每一个实例对象都会在其内部初始化一个属性[[prototype]],大部分浏览器器就是__proto__来实现,当咱们访问一个对象的属性时,若是这个对象内部不存在这个属性,那么他就会去__proto__里找这个属性,这个__proto__又会有本身的__proto__,因而就这样 一直找下去,也就是咱们平时所说的原型链的概念。不过值得注意的是:按照标准,__proto__是不对外公开的,也就是说是个私有属性。不过,大部分浏览器都对外暴露了这个属性。
思考结果:
a、“一切(引用类型)皆为对象”
b、 “每一个函数(function)都有一个prototype”
c、 “每一个对象(object)都有一个__proto__”
5.一、函数/构造函数 —— 函数也是一个对象。
先区分下普通函数与构造函数:与普通函数相比,构造函数有如下特色(不成文默认使用规范)
a.用new关键字调用
b.函数内部可使用this关键字
c.默认不用return返回值
d.函数命名建议首字母大写,与普通函数区分开。
固然,构造函数也能够当作普通函数来使用,不过内部使用this 就跟当前运行上下文绑定到一块儿了
5.1.1 全部自定义构造器/函数实际上也是一个对象,这里比较特殊的是它的__proto__都指向Function.prototype 。
JavaScript中有内置(build-in)构造器/对象共计13个(ES5中新加了JSON,不包括null),这里列举了可访问的9个构造器(可以使用new 建立实例对象)。剩下如Global不能直接访问,Arguments仅在函数调用时由JS引擎建立,Math,JSON是以对象形式存在的,无需new。它们的__proto__是Object.prototype。以下
Number.__proto__ === Function.prototype //true Boolean.__proto__ === Function.prototype //true String.__proto__ === Function.prototype //true Object.__proto__ === Function.prototype//true Function.__proto__ === Function.prototype //true Array.__proto__ === Function.prototype // true RegExp.__proto__ === Function.prototype // true Error.__proto__ === Function.prototype // true Date.__proto__ === Function.prototype // true //普通函数对象+自定义构造函数 var func1 = function(){console.log()} function Person(name){ this.name = name; this.logName = function(){}; } console.log(func1.__proto__ === Function.prototype);//true console.log(func2.__proto__ === Function.prototype);//true //结论全部自定义构造器/函数和内置构造器实际上也是一个实例对象(构造函数Function 的实例对象) //它内部初始化属性__proto__都指向Function.prototype (function(){console.log(arguments.__proto__ == Object.prototype)})() //true Math.__proto__ === Object.prototype // true JSON.__proto__ === Object.prototype // true
从上面例子能够看出什么?全部的函数/构造函数(构造器)都来自于Function.prototype,甚至包括根构造器Object及Function自身(通俗的讲,函数/构造函数都是构造器Function的一个实例)。所以,全部构造器都继承了Function.prototype的属性及方法。如length、call、apply、bind(ES5)等属性或方法。
那咱们可能会产生疑问,全部函数/构造器都来自于 Function.prototype ,prototype 自己也是一个不可枚举对象(全部对象都有一个隐藏内部属性,指向其对应构造函数的原型),它又是由那个构造器建立的?
console.log(Function.prototype.__proto__ === Object.prototype) // true
一目了然,原来是来自于Object.prototype, 好吧,追根朔源,Object.prototype的__proto__。
Object.prototype.__proto__ === null // true
好吧到顶了,无法再继续了》》
var date = new Date(); var error = new Error(); var person = new Person(); console.log(date.__proto__ === Date.prototype);//true console.log(error.__proto__ === Error.prototype);//true console.log(person.__proto__ === Person.prototype);//true
5.2 全部对象(包括函数/构造函数(function)和普通实例对象(object))。
注意:每一个实例对象都又有一个constructor属性,能够获取它的构造器。即我以看下下面这段代码
//函数对象/构造函数-- Number.constructor === Function // true Boolean.constructor === Function// true String.constructor === Function// true Object.constructor === Function// true Function.constructor === Function// true Array.constructor === Function// true RegExp.constructor === Function// true Error.constructor === Function// true Date.constructor === Function// true Function.constructor=== Function //true 即: Function.constructor === Function //实例对象 var num = new Number(2); var date = new Date(); var str = new String(); var obj = new Object(); var func1 = new Function(); var error = new Error(); var arr = new Array(); var rep = new RegExp(); var person = new Person(); num.constructor. __proto__ === Function.prototype // true date.constructor. __proto__ === Function.prototype // true str.constructor. __proto__ === Function.prototype // true obj.constructor. __proto__ === Function.prototype // true error.constructor. __proto__ === Function.prototype // true func1.constructor. __proto__ === Function.prototype // true arr.constructor. __proto__ === Function.prototype // true rep.constructor. __proto__ === Function.prototype // true person.constructor. __proto__ === Function.prototype;
从而能够得出结论:
person.__proto__ === Perosion.protoype === person.constructor.prototype 即 指向同一个对象
上面提到过:用过字面量方式重写原型对象prototype的值会修改prototype的执行为Object
function Person(name) { this.name = name } // 重写原型 Person.prototype = { getName: function() {} } var p = new Person('jack') console.log(p.__proto__ === Person.prototype) // true console.log(p.__proto__ === p.constructor.prototype) // false //等价于 对象直接量{getName: function(){}}与根构造器 Object.protype 的空对象 {}比较 console.log(Object === p.constructor.prototype) // true 是由于 重写原型以后修改了constructor属性 // 建议重写原型对象默认增长constructor属性,修正原型指向 Person.prototype = { constructor:Person, getName: function() {} }
注意:__proto__目前在IE6/7/8/9中都不支持。
六、内存图解prototype、__proto__、constructor与Object、Function
这样将帮助咱们更加容易理解原型继承原理,及原型链查找属性方法的过程。很少说,上图上代码:
6.1 关于上面提到的函数对象,咱们来看如下例子,来讲明:
var o1 = {}; var o2 =new Object(); function f1(){} var f2 = function(){} var f3 = new Function('str','console.log(str)'); f3('aabb'); // aabb typeof Object //function typeof Function //function typeof o1 //object typeof o2 //object typeof f1//function typeof f2//function typeof f3 //function
function Animal(){ } var anim = new Animal(); console.log('***********Animal anim proto*****************'); typeof Animal.prototype //object anim.__proto__===Animal.prototype //true Animal.__proto__===Function.prototype)); //true Animal.prototype.__proto__===Object.prototype)); //true console.log('***********Function proto*****************'); typeof Function.prototype //function typeof Function.__proto__ //function typeof Function.prototype.prototype //undefined typeof Function.prototype.__proto__ //object Function.prototype===Function.__proto__ //true console.log('***********Object proto*****************'); typeof Object.prototype) //object typeof Object.__proto__ //function Object.prototype.prototype //undefied Object.prototype.__proto__===null; //null console.log('***********Function Object proto关系*****************'); Function.prototype===Object.__proto__; //true Function.__proto__===Object.__proto__; //true Function.prototype.__proto__===Object.prototype; //true
console.log('**************constructor****************'); console.log('anim.constructor===Animal:'+(anim.constructor===Animal)) ; //true console.log('Animal===Animal.prototype.constructor:'+(Animal===Animal.prototype.constructor)) ; //true console.log('Animal.constructor===Function.prototype.constructor:'+(Animal.constructor===Function.prototype.constructor)); //true console.log('Function.prototype.constructor===Function:'+(Function.prototype.constructor===Function)); //true console.log('Function.constructor===Function.prototype.constructor:'+(Function.constructor===Function.prototype.constructor)); //true console.log('Object.prototype.constructor===Object:'+(Object.prototype.constructor===Object)); //true console.log('Object.constructor====Function:'+(Object.constructor===Function)); //true
七、函数/构造器从Function 继承什么属性, 普通对象从Object获取到了什么属性。
也许上面这些还不够味,对象建立都能构造器原型继承隐藏属性的,这些隐藏属性是什么?
下面经过两张图来看下具体从这两个引用类型得到什么属性:
7.1 这张图展现__proto__(object类型--构造器为Object)从Object构造器继承一系列属性方法
卤煮在控制台定义了一个普通函数,并打印出来这个函数原型prototype,发现prototype也是个对象,这个对象包含两个属性:浏览器对外暴露的对象内部私有化属性__proto__和构造器constructor;其中constructor是一个函数对象,__proto__是一个普通对象(构造器为Object,所以继承了Object的许多方法)。
7.2 这张图展现constructor (function类型--构造器为Function)从Function构造器继承一系列属性方法
从上图能够看出 constructor 是一个函数对象,实际上 (function(){}).constructor.__proto__ = Function.prototype / (function(){}).__proto__ = Function.prototype,即任何一个函数的__proto__都指向Function.prototype, 任何一个对象的constructor(实际也为函数)的__proto__都指向Function.prototype(图中也很直观显示constructor 的构造器为 Function)
为何要说上面这一段呢?由于 在控制台 打印 console.log((function(){}).__proto__) 并不能直观看到Function的原型是什么--->因此只能间接经过constructor 查看Function.prototype属性:
apply:
call:
bind:
length:
name:
arguments:
总结:
一、全文下来:都是在说两个引用类型(javascript 一切皆是对象):函数(function)和对象(object) ——函数对象,普通实例对象。简单的说能够用typeof()来区分:function/object
二、全部对象都包含一个内部私有化属性___proto__ 和constructor
三、函数都包含一个prototype属性,同时做为一个对象,他也有__proto__ 和constructor属性
四、几个特殊的对象:
a、prototype:做为函数对象的属性,本质上也是一个实例对象,因此也有__proto__ 和constructor属性,特别的Function.prototype 是函数,但没有prototype属性;
b、__proto__ :做为全部对象都有的内部初始化属性(浏览器对外暴露的属性),指向对象的构造函数的原型prototype。
大体分两种状况:普通实例对象__proto__ 指向其构造器的prototype,函数对象__proto__ 均为:Function.prototype
c、constructor:做为全部对象一个属性,本质上是一个函数,指向对象的构造器(三种):object function 自定义构造器;特别的 prototype 做为一个对象,其constructor又反过来引用函数自己
即 func.prototype.constructor = func 所以能够理解为 原型对象prototype的 constructor是有对应函数构造的
五、做为普通实例对象从构造器Object得到什么属性或方法:
defineProperty:定义属性
hasProperty:判断是否拥有属性
isPrototypeOf():
propertyIsEnnumberable():
另外还有:toString(),toLocaleString(),valueOf()
六、做为函数从构造器Function得到哪些属性方法:
apply,call,bind,length, name,arguments,this。
七、经过 __proto__ 造成原型链。