JS基于原型链的继承和基于类的继承学习总结

1. new关键字创建一个对象的过程

   (1) 创建一个空的简单对象(即{})

(2)为步骤1新创建的对象添加属性_proto_,该属性连接至构造函数的原型对象

(3)将步骤1新创建的对象作为this的上下文

(4)执行new出来的对象指向的函数,如果该函数没有返回对象,则返回this

示例代码如下:

function Bar(name){
    this.name = name;
}

let foo = new Bar('hello');

foo就是我们new出来的对象,它的_proto_指向的就是Bar这个函数,因为Bar函数没有返回值,因此this指向的是new出来的这个对象本身。

2. 基于原型链的继承

2.1 原型链的作用?

个人理解的是,原型链可以帮我们理清楚哪些属性是new出来的对象本身所拥有的,哪些属性是new出来的这个对象通过继承的方式得来的,从而对对象有更清晰的认识,更灵活的使用对象。

对于javaScript的绝大多数对象来说,它们最终的原型都是Object。

2.2 _proto_与prototype

原型链的链接过程也可以称为继承。当谈到继承时,JavaScript 只有一种结构:对象。每个实例对象(object)都有一个私有属性(称之为 __proto__ )指向它的构造函数的原型对象(prototype)。该原型对象也有一个自己的原型对象(__proto__),层层向上直到一个对象的原型对象为 null。根据定义,null 没有原型,并作为这个原型链中的最后一个环节。

我们通过一个例子来说明_proto_与prototype之间的关系:

_proto_是对象A的属性,该属性可以将对象追溯到其构造函数对应的上级原型对象B.prototype上。上级原型B的构造函数自带prototype属性,该属性是一个指针,用于指向该构造函数对应的父级原型对象C。因此prototype.xxx就代表对象C上的一个属性。一般没声明的话prototype指向的就是空。

因此,_proto_与prototype之间的关系为:

A._proto_ = B.prototype

A._proto_._proto_ = B.prototype._proto_ = C.prototype

prototype起着一个连接上下文的作用,帮助对象把整个原型链接起来。

2.3 原型链示例

// 让我们从一个函数里创建一个对象o,它自身拥有属性a和b的:
let f = function () {
   this.a = 1;
   this.b = 2;
}
/* 这么写也一样
function f() {
  this.a = 1;
  this.b = 2;
}
*/
let o = new f(); // {a: 1, b: 2}

// 在f函数的原型上定义属性
f.prototype.b = 3;
f.prototype.c = 4;

// 不要在 f 函数的原型上直接定义 f.prototype = {b:3,c:4};这样会直接打破原型链
// o.[[Prototype]] 有属性 b 和 c
//  (其实就是 o.__proto__ 或者 o.constructor.prototype)
// o.[[Prototype]].[[Prototype]] 是 Object.prototype.
// 最后o.[[Prototype]].[[Prototype]].[[Prototype]]是null
// 这就是原型链的末尾,即 null,
// 根据定义,null 就是没有 [[Prototype]]。

// 综上,整个原型链如下:

// {a:1, b:2} ---> {b:3, c:4} ---> Object.prototype---> null

console.log(o.a); // 1
// a是o的自身属性吗?是的,该属性的值为 1

console.log(o.b); // 2
// b是o的自身属性吗?是的,该属性的值为 2
// 原型上也有一个'b'属性,但是它不会被访问到。
// 这种情况被称为"属性遮蔽 (property shadowing)"

console.log(o.c); // 4
// c是o的自身属性吗?不是,那看看它的原型上有没有
// c是o.[[Prototype]]的属性吗?是的,该属性的值为 4

console.log(o.d); // undefined
// d 是 o 的自身属性吗?不是,那看看它的原型上有没有
// d 是 o.[[Prototype]] 的属性吗?不是,那看看它的原型上有没有
// o.[[Prototype]].[[Prototype]] 为 null,停止搜索
// 找不到 d 属性,返回 undefined

2.4 原型链特性:属性遮蔽

原型链中的属性遮蔽指的是:以上述代码为例,如果对象O拥有一个名为a的属性,而函数f的原型上也拥有一个名为a的属性,那我们在访问O.a的属性值时,是以哪一个值为准?因为原型链的回溯是自底向上,从O开始的。因此当O拥有要访问的a属性时,就不需要继续向上遍历了。因此函数f的原型上的那个名为a的属性就不会被遍历到。这就叫做属性遮蔽

2.5 经典面试题

var F = function(){};

Object.prototype.a = function(){};
Function.prototype.b = function(){};

var f = new F();

问f拥有a和b哪个属性?

通过在浏览器控制台输出,可以得出结论,只拥有a这一个属性。

 

因为f._proto_指向的是一个对象而不是Function,而Object的上一级原型也是Object,该Object的prototype有a属性,所以f只拥有a属性

3. 基于类的继承

基于类的继承其实也是基于原型链的继承的语法糖,是在ES6阶段提出的新概念。个人认为,基于类的继承把原型链的过程描述的更清晰:

class Animals{
    constructor(name) {
        this.name = name;
    }
    greet(){
        console.log(`大家好,我叫${this.name}`);
    }
}

class Cat extends Animals{
    // super用于改变父类的某些属性
    // constructor(props) {
    //     super(props);
    // }
}

class Dog extends Animals{// Dog._proto_为Animals
    
}

const hua = new Cat('花花');//_proto_为Cat,chua._proto_._proto_为Animals
const cao = new Dog('草草');.//_proto_为Dog,cao._proto_._proto_为Animals
hua.greet();//因为子类Cat没有name属性,因此就会向上回溯,调用父类Animals的name属性
cao.greet();//因为子类Dog没有name属性,因此就会向上回溯,调用父类Animals的name属性
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Coding101

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值