原型
在JS里,万物皆对象。方法(Function)是对象,方法的原型(Function.prototype)是对象。因此,它们都会具有对象共有的特点。即:对象具有属性__proto__,可称为隐式原型,一个对象的隐式原型指向构造该对象的构造函数的原型,这也保证了实例能够访问在构造函数原型中定义的属性和方法。
- 可以将原型理解为对象的父亲,对象从原型对象继承来属性
- 原型就是对象除了是某个对象的父母外没有什么特别之处
- 所有函数的原型默认是 Object的实例,所以可以使用toString/toValues/isPrototypeOf 等方法的原因
- 原型对象为多个对象共享属性或方法
-如果对象本身不存在属性或方法将到原型上查找
-使用原型可以解决,通过构建函数创建对象时复制多个函数造成的内存占用问题
-原型包含 constructor 属性,指向构造函数
-对象包含___proto___指向他的原型对象
let xz = {
render() {
//如果自己有这个方法,就不会调用原型里的方法
console.log('xz,render');
}
}
//在xz的原型上添加这个方法,其他对象也会有,因为是将这个方法添加到了他父亲上
xz.__proto__.render = function() {
console.log('render');
}
let lsy = {}
xz.render()
lsy.render()
console.log(xz);
对象里是有__proto__
一个函数里既有prototype也有__proto__
以下例为例:
其中当User为对象的时候,就是用__proto__
当User为函数的时候,就是prototype,
hd的__proto__和user的prototype 是同一个东西,也就是说在user.prototype中添加方法,hd可以使用,但是如果在user.__proto中添加方法,hd不能使用
function User() {}
User.__proto__.view = function() {
console.log("User function view method");
};
// __proto__是用于对象
// User.view(); //静态调用
//prototype是用于函数
User.prototype.show = function() {
console.log("后盾人");
};
let hd = new User();//hd为user的实例化对象
//hd的__proto__和user的prototype 是同一个东西
console.log(User.prototype == hd.__proto__); //true
User.view()//User function view method
hd.view() //报错
hd.show();//后盾人
原型链
- 对象有属性__proto__,指向该对象的构造函数的原型对象。
- 方法除了有属性__proto__,还有属性prototype,prototype指向该方法的原型对象。
- 原型对象里就存储了,实例化后的对象可以调用的方法
知乎 - 原型链::js中__proto__和prototype的区别和关系
自定义对象的原型
Object.setPrototypeOf()
let hd = {
name: 'hd'
}
let parent = {
name: 'parent',
show() {
console.log('parent method:' + this.name);
}
}
Object.setPrototypeOf(hd, parent)
console.log(hd);
hd.show() //parent method:hd
parent.show() //parent method:parent
console.log(Object.getPrototypeOf(hd));
通过对象找出他的构造函数在创建对象
function User(name) {
this.name = name,
this.show = function() {
console.log('this:' + this.name);
}
}
let xz = new User('xiaozhuo')
function createByObject(obj, ...args) {
const constructor = Object.getPrototypeOf(obj).constructor
let fn = obj.__proto__.constructor
console.log(fn == constructor); //true
console.log(fn);
return new fn(...args)
}
let lsy = createByObject(xz, 'luoshiyi')
lsy.show()
console.log(lsy);
isPrototypeOf和 instanceof 的区别
- A.isPrototypeOf(B) 判断的是A对象是否存在于B对象的原型链之中
- A instanceof B 判断的是B.prototype是否存在与A的原型链之中
所以就有下面的结论:
如果 A.isPrototypeOf(B) 返回true 则B instanceof A 一定返回true
使用call或者apply借用其他对象的原型
let xz = {
name: 'xzzz',
data: [33, 1, 2, 3, 45, 67, 888, 523]
}
Object.setPrototypeOf(xz, {
car: function() {
console.log('show car');
},
max: function() {
// console.log(this.data);
this.data.sort((a, b) => {
return b - a
})
return this.data[0]
}
})
console.log(xz.max());
let lsy = {
name: 'luoshiyi',
data: [, 111, 333, 22, 1, 55, 78, 999]
}
console.log(xz.max.call(lsy));
Dom节点借用数组原型
<body>
<button message="后盾人" class="red">后盾人</button>
<button message="hdcms">hdcms</button>
</body>
<script>
let btns = document.querySelectorAll("button");
btns = Array.prototype.filter.call(btns, item => {
return item.hasAttribute("class");
});
</script>
原型的继承
下例中:people的prototype继承user的property
function user() {}
user.prototype = {
constructor: user,
usershow: function() {
console.log('继承成功user', this.name);
}
}
let user1 = new user()
// console.log(user1);
function people(name) {
this.name = name
this.peopleshow = function() {
console.log('peopleshow');
}
}
people.prototype = {
constructor: people,
peopleshowpro: function() {
console.log('peopleshowpro');
}
}
//people.prototype.__proto__原本是指向最终的Object.property,现在把他改为user.prototype
people.prototype.__proto__ = user.prototype
let people1 = new people('xz')
people1.usershow() //继承成功user xz
people1.peopleshow() //peopleshow
people1.peopleshowpro() //peopleshowpro
Object.create()
通过Object.create()实现原型的继承
- Object.creat()方法创建一个新对象,新创建的对象的__proto__为creat()里的对象。
//使用Object.create是使Member.prototype.__proto__为User.prototype
Member.prototype = Object.create(User.prototype);
console.log(Member.prototype.__proto__ == User.prototype);//true
Member.prototype = User.prototype
console.log(Member.prototype == User.prototype);//true
使用第一个参数的对象,作为新对象的原型
- 因为有时根据得到的对象获取构造函数,然后再创建新对象所以需要保证构造函数存在,但如果直接设置了 Admin.prototype 属性会造成constructor丢失,所以需要再次设置constructor值。
function User() {}
User.prototype.getUserName = function() {};
function Admin() {}
Admin.prototype = Object.create(User.prototype);
Admin.prototype.constructor = Admin;//需要重新设置constructor
Admin.prototype.role = function() {};
function Member() {}
Member.prototype = Object.create(User.prototype);
//这里就是Member.prototype.__proto__==User.prototype
console.log(Member.prototype.__proto__ == User.prototype);//true
Member.prototype.email = function() {};
console.log(new Admin());
console.log(new Member());
使用父类构造函数构建初始属性
我们希望调用父类构造函数完成对象的属性初始化,但像下面这样使用是不会成功的。因为此时 this 指向了 window,无法为当前对象声明属性。
function User(name) {
this.name = name;
console.log(this); // Admin
}
User.prototype.getUserName = function() {
return this.name;
};
function Admin(name) {
User.call(this, name);
}
Admin.prototype = Object.create(User.prototype);
let xj = new Admin("向军大叔");
console.log(xj.getUserName()); //向军大叔
使用原型工厂封装继承
function extend(sub, parent) {
sub.prototype = Object.create(parent.prototype)
// sub.prototype.constructor = sub
Object.defineProperty(sub.prototype, 'constructor', {
value: sub,
enumerable: false
})
}
function user(name, age) {
this.name = name
this.age = age
this.show = function() {
console.log(`${this.name}+${this.age}`);
}
}
function people(...args) {
user.apply(this, args)
}
extend(people, user)
let xz = new people('xiaozhuo', 18)
console.log(xz);
xz.show()
使用Mixin模式实现多继承
这相当于将对象的prototype与要继承的对象进行合并,
注意:Object.assign()是浅拷贝
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);
};
// function Credit() {}
// Credit.prototype.total = function() {
// console.log("统计积分");
// };
//将原型对象里面的方法改写成对象里的方法
const Credit = {
total() {
console.log("统计积分");
}
};
const Request = {
ajax() {
console.log("请求后台");
}
};
function Admin(...args) {
User.apply(this, args);
}
extend(Admin, User); //Admin真正的继承User
console.log(Admin.prototype.__proto__ == User.prototype); //ture
Object.assign(Admin.prototype, Request, Credit); //将Admin.prototype与Request和Credit进行对象合并,如下图
console.dir(Admin);
let hd = new Admin("向军", 19);
hd.show();
hd.total(); //统计积分
hd.ajax(); //请求后台
super关键字
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(this.__proto__.ajax()+ ",统计积分")
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(); //请求后台