开场白
输出?
var A= function(){}
A.prototype.n=1
var b=new A()
A.prototype={
n:2,
m:3
}
var c=new A()
console.log(b.n, b.m, c.n, c.m)
console.log(A.prototype.__proto__)
prototype
每个构造函数都有一个prototype
属性,指向自己的原型对象;
什么是原型?每个对象创建的时候,都会与之关联另一个对象,这个就是原型对象,会从原型“对象”继承属性和方法。
__proto__
每个实例对象(object)都有一个私有属性__proto__
指向它的原型对象(prototype)。该原型对象也有一个自己的原型对象,层层向上直到一个对象的原型对象为null
。
function Person(name) {
this.name = name
}
Person.prototype.getName = function () {
return this.name
}
var person = new Person();
person.name = 'gaoting';
console.log(person.name) // gaoting
console.log(Person.prototype)
console.log(person.__proto__)
console.log(person.__proto__ === Person.prototype) // true
WX20180926-161836.png
constructor
每个原型对象,都有一个constructor
属性,用来指向自己关联的函数,默认指向关联的构造函数。
constructor 引用同样被委托给了 Person.prototype,而 Person.prototype.constructor 默认指向 Person
Person.prototype 的 .constructor 属性只是 Person函数在声明时的默认属性。
如果 你创建了一个新对象并替换了函数默认的 .prototype 对象引用,那么新对象并不会自动获 得 .constructor 属性。
function Person(name) {
this.name = name
}
Person.prototype.getName = function () {
return this.name
}
var person = new Person();
person.name = 'gao';
console.log(person.constructor === Person, Person.prototype.constructor === Person)
//test constructor
Person.prototype = {
age: 18,
company: 'yck'
}
var gao = new Person()
console.log(gao.constructor === Person) // false
// 获取对象原型
console.log(Object.getPrototypeOf(gao)) // { age: 18, company: 'yck' }
gao 并没有 .constructor 属性,所以它会委托 [[Prototype]] 链上的 Person. prototype。但是这个对象也没有 .constructor 属性(不过默认的 Person.prototype 对象有这 个属性!),所以它会继续委托,这次会委托给委托链顶端的 Object.prototype。这个对象 有 .constructor 属性,指向内置的 Object(..) 函数。
以上为构造函数、实例、实例原型之间的关系
基于原型链的继承
JavaScript对象有一个指向一个原型对象的链。当试图访问一个对象的属性时,它不仅仅在该对象上搜寻,还会搜寻该对象的原型,以及该对象的原型的原型,依次层层向上搜索,直到找到一个名字匹配的属性或到达原型链的末尾。
4.pngfunction Person(name) {
this.name = name
}
Person.prototype.getName = function () {
return this.name
}
function Man (sex) {
this.sex = sex
}
Man.prototype = new Person()
var man = new Man('male')
man.name = 'qi'
console.log(man.getName())
console.log(man instanceof Man, man instanceof Person) // true true
问题一
即在通过原型链实现继承时,不能使用对象字面量创建原型方法。因为这样做原型指向了另一个对象
问题二
在通过原型来实现继承时,原型实际上会变成另一个类型的实例。于是,原先的实例属性也就顺理成章地变成了现在的原型属性了
function SuperType(){
this.colors = ["red", "blue", "green"];
}
function SubType(){
}
//继承了 SuperType
SubType.prototype = new SuperType(); // SuperType的原型对象将拥有colors属性,之后的实例对colors的修改,将直接作用到原型上
var instance1 = new SubType();
instance1.colors.push("black");
console.log(instance1.colors); //"red,blue,green,black"
var instance2 = new SubType();
console.log(instance2.colors); //"red,blue,green,black"
错误做法
//和你想要的机制不一样!
Bar.prototype = Foo.prototype;
只会让Bar.prototype直接引用Foo.prototype对象,当执行类似Bar.prototype.getName的时候,会直接修改Foo.prototype对象本身。这不是想要的结果。
// 基本上满足你的需求,但是可能会产生一些副作用 :(
Bar.prototype = new Foo();
借用构造函数
为了解决原型中包含引用类型值的问题,可以使用借用构造函数,在子类型构造函数的内部调用超类型构造函数。
function SuperType () {
this.colors = ['red', 'blue', 'green']
}
SuperType.prototype.getColors = function(){
return this.colors.join(',')
}
function SubType () {
//继承了 SuperType,在新构造函数中调用SuperType初始化对象的代码,每个实例保存colors副本
SuperType.call(this)
}
var ins = new SubType()
ins.colors.push('black')
var ins2 = new SubType()
console.log(ins2) // ['red', 'blue', 'green']
ins2.getColors() // 报错
缺点,更为致命,无法访问超类型SubType.prototype
中定义的函数,仅仅获取了超类型构造函数中定义的属性,没有用到继承的思想。
组合继承
将原型链和组合继承技术组合,通过原型链实现对原型属性和方法的继承,通过借用构造函数,让实例拥有自己的属性。
function SuperType (name) {
this.name = name
this.colors = ['red', 'blue', 'green']
}
function SubType (name, age = 18) {
SuperType.call(this, name)
this.age = age
}
SubType.prototype = new SuperType()
SubType.prototype.getAge = function () {
return this.age
}
var ins = new SubType('seven', 22)
ins.colors.push('black')
var ins2 = new SubType('six', 11)
console.log(ins2)
原型式继承
Object.create()
function Foo(name) {
this.name = name;
}
Foo.prototype.myName = function() {
return this.name;
};
function Bar(name,label) {
Foo.call( this, name );
this.label = label;
}
// 我们创建了一个新的 Bar.prototype 对象并关联到 Foo.prototype
Bar.prototype = Object.create( Foo.prototype );
Bar.prototype.myLabel = function() {
return this.label;
};
var a = new Bar( "a", "obj a" );
a.myName(); // "a"
a.myLabel(); // "obj a"
网友评论