前端基础回顾-1-原型和原型链

兜兜转转工作了也有快两年了,最终还是决定拾起当初的js基础。
毕竟完全理解它才能更加深入前端这个行业,技术才能有更大的突破。

本文是重学基础的第一篇,关于原型和原型链

开始

最近想尝试阅读一下axios的源码,参考大佬的架构文章,在看到第一个函数时就有点吃力 😓。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
function createInstance(defaultConfig) {
const context = new Axios(defaultConfig);

const instance = bind(Axios.prototype.request, context);

// Copy axios.prototype to instance
utils.extend(instance, Axios.prototype, context, {allOwnKeys: true});

// Copy context to instance
utils.extend(instance, context, null, {allOwnKeys: true});

// Factory for creating new instances
instance.create = function create(instanceConfig) {
return createInstance(mergeConfig(defaultConfig, instanceConfig));
};

return instance;
}
const axios = createInstance(defaults)

axios.xxx = xxx
// ...

平常在使用的时候就发现,axios导出的实例有非常多的写法以及api
axios.post()
axios()
axios.create
似乎是更改了实例的原型链关系。

图片来源 学习 axios 源码整体架构,打造属于自己的请求库

概念

  • 原型
    js当中,每一个对象(除了null)都会有一个关联对象,像是父子关系,它就是原型。
    对象都会从原型上面继承它的属性。
  • 原型链
    连续关联的原型即是原型链。
    比如A -> B -> Object
  • 基础类型和引用类型
    基础类型和引用类型的区别就是在堆栈中的存储方式不同。
    基础类型存储的是他的值。
    引用类型存储的是他的值的引用。
    比如下面这样
1
2
3
4
5
6
7
8
9
10
var a = 100 
var b = a
a = 200
console.log(a, b) // 200 100

var objA = { a: 100 }
var objB = objA
objA.a = 200
// 引用类型的值只是一个引用的地址,当实际值发生变化时,所有引用地址的值都会发生变化
console.log(objA.a, objB.a) // 200 200
  • __proto__
    所有的引用类型都有一个__proto__属性,一个普通的对象。
1
2
3
4
{
constructor: xxx,
__proto__: xx
}
  • prototype
    __proto__指向向了它的构造函数的prototype属性
1
2
3
{
constructor: xxx,
}

根据上面的图可以看到const obj = {}__proto__指向的就是构造函数Objectprototype
当试图去获取一个当前值上不存在的某个属性时,它会去它的__proto__上找,一直往上,直到为null为止(最顶层为null)。

每个对象都有__proto__对象,只有函数对象才会有prototype对象。

构造函数的原型链关系

记录一下完整的构造函数的原型链关系

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
class Parent {

name = "parent"

getName() {
return this.name
}

}

class Child extends Parent {

name = "child"

}

const parent = new Parent()
const child = new Child()

console.log(parent.__proto__ === Parent.prototype) // true
console.log(child.__proto__ === Child.prototype) // true
console.log(child.__proto__ === Parent.prototype) // false
console.log(Child.prototype === Parent.prototype) // false
console.log(Child.__proto__ === Parent) // true
console.log(Parent.prototype.__proto__ === Object.prototype) // true

图片来源 轻松理解JS 原型原型链

小练习

  1. parent.proto === Parent.prototype
点击显示答案
true
  1. child.proto === Child.prototype
点击显示答案
true
  1. Child.proto === Parent
点击显示答案
true
  1. Parent.prototype.proto === Object.prototype
点击显示答案
true
  1. Parent.prototype.constructor === Parent
点击显示答案
true
  1. Object.prototype.proto = ?
点击显示答案
null
  1. Function.prototype.proto = ?
点击显示答案
Object.prototype

继承

  • 构造函数继承
1
2
3
4
5
6
7
8
9
10
11
12
13
function Parent(name) {
this.name = name
}
Parent.prototype.say = function() {
console.log(this.name)
}
function Child(name, subName) {

}
// 原型执行Parent,可以访问到Parent的原型上的属性
Child.prototype = new Parent()
// 构造器还是指向自己
Child.prototype.constructor = Child
  • 寄生组合继承
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
function Parent(name) {
this.name = name
}
Parent.prototype.say = function() {
console.log(this.name)
}
function Child(name, subName) {
// 唯一和上面不一样的地方
// 为了能将参数正确赋值给构造函数
Parent.call(this, name)
this.subName = subName
}
// 原型执行Parent,可以访问到Parent的原型上的属性
Child.prototype = new Parent()
// 构造器还是指向自己
Child.prototype.constructor = Child

总结

四准则

  1. js分为函数对象和普通对象,每个对象都有__proto__属性,但是只有函数对象才有prototype属性
  2. Object、Function都是js内置的函数, 类似的还有我们常用到的Array、RegExp、Date、Boolean、Number、String
  3. 属性__proto__是一个对象,它有两个属性,constructor和__proto__
  4. 原型对象prototype有一个默认的constructor属性,用于记录实例是由哪个构造函数创建

结束

结束🔚。

参考资料
轻松理解JS 原型原型链
做了一份前端面试复习计划,保熟~
学习 axios 源码整体架构,打造属于自己的请求库
面不面试的,你都得懂原型和原型链
原型链继承图解


本博客所有文章除特别声明外,均采用 CC BY-SA 4.0 协议 ,转载请注明出处!