30学前端- #笔记# 02原型与原型链

150 阅读3分钟

构造函数、实例、原型对象的三角关系

function Parent(){
    
}
Parent.prototype.num = 1

const son =  new Parent()

定义

  1. Parent 函数是构造函数;
  2. son 是构造函数Parent的实例;
  3. Parent.prototype 是原型对象;

指向

  1. 实例的隐式原型指向构造函数的原型对象: son.__proto__ === Parent.prototype
  2. 构造函数的显式原型也指向原型对象:Parent.prototype
  3. 原型对象的构造器指向构造函数:Parent.prototype.constructor === Parent

拓展

  1. 所有构造函数的__proto__指向Function.prototype
  2. Function.prototype的__proto__ 指向 Object.prototype
  3. Object.prototype的__proto__ 指向null
Function.prototype.__proto__ === Object.prototype
Object.prototype.__proto__ === null
  1. __proto__ 来自与Object.prototype,当使用obj.__proto__时可以理解为Object.getPrototypeOf(obj),获取obj的原型对象。

继承与原型链

定义

继承只有一种数据类型(对象),每一个实例对象都有一个隐式属性(proto)指向它的构造函数的原型对象(prototype)。

实例顺着__proto__一直向上查找的直到(null)过程中串起来一个链条,称之为原型链。

function Parent(){
    this.a = 1;
    this.b = 2;
}
let son = new Parent();
Parent.prototype.b =3;
Parent.prototype.c =4;

以上的原型链就是: {a:1, b:2} ---> {b:3, c:4} ---> Object.prototype---> null

console.log(son.a) // 1
console.log(son.b) // 2
console.log(son.c) // 4 去原型链上查找

// 此时如果删除o.b属性,就会向上查找`f.prototype.b`
delete son.b
console.log(son.b) // 3

原型检测

  • Object.getPrototypeOf(obj) 返回指向对象的原型对象
  • instantof 运算符,检测构造函数的 prototype 属性是否出现在某个实例对象的原型链
Function.prototype === Object.getPrototypeOf(Parent) // true
Object.prototype === Object.getPrototypeOf({}) // true
Parent.prototype === Object.getPrototypeOf(son) // true


son instanceof Object // true
son instanceof Parent // true

巩固(经典问题)

一下习题来自 JS面试题-原型链相关,感谢!

1. 入参的影响

function A() {}
function B(a) {
    this.a = a;
}

function C(a) {
    if (a) {
        this.a = a;
    }
}

A.prototype.a = 1;
B.prototype.a = 1;
C.prototype.a = 1;

console.log(new A().a); // 1
console.log(new B().a); // undefined
console.log(new C(2).a); // 2

分析:new B().a 没有入参,this.a=undefined

2. 各自的原型链

var F = function () {
}
Object.prototype.a = function () {
    console.log('a')
}
Function.prototype.b = function () {
    console.log('b')
}
var f = new F()
console.log(F.a()) // a
console.log(F.b()) // 
console.log(f.a())
console.log(f.b()) // f.b is not a function

分析:

  1. F.__proto__ => Function.prototypeFunction.prototype.__proto__ => Object.prototype F可以找到a、b方法;

  2. f.__proto__ => F.prototype,F.prototype.__proto__=>Object.prototype f函数没有b方法,报错

3. 原型对象属性修改对实例的影响

var A = function () {
}
A.prototype.n = 1
var b = new A()
A.prototype = {
    n: 2,
    m: 3
}
var c = new A()
A.prototype = {
    // 这种写法会清空之前的属性
}
var d = new A()
console.log(b.n, b.m, c.n, c.m, d.n, d.m)

分析:原型对象的属性修改,不改变已经实例化的对象,对之后的生成新的实例有影响。

b.m 、d.n、d.m 输出undefined

4. 原型和构造函数的属性互不干涉

function Fn () {
    this.x = 100
    this.y = 200
    this.getX = function () {
        console.log(this.x)
    }
}
Fn.prototype.getX = function () {
    console.log(this.x)
}
Fn.prototype.getY = function () {
    console.log(this.y)
}
var f1 = new Fn
var f2 = new Fn


// 输出
console.log(f1.getX === f2.getX) // false 
//f1.getX、f2.getX 返回值是实例自身具有的函数,两个函数之间没有相等关系

console.log(f1.getY === f2.getY) // true
//f1.getY、f2.getY 同时指向原型链Fn.prototype.getY函数

console.log(f1.__proto__.getY === Fn.prototype.getY) // true
// 没有疑问,f1.__proto__ === Fn.prototype

console.log(f1.__proto__.getX === f2.getX) // false
// 又是在比较两个函数,false

console.log(f1.__proto__.getX === Fn.prototype.getX) // true
// 同一个函数


console.log(f1.constructor)	// Fn
console.log(Fn.prototype.__proto__.constructor)	// Object
// Fn.prototype.__proto__.constrnctor => Fn.prototype的构造函数是Object

f1.getX() // 100
f1.__proto__.getX()	// undefined 
f2.getY() // 200
Fn.prototype.getY()	// undefined

// Fn.prototype 和 Fn中的this都是指向自己,Fn.prototype取不到Fn中的属性的。

5. this 指向问题(有趣)

function fun () {
    this.a = 0
    this.b = function () {
        console.log(this.a)
    }
}
fun.prototype = {
    b: function () {
        this.a = 20
        console.log(this.a)
    },
    c: function () {
        this.a = 30
        console.log(this.a)
    }
}

var my_fun = new fun()
my_fun.b() // 0
my_fun.a // 0
my_fun.c() // 30 !import
my_fun.a // 30


var my_fun2 = new fun()
console.log(my_fun2.a) // 0
my_fun2.__proto__.c() // 30 !import
console.log(my_fun2.a) // 0
console.log(my_fun2.__proto__.a) // 30

分析: my_fun.c() 执行完毕,this.a = 30将修改 my_fun 函数的a属性为30 my_fun = {a:30;...}

my_fun2.__proto__.c()执行完毕,this.a =30fun.prototype函数添加了一个a=30的属性。