原型与继承
获取对象的原型
Object.getPrototypeOf()
let hd = {};
let xj = {};
console.log(Object.getPrototypeOf(hd) == Object.getPrototypeOf(xj));//true
console.log(Object.getPrototypeOf(hd) == Object.prototype);//true
创建对象并指定原型
Object.create();
创建一个新对象,第一个参数是这个对象的原型,第二个参数用以对对象的属性进行进一步描述。
//创建一个没有原型的对象
let hd = Object.create(null, {
name: {
value: '后盾人'
}
});
console.log(Object.getPrototypeOf(hd));//null
原型方法与对象方法优先级
如果对象自己有方法,则优先执行自己的方法
Object.prototype.render = function() {
console.log('parent');
};
let hd = {
render() {
console.log('child');
}
};
hd.render();//child
函数拥有多个长辈
函数拥有多个原型; 函数既是对象又是构造函数。
函数作为构造函数使用时,会给创建的对象自动指定一个原型对象 User.prototype
function User(){}
let hd = new User();
console.log(hd.__proto__ === User.prototype);//true
函数作为对象使用时,可以使用 User.__proto__
中的属性和方法。服务于函数自己
function User(){}
User.__proto__.view = function() {
console.log('view');
};
User.view();//view
原型关系详解
原型也是个对象,也会有自己的父级。
User.prototype.__proto__ 指向 Object.prototype
User.__proto__ .__proto__ 指向 Object.prototype
Object.prototype 没有原型,指向 null
function User(){}
Object.prototype.show = function(){
console.log('show');
};
User.show();//show
(new User()).show();//show
console.log(User.prototype.__proto__ === Object.prototype);//true
console.log(User.__proto__.__proto__ === Object.prototype);//true
console.log(User.prototype.__proto__ === User.__proto__.__proto__);//true
console.log(Object.prototype.__proto__ === null);//true
系统构造函数的原型体现
let arr = [];
console.log(arr.__proto__ == Array.prototype);//true
let str = '';
console.log(str.__proto__ == String.prototype);//true
自定义对象的原型设置
Object.setPrototypeOf();
let hd = {name: 'hd'};
let parent = {name: 'parent'};
Object.setPrototypeOf(hd, parent);
原型中的 constructor 引用
作用是,通过原型找到他的构造函数。
function User(){}
console.log(User.prototype.constructor == User);//true
//改变原型是 constructor 属性得加上,否则无法找到他的构造函数
User.prototype = {
constructor: User,
show() {
console.log('show');
}
}
根据对象创建新对象
constructor 属性。
function User(name) {
this.name = name;
this.show = function () {
console.log(this.name);
}
}
let hd = new User('后盾人');
hd.show();//后盾人
function createByObject(obj, ...args) {
const constructor = Object.getPrototypeOf(obj).constructor;
return new constructor(...args);
}
let xj = createByObject(hd, '向军');
xj.show();//向军
原型链
let a = {
name: 'a'
};
let c = {
name: 'c'
};
let b = {
name: 'b',
show() {
console.log(this.name);
}
};
Object.setPrototypeOf(a, b);
Object.setPrototypeOf(c, b);
a.show(); //a
c.show(); //c
原型链检测
instanceof
检测一个对象的原型链上是否有构造函数的prototype。
function User(){}
let hd = new User();
console.log(hd instanceof User);//true
console.log(hd instanceof Object);//true
isPrototypeOf
检测一个对象是否在另一个对象的原型链上。
let a = {name: 'a'};
let b = {name: 'b'};
let c = {name: 'c'};
console.log(b.isPrototypeOf(a));//false
Object.setPrototypeOf(a, b);
Object.setPrototypeOf(b, c);
console.log(b.isPrototypeOf(a));//true
console.log(c.isPrototypeOf(a));//true
属性检测
in
不仅检测当前对象,还会检测原型链上的属性。
let a = {url: 'houdunren'};
let b = {name: '后盾人'};
Object.prototype.web = 'hdcms.com';
console.log('web' in a);//true
hasOwnProperty
仅检测当前对象。
let a = {url: 'houdunren'};
let b = {name: '后盾人'};
Object.prototype.web = 'hdcms.com';
console.log(a.hasOwnProperty('web'));//false
借用原型链
call、apply
//#1
let hd = {
data: [1, 2, 3, 34, 5, 7]
};
Object.setPrototypeOf(hd, {
max(data) {
return data.sort((a, b) => b-a)[0];
}
});
let xj = {
lessons: {
js: 87,
php: 63,
node: 99,
linux: 88
}
};
console.log(hd.max.call(null, Object.values(xj.lessons)));//99
//#2
let hd = {
data: [1, 2, 3, 34, 5, 7]
};
console.log(Math.max.apply(null, hd.data)); //34
let xj = {
lessons: {
js: 87,
php: 63,
node: 99,
linux: 88
}
};
console.log(Math.max.apply(null, Object.values(xj.lessons))); //99
DOM节点借用Array原型方法
<button message="后盾人" class="red">后盾人</button>
<button message="hdcms">hdcms</button>
<script>
let btns = document.querySelectorAll('button');
btns = Array.prototype.filter.call(btns, function (item) {
return item.hasAttribute('class');
});
console.log(btns[0].innerHTML);//后盾人
</script>
合理的构造函数方法声明
可以把公用的方法声明在构造函数的原型prototype上,减少额外内存开销。
//#1
function User(name) {
this.name = name;
}
User.prototype.show = function () {
console.log(this.name);
}
User.prototype.get = function () {
console.log('...get');
}
let lisi = new User('李四');
let xj = new User('向军');
lisi.show();//李四
xj.show();//向军
//#2
function User(name) {
this.name = name;
}
User.prototype = {
constructor: User,
show: function () {
console.log(this.name);
},
get: function () {
console.log('...get');
}
}
let lisi = new User('李四');
let xj = new User('向军');
lisi.show(); //李四
xj.show(); //向军
this 和原型没有关系
this 永远指向调用属性的对象。
let hd = {
name: '后盾人'
};
let User = {
name: '向军',
show() {
console.log(this.name);
}
}
Object.setPrototypeOf(hd, User);
hd.show();//后盾人
不要滥用原型
不建议在系统的原型链上增加方法。
<button onclick="this.hide()">点我隐藏</button>
<script src="a.js"></script>
<script src="b.js"></script>
a.js
Object.prototype.hide = function() {
this.setAttribute("hide", true);
}
b.js
Object.prototype.hide = function() {
this.style.display = 'none';
}
Object.create 和 __proto__
Object.create 可以定义对象的原型,但是没有提供获取的方法。
而非标准的(浏览器厂商提供) __proto__ 既可以设置也可以获取。__proto__ 其实是一个 getter 和 setter,只能设置为对象类型(原型的上有做限制)。
使用 Object.setPrototypeOf 和 Object.getPrototypeOf 代替 __proto__。
改变构造函数原型并不是继承
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();
let m = new Member();
a.role();//member.role
m.role();//member.role
继承是原型的继承
#1
function User() {}
User.prototype.name = function () {
console.log('user.name');
}
function Admin() {}
Admin.prototype.__proto__ = 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');
}
let a = new Admin();
let m = new Member();
a.role();//admin.role
a.name();//user.name
m.role();//member.role
m.name();//user.name
#2
function User() {}
User.prototype.name = function () {
console.log('user.name');
}
function Admin() {}
Admin.prototype = Object.create(User.prototype, {
constructor: {
value: Admin
}
});
Admin.prototype.role = function() {
console.log('admin.role');
}
function Member() {}
Member.prototype = Object.create(User.prototype, {
constructor: {
value: Member
}
});
//Admin.prototype.constructor = Member;
Member.prototype.role = function() {
console.log('member.role');
}
let a = new Admin();
let m = new Member();
a.role();//admin.role
a.name();//user.name
m.role();//member.role
m.name();//user.name
方法重写和父属性访问
function User() {}
User.prototype.show = function () {
return 'user.show';
}
function Admin() {}
Admin.prototype = Object.create(User.prototype, {
constructor: {
value: Admin
}
});
Admin.prototype.show = function() {
return User.prototype.show() + '|admin.show';
}
let a = new Admin();
console.log(a.show());//user.show|admin.show
面向对象的多态
function User() {}
User.prototype.show = function () {
console.log(this.description());
}
function Admin() {}
Admin.prototype = Object.create(User.prototype, {
constructor: {
value: Admin
}
});
Admin.prototype.description = function() {
return '管理员';
}
function Member() {}
Member.prototype = Object.create(User.prototype, {
constructor: {
value: Member
}
});
Member.prototype.description = function() {
return '会员';
}
let a = new Admin();
let m = new Member();
a.show();//管理员
m.show();//会员
使用父类构造函数初始属性
function User(name, age) {
this.name = name;
this.age = age;
}
User.prototype.show = function() {
console.log(this.name, this.age);
}
function Admin(...args) {
User.apply(this, args)
}
Admin.prototype = Object.create(User.prototype, {
constructor: {
value: Admin
}
});
function Member(...args) {
User.apply(this, args)
}
Member.prototype = Object.create(User.prototype, {
constructor: {
value: Member
}
});
let xj = new Admin('向军', 18);
let lisi = new Member('李四', 19);
xj.show();//向军 18
lisi.show();//李四 19
封装继承
#1
function extend(sub, sup) {
sub.prototype = Object.create(sup.prototype, {
constructor: {
value: Member
}
});
}
function User(name, age) {
this.name = name;
this.age = age;
}
User.prototype.show = function() {
console.log(this.name, this.age);
}
function Admin(...args) {
User.apply(this, args)
}
function Member(...args) {
User.apply(this, args)
}
extend(Admin, User);
extend(Member, User);
let xj = new Admin('向军', 18);
let lisi = new Member('李四', 19);
xj.show();//向军 18
lisi.show();//李四 19
#2
function User(name, age) {
this.name = name;
this.age = age;
}
User.prototype.show = function() {
console.log(this.name, this.age);
}
function admin(name, age) {
const instance = Object.create(User.prototype);
User.call(instance, name, age);
return instance;
}
let hd = admin('向军', 18);
hd.show();//向军 18
使用 mixin 实现多继承
mixin
类是一个包含许多供其它类使用的方法的类。
mixin
类不用来继承做为其它类的父类。
function extend(sub, sup) {
sub.prototype = Object.create(sup.prototype);
sub.prototype.constructor = sub;
}
function User(name, age) {
this.name = name;
this.age = age;
}
User.prototype.show = function() {
console.log(this.name, this.age);
};
const Credit = {
total() {
console.log("统计积分");
}
};
const Request = {
ajax() {
console.log("请求后台");
}
};
function Admin(...args) {
User.apply(this, args);
}
extend(Admin, User);
Object.assign(Admin.prototype, Request, Credit);
let hd = new Admin("向军", 19);
hd.show();
hd.total(); //统计积分
hd.ajax(); //请求后台
super 关键字
super
是在 mixin
类的原型中查找,而不是在 User
原型中
function extend(sub, sup) {
sub.prototype = Object.create(sup.prototype);
sub.prototype.constructor = sub;
}
function User(name, age) {
this.name = name;
this.age = age;
}
User.prototype.show = function() {
console.log(this.name, this.age);
};
const Request = {
ajax() {
return "请求后台";
}
};
const Credit = {
__proto__: Request,
total() {
console.log(super.ajax() + ",统计积分");
}
};
function Admin(...args) {
User.apply(this, args);
}
extend(Admin, User);
Object.assign(Admin.prototype, Request, Credit);
let hd = new Admin("向军", 19);
hd.show();
hd.total(); //统计积分
hd.ajax(); //请求后台