Skip to content

__proto__和prototype的关系和区别 #7

@chenjiangsong

Description

@chenjiangsong

我们先来看个简单的构造函数创建实例的例子

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
  })
})

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions