再看原型链

开场白

输出?

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

3.png

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.png
function 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"

参考链接

JavaScript深入之从原型到原型链

你可能感兴趣的:(再看原型链)