全文没有废话,需字斟句酌
需要理解的两个重要概念
- 原型链只是js搜索对象属性的机制,和继承没有本质上的关系,你需要先掌握js是如何在原型链里搜索对象属性的
- 继承的本质是复用父对象的属性,而这些属性分为两类:实例属性和原型属性(位于原型链上的属性)
继承的最终目标:
子对象的实例属性包括:父对象的实例属性+子对象自定义的实例属性
子对象的原型属性:父原型的属性(__proto__和constructor不同,这是重点后面解释) + 子原型自定义的属性
理解Object.create()方法:
// 解析Object.create(o) 以o为原型创建对象(o作为新对象原型链上一级 且o没有实例属性)
function create(o) {
function F() {}
F.prototype = o;
return new F();
}
要理解Object.create()又得先理解new操作符:
// 解析new操作符
function myNew(Func) {
var o = new Object(); // 创建一个新对象
Func.apply(o); // 以新对象为环境执行构造函数
o.__proto__ = Func.prototype; // 新对象[[Prototype]]指向构造函数的原型对象
return o;
}
先说new操作符:
- Func.apply(o) 指定对象的实例属性 实例属性由new操作符的构造函数提供
- o.__proto__ = Func.prototype 建立原型链,指定对象的原型属性
接着就能看懂Object.create(o)的代码:
- 创建的对象以o为原型,o指定了对象的原型属性
- 以一个空函数定义实例属性 所以 新对象没有实例属性
最后再来看实现继承的逻辑就很清楚了:
- 子构造函数很简单 调用父构造函数就可以了
- 构造子原型对象sp是重点 sp要复用父原型的属性,然后sp.__proto__ = 父原型,sp.constructor = 子构造函数
复用父原型属性很简单,var sonPrototype = Object.create(Parent.prototype); 同时完成了原型链的连接
子构造函数本身复用了父构造函数,直接指定它即可 sonPrototype.constructor = Son;
具体代码如下,
// 定义对象 构造函数定义实例属性(一般是属性) 原型对象定义共享属性(一般是方法)
// 定义父对象
function Parent(name) {
this.name = name;
console.log('Parent');
}
Parent.prototype.sayName = function() {
console.log(this.name);
}
// 继承 借用父构造函数复用父实例属性 原型对象复用父原型属性 想要理解 必须理解Object.create()和new
// 定义子对象
function Son(name, age) {
Parent.call(this, name); // 复用父实例属性
this.age = age;
console.log('Son');
}
// 两步构造子原型对象
var sonPrototype = Object.create(Parent.prototype); // 提取出父原型属性 构造函数为空函数
sonPrototype.constructor = Son; // 构造函数改为子构造函数 复用父实例属性
Son.prototype = sonPrototype; // 将上面构造好的子原型对象赋给子原型
// 创建子对象
var s = new Son('mm', 18);