[JS]JS原型与原型链

来源:互联网 发布:美女主播秀软件 编辑:程序博客网 时间:2024/05/16 08:25

普通对象与函数对象

JavaScript中,万物皆对象!但对象也是有区别的。分为普通对象和函数对象,Object,Function 是JS自带的函数对象。
function f1(){};var f2 = function(){};var f3 = new Function('str','console.log(str)');var o3 = new f1();var o1 = {};var o2 = new Object();console.log(typeof Object); //functionconsole.log(typeof Function); //function //Object,Function 是JS自带的函数对象。还有Number、Boolean、String、Array、RegExp、Error、Date也是function类型console.log(typeof o1); //objectconsole.log(typeof o2); //objectconsole.log(typeof o3); //objectconsole.log(typeof f1); //functionconsole.log(typeof f2); //functionconsole.log(typeof f3); //function

在上面的例子中 o1 o2 o3 为普通对象,f1 f2 f3 为函数对象。怎么区分,其实很简单,凡是通过 new Function() 创建的对象都是函数对象,其他的都是普通对象。f1,f2,归根结底都是通过 new Function()的方式进行创建的。Function Object 也都是通过 New Function()创建的。

使用function定义的对象与使用new操作符生成的对象之间有一个重要的区别,这个区别就是function定义的对象有一个prototype属性,使用new生成的对象就没有这个prototype属性。

原型对象

在JavaScript中每当定义一个对象(函数)时候,对象中都会包含一些预定义的属性。其中函数对象的一个属性就是原型对象prototype。注:普通对象没有prototype,但有__proto__属性;当然函数对象也有__proto__属性

使用function定义的对象与使用new操作符生成的对象之间有一个重要的区别。这个区别就是function定义的对象有一个prototype属性,使用new生成的对象就没有这个prototype属性。prototype属性又指向了一个prototype对象,注意prototype属性prototype对象是两个不同的东西,要注意区别。在prototype对象中又有一个constructor属性,这个constructor属性同样指向一个constructor对象,而这个constructor对象恰恰就是这个function函数本身。


原型对象其实就是普通对象(Function.prototype除外,它是函数对象,但它很特殊,他没有prototype属性(前面说道函数对象都有prototype属性))。

 function f1(){}; console.log(f1.prototype) //f1{} console.log(typeof(f1.prototype)) //object console.log(typeof(Function.prototype)) //function,这个特殊 console.log(typeof(Object.prototype)) //object console.log(typeof(Function.prototype.prototype)) //undefined

从这句console.log(f1.prototype) //f1 {} 的输出就结果可以看出,f1.prototype就是f1的一个实例对象就是在f1创建的时候,创建了一个它的实例对象并赋值给它的prototype,基本过程如下:

function f1(){};  console.log(f1.prototype); //f1{} //等价于function f1(){};var temp = new f1();  f1.prototype = temp; console.log(f1.prototype); //f1{} 
所以,Function.prototype为什么是函数对象就迎刃而解了,上文提到凡是new Function ()产生的对象都是函数对象,所以temp1是函数对象。

 var temp1 = new Function (); Function.prototype = temp1;
那原型对象是用来做什么的呢?主要作用是用于继承。
var person = function(name){this.name = name};person.prototype.getName = function(){return this.name; }var zjh = new person("zhangjiahao");zjh.getName(); //zhangjiahao
从这个例子可以看出,通过给person.prototype设置了一个函数对象的属性,那有person实例(例中:zjh)出来的普通对象就继承了这个属性。具体是怎么实现的继承,就要讲到下面的原型链了。

原型链

JS在创建对象(不论是普通对象还是函数对象)的时候,都有一个叫做__proto__的内置属性,用于指向创建它的函数对象的原型对象prototype。

  console.log(zjh.__proto__ === person.prototype) //true
同样,person.prototype对象也有__proto__属性,它指向创建它的函数对象(Object)的prototype
  console.log(person.prototype.__proto__ === Object.prototype) //true
继续,Object.prototype对象也有__proto__属性,但它比较特殊,为null
  console.log(Object.prototype.__proto__) //null
我们把这个有__proto__串起来的直到Object.prototype.__proto__为null的链叫做原型链

内存结构图

为了更加深入和直观的进行理解,下面我们画一下上面的内存结构图

画图约定:


疑点解释:

  • Object.__proto__ === Function.prototype // true

Object是函数对象,是通过new Function()创建,所以Object.__proto__指向Function.prototype。

  • Function.__proto__ === Function.prototype // true

Function也是对象函数,也是通过new Function()创建,所以Function.__proto__指向Function.prototype。
自己是由自己创建的,好像不符合逻辑,但仔细想想,现实世界也有些类似,你是怎么来的,你妈生的,你妈怎么来的,你姥姥生的,……类人猿进化来的,那类人猿从哪来,一直追溯下去……,就是无,(NULL生万物)
正如《道德经》里所说“无,名天地之始”。

  • Function.prototype.__proto__ === Object.prototype //true

