第十一章 原型
面经
Js使用原型链的优点?
优点:
- 内存开销:若以传统的new对象的方法,每一个对象创建都会申请内存地址,性能开销将非常巨大
- 父级区分:可以区分出其父级,部分设计模式中,比如工厂方法,无法直接检测到其工厂是谁
如何理解原型链?
原型对象拥有constructor,指向构造函数 构造函数可以通过new来创建实例,也可以通过prototype指针返回到原型对象上 创造的实例拥有proto指针,指向原型对象 本质上实例对象的proto指针和构造函数prototype指针指向的是一个东西——原型对象
以上是第一层,而当原型是:另一个基于原型模式创建的构造函数 其new出来的实例后————整个系统就可以串起来,形成原型链
基础
实现继承,方法接用。
先学做饭,再学订外卖。
Class类根本上还是原型。
★原型链
将构造函数作为对象调用时,使用__proto__原型,服务于对象自身
通过构造函数new 出来对象的时候,使用prototype。
原型链的核心是能够通过原型链中的一个来获取与原型链中其它元素的关系。
原型链相当于家谱。
__proto__对应父级 在chrome里显示为[[Prototype]]
prototype对应本级
所有对象原型链中的最顶层&原型链天花板(对象)
Object.prototype
原型链:
obj===>obj父亲===>obj爷爷 三级原型链
判断:使用Object.getPrototypeOf()判断原型
并非所有对象都有原型。
通过Object.creat(null,{})等方法自定义出来的变量是没有原型的。原型方法也全部无法使用。
当obj本身和父级拥有相同的方法时,优先使用obj本身方法。
自己有钱了就不要啃老了。就近原则。
console展开详细结构:
console.dir()
向父级添加属性(向指定原型添加数据):
obj.__proto__.render = function(){}
__proto__是每一个对象都具有的属性
函数拥有多个原型。
人拥有多个长辈。
构造函数具有.prototype 方法,指向的是构造函数的原型
当构造函数执行new操作,会把变量的__proto__重定向为构造函数的.prototype
构造函数里有prototype和__proto__两个属性,前者服务于new方法创建的示例;后者服务于构造函数本身。
通过构造函数构建的函数都指向Object下的prototype。
通过字面量形式创建的变量,原型也最终指向Objcet.prototype。
let str = "";
let bool = true;
let reg = /a/i;
let obj = {};
let arr = [];
obj.prototype.__proto__ ==Object.prototype
原型设置
自定义对象:
let yz = {};
let zh = {};
自定义使用方法:Object.setPrototypeOf(son,parent);
原型设置:Object.setPrototypeOf(a,b) 将a的原型设置为b
原型获取:Object.getPrototypeOf(obj);
原型检测
构造函数检测:
obj1.instanceof(obj2)
如果返回true,说明obj1的上方链条存在obj2
对象检测:
a.isPrototypeOf(b)
如果返回true,说明a是b原型链上侧的一份子
obj.__proto__ == Object.prototype
对象属性检测
1:obj.hasOwnProperty(attribute) 检测自身是否有该属性
2: a in b 使用in检测,不仅会检测b,还会检测b的原型链上是否有a属性
在forin遍历的时候,自动添加if(obj.hasOwnProperty(key))一行是出于只遍历当前属性的考虑。删去后说明for in 遍历
DOM节点借用
<div class="demo"name = "d" class>this is first </div>
<div class="demo"name = "d2" class="">this is second</div>
<div class="demo" name = "d"> this is third</div>
let divs = document.querySelectorAll("[name]");
let res = Array.prototype.filter.call(divs,(item)=>{
return item.hasAttribute("class");
});
console.log(res[0].innerHTML);
这里的Array.prototype 可以换为[]
构造函数的原型设置
对于同类型的数据,在对象的原型中添加方法而不是创建新的对象,这样可以减少不必要的内存消耗。
对原型设置属性而不是对对象设置属性
{
function User(name) {
this.name = name;
}
User.prototype = {
constructor: User,
get() {
console.log(this.name);
},
set(attr) {
let obj = { attr: String(attr) };
User.call(obj);
}
}
let yang = new User("yh");
let zhao = new User("zk");
yang.get()
zhao.get()
console.dir(User)
}
this与原型
没什么关系。
{
let hd = {
name :"yang",
show(){
console.log(this.age);
}
}
let yh = {
age :80
}
Object.setPrototypeOf(yh,hd);
yh.show();
}
this永远指向调用属性的对象, 只与调用相关
原型滥用
不建议在原型中追加其它方法。
因为需要引用其它人写的库,容易造成方法污染。只执行后加载的代码。
对象设置
设置对象的原型:
1 Object.creat(); 参数为设置的原型 创建的时候设置
对于原型,是设置原型的原型。
2 .__proto__ = xxx;
3 Object.setPrototypeOf(); 创建后设置
强制设置属性;
改变对象原型为null,通过Object.creat()方法。
继承
继承是原型的继承,而不是改变构造函数的原型。
1 对祖级继承。继承是父级的继承,设置的是祖级。
2 设置新的父级。父级的父级指向继承对象。
如果改变了构造函数的原型,那么原型里将删除constructor属性,所以在对构造函数原型设置的时候需要加上constructor属性。而此时打印出的原型实际上是继承的原型中的constructor
对于新增对象,继承设置的作用:
新增对象原型指向于初次创建时的原型设置。
在对象设置完毕后,改变其构造函数的设置对象原型不会改变新增的设置对象原型。即原有原型并不消失,而是继续被该新增对象所指定
当原型同时具有原型和对象两种身份,那么这个原型还有原型。
Constructor需要禁止被遍历。当定义constructor被定义的时候,可遍历为true。如果此时进行遍历,则会打印出原型链中其它的值。
父类方法不够使用时,可以重写方法,为子集定义方法。
子类当中有时也需要父类方法进行调用来完成需求。
多态
面向对象的多态继承
父类==父级==基类
1、先定义,再改变原型使用继承
2、一个父级,多个子集
3、使用this来进行相对判断
{
function User() { };
User.prototype.show = function () {
console.log(this.description());
}
function Admin() { };
Admin.prototype = Object.create(User.prototype);
Admin.prototype.description = function () {
return "管理人员账户"
}
function Enterprise() { };
Enterprise.prototype = Object.create(User.prototype);
Enterprise.prototype.description = function () {
return "企业账户"
}
function Member() { };
Member.prototype = Object.create(User.prototype);
Member.prototype.description = function () {
return "员工账户"
}
for (const obj of [new Admin, new Member, new Enterprise]) {
obj.show();
}
}
使用父类构造函数初始属性
调用父级方法,使用apply/call改变this指向
{
function father(name, age) {
this.name = name;
this.age = age;
}
function son(...args) {
father.apply(this, args);
}
let demo = new son("yang", 18);
console.log(demo.name, demo.age);
}
封装原型继承函数:
{
function Factor(sub, sup) {
sub.prototype = Object.create(sup.prototype);
Object.defineProperty(sub.prototype, "constructor", {
value: sub,
emumerable: false
})
}
function father(name, age) {
this.name = name;
this.age = age;
}
function son(...args) {
father.apply(this, args)
}
Factor(son, father);
let demo = new son("yang", "asdas");
console.log(demo.name, demo.age)
}
----------------------------------------------------
JS无多继承,实现多继承本质上是堆叠原型。
多继承实现(堆叠原型)
function Factor(sub, sup) {
sub.prototype = Object.create(sup.prototype);
Object.defineProperty(sub.prototype, "constructor", {
value: sub,
emumerable: false
})
}
function address() {};
address.prototype.addd = function () {
console.log("请求地址")
};
function request() { };
Factor(request, address);
request.prototype.ajax = function () {
console.log("请求后台");
};
function User(name, age) {
this.name = name;
this.age = age;
}
Factor(User, request);
function Admin(name, age) {
User.call(this, name, age);
}
Factor(Admin, User);
let demo = new Admin("yang", 14);
demo.ajax();
demo.addd();
console.log(demo.name, demo.age);
mixin方法
原理:定义多个功能对象,当使用时通过assign合并到一起。
{
function Factor(sub, sup) {
sub.prototype = Object.create(sup.prototype);
Object.defineProperty(sub.prototype, "constructor", {
value: sub,
emumerable: false
})
}
const address = {
get_add() {
console.log("get_add");
}
}
const request = {
get_request() {
console.log("get_reque");
}
}
function User(name, age) {
this.name = name;
this.age = age;
}
User.prototype = Object.assign(User.prototype, address, request);
function Admin(name, age) {
User.call(this, name, age);
}
Factor(Admin, User);
let demo = new Admin("yang", 14);
demo.get_add();
demo.get_request();
console.log(demo.name, demo.age);
}
super
super = this.__proto__;
super指的是本质上调用this的对象。
{
function Factor(sub, sup) {
sub.prototype = Object.create(sup.prototype);
Object.defineProperty(sub.prototype, "constructor", {
value: sub,
emumerable: false
})
}
const request = {
get_request() {
return "get request!";
},
};
const address = {
__proto__: request,
get_address() {
console.log("get_address");
},
show(){
console.log(super.get_request());
}
};
function User(name, age) {
this.name = name;
this.age = age;
}
Object.assign(User.prototype, address, request);
function Admin(name, age) {
User.call(this, name, age);
}
Factor(Admin, User);
let user = new Admin("yang", 14);
user.show();
}
}