多是以前开发和思考的层次比较浅,我的感受多数的开发工做与原型和原型链的关系不大,甚至不知道的状况下也可以把任务完成。网上也有很多讲解原型的文章,更有很多面经中提到这个考题,若是不是为了炫技那必是开发中必需要掌握的一个知识点。最近也看了这方面的资料,就上面的三个问题总结一下本身的想法,其实学以至用才是最棒的,目前开发经验少不能体会原型的好处,只作记录方便以后遇到问题可以知道该去查找哪方面的资料。 javascript
几乎全部函数都有一个显示的 prototype
属性,除了下面的这种状况java
let fun = Function.prototype.bind()
复制代码
prototype
属性是一个对象,在函数声明的时候就会自动建立,并且默认状况下只有一个属性 constructor
用来指向函数自己。
git
每一个对象都会有的一个隐式原型属性,指向了建立该对象的构造函数的原型。
es6
__proto__
是在对象建立的时候添加属性,连接到构造调用的函数的原型上的。当访问对象上不存在的属性的时候能够顺着这条链查找属性。
new
一个对象的过程能够解析为一下4步:
obj
obj
的 __proto__
连接到函数的 prototype
obj
// new 语法的简单模拟
var creatObject = function(constructor,...args) {
var obj = Object.create(null)
// 连接原型
Object.setPrototypeOf(obj,constructor.prototype)
res = constructor.apply(obj,args)
return typeof res === 'object' ? res : obj
}
复制代码
在JavaScript中没有类这个概念,所谓的继承也是经过原型链来实现的,包括加入的 class
语法糖也是为了更好的理解JavaScript中的“继承”并无改变JavaScript中对象的工做机制,底层仍是原型。那么很天然的想到原型链中也有相似继承的好处。
个人理解上原型有两个主要的做用:github
在对象上查找属性的时候,若对象自己不具有该属性,则被查找的属性会被委托在整个原型链上,只有当没有更多的原型能够查找的时候,才中止查找。
咱们定义一个对象 var o = {a:1,b:2}
咱们知道可使用 hasOwnProperty
来判断某属性是否对象自身拥有的属性,可是在建立对象的时候并无建立 hasOwnProperty
方法,这个是如何经过 .
运算调用的呢?
app
o.__proto__===Object.prototype
能够知道经过字面量的方式建立对象实例
o
的时候的函数是
Object
且如今
o
的原型链上有
Object.prototype
Object.prototype
发现这个对象中含有属性
hasOwnProperty
o
中没有找到
hasOwnProperty
,就顺着原型链往上找,首先找
o.__proto__
也就是
Object.prototype
,很巧在这里就找到了(若是没有找到就会一直顺着这条链往上找),因而拿过来使用。
前面略带的提过这个属性 constructor
,经过这个属性能够访问建立该对象时所用的函数,经过这个咱们能够判断两个实例是不是同一个函数建立而来的,我想这个属性的做用差很少也应该是这样了。
函数
obj instanceof Foo
应该被理解为检测函数 Foo
是否出如今 obj
的原型链中,并非单纯的理解过对象实例是否由某个函数构造调用建立。只要经过 __proto__
一层一层的往上找可以找到这个函数的 prototype
就说明是这个函数建立的。
ui
dog.__proto__!==Dog.prototype
并且顺着
__proto__
也找不到
Dog.prototype
因此这里返回
false
当实例属性和原型属性中有同名属性或者方法的时候优先使用实例属性。下面实例属性中和原型属性中都有 say
函数,这里使用实例属性。顺着原型链查找如今实例属性中找到了就中止查找
this
对象与函数原型之间的引用关系是在对象建立的时候创建的,新建立的对象会根据当前的函数原型创建引用。
es5
上面例子中用一个对象字面量重写了 Dog.prototype
此时 constructor
属性不见了,这个在实习继承效果的时候也须要注意,咱们在修改完原型以后须要补上 constructor
// es5
function Animal() {}
Animal.prototype.say = function() {
console.log('动物叫')
}
function Dog() {}
let dog = new Dog()
// 连接原型并加上constructor
Dog.prototype = Object.create(Animal.prototype, {
constructor: {
value: Dog, // 指向Dog
enumerable: false,
writable: true,
configurable: true
}
})
//es6 引入了class 和 extends 减小对原型覆盖的反作用
class Animal{
constructor(name) {
this.name = name
}
say(){
console.log('动物叫')
}
}
class Dog extends Animal{
constructor(name,type){
super(name) // 调用父类构造函数
this.type = type
}
say(){
console.log('汪汪')
}
}
var dog = new Dog('道格','中华田园犬')
dog.say() // 汪汪
console.log(dog instanceof Dog) // true
console.log(dog instanceof Animal) // true
复制代码
一些公共方法若是做为实例方法建立在每建立一个实例的时候都会产生一个副本,不只占内存并且修改起来麻烦。能够在函数的原型对象上建立对象方法,这样可使得一个方法被全部对象实例共享(能够经过原型链访问)。
// 翻转字符串为例
String.prototype.reversed = function(){
return Array.from(this).reverse().join('')
}
"abcdef".reversed() // "fedcba"
复制代码