其实这一点我也有点困惑,不过也可以试着解释一下。
Function.prototype是个函数对象,理论上他的__proto__应该指向 Function.prototype,就是他自己,自己指向自己,没有意义。
JS一直强调万物皆对象,函数对象也是对象,给他认个祖宗,指向Object.prototype。Object.prototype.__proto__ === null,保证原型链能够正常结束。

constructor

原型对象prototype中都有个预定义的constructor属性,用来引用它的函数对象。这是一种循环引用。

function Foo(y){ this.y = y ;}Foo.prototype.x = 10;Foo.prototype.calculate = function(z){return this.x+this.y+z;};var b = new Foo(20); console.log(b.calculate(30));//从下图所得console.log(b.constructor === Foo);console.log(Foo.prototype.constructor === Foo);console.log(Foo.constructor === Function);console.log(Function.constructor === Function);console.log(Function.prototype.constructor === Function);console.log(Object.constructor === Function);console.log(Object.prototype.constructor === Object);


  person.prototype.constructor === person //true  Function.prototype.constructor === Function //true  Object.prototype.constructor === Object //true
完善下上面的内存结构图:

有两点需要注意:

  1. 注意Object.constructor===Function;//true 本身Object就是Function函数构造出来的
  2. 如何查找一个对象的constructor,就是在该对象的原型链上寻找碰到的第一个constructor属性所指向的对象

总结

  • 原型和原型链是JS实现继承的一种模型。
  • 原型链的形成是真正是靠__proto__ 而非prototype

1.凡是通过new Function()创建的对象都是函数对象,其他的都是普通对象。Object、Function、Number、Boolean、String、Array、RegExp、Error、Date是JS自带的函数对象。函数对象有一个原型属性prototype,而普通对象这没有原型属性prototype。这是函数对象和普通对象的一个重要区别。

var obj = { name : "joy"};console.log(obj.prototype);//undefinedvar arr = [1,2];console.log(arr.prototype);//undefinedvar fun = function(){};console.log(fun.prototype);//{}console.log(Object.prototype);//{}console.log(Function.prototype);//[Function]console.log(Array.prototype);//[]console.log(Number.prototype);//[Number: 0]console.log(Boolean.prototype);//[Boolean: false]console.log(String.prototype);//[String: '']console.log(Date.prototype);//Date {}
2.函数对象有一个原型属性prototype,原型属性prototype它指向原型对象prototype,它的作用是显示修改对象的原型的属性,原型对象prototype是普通对象(Function.prototype除外,它是函数对象,但它很特殊,他没有prototype属性),原型对象prototype中有一个构造属性constructor,构造属性constructor指向这个函数对象;若这个函数对象的原型对象被重新赋值了,则构造属性constructor它就指向它的函数对象的原型对象的constructor构造属性所指向的函数对象。
function Person(name) {    this.name = name;}console.log(Person.prototype) //Person {}console.log(Person.prototype.constructor === Person) //true//重写Person的原型对象Person.prototype = {    getName: function() {}}console.log(Person.prototype) // { getName: [Function] }console.log(Person.prototype.constructor === Object) //true//重写Person的原型对象Person.prototype = function(){    return this.name;}console.log(Person.prototype) // [Function]console.log(Person.prototype.constructor === Function) //true
3.每个对象(包括普通对象和函数对象)都有一个__proto__内置属性,__proto__内置属性是JS内部使用寻找原型链的属性,__proto__内置属性它指向创建它的函数对象的原型对象prototype,原型对象prototype主要用于继承。
function Person(name) {    this.name = name;}var p = new Person('jack');console.log(p.__proto__ === Person.prototype) // true//重写Person的原型对象Person.prototype = {    getName: function() {}}var p = new Person('lili');console.log(p.__proto__ === Person.prototype) // true//这说明,无论函数对象的原型是否重写,函数对象所创建出来的普通对象,这个普通对象的__proto__属性都指向函数对象的原型对象prototype。这也说明,__proto__内置属性是JS内部使用寻找原型链的属性。console.log(Function.__proto__ === Function.prototype) //trueconsole.log(Object.__proto__ === Function.prototype) //trueconsole.log(Array.__proto__ === Function.prototype);//trueconsole.log(Number.__proto__ === Function.prototype);//trueconsole.log(Boolean.__proto__ === Function.prototype);//trueconsole.log(Function.prototype.prototype);//undefined,说明Function.prototype是普通对象console.log(Function.prototype.__proto__ === Object.prototype);//trueconsole.log({name:"lili"}.__proto__ === Object.prototype);//true,说明所有普通对象的__proto__属性都指向Object.prototype对象console.log(Object.prototype.__proto__);//null,而不是undefined。//从上面可以看出整个原型链的脉络。
4.每个对象(包括普通对象和函数对象)都有一个构造属性constructor,构造属性constructor它指向它的函数对象;若这个函数对象的原型对象被重新赋值了,则构造属性constructor它就指向它的函数对象的原型对象的函数对象。

function Person(name) {    this.name = name;}var p = new Person('jack');console.log(p.constructor === Person) // truePerson.prototype = {    getName: function() {}}var p = new Person('lili');console.log(p.constructor === Object) // truePerson.prototype = function(){    return this.name;}var p = new Person('joy');console.log(p.constructor === Function) // true


0 0
原创粉丝点击