-
Notifications
You must be signed in to change notification settings - Fork 3
Description
我们先来看个简单的构造函数创建实例的例子
function Person(name) {
this.name = name
}
Person.prototype.age = 25
var me = new Person('cjs')
console.log(me) // { name: 'cjs'}
console.log(me.age) // 25
创建新对象me之后,me同时也有了age属性(定义在Person.prototype上),但打印me的结果是 { name: 'cjs'}
,并没有age字段,虽然我们都知道age是原型属性,实例公有,但是究竟是怎么访问到的呢?答案就是__proto__构成的原型链。
先看一下《JavaScript高级程序设计》里对prototype和__proto__的定义:
无论什么时候,只要创建了一个新函数,就会根据一组特定的规则为该函数创建一个prototype属性,这个属性指向函数的原型对象....
当调用构造函数创建一个新实例后,该实例的内部将包含一个指针(内部属性),指向构造函数的原型对象。ECMA-262第5版中管这个指针叫[[Prototype]]。虽然在脚本中没有标准的方式访问[[Prototype]],但Firefox、Safari和Chrome在每个对象上都支持一个属性__proto__
划重点
1.prototype是函数特有的属性,__proto__是通过调用构造函数创建的实例共有的内部属性(隐藏属性)。__proto__指向该构造函数的prototype
me.__proto__ === Person.prototype // true
实际上通过字面量创建的Object,Number,Array,String等也都有__proto__属性,因为他们的本质是通过new Obejct()、 new Numer()、new Array()、new String()等构造函数创建的。
var a = {}
var b = 1
var c = []
var d = 'str'
a.__proto__ === Object.prototype //true
b.__proto__ === Number.prototype // true
c.__proto__ === Array.prototype // true
d.__proto__ === String.prototype //true
2.修改__proto__对象的属性将会影响构造函数的prototype,所有的实例都会发生改动,除非直接修改__proto__对象
//接上Person
var you = new Person('bbk')
//重写you.__proto__.age属性,实际上是重写Person.prototype.age
you.__proto__.age = 30
console.log(me.age) // 30 me的age属性也发生变化
var him = new Person('hehe')
//重写him的__proto__对象,使him.__proto__的指向一个新的内存空间,不再指向Person.prototype.age
him.__proto__ = { age: 40 }
console.log(him.age) //40
console.log(me.age) // 30 不变
经典应用
检测数据类型
instanceof 运算符用来测试一个对象在其原型链中是否存在一个构造函数的 prototype 属性。
a instanceof Object // true
b instanceof Number //true
c instanceof Array //true
d instanceof String //true
me instanceof Person //true
you instanceof Person //true
him instanceof Person //false 因为him.__proto__已经改变指向了!
mvvm中的监听数组变化
重写每一个数组对象的__proto__里的push,pop,shift,unshift,splice,sort,reverse方法,再执行该数组的原生方法后,再调用notify()通知视图更新
[
'push',
'pop',
'shift',
'unshift',
'splice',
'sort',
'reverse'
]
.forEach(function (method) {
// cache original method
var original = arrayProto[method]
def(arrayMethods, method, function mutator () {
// avoid leaking arguments:
// http://jsperf.com/closure-with-arguments
var i = arguments.length
var args = new Array(i)
while (i--) {
args[i] = arguments[i]
}
var result = original.apply(this, args)
var ob = this.__ob__
var inserted
switch (method) {
case 'push':
inserted = args
break
case 'unshift':
inserted = args
break
case 'splice':
inserted = args.slice(2)
break
}
if (inserted) ob.observeArray(inserted)
// notify change
ob.dep.notify()
return result
})
})