JavaScript原型链详解

  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

  

原文地址:https://www.cnblogs.com/daqianduan/p/4384272.html