原型、继承等相关问题
# 原型、继承等相关问题
# 对原型链的理解
JS 中,每个函数都有一个prototype
属性指向自己的原型对象,原型对象则有一个constructor
属性指向该函数。
当通过new
调用一个函数时,这个函数就作为了构造函数
,它返回一个实例对象,这个对象有一个__proto__
属性,也指向函数的原型对象。
因此,每个对象的__proto__
都指向其原型对象,形成了一条链,也就是原型链。几乎所有对象都是位于顶端的Object
的实例,Object
的proto指向null
,作为原型链的结束。
什么对象不是 Object 的实例呢?
对于这个问题,可以参考这篇文章 (opens new window)
简单来说就是自己进行构造:
const obj = Object.create(null);
Object.prototype.isPrototypeOf(obj); // false
2
# this
# this 的指向问题
对象的方法内的 this,通过对象调用函数时,指向调用的对象
直接调用的函数,指向全局对象
window
或global
通过 new 调用构造函数,内部的 this 指向新创建的对象
箭头函数:没有 this,内部作用域为函数声明时所在的上下文
var x = 11; var obj = { x: 22, say: () => { // 定义时的上下文为obj对象,obj的执行上下文是window console.log(this.x); //输出的值为11,即window.x }, }; var obj2 = { x: 22, say: function() { console.log(x); // 11定义时的上下文为obj2对象,obj2的执行上下文是window console.log(this.x); // 22 }, };
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
# 改变 this 的指向
三种改变的方法:
apply:将所有参数作为一个数组传入
call:将参数一个个传入
bind:将参数一个个传入。
bind 与前者的区别,调用后返回一个函数,而不是直接执行。
箭头函数因为没有 this,这三种方法不起作用
# new
三种创建对象的方式:
- 字面量
- new
- Object.create
接收两个参数,分别是新创建对象的原型对象,以及在新对象上添加的属性
# new 一个对象的过程
- 创建一个空对象
obj
,将obj.__proto__
指向构造函数的prototype
- 将
this
指向obj
- 执行构造函数中的代码
- 若函数没有用
return
返回其他对象,return obj
# 三者创建对象的区别
- 通过字面量或 new 创建的对象,原型指向 Object.prototype,会继承 Object 的属性和方法
- Object.create(null)创建的对象,其原型指向 null,即本身为原型链的顶端
# 继承的 5 种实现方式
- 在构造函数中使用 call 调用父类构造函数
- 缺点:只能继承父类自身的属性,其原型对象中的属性无法继承
function Parent1() {
this.name = "parent1";
}
function Child1() {
Parent1.call(this);
this.type = "child1";
}
2
3
4
5
6
7
- 原型链继承
- 可以复用父类构造函数原型上的属性
- 缺点:共享父类构造函数的引用类型的属性
function Parent2() {
this.name = "parent2";
this.play = [1, 2, 3];
}
function Child2() {
this.type = "child2";
}
Child2.prototype = new Parent2();
2
3
4
5
6
7
8
- 组合继承
- 可复用方法,不会共用引用属性
- 缺点:子类的对象,其 constructor 属性指向父类构造函数
function Parent3() {
this.name = "parent3";
this.play = [1, 2, 3];
}
function Child3() {
Parent3.call(this);
this.type = "child3";
}
Child3.prototype = new Parent3();
2
3
4
5
6
7
8
9
- 寄生组合继承
function Parent2() {
this.name = "parent2";
this.play = 1;
}
function Child2() {
this.type = "child2";
Parent2.call(this);
}
Child2.prototype = Object.create(Parent2.prototype, {
constructor: {
value: Child2,
},
});
2
3
4
5
6
7
8
9
10
11
12
13
- ES6 的 extend