原型
认识原型
在JavaScript中,原型也是一个对象,通过原型可以实现对象的属性继承,JavaScript的对象中都包含了一个” [[Prototype]]”内部属性,这个属性所对应的就是该对象的原型。
“[[Prototype]]”作为对象的内部属性,是不能被直接访问的。所以为了方便查看一个对象的原型,Firefox和Chrome中提供了”__proto__
“这个非标准(不是所有浏览器都支持)的访问器(ECMA引入了标准对象原型访问器”Object.getPrototype(object)”)。
prototype
和 __proto__
- 对于所有的对象,都有
__proto__
属性,这个属性对应该对象的原型 - 对于函数对象,除了
__proto__
属性之外,还有prototype
属性,当一个函数被用作构造函数来创建实例时,该函数的prototype属性值将被作为原型赋值给所有对象实例(也就是设置实例的__proto__
属性)
constructor
从上面的图我们可以注意到,每个原型对象还有一个属性:constructor
,constructor是对象才有的属性。原型对象的constructor属性指向的是我们自己定义的构造函数,或者是内置的构造函数。
构造函数
- 构造函数是专门用来创建对象的函数
- 一个构造函数我们也可以称为一个类
- 通过一个构造函数创建的对象,我们称该对象时这个构造函数的实例
- 通过同一个构造函数创建的对象,我们称为一类对象
- 构造函数就是一个普通的函数,只是他的调用方式不同
- 如果直接调用,它就是一个普通函数
- 如果使用
new
来调用,则它就是一个构造函数
function Person(fName,lName,age){
this.name = fName + lName;
this.age = age;
this.show = function(){
console.log(this.name);
}
}
let p1 = new Person("f","z",20)
console.log(instanceof p1) // Person
为了避免创建对象时方法的重复创建导致浪费内存,可以将方法放入构造函数的原型中
function Person(fName,lName,age){
this.name = fName + lName;
this.age = age;
}
Person.prototype.show = function(){
console.log(this.name);
};
let p1 = new Person("f","z",20);
let p2 = new Person("z","w",21);
p1.show();
p2.show();
console.log(p1.show == p2.show)
原型链
什么是原型链
因为每个对象和原型都有原型,对象的原型指向对象的父,而父的原型又指向父的父,这种原型层层连接起来的就构成了原型链。
属性查找
当查找一个对象的属性时,JavaScript 会向上遍历原型链,直到找到给定名称的属性为止,到查找到达原型链的顶部,如果仍然没有找到指定的属性,就会返回 undefined。比如:
function Person(name, age){
this.name = name;
this.age = age;
}
var p = new Person("fff", 20);
console.log(p.School);// undefined
Person.prototype.School = "SZU";
console.log(p.School);// SZU
hasOwnProperty()
- 这个方法可以用来检查对象自身是否含有某个属性
- 不会向上查找原型链
- 语法:
对象.hasOwnProperty(“属性名”)
原型和原型链的总结
- 原型: 可以理解为是对象的
__proto__
属性或者是函数的prototype
属性所指向的对象 - 原型对象: 构造函数的
prototype
属性所指的对象 - 原型链: 对象的
__proto__
访问器属性,或者[Prototype]内部属性指向的逐级向上最终为null的链式路径,大概的意思就是object.__proto__
(父级).__proto__
(祖父级).null(最后的__proto__
)
继承
原型链继承
function User() {}
//给父类型的原型上增加一个show方法
User.prototype.show = function () {
console.log("user.name")
};
//子类型
function Admin() {}
// 子类型的原型为父类型的一个实例对象
Admin.prototype = Object.create(User.prototype);
// 让子类型的原型的constructor指向子类型
Admin.prototype.constructor = Admin;
//给子类型的原型上增加一个[showSubProp]方法,打印自身subProp
let u1 = new Admin();
u1.show();// output: "user.name"
组合继承
使用call()
使用父类构造函数初始属性。
function User(name, age) {
this.name = name;
this.age = age;
}
User.prototype.show = function () {
console.log("user.name")
};
//子类型
function Admin(name, age) {
User.call(this, name, age);
}
// 子类型的原型为父类型的一个实例对象
Admin.prototype = Object.create(User.prototype);
let u2 = new Admin("fmou", 20);
u2.show(); // output: "user.name"
多态
function User() {}
User.prototype.show = function () {
console.log(this.description());
};
function Admin() {}
Admin.prototype = Object.create(User.prototype);
Admin.prototype.constructor = Admin;
Admin.prototype.description = function() {
return "我是管理员";
}
function Member() {}
Member.prototype = Object.create(User.prototype);
Member.prototype.constructor = Admin;
Member.prototype.description = function() {
return "我是会员";
}
function Enterprise() {}
Enterprise.prototype = Object.create(User.prototype);
Enterprise.prototype.constructor = Admin;
Enterprise.prototype.description = function() {
return "企业账号";
}
for (const obj of [new Admin, new Member, new Enterprise]) {
obj.show();
}
// output:
// 我是管理员
// 我是会员
// 企业账号
使用mixin实现多继承
mixin 可以理解为一个混合功能
function extend(sub, sup) {
sub.prototype = Object.create(sup.prototype);
Object.defineProperty(sub.prototype, "constructor",{
value: sub,
enumerable: false
});
}
const Address = {
getAddress() {
console.log("获取收货地址");
}
};
const Credit = {
total() {
console.log("积分统计");
}
};
const Request = {
ajax() {
console.log("请求后台");
}
}
function User(name, age) {
this.name = name;
this.age = age;
}
User.prototype.show = function () {
console.log("user.name")
};
function Admin(name, age) {
User.call(this, name, age);
}
extend(Admin, User);
Admin.prototype = Object.assign(Admin.prototype, Address, Credit, Request);
let admin = new Admin("zz", 20);
admin.show();// "user.name"
admin.ajax();// "请求后台"
admin.total();// "积分统计"
admin.getAddress();// "获取收货地址"
内部继承和super
关键字
super
指向的是当前对象的原型对象。
function extend(sub, sup) {
sub.prototype = Object.create(sup.prototype);
Object.defineProperty(sub.prototype, "constructor",{
value: sub,
enumerable: false
});
}
const Request = {
ajax() {
return "请求后台";
}
}
const Credit = {
__proto__: Request,
total() {
console.log(super.ajax() + ": 积分统计");
}
};
function User(name, age) {
this.name = name;
this.age = age;
}
User.prototype.show = function () {
console.log("user.name")
};
function Admin(name, age) {
User.call(this, name, age);
}
extend(Admin, User);
Admin.prototype = Object.assign(Admin.prototype, Credit, Request);
let admin = new Admin("zz", 20);
admin.show();// "user.name"
admin.total();// "请求后台: 积分统计"
GitHub Issues