前端攻城狮查缺补漏系列-原型、原型、继承。

112 阅读4分钟

原型、原型链

函数对象、普通对象。

所有通过 new Function() 实例化的对象都是 函数对象,其他的都是普通对象。

构造函数

代码演示

function Person(name, age, job) {
 this.name = name;
 this.age = age;
 this.job = job;
 this.sayName = function() { alert(this.name) } 
}
var person1 = new Person('msc', 28, 'web');
var person2 = new Person('xx', 23, 'java');

上面的例子中 person1 和 person2 都是 Person 的实例。这两个实例都有一个 constructor (构造函数)属性,该属性(是一个指针)指向 Person。 即:

console.log(person1.constructor == Person); //true
console.log(person2.constructor == Person); //true

原型对象 prototype

在 JavaScript 中,每当定义一个对象(函数也是对象)时候,对象中都会包含一些预定义的属性。其中每个函数对象都有一个prototype 属性(基本对象只要_proto_属性),这个属性指向函数的原型对象。代码演示

function Person() {}
Person.prototype.name = '马士春';
Person.prototype.age  = 28;
Person.prototype.job  = 'web';
Person.prototype.sayName = function() {
  alert(this.name);
}
var person1 = new Person();
person1.sayName(); // '马士春'
var person2 = new Person();
person2.sayName(); // '马士春'
console.log(person1.sayName == person2.sayName); //true

原型对象,它就是一个普通对象。从现在开始你要牢牢记住原型对象就是 Person.prototype。

在默认情况下,所有的 原型对象 都会自动获得一个 constructor(构造函数)属性,这个属性(是一个指针)指向 prototype 属性所在的函数(Person)即:Person.prototype.constructor == Person

实例上的constructor 和 原型对象上的constructor 两者的区别:

前面在构造函数中提到了 实例上会有constructor属性,并指向构造函数Person, 那跟原型对象上的constructor有什么关系呢?

person1.constructor == Person
Person.prototype.constructor == Person
总结一下:
实例person1上的constructor指向构造出person1的构造函数 Person。
Person.prototype原型对象上的constructor,是在Person创建时,创建了一个它的实例对象并赋值给它的 prototype,即:
Person.prototype = new Person();

_proto_

JS 在创建对象(不论是普通对象还是函数对象)的时候,都有一个叫做__proto__ 的内置属性,用于指向创建它的构造函数的 原型对象(prototype) 。例如:实例对象 person1 有一个 __proto__属性,创建它的构造函数是 Person,构造函数的原型对象是 Person.prototype ,所以:person1.__proto__== Person.prototype

图示

要明确的真正重要的一点就是,这个连接存在于实例(person1)与构造函数(Person)的原型对象(Person.prototype)之间,而不是存在于实例(person1)与构造函数(Person)之间。

原型链

每个对象都有一个 _proto_属性,并且指向他的原型对象。当我们访问对象的属性时,如果该对象内部不存在这个属性,他就会去他的_proto_属性所值向的对象(父类对象)中查找,如果还是找不到,就会去他父类的_proto__属性所值的对象(父类的父类)继续查找,直到找到null。这个查找的过程就形成了 原型链

上面介绍的如果都能理解透彻,那应该能明白下面这张图

lauyout

继承

1:类继承

function SuperClass() {
  this.superValue = true;
}
SuperClass.prototype.getSuperValue = function() {
  return this.superValue;
}
function SubClass() {
  this.subValue = false;
}
SubClass.prototype = new SuperClass();

SubClass.prototype.getSubValue = function() {
  return this.subValue;
}
var instance = new SubClass();
console.log(instance instanceof SuperClass)//true
console.log(instance instanceof SubClass)//true
console.log(SubClass instanceof SuperClass)//false

子类通过其原型prototype对父类实例化,继承了父类
缺点1:如果父类中有属性时引用类型,这样就会被所有子类引用,当一个子类修改了这个引用类型,那么所有的子类继承的共享属性都会受影响。
缺点2

拓展: indexof instanceof区别

1:indexof

js 在底层存储变量的时候,会在变量的机器码的低位1-3位存储其类型信息,如下所示:

  • 000 : 对象
  • 010 : 浮点数
  • 100 : 字符串
  • 110 : 布尔
  • 1 : 整数
  • null:所有机器码均为0
  • undefined:用 −2^30 整数来表示

js的typeof 数据类型时是通过这些机器码返回对应的类型的。 在判断 null 的时候就出现问题了,由于 null 的所有机器码均为0,因此直接被当做了对象来看待,这是自js诞生以来就一直留下的一个坑。所以在用 typeof 来判断变量类型的时候,需要注意,最好是用 typeof 来判断基本数据类型(包括symbol),避免对 null 的判断。

2:instanceof

objectL instanceof objectR 通过判断objectL原型链上是否存在objectR.prototype

1: objectR.prototype = Object.prototype
2: obj._proto_ = Function.protoType
3: Function.protoType != Object.prototype 在原型链上继续查找 比较
4: Function.prototype._proto_ = Object.prototype
5: Object.prototype === Object.prototype  返回true