详解JavaScript的继承和原型链(prototype)

一、原型链 是什么?

JavaScript 的每个实例对象都有一个指向上一层对象的私有属性(称之为 __proto__) ,上一层对象又指向再上一层对象,就这样层层向上直至 __proto__ 属性为 null ,最后这个对象就是 Object

这种通过 __proto__ 属性来链接对象的方法,称之为 原型链

var o = {a: 1};
// 原型链如下:
// o ---> Object.prototype ---> null

var a = ["yo", "whadup", "?"];
// a ---> Array.prototype ---> Object.prototype ---> null

function f(){
  return 2;
}
// 原型链如下:
// f ---> Function.prototype ---> Object.prototype ---> null

function f() {
  this.a = 1;
  this.b = 2;
}
let o = new f();
f.prototype.b = 3;
f.prototype.c = 4;
// 原型链如下:
// {a:1, b:2} ---> {b:3, c:4} ---> Object.prototype---> null

二、怎么使用?
1、Object.getPrototypeOf() 和 Object.setPrototypeOf()

ECMAScript 6 开始,可以通过 Object.getPrototypeOf() 和 Object.setPrototypeOf() 访问器来访问 __proto__ 属性。

function f() {
  this.a = 1;
  this.b = 2;
}

let o = new f();
// 1、为 f 的上层对象新增属性(推荐此种方式新增属性)
f.prototype.b = 3;
f.prototype.c = 4;

// 2、查看上层对象属性
console.log(Object.getPrototypeOf(o));

// 3、此种方式设置上层对象的属性,会覆盖掉所有旧属性,慎用
console.log(Object.setPrototypeOf(o, {e:5}));

// 4、查看新的上层对象属性
console.log(Object.getPrototypeOf(o));

2、prototype 与 Object.getPrototypeOf

两者功能一致,都是用来访问原型属性,区别是:

  • prototype 用于类,而 Object.getPrototypeOf() 用于实例;
  • 共享:所有实例都可以访问,新增的 prototype 属性。
function A(){
  this.a = "a";
}

let aIns = new A();

// 1、一个用于类型、一个用于实例
console.log(A.prototype);
console.log(Object.getPrototypeOf(aIns));

let bIns = new A();
// 2、共享:所有实例都可以访问 新增的原型属性
A.prototype.b = "b";
console.log(Object.getPrototypeOf(aIns).b);
console.log(Object.getPrototypeOf(bIns).b);
var o = new Foo();

// 上面代码是让 JavaScript 执行了下面代码

var o = new Object();
o.__proto__ = Foo.prototype;
Foo.call(o);

3、hasOwnProperty

当访问一个对象的属性时,它不仅仅在该对象上搜寻,还会搜寻原型链上的所有对象,最后判断属性是否存在。hasOwnProperty 是 JavaScript 中唯一一个不会遍历原型链的方法。

const object1 = {};
object1.property1 = 42;

console.log(object1.hasOwnProperty('property1'));
// expected output: true

console.log(object1.hasOwnProperty('toString'));
// expected output: false

4、undefined

没有赋值的属性是 undefined,所以无法通过 undefined 来判断属性是否存在。


三、继承

继承也是基于原型链的特性。ECMAScript6 引入 class 关键字来实现 类对象,但类对象也是基于原型链。

1、Object.create

用 Object.create 来创建继承对象。

var a = {a: 1};
// a ---> Object.prototype ---> null

var b = Object.create(a);
// b ---> a ---> Object.prototype ---> null
console.log(b.a); // 1 (继承而来)

var c = Object.create(b);
// c ---> b ---> a ---> Object.prototype ---> null

var d = Object.create(null);
// d ---> null
console.log(d.hasOwnProperty); // undefined, 因为d没有继承Object.prototype

四、其他
1、性能

试图访问不存在的属性时会遍历整个原型链,在原型链上查找属性比较耗时,对性能有副作用。


2、错误实践

扩展内置原型会破坏封装,这不是好的解决方案,使用扩展内置原型的唯一理由是支持 JavaScript 引擎的新特性,如 Array.forEach

原生原型不应该被扩展,除非它是为了与新的 JavaScript 特性兼容。


五、参考文档
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值