什么是原型
你没车,你爸有车,你借着开就是原型
有些对象可以没有原型
(Object.create(null,{}))
如上创建的方法都没有
据说Vue中创建对象都是这么创建的
原型链调用方式
- a.__ proto __(当函数当作对象调用时):自己用的
- b.prototype(函数实例化出来之后自动给新创建对象指定原型):用来给别人的只有函数有
简而言之,第一个是自己本身的,第二个是当他作为构造函数时候用的
向军大叔的图:
这里很重要所以自己跑一下demo
let ABC = function () {
console.log('我是abc');
}
ABC.prototype.name = function () {
console.log('我是sunyf');
}
let def = new ABC;
def.name();
console.log(def);
console.log(def.__proto__);
console.log(ABC.prototype);
console.dir(ABC.__proto__);
原型方法与对象方法优先级
就近原则,自己有就用自己的
自定义对象的原型设置
- Object.setPrototypeOf(孤儿, 领养者);//设置
- Object.getPrototypeOf(孤儿);//获取
原型中的constructor引用
- 原型:
User.prototype.constructor == User
- 新增原型: User.prototype.name = function
User.prototype.__proto__ == Object.prototype
所以可以通过创建出来的对象找出创建它他的构造函数,从而创建更多的对象
- 一个构造函数的prototype的constructor是他自己(因为它用来给别人构造时候应该调用生成它的方法)
- 同样生成出来的新对象的constructor也是生成对象的构造函数
- 大家的prototype都是一个当方面不断增加更新(这句不知道对不对)
- 对象自己找原型用__proto__构造函数找用.prototype
一个Object一个世界
function User(name) {
this.name = name;
}
console.dir(User);
console.dir(User.prototype);
console.dir(User.prototype.__proto__);
console.dir(User.prototype.constructor);
let s = new User('sss');
console.dir(s);
console.dir(Object.getPrototypeOf(s).constructor==User);
原型链检测
- instanceof
- 运算符用来测试一个对象在其原型链中是否存在一个构造函数的 prototype 属性。
console.log(a instanceof A)
查看a的原型链上是否有A
- isPrototypeOf()
方法用于测试一个对象是否存在于另一个对象的原型链上。
console.log(b.isPrototypeOf(a));
属性检测
- in
检测当前对象中是否有该属性/是否在原型链上
name" in a
- hasOwnProperty
检测当前对象是否有该属性
a.hasOwnProperty(key)
使用call或apply借用原型链
apply:
let ss = {
data: [1, 2, 3, 34, 5, 7]
};
Object.setPrototypeOf(ss, {
max() {
return this.data.sort((a, b) => b - a)[0];
}
});
console.log(ss.max());
let xj = {
lessons: { js: 87, php: 63, node: 99, linux: 88 },
// getter
get data() {
return Object.values(this.lessons);
}
};
console.log(ss.max.apply(xj));
借用方法优化
let ss = {
lessons: { js: 87, php: 63, node: 99, linux: 88 }
};
console.log(Math.max.apply(null, Object.values(ss.lessons)));
DOM元素的借用方法优化
let btns = document.querySelectorAll("button");
btns = [].filter.call(btns, item => {
return item.hasAttribute("class");
});
console.log(btns);
合理的构造函数方法声明
function User(name) {
this.name = name;
}
User.prototype = {
constructor: User,
show() {
console.log(this.name);
},
get() {
console.log("get..");
}
};
let lisi = new User("李四");
let ss = new User("ss");
lisi.show();
ss.show();
为一个对象自定义原型
- Object.create(官方):无法获取只能定义
let ss = Object.create(user, {
name: {
value: "sss"
}
});
console.log(ss.show());
- __proto(非官方,浏览器厂商定义):可以定义以及获取
let ss = { name: "ss" };
ss.__proto__ = user;
console.log(ss.__proto__);
- Object.setPrototypeOf(官方,设置)
Object.setPrototypeOf(ss, user);
- Object.getPrototypeOf(官方,查询)
console.log(Object.getPrototypeOf(ss));
__proto__底层为属性访问器
其实就是一个getter与setter只能接收对象为参数,无法接受字符串等
let ss = {
action: {},
get proto() {
return this.action;
},
set proto(obj) {
if (obj instanceof Object) {
this.action = obj;
}
}
};
ss.proto = { view: function() {} };
ss.proto = "abc";
console.log(ss.proto);
如果想设置:let ss = Object.create(null);
即可
原型的继承,不是改变构造函数的原型
如下例子:如果这样继承,每个人都继承了user的prototype,导致再次添加方法会导致其他构造函数内部方法的混乱故错误
function User() { }
User.prototype.name = function () {
console.log("user.name");
};
function Admin() { }
Admin.prototype = User.prototype;
Admin.prototype.role = function () {
console.log("admin.role");
};
function Member() { }
Member.prototype = User.prototype;
Member.prototype.role = function () {
console.log("member.role");
};
let a = new Admin();
a.role();
继承是原型的继承
也就是说,是爸爸继承别人的爸爸变成爷爷
而不是换一个新爸爸
优点:
避免方法冗余,并且不会重名
如上代码可以改写为:
function User() { }
User.prototype.name = function () {
console.log("user.name");
};
function Admin() { }
// Admin.prototype.__proto__ = User.prototype;
Admin.prototype = Object.create(User.prototype);
Admin.prototype.role = function () {
console.log("admin.role");
};
function Member() { }
Member.prototype.__proto__ = User.prototype;
Member.prototype.role = function () {
console.log("member.role");
};
ps:用Object.create时候要注意顺序
注意 在改变原型指向之后记得给一个constructor(注意设置不可遍历)
js多态
可优化代码,仅执行show方法
function User() { }
User.prototype.show = function () {
console.log(this.description());
};
function Admin() { }
// Admin.prototype = Object.create(User.prototype);
Admin.prototype.showAdmin = function () {
return "管理员在此";
};
function Member() { }
// Member.prototype = Object.create(User.prototype);
Member.prototype.showMember = function () {
return "我是会员";
};
function Enterprise() { }
// Enterprise.prototype = Object.create(User.prototype);
Enterprise.prototype.showEnterprise = function () {
return "企业帐户";
};
for (const obj of [new Admin(), new Member(), new Enterprise()]) {
if (obj instanceof Admin) {
console.log(obj.showAdmin());
} else if (obj instanceof Member) {
console.log(obj.showMember());
} else if (obj instanceof Enterprise) {
console.log(obj.showEnterprise());
}
}
对象工厂派生对象并实现继承
function User(name, age) {
this.name = name;
this.age = age;
}
User.prototype.show = function () {
console.log(this.name, this.age);
};
let obj = {};
console.log(obj.__proto__ == Object.prototype);
function admin(name, age) {
const instance = Object.create(User.prototype);
User.call(instance, name, age);
instance.role = function () {
console.log("role");
};
return instance;
}
let ss = admin("ss", 19);
ss.show();
ss.role();
function member(name, age) {
const instance = Object.create(User.prototype);
User.call(instance, name, age);
return instance;
}
let lisi = member("李四", 23);
lisi.show();
多继承问题
js语言不支持多继承,只能让父级的原型继续继承所需要的原型
使用mixin实现多继承
每一个功能都设置成一个对象
由于prototype也是一个对象 往里面推就好(object.assign)
super关键字
super == this.proto(当前对象的原型而不是调用者的原型)