原型

认识原型

在JavaScript中,原型也是一个对象,通过原型可以实现对象的属性继承,JavaScript的对象中都包含了一个” [[Prototype]]”内部属性,这个属性所对应的就是该对象的原型。

“[[Prototype]]”作为对象的内部属性,是不能被直接访问的。所以为了方便查看一个对象的原型,Firefox和Chrome中提供了”__proto__“这个非标准(不是所有浏览器都支持)的访问器(ECMA引入了标准对象原型访问器”Object.getPrototype(object)”)。

prototype__proto__

  • 对于所有的对象,都有__proto__属性,这个属性对应该对象的原型
  • 对于函数对象,除了__proto__属性之外,还有prototype属性,当一个函数被用作构造函数来创建实例时,该函数的prototype属性值将被作为原型赋值给所有对象实例(也就是设置实例的__proto__属性)

constructor

从上面的图我们可以注意到,每个原型对象还有一个属性:constructor,constructor是对象才有的属性。原型对象的constructor属性指向的是我们自己定义的构造函数,或者是内置的构造函数。

构造函数

  • 构造函数是专门用来创建对象的函数
  • 一个构造函数我们也可以称为一个类
  • 通过一个构造函数创建的对象,我们称该对象时这个构造函数的实例
  • 通过同一个构造函数创建的对象,我们称为一类对象
  • 构造函数就是一个普通的函数,只是他的调用方式不同
    • 如果直接调用,它就是一个普通函数
    • 如果使用new来调用,则它就是一个构造函数
1
2
3
4
5
6
7
8
9
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

为了避免创建对象时方法的重复创建导致浪费内存,可以将方法放入构造函数的原型中

1
2
3
4
5
6
7
8
9
10
11
12
13
14
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。比如:

1
2
3
4
5
6
7
8
9
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__)

继承

原型链继承

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
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()使用父类构造函数初始属性。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
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"

多态

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
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 可以理解为一个混合功能

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
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指向的是当前对象的原型对象。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
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();// "请求后台: 积分统计"