本文已参与「新人创作礼」活动, 一起开启掘金创作之路。
原型模式
每一个函数都会创建一个prototype属性,这个属性是一个对象,这个对象也就是原型对象,事实上原型对象就是通过调用构造函数创建的对象实例的原型,所有的经过调用同一个构造函数而产生的对象实例都共享原型对象上的属性和方法。
然而每当调用构造函数创建一个实例的时候,这个实例内部会有一个[[prototype]]指向其构造函数的原型,我们把它叫做隐式原型,也写作_proto_,隐式原型和原型的存在从而构成了原型链
原型链
如上图是一个完整的原型链的示意图,在这里我觉得需要注意以下几点
- 构造函数Function的原型指向他自己,Object原型的隐式原型指向NULL
- 要记住“万物皆对象”,函数同时也是对象;
- 每一个原型对象当然都是一个对象,那么就是通过
new Object()从而实现创造对象,所以它们的隐式原型都指向其构造函数Object的原型 - 自定义函数是通过
new Function()实现的,所以自定义函数的隐式原型指向其构造函数Function的原型.
原型链的问题:
- 在原型中包含引用值时
实例属性变成了原型属性,那么构造的新的实例之间的改变,会影响下面的所有实例
function FatherClass() {
this.color = ["blue", "green", "yellow"];
}
function SonClass() {}
//继承
SonClass.prototype = new FatherClass();
var son1 = new SonClass();
son1.color.push("orange");
var son2 = new SonClass();
console.log(son1.color, son2.color);
console.log(son1.color === son2.color); //true
- 子类型实例化时不能给父类型的构造函数传参
这个很显然是无法传参的
解决原型链的问题
盗用构造函数(基本上不单独使用)
为了解决原型包含引用值导致得继承问题,从而有一种技术用于解决,叫做"盗用构造函数"(也叫"对象伪装","经典继承");
- 解决引用值得问题 基本思路:
在子类得构造函数中调用父类的构造函数,使用 apply()和 call()方法以新创建的对象为上下文,执行构造函数
// 代码段
function FatherClass() {
this.color = ["blue", "green", "yellow"];
}
function SonClass() {
FatherClass.call(this);
}
var son1 = new SonClass();
son1.color.push("orange");
var son2 = new SonClass();
console.log(son1.color, son2.color);
- 传递参数
还是改变 this 指向,达到目的
function FatherClass(name) {
this.name = name;
this.age = 40;
}
function SonClass() {
// 传入参数
FatherClass.call(this, "李华");
this.age = 18;
}
var person = new SonClass();
console.log(person.name); //李华
console.log(person.age); //18
组合继承
就是集合原型链和盗用构造函数的优点, 基本思路:
将实例属性就写在构造函数上,将方法写在原型上,然后从两个方面达到全部继承的目的
原型链的继承
- 方式一:
//直接封装一个继承函数
function inherit(Target, Origin) {
Target.prototype = Origin.prototype;
}
inherit(Son, Father);
- 方式二:
圣杯模式
//引入一个函数F(),充当中间量
function inherit(Target, Origin) {
function F() {
F.prototype = Origin.prototype;
Target.prototype = new F();
Target.prototype.constuctor = Target; //把目标的constuctor改到他本身上
Target.prototype.uber = Origin.prototype;
}
}
function Father() {}
function Son() {}
inherit(Son, Father);
var son = new Son();
var father = new Father();
Son.prototype.sex = male;
- 推荐使用的方法:
var inherit = (function () {
var F = function () {};
return function (Target, Origin) {
F.prototype = Origin.prototype;
Target.prototype = new F();
Target.prototype.constuctor = Target; //把目标的constuctor改到他本身上
Target.prototype.uber = Origin.prototype;
};
})();
相比较而言,方式 1 的话 Son 与 Father 的原型指向的地址是同一个,如果想要给 Son 添加,那么 Father 也会受到影响。
利用原型链的知识创建没有隐式原型的对象(三种)
// 创建一个没有隐式原型的用户对象,随意添加一些属性
//两种方式创建一个没有隐式原型的对象(可以提升效率)
//不推荐
// var person = {
// name: "力宏",
// price: "112",
// };
// person.__proto__ = null;
// console.log(person.__proto__);
// var q = Object.create(null);
// q.a = 2;
// q.c = 1;
// console.log(q);
var obj = {
a: 1,
b: 2,
};
Object.setPrototypeOf(obj, nulls);