prototype属性是JavaScript为每个Function()实例创建的一个对象。具体地说,它将通过new关键字创建的对象实例 链接回创建它们的构造函数。因此,实例才可以共享或继承通用方法和属性。
重要的是,共享发生在属性查找时。
注意:JavaScript会为每个函数创建原型对象,不管你是否打算将这个函数作为构造函数使用。
即使不直接调用Function()构造函数(如var add = new Function('x', 'y', 'return x+y');),而是使用字面量表示法(如var add = function(x, y) {return x+y};),所有函数也都是由Function()构造函数创建的。
var myFunction = function() {} console.log(myFunction.prototype) // object {}, protoype来自于Function构造函数 console.log(typeof myFunction.prototype) // 'object'
默认的prototype属性是Object()对象
原型只是一个被称为“原型”的空对象属性,它是由javascript在后台创建,并通过调用Function()构造函数来使用。
虽然原型只是一个对象,但它是特殊的,因为原型链将每个实例都链接至其构造函数的prototype属性。使用new关键字由构造函数创建对象时(或为原始值穿件对象包装器时),都会在创建的对象实例和创建对象的构造函数的prototype属性之间添加一个隐藏的链接。该链接在实例中被称为__proto__。
Array.prototype.foo = 'foo' var myArray = new Array() console.log(myArray.__proto__.foo) // foo,访问Array.prototype
由于访问__proto__并不是ECMA标准的一部分,有一个更通用的方法,用于跟踪从对象到它集成的原型对象的链接,就是使用构造函数
console.log(myArray.constructor.prototype.foo) // foo,访问Array.prototype
// 或使用链
console.log(myArray.foo) // foo,追踪到Array.prototype
myArray.__proto__和myArray.constructor.prototype引用Array.prototype
原型链的最后是Object.prototype,与作用域链类似,原型链查找时将使用它找到的第一个值
Object.prototype.foo = 'object-foo' Array.prototype.foo = 'array-foo' var myArray = [] console.log(myArray.foo) myArray.foo = 'bar' console.log(myArray.foo)
用新对象替换prototype属性会删除默认构造函数属性
var Foo = function() {} Foo.prototype.constructor === Foo // true var fooInstance = new Foo() fooInstance.constructor === Foo // true Foo.prototype = {} // Foo.prototype.constructor 被修改为默认的Object var fooInstance = new Foo() fooInstance.constructor === Foo // false fooInstance.constructor === Object // true
如果要替换javascript设置的默认prototype属性,应该重新连接引用该构造函数的构造函数属性。
var Foo = function() {} Foo.prototype = {constructor: Foo} var fooInstance = new Foo() fooInstance.constructor === Foo // true
继承原型属性的实例总是能获得最新值
var Foo = function() {} Foo.prototype.x = 1 var fooInstance = new Foo() console.log(fooInstance.x) // 1 Foo.prototype.x = 2 console.log(fooInstance.x) // 2
var Foo = function() {} Foo.prototype = {x: 1} // 不管是使用默认原型对象还是使用自己的对象覆盖它,查找链的工作方式都是相同的 var fooInstance = new Foo() console.log(fooInstance.x) // 1 Foo.prototype.x = 2 console.log(fooInstance.x) // 2
使用新对象替换prototype属性不会更新以前的实例
var Foo = function() {}
Foo.prototype.x = 1
var fooInstance = new Foo()
console.log(fooInstance.x) // 1
Foo.prototype = {x: 2}
console.log(fooInstance.x) // 1
var newFooInstance = new Foo()
console.log(newFooInstance.x) // 2
//一旦开始创建实例,就不应该用一个新对象替换对象的原型。这样做将导致实例有一个指向不同原型链的链接
用户自定义构造函数像原生构造函数一样原型继承
var Person = function() {} Person.prototype.legs = 2 Person.prototype.arms = 2 Person.prototype.countLimbs = function() {return this.legs + this.arms} var chuck = new Person() console.log(chuck.countLimbs()) // 4
为了更好的演示如何使用原型,可以创建一个构造函数,如果这些属性不是作为参数提供的话,则该构造函数的实例继承legs和arms属性
var Person = function(legs, arms) { this.legs = legs; this.arms = arms; } Person.prototype.legs = 2 Person.prototype.arms = 2 Person.prototype.countLimbs = function() {return this.legs + this.arms} var chuck = new Person(0, 0) console.log(chuck.countLimbs()) // 0