JavaScript 中万物皆对象,对象皆出自构造函数。
对象分为:函数对象和普通对象。
对象独有:__proto__ constructor;
函数独有:prototype。
JavaScript 中函数也是对象,故函数也拥有__proto__ constructor 属性。
var A = function(){}; // A是一个方法,当然也是个对象
var a = new A();
原型
定义:给其它对象提供共享属性的对象。
prototype 自己也是对象,只是被用以承担了某个职能。
公式:
`实例.__proto__ === 实例的构造函数.prototype`
JavaScript 中的 Object和Function 就是典型的函数对象。
所有函数对象的__proto__都指向Function.prototype。
原型链
原型链的形成是靠 __proto__ 而非prototype。
instanceof
object instanceof constructor
instanceof 用来检测 contructor.prototype 是否在 object 的原型链上。
function instance_of(l, r) {
var rProto = r.prototype
var lProto = l.__proto__
console.log(typeof rProto)
console.log(typeof lProto)
if (!lProto) return false
while(l) {
console.log(typeof lProto)
if (lProto === rProto) {
return true
}
lProto = lProto.__proto__
}
}
解析:Object instanceof Object
rProto = Object.prototype
lProto = Object.__proto__ = Function.prototype
// 进入循环体,第一次循环
lProto !== rProto
lProto = lProto.__proto__ = Function.prototype.__proto__ = Object.prototype
// 第二次循环
lProto === rProto
解析:Function instanceof Function
继承
ES5 中的继承实现方式
new 关键字
- 创建对象 obj;
- 将对象 obj 的原型指向构造函数,使对象 obj 可以访问到构造函数原型对象的属性;
- 将构造函数的
this绑定到当前对象 obj,使对象 obj 可以访问到构造函数的属性; - 如果构造函数返回一个对象,那么我们返回这个对象,否则返回对象 obj。
以 new 操作符调用构造函数的时候,函数内部实际上发生以下变化:
- 创建一个空对象,并且 this 变量引用该对象,同时还继承了该函数的原型。
- 属性和方法被加入到 this 引用的对象中。
- 新创建的对象由 this 所引用,并且最后隐式的返回 this.
function objectFactory() {
var obj = new Object()
var Constructor = [].shift.call(arguments) // 第一个参数就是构造函数
function F(){}
F.prototype = Constructor.prototype
obj = new F()
var result = Constructor.apply(obj, arguments)
return typeof result === 'object' ? result : obj
}
类式继承
子元素.prototype.__proto__ = 父元素.prototype
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对父类实例化,继承了父类的属性和方法,如果父类中有引用类型的属性,会被共享。一个子类实例对共享属性的修改会对其他实例的属性造成影响。 - 子类的继承是靠其原型
prototype属性对父类实例进行实例化的,因此不能对父类传递参数。
构造函数继承
function SuperClass() {
this.superValue = true;
}
SuperClass.prototype.getSuperValue = function() {
return this.superValue;
}
function SubClass() {
SuperClass.call(this, arguments)
}
缺点:
- 父类的原型方法不会被继承;
- 如果想被继承,就需要把
prototype放到构造函数中,这样创建的每个实例都会有一套单独的属性和方法,不能复用。
组合继承
function SuperClass() {
this.superValue = true;
}
SuperClass.prototype.getSuperValue = function() {
return this.superValue;
}
function SubClass() {
SuperClass.call(this, arguments)
}
SubClass.prototype = new SuperClass()
缺点:父类构造函数被调用2次。
原型式继承
function inheritObject(o) {
//声明一个过渡对象
function F() { }
//过渡对象的原型继承父对象
F.prototype = o;
//返回过渡对象的实例,该对象的原型继承了父对象
return new F();
}
缺点:
- 原型式继承和类式继承一个样子,对于引用类型的变量,还是存在子类实例共享的情况。
寄生式继承
function CreateObj(obj) {
var o = inheritObject(obj)
o.method = function(){}
return o
}
寄生组合式继承
function inheritObject(o) {
//声明一个过渡对象
function F() { }
//过渡对象的原型继承父对象
F.prototype = o;
//返回过渡对象的实例,该对象的原型继承了父对象
return new F();
}
function inheritPrototype(subClass,superClass) {
// 复制一份父类的原型副本到变量中
var p = inheritObject(superClass.prototype);
// 修正因为重写子类的原型导致子类的constructor属性被修改
p.constructor = subClass;
// 设置子类原型
subClass.prototype = p;
}