Object() Function() Array() 等等都为构造函数。
Js 面向对象与传统oop 有些不同,语法较高级 语法糖封装。
This 为指针。指向this作用域的调用者
1.原型继承链顶端为Object 。Js函数是对象
2.当读取对象中的属性时,先去实例本身中搜索,如搜索不到则去指向的原型中搜索
1.原型的存在意义在于实现继承共享,是在构造函数中定义的一个成员对象,在下次实例化时不需要在构造函数中定义成员 就可实现实例共享方法属性。
例子:通常为。构造函数.prototype.xxx=我想实现实例继承的东西 -》 new 构造函数 -》新实例a对象.原型指针指向构造函数的xxx对象(引用类型)
例子:Array 不等于 Array() 原因 Array为一个函数,而Array()为一个构造函数调用语句,故Array拥有prototype对象用于实例的共享继承,Array()产生一个实例 故只能拥有prototype对象的私有指针 proto
2.在使用原型继承时 不能使用字面量 构造函数.prototype={} 的方式重写原型对象 。因为会导致该原型对象的constructor属性被重写,在生成的实例中导致constructor指向Object并且会切断之前原型对象的联系,破坏原型链。
3.JavaScript 主要通过原型链实现继承。原型链的构建是通过将一个类型的实例赋值给另一个构造函数的原型实现的
例子:
xxx实例. proto -》function xxx()构造函数.prototype对象故xxx. proto === xxx.prototype
xxx.prototype. proto -》Object.prototype因为所有对象都为Object函数构造来的。故xxx.prototype. proto ===Object.prototype。
Object.prototype. proto 为原型链顶端 proto 定义了尚未使用所以为null故Object.prototype. proto ===null约定俗成。
instanceof 用来判断某实例是否为某构造函数的实例
isPrototypeOf 用于判断某实例是否拥有某构造函数的原型对象指针
1.原型模式有忽略构造函数定义初始值步骤及原型中操作引用类型的缺点。
所以需要组合使用 构造函数模式+原型模式 来创建实例。在构造函数中定义实例的属性,而需共享的方法就定义在原型对象中。
继承:在子构造函数中调用 父.call(this, name)实现构造函数之间的属性继承。使用 子.prototype = new 父()子.prototype.constructor = 子实现方法的继承。
2.如要在生产环境下的构造函数新增方法(如Array) 需要使用一个类似于工厂函数的寄生构造函数模式 在构造函数中返回一个修改后的对象
先举一个非常简单的例子,我有一个类叫Humans(人类),然后我有一个对象叫Tom(一个人)和另一个对象叫Merry(另一个人),很明显Tom和Merry都是由Humans这一个类实例化之后得到的,然后可以把这个例子写成如下代码:functionHumans(){this.foot=2}Humans.prototype.ability=truevarTom=newHumans()varMerry=newHumans()alert(Tom.foot)//结果:2alert(Tom.ability)//结果:truealert(Merry.foot)//结果:2alert(Merry.ability)//结果:true以上是一个非常简单的面向对象的例子,相信都能看懂,如果尝试修改Tom的属性ability,则functionHumans(){this.foot=2}Humans.prototype.ability=truevarTom=newHumans()varMerry=newHumans()Tom.ability=falsealert(Tom.foot)//结果:2alert(Tom.ability)//结果:falsealert(Merry.foot)//结果:2alert(Merry.ability)//结果:true以上可以看出Tom的ability属性的值改变了,但并不影响Merry的ability属性的值,这正是我们想要的结果,也是面向对象的好处,由同一个类实例化得到的各个对象之间是互不干扰的;OK,接下来给ability换成object对象又如何?代码如下:functionHumans(){this.foot=2}Humans.prototype.ability={run:'100米/10秒',jump:'3米'}varTom=newHumans()varMerry=newHumans()Tom.ability={run:'50米/10秒',jump:'2米'}alert(Tom.ability.run)//结果:'50米/10秒'alert(Tom.ability.jump)//结果:'2米'alert(Merry.ability.run)//结果:'100米/10秒'alert(Merry.ability.jump)//结果:'3米'以上代码就是在原型链上使用了对象,但从以上代码可以看出Tom的ability属性的改变依然丝毫不会影响Merry的ability的属性,于是乎你会觉得这样的做法并无不妥,为什么说不能在原型链上使用对象?接下来的代码就会显得很不一样,并且可以完全表达出原型链上使用对象的危险性:functionHumans(){this.foot=2}Humans.prototype.ability={run:'100米/10秒',jump:'3米'}varTom=newHumans()varMerry=newHumans()Tom.ability.run='50米/10秒'Tom.ability.jump='2米'alert(Tom.ability.run)//结果:'50米/10秒'alert(Tom.ability.jump)//结果:'2米'alert(Merry.ability.run)//结果:'50米/10秒'alert(Merry.ability.jump)//结果:'2米'没错,从以上代码的输出结果可以看出Tom的ability属性的改变影响到Merry的ability属性了,于是就可以明白在原型链上使用对象是非常危险的,很容易会打破实例化对象之间的相互独立性,这就是为什么不能在原型链上使用对象的原因?是的,但我想说的可不只如此,而是其中的原理,看完后面JS原型链的深层原理之后,相信你会完全明白。在以下第二部份解释JS原型链的深层原理之前,先来明确一个概念:原型链上的属性或方法都是被实例化对象共用的,正因如此,上面的Tom.ability.run='50米/10秒',改动了原型连上的ability才导致另一个对象Merry受影响,既然如此,你可能会问Tom.ability={}不也是改动了原型链上的ability吗,为什么Merry没有受影响?答案是Tom.ability={}并没有改动原型链上的ability属性,而是为Tom添加了一个自有属性ability,以后访问Tom.ability的时候不再需要访问原型链上的ability,而是访问其自有属性ability,这是就近原则。本文主要参考了 MDN文档 和 知乎讨论 。
在js中,大部分东西都是对象,数组是对象,函数也是对象,对象更加是对象。 不管我们给数组和函数定义什么内容,它们总是有一些相同的方法和属性 。比如说hasOwnProperty(),toString()等:
这说明一个对象所拥有的属性不仅仅是它本身拥有的属性,它还会从其他对象中继承一些属性。当js在一个对象中找不到需要的属性时,它会到这个对象的父对象上去找,以此类推,这就构成了对象的原型链 。理解js的原型链对使用js的对象非常有帮助。
让我们通过一个例子由浅到深地理解原型链:
这是我们经常使用的创建对象的方式,将共同的方法放到Foo.prototype中,所有实例都共有这个方法了。
这是怎么实现的呢?我们看下面这张图的第一行:
我们定义的show函数在Foo.prototype中,当我们执行f1.show()时,js发现f1本身没有show这个属性,所以它就到f1的原型(也就是__proto__指向的对象)去找,找到了就可以调用。
图片第一行告诉了我们4点:
我们先看看Foo的原型吧!Foo是一个函数,它的构造函数是js内部的function Function(),Function的prototype指向了一个对象Function.prototype,因此Foo的__proto__就指向了Function.prototype,如图。
我们继续深入下去,Function.prototype这个对象,它就是一个普通的对象,它的构造函数是js内置的function Object(),function Object()的prototype指向Object.prototype,因此Function.prototype.__proto__就指向Object.prototype,这个对象中定义了所有对象共有的属性,比如我们之前说的hasOwnProperty()和toString()等。
Object.prototype就是原型链的终点了,它的__proto__是null,js查找属性时,如果到这里还没有找到,那就是undefined了。
到这里就不难理解为什么我们说在js中,函数也是对象了,它就是继承自对象的!
如果如有疑问,欢迎指出!