关于js原型链的理解
原型链概念
ECMAScript 中描述了原型链的概念,并将原型链作为实现继承的主要方法。其基本思想是利用原 型让一个引用类型继承另一个引用类型的属性和方法构造函数、原型和实例的关系:每 个构造函数都有一个原型对象,原型对象都包含一个指向构造函数的指针,而实例都包含一个指向原型 对象的内部指针。那么,假如我们让原型对象等于另一个类型的实例,结果会怎么样呢?显然,此时的 原型对象将包含一个指向另一个原型的指针,相应地,另一个原型中也包含着一个指向另一个构造函数 的指针。假如另一个原型又是另一个类型的实例,那么上述关系依然成立,如此层层递进,就构成了实 例与原型的链条。这就是所谓原型链的基本概念。
对象分为函数对象和普通对象
了解原型链之前先了解js里的对象,ECMA-262 把对象定义为:“无序属性的集合,其属性可以包含基本值、对象或者函数。”严格来讲, 这就相当于说对象是一组没有特定顺序的值。对象的每个属性或方法都有一个名字,而每个名字都映射 到一个值。 正因为这样(以及其他将要讨论的原因),我们可以把 ECMAScript 的对象想象成散列表:无非就是一组名值对,其中值可以是数据或函数。
js的内置对象Object、Function、Array、Date等都是函数对象,Function的实例也都是函数对象,其他内置对象的 实例都是普通对象
理解prototype和__proto__
所有的对象都有一个隐式属性__proto__(IE浏览器不支持),它是一个普通对象
所有的函数都有一个prototype属性,该属性也是一个普通对象;
所有的函数都是Function的实例,他们的默认原型都是Object的实例
特别注意:任何内置函数对象本身的__proto__都指向Function的原型对象(因为所有的函数都是Function的实例)
console.log(Function.__proto__ === Function.prototype) //true
console.log(Object.__proto__ === Function.prototype) //true
console.log(Date.__proto__ === Function.prototype) //true
console.log(Array.__proto__ === Function.prototype) //true
通过例子深入理解
//构造函数
function Person(name){
this.name = name
}
Perosn.prototype.name = "sang"
Person.prototype.sayName = function(){
alert(this.name)
}
var perosn = new Person("Jeremy") //实例化构造函数Person
person.sayName() // Jeremy > person是构造函数Perosn的实例对象,对象都有__proto__属性,在打印台打印 person对象
可以看到perosn对象有自己的name属性和__proto__属性,该属性是一个普通对象,指向构造函数Person的原型
我们打印一下构造函数Person.prototype,可以看出此时的 Person.prototype和person.proto__是一样的都是指向函数Person的原型对象 所以说明 person包含一个指针指向构造函数Person的原型对象 即 person.__proto === Person.prototype
只要创建了一个新函数,就会根据一组特定的规则为该函数创建一个 prototype 属性,这个属性指向函数的原型对象。在默认情况下,所有原型对象都会自动获得一个 constructor (构造函数)属性(该属性指向构造函数Person本身),这个属性包含一个指向 prototype 属性所在函数的指针 所以 所以 Person.prototype.constructor.prototype === Person.prototype 打印 Person.prototype.constructor.prototype 会指向原型对象 原型搜索机制。读取模式访问一个实例属性时,首先会在实例中搜索该属性。如果没有找到该属性,则会继续搜索实例的原型。在通过原型链实现继承的情况下,搜索过程就得以沿着原型链继续向上。就拿上面的例子来说,调用 person.name会经历三个搜索步骤:1)搜索实例;2)搜索 Person.prototype; 最后一步才会找到该方法。在找不到属性或方法的情况下,搜索过 程总是要一环一环地前行到原型链末端才会停下来。